From 14966b8249b9c0bc071ab6125e4f8e499c4a0f4d Mon Sep 17 00:00:00 2001 From: a1 Date: Wed, 31 Jan 2024 19:28:15 +0900 Subject: [PATCH 01/24] =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EC=99=84=EB=A3=8C,=20=EB=A6=AC=ED=8C=A9=ED=84=B0?= =?UTF-8?q?=EB=A7=81(=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 9 ++ package.json | 1 + src/component/admin/EditModal.js | 0 src/component/admin/IntroBox.js | 44 ++++++++ src/component/admin/PaginateComponent.js | 45 ++++++++ src/component/common/ContextMenu.js | 39 +++++++ src/component/common/InOut.js | 80 +++++++++++++++ src/component/common/Login.js | 12 ++- src/modules/LoginSlice.js | 15 +++ src/modules/rootReducer.js | 12 +++ src/page/AdminPage.js | 23 +++-- src/page/CenterPage.js | 41 +------- src/page/DetailPage.js | 6 +- src/page/ManageGenPage.js | 62 +----------- src/page/ManagePage.js | 62 +----------- src/page/ManageRolePage.js | 60 +---------- src/page/ProductPage.js | 124 +---------------------- src/page/QnAPage.js | 62 +----------- src/resource/data/adminInfo.js | 32 ++++++ yarn.lock | 7 +- 20 files changed, 335 insertions(+), 401 deletions(-) create mode 100644 src/component/admin/EditModal.js create mode 100644 src/component/admin/IntroBox.js create mode 100644 src/component/admin/PaginateComponent.js create mode 100644 src/component/common/ContextMenu.js create mode 100644 src/component/common/InOut.js create mode 100644 src/modules/LoginSlice.js create mode 100644 src/resource/data/adminInfo.js diff --git a/package-lock.json b/package-lock.json index edc8a87..7d91e55 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,7 @@ "react-router-dom": "^6.6.2", "react-scripts": "^5.0.1", "redux": "^4.2.1", + "redux-persist": "^6.0.0", "shx": "^0.3.4", "styled-components": "^5.3.6", "swiper": "^9.0.1", @@ -15456,6 +15457,14 @@ "deep-diff": "^0.3.5" } }, + "node_modules/redux-persist": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz", + "integrity": "sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ==", + "peerDependencies": { + "redux": ">4.0.0" + } + }, "node_modules/redux-thunk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", diff --git a/package.json b/package.json index 8342670..a08ee93 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "react-router-dom": "^6.6.2", "react-scripts": "^5.0.1", "redux": "^4.2.1", + "redux-persist": "^6.0.0", "shx": "^0.3.4", "styled-components": "^5.3.6", "swiper": "^9.0.1", diff --git a/src/component/admin/EditModal.js b/src/component/admin/EditModal.js new file mode 100644 index 0000000..e69de29 diff --git a/src/component/admin/IntroBox.js b/src/component/admin/IntroBox.js new file mode 100644 index 0000000..ea735ef --- /dev/null +++ b/src/component/admin/IntroBox.js @@ -0,0 +1,44 @@ +import styled, { css } from 'styled-components'; + +export default function IntroBox({ introInfo }) { + return ( + <> + + {introInfo.title} + {introInfo.description} + + + ); +} + +const IntroDiv = styled.div` + position: relative; + width: 700px; + height: 130px; + background-color: #f2f2f2; + margin: 0 auto 2rem auto; + top: 20px; + border-radius: 20px; + padding-top: 50px; +`; + +const Text = styled.div` + font-style: normal; + text-align: center; + letter-spacing: 1px; + text-transform: uppercase; + color: ${(props) => (props.type === 'title' ? '#424242' : '#848484')}; + font-weight: ${(props) => + props.type === 'top' ? 100 : props.type === 'title' ? 600 : 100}; + margin-bottom: 3px; + white-space: pre-line; + + ${(props) => + props.type === 'title' + ? css` + font-size: ${(props) => props.theme.fontSize.tablet.title}; + ` + : css` + font-size: ${(props) => props.theme.fontSize.tablet.caption}; + `} +`; diff --git a/src/component/admin/PaginateComponent.js b/src/component/admin/PaginateComponent.js new file mode 100644 index 0000000..eded9fb --- /dev/null +++ b/src/component/admin/PaginateComponent.js @@ -0,0 +1,45 @@ +import styled from 'styled-components'; +import { useState } from 'react'; +import Pagination from '../manage/Pagenation'; + +export default function PaginateComponent(data) { + // 페이지네이션을 구현할때 사용합니다. + const [currentPage, setCurrentPage] = useState(1); + const itemsPerPage = 10; + + // 페이지당 데이터를 분할하는 함수입니다. + const paginateData = (data, currentPage, itemsPerPage) => { + const startIndex = (currentPage - 1) * itemsPerPage; + const endIndex = startIndex + itemsPerPage; + return data.slice(startIndex, endIndex); + }; + + const getCurrentPageData = () => { + return paginateData(data, currentPage, itemsPerPage); + }; + + // 페이지 변경 핸들러 + const handlePageChange = (pageNumber) => { + setCurrentPage(pageNumber); + }; + + return ( + <> + + {/* 페이지네이션 컨텐츠 */} + + + + ); +} + +const PaginationContainer = styled.div` + display: flex; + justify-content: center; + margin-top: 20px; /* 조정 가능한 마진 값 */ +`; diff --git a/src/component/common/ContextMenu.js b/src/component/common/ContextMenu.js new file mode 100644 index 0000000..c9f1bd5 --- /dev/null +++ b/src/component/common/ContextMenu.js @@ -0,0 +1,39 @@ +export default function ContextMenu() { + const [contextMenuVisible, setContextMenuVisible] = useState(false); + const [contextMenuPosition, setContextMenuPosition] = useState({ + x: 0, + y: 0, + }); + const contextMenuRef = useRef(null); + + return ( + <> + {contextMenuVisible && ( + + 수정 + 삭제 + + )} + + ); +} + +const ContextMenu = styled.div` + position: absolute; + display: flex; + flex-direction: column; + width: 100px; + height: 100px; + background-color: white; + border-radius: 8px; + border: 1px solid #ccc; + box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2); + z-index: 1000; + color: grey; +`; diff --git a/src/component/common/InOut.js b/src/component/common/InOut.js new file mode 100644 index 0000000..4d0cc81 --- /dev/null +++ b/src/component/common/InOut.js @@ -0,0 +1,80 @@ +import { Link } from 'react-router-dom'; +import logo from '../../resource/img/navbar_logo/logo_black.png'; +import LoginImg from '../../resource/img/login.png'; +import LogOutImg from '../../resource/img/logout.png'; +import { useSelector, useDispatch } from 'react-redux'; +import { useEffect } from 'react'; +import axios from 'axios'; +import styled from 'styled-components'; + +export default function InOut() { + const isLoggedIn = useSelector((state) => state.login.isLoggedIn); + const dispatch = useDispatch(); + + // 로그인 여부 확인 + useEffect(() => { + const storedToken = window.localStorage.getItem('token'); + + if (storedToken) { + axios.defaults.headers.common['X-AUTH-TOKEN'] = storedToken; + } + dispatch({ + type: 'login/setLogin', + payload: { isLoggedIn: storedToken ? true : false }, + }); + }, []); + + const onLogout = () => { + window.localStorage.removeItem('token'); + dispatch({ + type: 'login/setLogin', + payload: { isLoggedIn: false }, + }); + + alert('로그아웃 되었습니다.'); + }; + return ( + + + {isLoggedIn ? ( + + logout + + ) : ( + + + + + + )} + + ); +} + +const LogoImg = styled.img` + margin-right: auto; +`; + +const LoginBtn = styled.button` + border: none; + background-color: transparent; + &:hover { + cursor: pointer; + transition: 0.3s ease-in-out; + opacity: 0.5; + } +`; + +const NavBar = styled.div` + position: absolute; + display: flex; + justify-content: center; + position: relative; + height: 25px; + width: 730px; + margin: 55px auto 10px auto; + + .menu { + margin-left: auto; + } +`; diff --git a/src/component/common/Login.js b/src/component/common/Login.js index 2f3f561..51c39f4 100644 --- a/src/component/common/Login.js +++ b/src/component/common/Login.js @@ -1,12 +1,16 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import styled from 'styled-components'; import axios from 'axios'; import { useNavigate } from 'react-router-dom'; +import { useSelector, useDispatch } from 'react-redux'; export default function Login() { + const navigate = useNavigate(); const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); - const navigate = useNavigate(); + + const isLoggedIn = useSelector((state) => state.login.isLoggedIn); + const dispatch = useDispatch(); const handleUsernameChange = (e) => { setUsername(e.target.value); @@ -34,6 +38,10 @@ export default function Login() { window.localStorage.setItem('token', token); navigate('/admin'); + dispatch({ + type: 'login/setLogin', + payload: { isLoggedIn: true }, + }); }) .catch((err) => { alert('아이디 또는 비밀번호가 틀렸습니다.'); diff --git a/src/modules/LoginSlice.js b/src/modules/LoginSlice.js new file mode 100644 index 0000000..7989f1a --- /dev/null +++ b/src/modules/LoginSlice.js @@ -0,0 +1,15 @@ +import { createSlice } from '@reduxjs/toolkit'; + +export const LoginSlice = createSlice({ + name: 'login', + initialState: { + isLoggedIn: false, + }, + reducers: { + setLogin: (state, action) => { + state.isLoggedIn = action.payload.isLoggedIn; + }, + }, +}); + +export const { setLogin } = LoginSlice.actions; diff --git a/src/modules/rootReducer.js b/src/modules/rootReducer.js index fca352f..4b6b508 100644 --- a/src/modules/rootReducer.js +++ b/src/modules/rootReducer.js @@ -2,11 +2,23 @@ import { homeSlice } from './homeSlice'; import { ourTeamSlice } from './ourTeamSlice'; import { faqSlice } from './faqSlice'; import { ProductSlice } from './ProductSlice'; +import { LoginSlice } from './LoginSlice'; + +// redux-persist를 위해 localStorage를 불러옴 +import { persistReducer } from 'redux-persist'; +import storage from 'redux-persist/lib/storage'; + +const persistConfig = { + key: 'root', + storage, + whitelist: ['home', 'ourTeam', 'faq', 'product', 'login'], +}; const rootReducer = { home: homeSlice.reducer, ourTeam: ourTeamSlice.reducer, faq: faqSlice.reducer, product: ProductSlice.reducer, + login: LoginSlice.reducer, }; export default rootReducer; diff --git a/src/page/AdminPage.js b/src/page/AdminPage.js index 9f0efec..8937be0 100644 --- a/src/page/AdminPage.js +++ b/src/page/AdminPage.js @@ -1,19 +1,14 @@ import styled, { css } from 'styled-components'; -import { HiBars3 } from 'react-icons/hi2'; import { Link } from 'react-router-dom'; -import logo from '../resource/img/navbar_logo/logo_black.png'; -import { useState } from 'react'; +import { useSelector, useDispatch } from 'react-redux'; +import { useEffect } from 'react'; import axios from 'axios'; +import InOut from '../component/common/InOut'; export default function AdminPage() { return ( <> - - - - 로그인 - - + {'홈페이지 대시보드'} {'환영합니다.'} @@ -50,7 +45,15 @@ const LogoImg = styled.img` margin-right: auto; `; -const LoginBtn = styled.button``; +const LoginBtn = styled.button` + border: none; + background-color: transparent; + &:hover { + cursor: pointer; + transition: 0.3s ease-in-out; + opacity: 0.5; + } +`; const MenuText = styled.div` text-align: left; diff --git a/src/page/CenterPage.js b/src/page/CenterPage.js index 177ee27..ae16411 100644 --- a/src/page/CenterPage.js +++ b/src/page/CenterPage.js @@ -1,31 +1,11 @@ import styled, { css } from 'styled-components'; -import axios from 'axios'; import { Link } from 'react-router-dom'; -import logo from '../resource/img/navbar_logo/logo_black.png'; -import { useState } from 'react'; +import InOut from '../component/common/InOut'; export default function AdminPage() { - const [login, setLogin] = useState(false); - - const onClick = async () => { - const response = await axios - .post('https://server.inuappcenter.kr/sign/sign-in', { - id: 'appcenter', - password: '1q2w3e4r!Appcenter', - }) - .then((res) => { - axios.defaults.headers.common['X-AUTH-TOKEN'] = res.data.token; - }) - .then(setLogin(true)); - }; return ( <> - - logo - - + {'앱센터 동아리원 관리'} @@ -109,23 +89,6 @@ const InfoBox = styled.div` } `; -const NavBar = styled.div` - position: absolute; - display: flex; - justify-content: center; - position: relative; - height: 25px; - width: 730px; - margin: 45px auto 0 auto; - - .menu { - margin-left: auto; - border: none; - background-color: #fff; - size: 1.5rem; - } -`; - const IntroBox = styled.div` position: relative; width: 700px; diff --git a/src/page/DetailPage.js b/src/page/DetailPage.js index 398241d..c3d1336 100644 --- a/src/page/DetailPage.js +++ b/src/page/DetailPage.js @@ -5,6 +5,7 @@ import React, { useState, useEffect, useRef } from 'react'; import Modal from 'react-modal'; // react-modal 라이브러리 import import Pagination from '../component/manage/Pagenation'; import logo from '../resource/img/navbar_logo/logo_black.png'; +import InOut from '../component/common/InOut'; export default function DetailPage() { const [data, setData] = useState([]); @@ -208,10 +209,7 @@ export default function DetailPage() { }; return ( <> - - logo - - + {'동아리원 관리'} diff --git a/src/page/ManageGenPage.js b/src/page/ManageGenPage.js index bb4947f..179d84b 100644 --- a/src/page/ManageGenPage.js +++ b/src/page/ManageGenPage.js @@ -1,10 +1,11 @@ import styled, { css } from 'styled-components'; -import { HiBars3 } from 'react-icons/hi2'; import axios from 'axios'; import React, { useState, useEffect, useRef } from 'react'; import Modal from 'react-modal'; // react-modal 라이브러리 import import Pagination from '../component/manage/Pagenation'; -import logo from '../resource/img/navbar_logo/logo_black.png'; +import InOut from '../component/common/InOut'; +import IntroBox from '../component/admin/IntroBox'; +import { introInfo } from '../resource/data/adminInfo'; export default function ManageGenPage() { const [data, setData] = useState([]); @@ -182,16 +183,8 @@ export default function ManageGenPage() { return ( <> - - logo - - - - {'기수 관리'} - - {'기수에 역할과 동아리원을 추가, 삭제, 수정을 할 수 있어요'} - - + + 편성 목록 @@ -525,48 +518,3 @@ const MemberList = styled.div` margin-left: auto; } `; - -const NavBar = styled.div` - position: absolute; - display: flex; - position: relative; - height: 25px; - width: 730px; - margin: 45px auto 0 auto; - - .menu { - margin-left: auto; - } -`; - -const IntroBox = styled.div` - position: relative; - width: 700px; - height: 130px; - background-color: #f2f2f2; - margin: 0 auto 2rem auto; - top: 20px; - border-radius: 20px; - padding-top: 50px; -`; - -const Text = styled.div` - font-style: normal; - text-align: center; - letter-spacing: 1px; - text-transform: uppercase; - color: ${(props) => (props.type === 'title' ? '#424242' : '#848484')}; - font-weight: ${(props) => - props.type === 'top' ? 100 : props.type === 'title' ? 600 : 100}; - margin-bottom: 3px; - white-space: pre-line; - - ${(props) => - props.type === 'title' - ? css` - font-size: ${(props) => props.theme.fontSize.tablet.title}; - ` - : css` - font-size: ${(props) => props.theme.fontSize.tablet.caption}; - `} -`; diff --git a/src/page/ManagePage.js b/src/page/ManagePage.js index 8017bfd..918da60 100644 --- a/src/page/ManagePage.js +++ b/src/page/ManagePage.js @@ -1,10 +1,11 @@ import styled, { css } from 'styled-components'; -import { HiBars3 } from 'react-icons/hi2'; import axios from 'axios'; import React, { useState, useEffect, useRef } from 'react'; import Modal from 'react-modal'; // react-modal 라이브러리 import import Pagination from '../component/manage/Pagenation'; -import logo from '../resource/img/navbar_logo/logo_black.png'; +import InOut from '../component/common/InOut'; +import IntroBox from '../component/admin/IntroBox'; +import { introInfo } from '../resource/data/adminInfo'; export default function ManagePage() { const [data, setData] = useState([]); @@ -208,16 +209,8 @@ export default function ManagePage() { }; return ( <> - - logo - - - - {'동아리원 관리'} - - {'동아리원 추가, 삭제, 수정을 할 수 있어요'} - - + + 동아리원 목록 {loading &&
loading...
} @@ -605,48 +598,3 @@ const MemberList = styled.div` margin-left: auto; } `; - -const NavBar = styled.div` - position: absolute; - display: flex; - position: relative; - height: 25px; - width: 730px; - margin: 45px auto 0 auto; - - .menu { - margin-left: auto; - } -`; - -const IntroBox = styled.div` - position: relative; - width: 700px; - height: 130px; - background-color: #f2f2f2; - margin: 0 auto 2rem auto; - top: 20px; - border-radius: 20px; - padding-top: 50px; -`; - -const Text = styled.div` - font-style: normal; - text-align: center; - letter-spacing: 1px; - text-transform: uppercase; - color: ${(props) => (props.type === 'title' ? '#424242' : '#848484')}; - font-weight: ${(props) => - props.type === 'top' ? 100 : props.type === 'title' ? 600 : 100}; - margin-bottom: 3px; - white-space: pre-line; - - ${(props) => - props.type === 'title' - ? css` - font-size: ${(props) => props.theme.fontSize.tablet.title}; - ` - : css` - font-size: ${(props) => props.theme.fontSize.tablet.caption}; - `} -`; diff --git a/src/page/ManageRolePage.js b/src/page/ManageRolePage.js index b105e86..833fe31 100644 --- a/src/page/ManageRolePage.js +++ b/src/page/ManageRolePage.js @@ -1,10 +1,11 @@ import styled, { css } from 'styled-components'; -import { HiBars3 } from 'react-icons/hi2'; import axios from 'axios'; import React, { useState, useEffect, useRef } from 'react'; import Modal from 'react-modal'; // react-modal 라이브러리 import import Pagination from '../component/manage/Pagenation'; -import logo from '../resource/img/navbar_logo/logo_black.png'; +import InOut from '../component/common/InOut'; +import IntroBox from '../component/admin/IntroBox'; +import { introInfo } from '../resource/data/adminInfo'; export default function ManageRolePage() { const [data, setData] = useState([]); @@ -177,14 +178,8 @@ export default function ManageRolePage() { return ( <> - - logo - - - - {'역할 관리'} - {'역할 추가, 삭제, 수정을 할 수 있어요'} - + + 역할 목록 @@ -512,48 +507,3 @@ const MemberList = styled.div` margin-left: auto; } `; - -const NavBar = styled.div` - position: absolute; - display: flex; - position: relative; - height: 25px; - width: 730px; - margin: 45px auto 0 auto; - - .menu { - margin-left: auto; - } -`; - -const IntroBox = styled.div` - position: relative; - width: 700px; - height: 130px; - background-color: #f2f2f2; - margin: 0 auto 2rem auto; - top: 20px; - border-radius: 20px; - padding-top: 50px; -`; - -const Text = styled.div` - font-style: normal; - text-align: center; - letter-spacing: 1px; - text-transform: uppercase; - color: ${(props) => (props.type === 'title' ? '#424242' : '#848484')}; - font-weight: ${(props) => - props.type === 'top' ? 100 : props.type === 'title' ? 600 : 100}; - margin-bottom: 3px; - white-space: pre-line; - - ${(props) => - props.type === 'title' - ? css` - font-size: ${(props) => props.theme.fontSize.tablet.title}; - ` - : css` - font-size: ${(props) => props.theme.fontSize.tablet.caption}; - `} -`; diff --git a/src/page/ProductPage.js b/src/page/ProductPage.js index 838f98d..15f80e4 100644 --- a/src/page/ProductPage.js +++ b/src/page/ProductPage.js @@ -1,15 +1,15 @@ import styled, { css } from 'styled-components'; -import { HiBars3 } from 'react-icons/hi2'; import axios from 'axios'; import React, { useState, useEffect, useRef } from 'react'; -import Modal from 'react-modal'; // react-modal 라이브러리 import import Pagination from '../component/manage/Pagenation'; -import logo from '../resource/img/navbar_logo/logo_black.png'; import RegisModal from '../container/product/RegisModal'; import { RMopen, MODopen } from '../modules/ProductSlice'; import { useSelector, useDispatch } from 'react-redux'; import { useCallback } from 'react'; import ModifyModal from '../container/product/ModifyModal'; +import InOut from '../component/common/InOut'; +import IntroBox from '../component/admin/IntroBox'; +import { introInfo } from '../resource/data/adminInfo'; export default function ProductPage() { const [data, setData] = useState([]); @@ -147,16 +147,8 @@ export default function ProductPage() { }; return ( <> - - logo - - - - {'앱 관리'} - - {'홈페이지에 게재된 앱 정보와 목록을 관리할 수 있어요'} - - + + 앱 목록 {loading &&
loading...
} @@ -250,67 +242,6 @@ const PaginationContainer = styled.div` margin-top: 20px; /* 조정 가능한 마진 값 */ `; -const ModalContainer = styled(Modal)` - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - background-color: #fff; - border-radius: 8px; - border: 2px solid #5858fa; - padding: 20px; - max-width: 400px; - margin: 0 auto; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -`; - -const ModalTitle = styled.h2` - font-size: 1.5rem; - margin-bottom: 15px; -`; - -const ModalLabel = styled.label` - font-size: 1rem; - margin-bottom: 5px; -`; - -const ModalInput = styled.input` - width: 100%; - padding: 8px; - margin-bottom: 15px; - border: 1px solid #ccc; - border-radius: 4px; - font-size: 1rem; -`; - -const ModalButtonWrapper = styled.div` - display: flex; - justify-content: space-between; - margin-top: 15px; -`; - -const ModalButton = styled.button` - background-color: #5858fa; - color: #fff; - border: none; - border-radius: 4px; - padding: 8px 16px; - font-size: 1rem; - cursor: pointer; - transition: background-color 0.2s ease-in-out; - - & + & { - margin: 0 10px; - } - - &:hover { - background-color: #8181f7; - } -`; - const MenuItem = styled.div` display: flex; align-items: center; @@ -398,48 +329,3 @@ const MemberList = styled.div` margin-left: auto; } `; - -const NavBar = styled.div` - position: absolute; - display: flex; - position: relative; - height: 25px; - width: 730px; - margin: 45px auto 0 auto; - - .menu { - margin-left: auto; - } -`; - -const IntroBox = styled.div` - position: relative; - width: 700px; - height: 130px; - background-color: #f2f2f2; - margin: 0 auto 2rem auto; - top: 20px; - border-radius: 20px; - padding-top: 50px; -`; - -const Text = styled.div` - font-style: normal; - text-align: center; - letter-spacing: 1px; - text-transform: uppercase; - color: ${(props) => (props.type === 'title' ? '#424242' : '#848484')}; - font-weight: ${(props) => - props.type === 'top' ? 100 : props.type === 'title' ? 600 : 100}; - margin-bottom: 3px; - white-space: pre-line; - - ${(props) => - props.type === 'title' - ? css` - font-size: ${(props) => props.theme.fontSize.tablet.title}; - ` - : css` - font-size: ${(props) => props.theme.fontSize.tablet.caption}; - `} -`; diff --git a/src/page/QnAPage.js b/src/page/QnAPage.js index d53f4ae..9fb32c9 100644 --- a/src/page/QnAPage.js +++ b/src/page/QnAPage.js @@ -1,10 +1,11 @@ import styled, { css } from 'styled-components'; -import { HiBars3 } from 'react-icons/hi2'; import axios from 'axios'; import React, { useState, useEffect, useRef } from 'react'; import Modal from 'react-modal'; // react-modal 라이브러리 import import Pagination from '../component/manage/Pagenation'; -import logo from '../resource/img/navbar_logo/logo_black.png'; +import InOut from '../component/common/InOut'; +import IntroBox from '../component/admin/IntroBox'; +import { introInfo } from '../resource/data/adminInfo'; export default function QnAPage() { const [data, setData] = useState([]); @@ -185,16 +186,8 @@ export default function QnAPage() { }; return ( <> - - logo - - - - {'질문 관리'} - - {'질문과 답변을 추가, 삭제, 수정을 할 수 있어요'} - - + + 질문 및 답변 목록 {loading &&
loading...
} @@ -521,48 +514,3 @@ const MemberList = styled.div` margin-left: auto; } `; - -const NavBar = styled.div` - position: absolute; - display: flex; - position: relative; - height: 25px; - width: 730px; - margin: 45px auto 0 auto; - - .menu { - margin-left: auto; - } -`; - -const IntroBox = styled.div` - position: relative; - width: 700px; - height: 130px; - background-color: #f2f2f2; - margin: 0 auto 2rem auto; - top: 20px; - border-radius: 20px; - padding-top: 50px; -`; - -const Text = styled.div` - font-style: normal; - text-align: center; - letter-spacing: 1px; - text-transform: uppercase; - color: ${(props) => (props.type === 'title' ? '#424242' : '#848484')}; - font-weight: ${(props) => - props.type === 'top' ? 100 : props.type === 'title' ? 600 : 100}; - margin-bottom: 3px; - white-space: pre-line; - - ${(props) => - props.type === 'title' - ? css` - font-size: ${(props) => props.theme.fontSize.tablet.title}; - ` - : css` - font-size: ${(props) => props.theme.fontSize.tablet.caption}; - `} -`; diff --git a/src/resource/data/adminInfo.js b/src/resource/data/adminInfo.js new file mode 100644 index 0000000..b7cb20a --- /dev/null +++ b/src/resource/data/adminInfo.js @@ -0,0 +1,32 @@ +export const introInfo = [ + { + key: 0, + title: '동아리원 관리', + description: '동아리원 추가, 삭제, 수정을 할 수 있어요', + }, + { + key: 1, + title: '기수 관리', + description: '동아리원을 기수에 추가, 삭제, 수정을 할 수 있어요', + }, + { + key: 2, + title: '역할 관리', + description: '역할군을 관리할 수 있어요', + }, + { + key: 3, + title: '전체', + description: '홈페이지에 게재된 앱 정보와 목록을 관리할 수 있어요', + }, + { + key: 4, + title: '질문 관리', + description: '질문과 답변을 추가, 삭제, 수정을 할 수 있어요', + }, + { + key: 5, + title: '앱 관리', + description: '홈페이지에 게재된 앱 정보와 목록을 관리할 수 있어요', + }, +]; diff --git a/yarn.lock b/yarn.lock index d5e1c81..cc6efe4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8348,12 +8348,17 @@ redux-logger@^3.0.6: dependencies: deep-diff "^0.3.5" +redux-persist@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/redux-persist/-/redux-persist-6.0.0.tgz" + integrity sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ== + redux-thunk@^2.4.2: version "2.4.2" resolved "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz" integrity sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q== -redux@^4, redux@^4.2.0, redux@^4.2.1: +redux@^4, redux@^4.2.0, redux@^4.2.1, redux@>4.0.0: version "4.2.1" resolved "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz" integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w== From 7956be26d40673359ee333cea4a6fb2dfaf02354 Mon Sep 17 00:00:00 2001 From: a1 Date: Thu, 1 Feb 2024 10:48:25 +0900 Subject: [PATCH 02/24] Jungwoo/Login --- src/component/admin/MemberTable.js | 105 +++++++++++++++++++++++++++++ src/component/common/InOut.js | 8 ++- src/component/common/Login.js | 5 -- src/component/home/Slogan.js | 4 +- src/page/ManageGenPage.js | 2 +- src/page/ManagePage.js | 2 +- src/page/ManageRolePage.js | 23 ------- src/page/ProductPage.js | 2 +- src/page/QnAPage.js | 2 +- 9 files changed, 118 insertions(+), 35 deletions(-) create mode 100644 src/component/admin/MemberTable.js diff --git a/src/component/admin/MemberTable.js b/src/component/admin/MemberTable.js new file mode 100644 index 0000000..e2deac5 --- /dev/null +++ b/src/component/admin/MemberTable.js @@ -0,0 +1,105 @@ +import styled from 'styled-components'; +import { useState } from 'react'; +import Pagination from '../manage/Pagenation'; + +export default function MemberTable(data, loading) { + const [contextMenuVisible, setContextMenuVisible] = useState(false); + const [contextMenuPosition, setContextMenuPosition] = useState({ + x: 0, + y: 0, + }); + const [selectedMemberId, setSelectedMemberId] = useState(null); + const contextMenuRef = useRef(null); + + // 페이지네이션을 구현할때 사용합니다. + const [currentPage, setCurrentPage] = useState(1); + const itemsPerPage = 10; + + // 페이지당 데이터를 분할하는 함수입니다. + const paginateData = (data, currentPage, itemsPerPage) => { + const startIndex = (currentPage - 1) * itemsPerPage; + const endIndex = startIndex + itemsPerPage; + return data.slice(startIndex, endIndex); + }; + + const getCurrentPageData = () => { + return paginateData(data, currentPage, itemsPerPage); + }; + + // 페이지 변경 핸들러 + const handlePageChange = (pageNumber) => { + setCurrentPage(pageNumber); + }; + return ( + <> + + {loading &&
loading...
} + + {getCurrentPageData().map((content) => ( + { + e.preventDefault(); + setSelectedQnaId(content.id); + setContextMenuPosition({ + x: e.clientX, + y: e.clientY, + }); + setContextMenuVisible(true); + console.log(content.id); + }} + > + {content.part} + {content.question} + {content.answer} + + ))} + +
+ + {/* 페이지네이션 컨텐츠 */} + + + + ); +} + +const PaginationContainer = styled.div` + display: flex; + justify-content: center; + margin-top: 20px; /* 조정 가능한 마진 값 */ +`; + +const MemberTable = styled.table` + width: 700px; + border-collapse: collapse; + margin: 20px auto 20px auto; + + th, + td { + padding: 5px; + text-align: center; + } + + th { + font-weight: 700; + } + + a { + color: #0078d4; + text-decoration: none; + } + + tr { + border-radius: 20%; + } + + tr:hover { + background-color: #f2f2f2; + } +`; diff --git a/src/component/common/InOut.js b/src/component/common/InOut.js index 4d0cc81..53e256d 100644 --- a/src/component/common/InOut.js +++ b/src/component/common/InOut.js @@ -11,7 +11,7 @@ export default function InOut() { const isLoggedIn = useSelector((state) => state.login.isLoggedIn); const dispatch = useDispatch(); - // 로그인 여부 확인 + // 로그인 여부 확인. 로컬 스토리지에 키가 있으면 로그인 상태로 설정 useEffect(() => { const storedToken = window.localStorage.getItem('token'); @@ -33,6 +33,12 @@ export default function InOut() { alert('로그아웃 되었습니다.'); }; + + // 프로젝트 종료시 로그아웃 + window.addEventListener('beforeunload', function (e) { + onLogout(); + }); + return ( diff --git a/src/component/common/Login.js b/src/component/common/Login.js index 51c39f4..9e44162 100644 --- a/src/component/common/Login.js +++ b/src/component/common/Login.js @@ -20,11 +20,6 @@ export default function Login() { setPassword(e.target.value); }; - const handleSubmit = (e) => { - e.preventDefault(); - // Add your login logic here - }; - // id : 'appcenter', pw : '1q2w3e4r!Appcenter' const onClick = async () => { const response = await axios diff --git a/src/component/home/Slogan.js b/src/component/home/Slogan.js index b8e79ce..f22f9ed 100644 --- a/src/component/home/Slogan.js +++ b/src/component/home/Slogan.js @@ -44,7 +44,7 @@ const Text = styled.div` margin-bottom: ${viewWidthCalc(24)}; white-space: pre-line; word-break: keep-all; - ${(props) => + ${(props = props.type === 'title' ? css` font-size: ${(props) => @@ -85,5 +85,5 @@ const Text = styled.div` font-size: ${(props) => props.theme.fontSize.mobile.caption}; } - `} + `)} `; diff --git a/src/page/ManageGenPage.js b/src/page/ManageGenPage.js index 179d84b..abf4ada 100644 --- a/src/page/ManageGenPage.js +++ b/src/page/ManageGenPage.js @@ -319,7 +319,7 @@ export default function ManageGenPage() { const PaginationContainer = styled.div` display: flex; justify-content: center; - margin-top: 20px; /* 조정 가능한 마진 값 */ + margin-top: 20px; `; const ModalContainer = styled(Modal)` diff --git a/src/page/ManagePage.js b/src/page/ManagePage.js index 918da60..2dddf16 100644 --- a/src/page/ManagePage.js +++ b/src/page/ManagePage.js @@ -401,7 +401,7 @@ export default function ManagePage() { const PaginationContainer = styled.div` display: flex; justify-content: center; - margin-top: 20px; /* 조정 가능한 마진 값 */ + margin-top: 20px; `; const ModalContainer = styled(Modal)` diff --git a/src/page/ManageRolePage.js b/src/page/ManageRolePage.js index 833fe31..2520290 100644 --- a/src/page/ManageRolePage.js +++ b/src/page/ManageRolePage.js @@ -275,29 +275,6 @@ export default function ManageRolePage() { ); } -const AddBox = styled.div` - display: flex; - font-size: 13px; - margin: 0 auto; - - width: 340px; - height: 40px; - - border-radius: 8px; - align-items: center; - justify-content: center; - text-align: center; -`; - -const AddInfo = styled.div` - padding-right: 4.5rem; - padding-left: 5rem; - - :nth-child(2) { - padding-left: 4rem; - } -`; - const PaginationContainer = styled.div` display: flex; justify-content: center; diff --git a/src/page/ProductPage.js b/src/page/ProductPage.js index 15f80e4..5022709 100644 --- a/src/page/ProductPage.js +++ b/src/page/ProductPage.js @@ -239,7 +239,7 @@ const AppImage = styled.img` const PaginationContainer = styled.div` display: flex; justify-content: center; - margin-top: 20px; /* 조정 가능한 마진 값 */ + margin-top: 20px; `; const MenuItem = styled.div` diff --git a/src/page/QnAPage.js b/src/page/QnAPage.js index 9fb32c9..7d7d3ef 100644 --- a/src/page/QnAPage.js +++ b/src/page/QnAPage.js @@ -305,7 +305,7 @@ export default function QnAPage() { const PaginationContainer = styled.div` display: flex; justify-content: center; - margin-top: 20px; /* 조정 가능한 마진 값 */ + margin-top: 20px; `; const ModalContainer = styled(Modal)` From 21b265026f52f6a41e92718ba83ce37dde0ece60 Mon Sep 17 00:00:00 2001 From: a1 Date: Thu, 1 Feb 2024 10:49:01 +0900 Subject: [PATCH 03/24] =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/home/Slogan.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/component/home/Slogan.js b/src/component/home/Slogan.js index f22f9ed..b8e79ce 100644 --- a/src/component/home/Slogan.js +++ b/src/component/home/Slogan.js @@ -44,7 +44,7 @@ const Text = styled.div` margin-bottom: ${viewWidthCalc(24)}; white-space: pre-line; word-break: keep-all; - ${(props = + ${(props) => props.type === 'title' ? css` font-size: ${(props) => @@ -85,5 +85,5 @@ const Text = styled.div` font-size: ${(props) => props.theme.fontSize.mobile.caption}; } - `)} + `} `; From 8354a8c1851dcdb785b7393f8e784ae260e696c9 Mon Sep 17 00:00:00 2001 From: a1 Date: Fri, 2 Feb 2024 15:26:07 +0900 Subject: [PATCH 04/24] =?UTF-8?q?style:=20=EA=B4=80=EB=A6=AC=EC=9E=90=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=93=B1=EB=A1=9D=20=EB=AA=A8?= =?UTF-8?q?=EB=8B=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/common/InOut.js | 11 +- src/component/common/Login.js | 60 ++++-- src/container/product/GenRegis.js | 171 +++++++++++++++ src/container/product/ManageRegis.js | 204 ++++++++++++++++++ .../{RegisModal.js => ProductRegis.js} | 2 +- src/container/product/QnARegis.js | 161 ++++++++++++++ src/container/product/RoleRegis.js | 147 +++++++++++++ src/page/ManageGenPage.js | 97 +++------ src/page/ManagePage.js | 140 +++--------- src/page/ManageRolePage.js | 88 +++----- src/page/ProductPage.js | 4 +- src/page/QnAPage.js | 98 +++------ src/resource/img/Login_logo.png | Bin 0 -> 27730 bytes src/resource/style/GlobalStyle.js | 2 +- 14 files changed, 859 insertions(+), 326 deletions(-) create mode 100644 src/container/product/GenRegis.js create mode 100644 src/container/product/ManageRegis.js rename src/container/product/{RegisModal.js => ProductRegis.js} (99%) create mode 100644 src/container/product/QnARegis.js create mode 100644 src/container/product/RoleRegis.js create mode 100644 src/resource/img/Login_logo.png diff --git a/src/component/common/InOut.js b/src/component/common/InOut.js index 53e256d..11890bd 100644 --- a/src/component/common/InOut.js +++ b/src/component/common/InOut.js @@ -13,7 +13,7 @@ export default function InOut() { // 로그인 여부 확인. 로컬 스토리지에 키가 있으면 로그인 상태로 설정 useEffect(() => { - const storedToken = window.localStorage.getItem('token'); + const storedToken = window.sessionStorage.getItem('token'); if (storedToken) { axios.defaults.headers.common['X-AUTH-TOKEN'] = storedToken; @@ -25,20 +25,15 @@ export default function InOut() { }, []); const onLogout = () => { - window.localStorage.removeItem('token'); + window.sessionStorage.removeItem('token'); dispatch({ type: 'login/setLogin', payload: { isLoggedIn: false }, }); - + window.location.reload(); alert('로그아웃 되었습니다.'); }; - // 프로젝트 종료시 로그아웃 - window.addEventListener('beforeunload', function (e) { - onLogout(); - }); - return ( diff --git a/src/component/common/Login.js b/src/component/common/Login.js index 9e44162..eb245ff 100644 --- a/src/component/common/Login.js +++ b/src/component/common/Login.js @@ -3,6 +3,7 @@ import styled from 'styled-components'; import axios from 'axios'; import { useNavigate } from 'react-router-dom'; import { useSelector, useDispatch } from 'react-redux'; +import LoginLogo from '../../resource/img/Login_logo.png'; export default function Login() { const navigate = useNavigate(); @@ -31,12 +32,12 @@ export default function Login() { const { token } = res.data; axios.defaults.headers.common['X-AUTH-TOKEN'] = token; - window.localStorage.setItem('token', token); - navigate('/admin'); + window.sessionStorage.setItem('token', token); dispatch({ type: 'login/setLogin', payload: { isLoggedIn: true }, }); + navigate(-1); }) .catch((err) => { alert('아이디 또는 비밀번호가 틀렸습니다.'); @@ -48,34 +49,48 @@ export default function Login() { return ( - 로그인 -
+ + 홈페이지 대시보드 + - -
+ +
); } +const InfoBox = styled.div` + padding-right: 7rem; +`; + +const LoginImg = styled.img` + width: 100px; + height: 100px; + margin-bottom: 1rem; +`; + const LoginBox = styled.div` - border: 2px solid black; padding: 2rem 0; + background-color: #444345; + border-radius: 8px; `; const Container = styled.div` @@ -88,27 +103,42 @@ const Container = styled.div` width: 500px; height: 1000px; + + margin-top: 0; `; const Title = styled.h1` - color: #333; + color: #fff; + margin-top: 0; `; const Label = styled.label` display: block; - margin-bottom: 10px; + margin-bottom: 5px; margin-right: 2px; + + &:nth-child(1) { + margin-left: 8px; + } `; const Input = styled.input` padding: 5px; - margin-bottom: 10px; + margin: 0 0 0 10px; + border-radius: 6px; + width: 60%; + + ${(props) => props.type === 'id' && `width: 61%;`} `; const Button = styled.button` - padding: 10px 20px; - background-color: #333; + position: absolute; + padding: 24px 24px; + background-color: #1e88e5; + border-radius: 8px; color: #fff; border: none; cursor: pointer; + margin-top: -4.4rem; + margin-left: 9.5rem; `; diff --git a/src/container/product/GenRegis.js b/src/container/product/GenRegis.js new file mode 100644 index 0000000..6786194 --- /dev/null +++ b/src/container/product/GenRegis.js @@ -0,0 +1,171 @@ +import { useState, useEffect, useCallback } from 'react'; +import styled from 'styled-components'; +import axios from 'axios'; +import Modal from 'react-modal'; // react-modal 라이브러리 import +import { RMopen, RMclose } from '../../modules/ProductSlice'; +import { useDispatch, useSelector } from 'react-redux'; +import _ from 'lodash'; + +export default function GenRegis() { + const [data, setData] = useState([]); + + // 상태관리 관련 + const dispatch = useDispatch(); + const regisModalOpen = useSelector((state) => state.product.regisModalOpen); + + // 새 멤버 추가 입력받을 상태 변수 + const [newRole, setNewRole] = useState({ + role_id: '', + member_id: '', + part: '', + year: 15, + }); + + const addData = async () => { + try { + const result = await axios.post( + `https://server.inuappcenter.kr/groups?member_id=${newRole.member_id}&role_id=${newRole.role_id}`, + newRole + ); + console.log('Success:', result.data); + + // POST 요청 성공 시, 새로운 역할을 data 상태 변수에 추가합니다. + setData([...data, result.data]); + + setNewRole({ + role_id: '', + member_id: '', + part: '', + year: 16, + }); + dispatch(RMclose()); + } catch (error) { + console.error('Error adding data:', error); + } + }; + + // 모달을 닫아주고 스크롤을 풀어줌. + const closeModal = () => { + dispatch(RMclose()); + openScroll(); + }; + + const openScroll = useCallback(() => { + document.body.style.removeProperty('overflow'); + }, []); + + return ( + <> + + 편성 추가 + 동아리원 ID + + setNewRole({ ...newRole, member_id: e.target.value }) + } + /> + 역할 ID + + setNewRole({ ...newRole, role_id: e.target.value }) + } + /> + 파트명 + + setNewRole({ ...newRole, part: e.target.value }) + } + /> + 기수 + + setNewRole({ ...newRole, year: e.target.value }) + } + /> + + 등록 + 취소 + + + ; + + ); +} + +const ModalContainer = styled(Modal)` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background-color: #fff; + border-radius: 8px; + border: 2px solid grey; + padding: 20px; + width: 500px; + margin: 0 auto; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +`; + +const ModalTitle = styled.h2` + font-size: 1.5rem; + margin-bottom: 15px; +`; + +const ModalLabel = styled.label` + font-size: 1rem; + margin-bottom: 5px; +`; + +const ModalInput = styled.input` + width: 70%; + padding: 8px; + margin-bottom: 15px; + border: 1px solid #ccc; + border-radius: 4px; + font-size: 1rem; + text-align: center; +`; + +const ModalButtonWrapper = styled.div` + display: flex; + justify-content: space-between; + margin-top: 15px; +`; + +const ModalButton = styled.button` + background-color: grey; + color: #fff; + border: none; + border-radius: 4px; + padding: 8px 16px; + font-size: 1rem; + cursor: pointer; + transition: background-color 0.2s ease-in-out; + + & + & { + margin: 0 10px; + } + + &:hover { + background-color: #8181f7; + } +`; diff --git a/src/container/product/ManageRegis.js b/src/container/product/ManageRegis.js new file mode 100644 index 0000000..12d10a6 --- /dev/null +++ b/src/container/product/ManageRegis.js @@ -0,0 +1,204 @@ +import { useState, useEffect, useCallback } from 'react'; +import styled from 'styled-components'; +import axios from 'axios'; +import Modal from 'react-modal'; // react-modal 라이브러리 import +import { RMopen, RMclose } from '../../modules/ProductSlice'; +import { useDispatch, useSelector } from 'react-redux'; +import _ from 'lodash'; + +export default function ManageRegis() { + const [data, setData] = useState([]); + + // 상태관리 관련 + const dispatch = useDispatch(); + const regisModalOpen = useSelector((state) => state.product.regisModalOpen); + + // 새 멤버 추가 입력받을 상태 변수 + const [newMember, setNewMember] = useState({ + member_id: '', + name: '', + description: '', + profileImage: '', + blogLink: '', + email: '', + gitRepositoryLink: '', + }); + + const addData = async () => { + try { + const result = await axios.post( + 'https://server.inuappcenter.kr/members', + newMember + ); + console.log('Success:', result.data); + + // POST 요청 성공 시, 새로운 동아리원을 data 상태 변수에 추가합니다. + setData([...data, result.data]); + + setNewMember({ + member_id: '', + name: '', + description: '', + profileImage: '', + blogLink: '', + email: '', + gitRepositoryLink: '', + }); + dispatch(RMclose()); + } catch (error) { + console.error('Error adding data:', error); + } + }; + + // 모달을 닫아주고 스크롤을 풀어줌. + const closeModal = () => { + dispatch(RMclose()); + openScroll(); + }; + + const openScroll = useCallback(() => { + document.body.style.removeProperty('overflow'); + }, []); + + return ( + <> + + 회원 등록 + 이름 + + setNewMember({ ...newMember, name: e.target.value }) + } + /> + 이메일 + + setNewMember({ ...newMember, email: e.target.value }) + } + /> + 블로그 URL + + setNewMember({ ...newMember, blogLink: e.target.value }) + } + /> + Git URL + + setNewMember({ + ...newMember, + gitRepositoryLink: e.target.value, + }) + } + /> + 프로필 이미지 + + setNewMember({ + ...newMember, + profileImage: e.target.value, + }) + } + /> + 설명 + + setNewMember({ + ...newMember, + description: e.target.value, + }) + } + /> + + 등록 + 취소 + + + ; + + ); +} + +const ModalContainer = styled(Modal)` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background-color: #fff; + border-radius: 8px; + border: 2px solid grey; + padding: 20px; + width: 500px; + margin: 0 auto; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +`; + +const ModalTitle = styled.h2` + font-size: 1.5rem; + margin-bottom: 15px; +`; + +const ModalLabel = styled.label` + font-size: 1rem; + margin-bottom: 5px; +`; + +const ModalInput = styled.input` + width: 70%; + padding: 8px; + margin-bottom: 15px; + border: 1px solid #ccc; + border-radius: 4px; + font-size: 1rem; + text-align: center; +`; + +const ModalButtonWrapper = styled.div` + display: flex; + justify-content: space-between; + margin-top: 15px; +`; + +const ModalButton = styled.button` + background-color: grey; + color: #fff; + border: none; + border-radius: 4px; + padding: 8px 16px; + font-size: 1rem; + cursor: pointer; + transition: background-color 0.2s ease-in-out; + + & + & { + margin: 0 10px; + } + + &:hover { + background-color: #8181f7; + } +`; diff --git a/src/container/product/RegisModal.js b/src/container/product/ProductRegis.js similarity index 99% rename from src/container/product/RegisModal.js rename to src/container/product/ProductRegis.js index 104720e..6b4c940 100644 --- a/src/container/product/RegisModal.js +++ b/src/container/product/ProductRegis.js @@ -9,7 +9,7 @@ import IMAGE from '../../resource/img/product/image_FILL0_wght400_GRAD0_opsz24.p import DELETE from '../../resource/img/product/backspace_FILL0_wght400_GRAD0_opsz24.png'; import CloseButton from '../../resource/img/product/close_button.png'; -export default function RegisModal() { +export default function ProductRegis() { const [data, setData] = useState([]); const [uploadImgUrl, setUploadImgUrl] = useState(''); const [showImages, setShowImages] = useState([]); diff --git a/src/container/product/QnARegis.js b/src/container/product/QnARegis.js new file mode 100644 index 0000000..73029ab --- /dev/null +++ b/src/container/product/QnARegis.js @@ -0,0 +1,161 @@ +import { useState, useEffect, useCallback } from 'react'; +import styled from 'styled-components'; +import axios from 'axios'; +import Modal from 'react-modal'; // react-modal 라이브러리 import +import { RMopen, RMclose } from '../../modules/ProductSlice'; +import { useDispatch, useSelector } from 'react-redux'; +import _ from 'lodash'; + +export default function QnARegis() { + const [data, setData] = useState([]); + + // 상태관리 관련 + const dispatch = useDispatch(); + const regisModalOpen = useSelector((state) => state.product.regisModalOpen); + + // 새 멤버 추가 입력받을 상태 변수 + const [newQna, setNewQna] = useState({ + answer: '', + id: '', + part: '', + question: '', + }); + + const addData = async () => { + try { + const result = await axios.post( + 'https://server.inuappcenter.kr/faqs', + newQna + ); + + // POST 요청 성공 시, 새로운 질문을 data 상태 변수에 추가합니다. + setData([...data, newQna]); + + setNewQna({ + answer: '', + id: '', + part: '', + question: '', + }); + dispatch(RMclose()); + } catch (error) { + console.error('Error adding data:', error); + } + }; + + // 모달을 닫아주고 스크롤을 풀어줌. + const closeModal = () => { + dispatch(RMclose()); + openScroll(); + }; + + const openScroll = useCallback(() => { + document.body.style.removeProperty('overflow'); + }, []); + + return ( + <> + + 질문 및 답변 추가 + 파트 + + setNewQna({ ...newQna, part: e.target.value }) + } + /> + 질문 + + setNewQna({ ...newQna, question: e.target.value }) + } + /> + 답변 + + setNewQna({ ...newQna, answer: e.target.value }) + } + /> + + 등록 + 취소 + + + ; + + ); +} + +const ModalContainer = styled(Modal)` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background-color: #fff; + border-radius: 8px; + border: 2px solid grey; + padding: 20px; + width: 500px; + margin: 0 auto; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +`; + +const ModalTitle = styled.h2` + font-size: 1.5rem; + margin-bottom: 15px; +`; + +const ModalLabel = styled.label` + font-size: 1rem; + margin-bottom: 5px; +`; + +const ModalInput = styled.input` + width: 70%; + padding: 8px; + margin-bottom: 15px; + border: 1px solid #ccc; + border-radius: 4px; + font-size: 1rem; + text-align: center; +`; + +const ModalButtonWrapper = styled.div` + display: flex; + justify-content: space-between; + margin-top: 15px; +`; + +const ModalButton = styled.button` + background-color: grey; + color: #fff; + border: none; + border-radius: 4px; + padding: 8px 16px; + font-size: 1rem; + cursor: pointer; + transition: background-color 0.2s ease-in-out; + + & + & { + margin: 0 10px; + } + + &:hover { + background-color: #8181f7; + } +`; diff --git a/src/container/product/RoleRegis.js b/src/container/product/RoleRegis.js new file mode 100644 index 0000000..09b1a4b --- /dev/null +++ b/src/container/product/RoleRegis.js @@ -0,0 +1,147 @@ +import { useState, useEffect, useCallback } from 'react'; +import styled from 'styled-components'; +import axios from 'axios'; +import Modal from 'react-modal'; // react-modal 라이브러리 import +import { RMopen, RMclose } from '../../modules/ProductSlice'; +import { useDispatch, useSelector } from 'react-redux'; +import _ from 'lodash'; + +export default function RoleRegis() { + const [data, setData] = useState([]); + + // 상태관리 관련 + const dispatch = useDispatch(); + const regisModalOpen = useSelector((state) => state.product.regisModalOpen); + + // 새 멤버 추가 입력받을 상태 변수 + const [newRole, setNewRole] = useState({ + roleName: '', + description: '', + }); + + const addData = async () => { + try { + const result = await axios.post( + 'https://server.inuappcenter.kr/roles', + newRole + ); + console.log('Success:', result.data); + + // POST 요청 성공 시, 새로운 역할을 data 상태 변수에 추가합니다. + setData([...data, result.data]); + + setNewRole({ + roleName: '', + description: '', + }); + dispatch(RMclose()); + } catch (error) { + console.error('Error adding data:', error); + } + }; + + // 모달을 닫아주고 스크롤을 풀어줌. + const closeModal = () => { + dispatch(RMclose()); + openScroll(); + }; + + const openScroll = useCallback(() => { + document.body.style.removeProperty('overflow'); + }, []); + + return ( + <> + + 역할 등록 + 역할 + + setNewRole({ ...newRole, roleName: e.target.value }) + } + /> + 설명 + + setNewRole({ ...newRole, description: e.target.value }) + } + /> + + 등록 + 취소 + + + ; + + ); +} + +const ModalContainer = styled(Modal)` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background-color: #fff; + border-radius: 8px; + border: 2px solid grey; + padding: 20px; + width: 500px; + margin: 0 auto; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +`; + +const ModalTitle = styled.h2` + font-size: 1.5rem; + margin-bottom: 15px; +`; + +const ModalLabel = styled.label` + font-size: 1rem; + margin-bottom: 5px; +`; + +const ModalInput = styled.input` + width: 70%; + padding: 8px; + margin-bottom: 15px; + border: 1px solid #ccc; + border-radius: 4px; + font-size: 1rem; + text-align: center; +`; + +const ModalButtonWrapper = styled.div` + display: flex; + justify-content: space-between; + margin-top: 15px; +`; + +const ModalButton = styled.button` + background-color: grey; + color: #fff; + border: none; + border-radius: 4px; + padding: 8px 16px; + font-size: 1rem; + cursor: pointer; + transition: background-color 0.2s ease-in-out; + + & + & { + margin: 0 10px; + } + + &:hover { + background-color: #8181f7; + } +`; diff --git a/src/page/ManageGenPage.js b/src/page/ManageGenPage.js index abf4ada..632d08a 100644 --- a/src/page/ManageGenPage.js +++ b/src/page/ManageGenPage.js @@ -6,17 +6,18 @@ import Pagination from '../component/manage/Pagenation'; import InOut from '../component/common/InOut'; import IntroBox from '../component/admin/IntroBox'; import { introInfo } from '../resource/data/adminInfo'; +import { RMopen } from '../modules/ProductSlice'; +import { useSelector, useDispatch } from 'react-redux'; +import { useCallback } from 'react'; +import GenRegis from '../container/product/GenRegis'; export default function ManageGenPage() { const [data, setData] = useState([]); - // 새 멤버를 추가할 때 사용합니다. - const [newRole, setNewRole] = useState({ - role_id: '', - member_id: '', - part: '', - year: 15, - }); + const regisModalOpen = useSelector((state) => state.product.regisModalOpen); + // prettier-ignore + const dispatch = useDispatch(); + const [contextMenuVisible, setContextMenuVisible] = useState(false); const [contextMenuPosition, setContextMenuPosition] = useState({ x: 0, @@ -70,28 +71,15 @@ export default function ManageGenPage() { setEditModalOpen(false); }; - const addData = async () => { - try { - const result = await axios.post( - `https://server.inuappcenter.kr/groups?member_id=${newRole.member_id}&role_id=${newRole.role_id}`, - newRole - ); - console.log('Success:', result.data); - - // POST 요청 성공 시, 새로운 역할을 data 상태 변수에 추가합니다. - setData([...data, result.data]); - - setNewRole({ - role_id: '', - member_id: '', - part: '', - year: 16, - }); - } catch (error) { - console.error('Error adding data:', error); - } + const addData = () => { + dispatch(RMopen()); + scrollLock(); }; + const scrollLock = useCallback(() => { + document.body.style.overflow = 'hidden'; + }, []); + useEffect(() => { const fetchData = async () => { const viewData = await axios @@ -103,7 +91,7 @@ export default function ManageGenPage() { }); }; fetchData(); - }, []); + }, [regisModalOpen, data.length]); useEffect(() => { const handleContextMenuClick = (e) => { @@ -237,44 +225,15 @@ export default function ManageGenPage() { itemsPerPage={itemsPerPage} onPageChange={handlePageChange} /> + { + addData(); + }} + > + 등록 + - 편성 추가 - - {/* 사용자 입력을 받을 UI 요소들 */} - - setNewRole({ ...newRole, member_id: e.target.value }) - } - /> - - setNewRole({ ...newRole, role_id: e.target.value }) - } - /> - - setNewRole({ ...newRole, part: e.target.value }) - } - /> - - setNewRole({ ...newRole, year: e.target.value }) - } - /> - 등록 - + {/* 컨텍스트 메뉴 */} {contextMenuVisible && ( 삭제 )} - + {regisModalOpen && } {/* 수정 팝업 모달 */} state.product.regisModalOpen); + // prettier-ignore + const dispatch = useDispatch(); + const [contextMenuVisible, setContextMenuVisible] = useState(false); const [contextMenuPosition, setContextMenuPosition] = useState({ x: 0, @@ -82,31 +80,15 @@ export default function ManagePage() { setEditModalOpen(false); }; - const addData = async () => { - try { - const result = await axios.post( - 'https://server.inuappcenter.kr/members', - newMember - ); - console.log('Success:', result.data); - - // POST 요청 성공 시, 새로운 동아리원을 data 상태 변수에 추가합니다. - setData([...data, result.data]); - - setNewMember({ - member_id: '', - name: '', - description: '', - profileImage: '', - blogLink: '', - email: '', - gitRepositoryLink: '', - }); - } catch (error) { - console.error('Error adding data:', error); - } + const addData = () => { + dispatch(RMopen()); + scrollLock(); }; + const scrollLock = useCallback(() => { + document.body.style.overflow = 'hidden'; + }, []); + useEffect(() => { const fetchData = async () => { isLoading(true); @@ -213,7 +195,6 @@ export default function ManagePage() { 동아리원 목록 - {loading &&
loading...
} {getCurrentPageData().map((content) => ( - {content.member_id} {content.name} {content.email} @@ -264,74 +244,14 @@ export default function ManagePage() { itemsPerPage={itemsPerPage} onPageChange={handlePageChange} /> + { + addData(); + }} + > + 등록 + - 동아리원 추가 - - {/* 사용자 입력을 받을 UI 요소들 */} - - setNewMember({ ...newMember, name: e.target.value }) - } - /> - - - setNewMember({ ...newMember, email: e.target.value }) - } - /> - - - setNewMember({ ...newMember, blogLink: e.target.value }) - } - /> - - - setNewMember({ - ...newMember, - gitRepositoryLink: e.target.value, - }) - } - /> - - - setNewMember({ - ...newMember, - profileImage: e.target.value, - }) - } - /> - - - setNewMember({ - ...newMember, - description: e.target.value, - }) - } - /> - 등록 - {/* 컨텍스트 메뉴 */} {contextMenuVisible && ( 삭제 )} - + {regisModalOpen && } {/* 수정 팝업 모달 */} state.product.regisModalOpen); + // prettier-ignore + const dispatch = useDispatch(); + const [contextMenuVisible, setContextMenuVisible] = useState(false); const [contextMenuPosition, setContextMenuPosition] = useState({ x: 0, @@ -67,27 +69,15 @@ export default function ManageRolePage() { setEditModalOpen(false); }; - const addData = async () => { - try { - const result = await axios.post( - 'https://server.inuappcenter.kr/roles', - newRole - ); - console.log('Success:', result.data); - - // POST 요청 성공 시, 새로운 역할을 data 상태 변수에 추가합니다. - setData([...data, result.data]); - - setNewRole({ - roleId: '', - roleName: '', - description: '', - }); - } catch (error) { - console.error('Error adding data:', error); - } + const addData = () => { + dispatch(RMopen()); + scrollLock(); }; + const scrollLock = useCallback(() => { + document.body.style.overflow = 'hidden'; + }, []); + useEffect(() => { const fetchData = async () => { const viewData = await axios @@ -98,7 +88,7 @@ export default function ManageRolePage() { }); }; fetchData(); - }, [data.length, isEditModalOpen]); + }, [data.length, isEditModalOpen, regisModalOpen]); useEffect(() => { const handleContextMenuClick = (e) => { @@ -211,28 +201,15 @@ export default function ManageRolePage() { itemsPerPage={itemsPerPage} onPageChange={handlePageChange} /> + { + addData(); + }} + > + 등록 + - 역할 추가 - - {/* 사용자 입력을 받을 UI 요소들 */} - - setNewRole({ ...newRole, roleName: e.target.value }) - } - /> - - setNewRole({ ...newRole, description: e.target.value }) - } - /> - 등록 - + {regisModalOpen && } {/* 컨텍스트 메뉴 */} {contextMenuVisible && ( - {regisModalOpen && } + {regisModalOpen && } {modifyModalOpen && } {/* 컨텍스트 메뉴 */} {contextMenuVisible && ( diff --git a/src/page/QnAPage.js b/src/page/QnAPage.js index 7d7d3ef..985b9e4 100644 --- a/src/page/QnAPage.js +++ b/src/page/QnAPage.js @@ -6,18 +6,19 @@ import Pagination from '../component/manage/Pagenation'; import InOut from '../component/common/InOut'; import IntroBox from '../component/admin/IntroBox'; import { introInfo } from '../resource/data/adminInfo'; +import { RMopen } from '../modules/ProductSlice'; +import { useSelector, useDispatch } from 'react-redux'; +import { useCallback } from 'react'; +import QnARegis from '../container/product/QnARegis'; export default function QnAPage() { const [data, setData] = useState([]); const [loading, isLoading] = useState(false); - // 새 멤버를 추가할 때 사용합니다. - const [newQna, setNewQna] = useState({ - answer: '', - id: '', - part: '', - question: '', - }); + const regisModalOpen = useSelector((state) => state.product.regisModalOpen); + // prettier-ignore + const dispatch = useDispatch(); + const [contextMenuVisible, setContextMenuVisible] = useState(false); const [contextMenuPosition, setContextMenuPosition] = useState({ x: 0, @@ -71,27 +72,15 @@ export default function QnAPage() { setEditModalOpen(false); }; - const addData = async () => { - try { - const result = await axios.post( - 'https://server.inuappcenter.kr/faqs', - newQna - ); - - // POST 요청 성공 시, 새로운 질문을 data 상태 변수에 추가합니다. - setData([...data, newQna]); - - setNewQna({ - answer: '', - id: '', - part: '', - question: '', - }); - } catch (error) { - console.error('Error adding data:', error); - } + const addData = () => { + dispatch(RMopen()); + scrollLock(); }; + const scrollLock = useCallback(() => { + document.body.style.overflow = 'hidden'; + }, []); + useEffect(() => { const fetchData = async () => { isLoading(true); @@ -105,8 +94,7 @@ export default function QnAPage() { }); }; fetchData(); - console.log(newQna); - }, []); + }, [regisModalOpen]); useEffect(() => { const handleContextMenuClick = (e) => { @@ -221,38 +209,14 @@ export default function QnAPage() { itemsPerPage={itemsPerPage} onPageChange={handlePageChange} /> + { + addData(); + }} + > + 등록 + - 질문 및 답변 추가 - - {/* 사용자 입력을 받을 UI 요소들 */} - - setNewQna({ ...newQna, part: e.target.value }) - } - /> - - - setNewQna({ ...newQna, question: e.target.value }) - } - /> - - - setNewQna({ ...newQna, answer: e.target.value }) - } - /> - 등록 - {/* 컨텍스트 메뉴 */} {contextMenuVisible && ( 삭제 )} - + {regisModalOpen && } {/* 수정 팝업 모달 */} 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 S?gyNFJS}xSwL0jN$o~P8O{z%% literal 0 HcmV?d00001 diff --git a/src/resource/style/GlobalStyle.js b/src/resource/style/GlobalStyle.js index 917857a..e809bbc 100644 --- a/src/resource/style/GlobalStyle.js +++ b/src/resource/style/GlobalStyle.js @@ -6,7 +6,7 @@ import PretendardBold from '../fonts/Pretendard-Bold.otf'; const GlobalStyle = createGlobalStyle` //이 안에 전체 프로젝트에 적용될 css를 작성하면 됩니다~! * { - font-family: 'Pretendard', sans-serif; + font-family: 'NanumSquareNeo'; } body { From cb732393e614122dedade4333bda308efca871a3 Mon Sep 17 00:00:00 2001 From: a1 Date: Sun, 4 Feb 2024 20:46:25 +0900 Subject: [PATCH 05/24] =?UTF-8?q?style:=EC=95=84=EC=9D=B4=EC=BD=98=20?= =?UTF-8?q?=EC=82=BD=EC=9E=85,=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 30 ++++++++++- package.json | 3 +- src/container/product/ManageRegis.js | 6 ++- src/page/AdminPage.js | 78 ++++++++++++++++++---------- src/page/CenterPage.js | 16 ++---- src/resource/img/camera.svg | 1 + src/resource/img/face.svg | 1 + src/resource/img/lock.svg | 1 + src/resource/img/phone.svg | 1 + src/resource/img/shortcut.svg | 1 + src/resource/img/undo.svg | 1 + yarn.lock | 13 +++-- 12 files changed, 107 insertions(+), 45 deletions(-) create mode 100644 src/resource/img/camera.svg create mode 100644 src/resource/img/face.svg create mode 100644 src/resource/img/lock.svg create mode 100644 src/resource/img/phone.svg create mode 100644 src/resource/img/shortcut.svg create mode 100644 src/resource/img/undo.svg diff --git a/package-lock.json b/package-lock.json index 7d91e55..a9c0f8f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,8 @@ "styled-components": "^5.3.6", "swiper": "^9.0.1", "uuid": "^9.0.0", - "web-vitals": "^2.1.4" + "web-vitals": "^2.1.4", + "zustand": "^4.5.0" }, "devDependencies": { "prettier": "^2.8.8", @@ -18370,6 +18371,33 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zustand": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.0.tgz", + "integrity": "sha512-zlVFqS5TQ21nwijjhJlx4f9iGrXSL0o/+Dpy4txAP22miJ8Ti6c1Ol1RLNN98BMib83lmDH/2KmLwaNXpjrO1A==", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index a08ee93..f10752b 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ "styled-components": "^5.3.6", "swiper": "^9.0.1", "uuid": "^9.0.0", - "web-vitals": "^2.1.4" + "web-vitals": "^2.1.4", + "zustand": "^4.5.0" }, "scripts": { "start": "react-scripts start", diff --git a/src/container/product/ManageRegis.js b/src/container/product/ManageRegis.js index 12d10a6..1c8c874 100644 --- a/src/container/product/ManageRegis.js +++ b/src/container/product/ManageRegis.js @@ -107,7 +107,7 @@ export default function ManageRegis() { }) } /> - 프로필 이미지 + 프로필 이미지 URL 대시보드 기능 - {'앱센터 동아리원 관리'} - - { - '동아리원 편성, 기수 관리, 동아리원 정보등을 관리할 수 있어요' - } - + + + + + {'앱센터 동아리 관리'} + + { + '동아리원 정보와 기수, 역할, 질문을 관리할 수 있어요' + } + + - {'앱 관리'} - - {'홈페이지에 게재된 앱 정보와 목록을 관리할 수 있어요'} - + + + + + {'앱 관리'} + + { + '홈페이지에 게재된 앱 정보와 목록을 관리할 수 있어요' + } + + - {'사진 게시판 관리'} - - {'활동 사진에 게재된 사진들을 관리할 수 있어요'} - + + + + + {'사진 게시판 관리'} + + {'활동 사진에 게재된 사진들을 관리할 수 있어요'} + + ); } -const LogoImg = styled.img` - margin-right: auto; +const PhotoBox = styled.div` + background-color: white; + margin-bottom: auto; `; -const LoginBtn = styled.button` - border: none; - background-color: transparent; - &:hover { - cursor: pointer; - transition: 0.3s ease-in-out; - opacity: 0.5; - } +const TextBox = styled.div` + flex-direction: column; + margin-bottom: 2rem; + margin-left: 1rem; +`; + +const PhotoImg = styled.img` + width: 40px; + height: 40px; `; const MenuText = styled.div` @@ -62,8 +84,8 @@ const MenuText = styled.div` text-transform: uppercase; color: ${(props) => (props.type === 'title' ? '#424242' : '#848484')}; font-weight: ${(props) => - props.type === 'top' ? 400 : props.type === 'title' ? 500 : 300}; - margin-bottom: 3px; + props.type === 'top' ? 400 : props.type === 'title' ? 300 : 300}; + margin: auto 0; white-space: pre-line; ${(props) => @@ -85,6 +107,7 @@ const IntroFunc = styled.div` `; const MenuBox = styled.div` + display: flex; padding: 2.5rem 0 0 1.4rem; width: 600px; height: 8rem; @@ -93,6 +116,7 @@ const MenuBox = styled.div` top: 20px; border-radius: 20px; align-items: center; + box-sizing: border-box; &:hover { background-color: #bdbdbd; @@ -133,7 +157,7 @@ const Text = styled.div` text-transform: uppercase; color: ${(props) => (props.type === 'title' ? '#424242' : '#848484')}; font-weight: ${(props) => - props.type === 'top' ? 600 : props.type === 'title' ? 900 : 600}; + props.type === 'top' ? 600 : props.type === 'title' ? 600 : 600}; margin-bottom: 3px; white-space: pre-line; diff --git a/src/page/CenterPage.js b/src/page/CenterPage.js index ae16411..dabb6f2 100644 --- a/src/page/CenterPage.js +++ b/src/page/CenterPage.js @@ -7,11 +7,9 @@ export default function AdminPage() { <> - {'앱센터 동아리원 관리'} + {'앱센터 동아리 관리'} - { - '동아리원 편성, 기수 관리, 동아리원 정보등을 관리할 수 있어요' - } + {'동아리원 정보와 기수, 역할, 질문을 관리할 수 있어요'} @@ -43,12 +41,6 @@ export default function AdminPage() { - - {'전체'} - - {'홈페이지에 게재된 앱 정보와 목록을 관리할 수 있어요'} - - {'질문 관리'} @@ -74,7 +66,7 @@ const InfoBox = styled.div` flex-direction: column; justify-content: flex-end; width: 250px; - height: 300px; + height: 250px; padding: 20px; background-color: #fff; border: none; @@ -124,7 +116,7 @@ const Text = styled.div` text-transform: uppercase; color: ${(props) => (props.type === 'title' ? '#424242' : '#848484')}; font-weight: ${(props) => - props.type === 'top' ? 600 : props.type === 'title' ? 700 : 400}; + props.type === 'top' ? 600 : props.type === 'title' ? 500 : 400}; margin-bottom: 3px; white-space: pre-line; diff --git a/src/resource/img/camera.svg b/src/resource/img/camera.svg new file mode 100644 index 0000000..e863b0d --- /dev/null +++ b/src/resource/img/camera.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/resource/img/face.svg b/src/resource/img/face.svg new file mode 100644 index 0000000..dca13b1 --- /dev/null +++ b/src/resource/img/face.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/resource/img/lock.svg b/src/resource/img/lock.svg new file mode 100644 index 0000000..20b9e39 --- /dev/null +++ b/src/resource/img/lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/resource/img/phone.svg b/src/resource/img/phone.svg new file mode 100644 index 0000000..456c7f4 --- /dev/null +++ b/src/resource/img/phone.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/resource/img/shortcut.svg b/src/resource/img/shortcut.svg new file mode 100644 index 0000000..dcd021b --- /dev/null +++ b/src/resource/img/shortcut.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/resource/img/undo.svg b/src/resource/img/undo.svg new file mode 100644 index 0000000..c451e1a --- /dev/null +++ b/src/resource/img/undo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index cc6efe4..76c6321 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2322,7 +2322,7 @@ dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^16.8 || ^17.0 || ^18.0", "@types/react@^17.0.0 || ^18.0.0": +"@types/react@*", "@types/react@^16.8 || ^17.0 || ^18.0", "@types/react@^17.0.0 || ^18.0.0", "@types/react@>=16.8": version "18.0.26" resolved "https://registry.npmjs.org/@types/react/-/react-18.0.26.tgz" integrity sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug== @@ -5556,7 +5556,7 @@ ignore@^5.2.0: resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz" integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== -immer@^9.0.16, immer@^9.0.7: +immer@^9.0.16, immer@^9.0.7, immer@>=9.0.6: version "9.0.17" resolved "https://registry.npmjs.org/immer/-/immer-9.0.17.tgz" integrity sha512-+hBruaLSQvkPfxRiTLK/mi4vLH+/VQS6z2KJahdoxlleFOI8ARqzOF17uy12eFDlqWmPoygwc5evgwcp+dlHhg== @@ -9593,7 +9593,7 @@ url-parse@^1.5.3: querystringify "^2.1.1" requires-port "^1.0.0" -use-sync-external-store@^1.0.0: +use-sync-external-store@^1.0.0, use-sync-external-store@1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz" integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== @@ -10176,3 +10176,10 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zustand@^4.5.0: + version "4.5.0" + resolved "https://registry.npmjs.org/zustand/-/zustand-4.5.0.tgz" + integrity sha512-zlVFqS5TQ21nwijjhJlx4f9iGrXSL0o/+Dpy4txAP22miJ8Ti6c1Ol1RLNN98BMib83lmDH/2KmLwaNXpjrO1A== + dependencies: + use-sync-external-store "1.2.0" From f06676984b2e62bd398c304f5a45f3af80834887 Mon Sep 17 00:00:00 2001 From: a1 Date: Mon, 5 Feb 2024 21:03:07 +0900 Subject: [PATCH 06/24] =?UTF-8?q?feat:=20=EB=A9=A4=EB=B2=84=20ID=20?= =?UTF-8?q?=EB=AA=A8=EB=8B=AC(=EA=B2=80=EC=83=89=20=EA=B8=B0=EB=8A=A5)=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/container/admin/FindMemId.js | 173 ++++++++++++++++++ src/container/admin/FindRole.js | 0 src/container/product/GenRegis.js | 42 ++++- src/container/product/ManageRegis.js | 12 +- src/container/product/QnARegis.js | 18 +- src/container/product/RoleRegis.js | 16 +- src/modules/ProductSlice.js | 27 ++- src/modules/rootReducer.js | 10 - src/page/AdminPage.js | 6 +- src/page/CenterPage.js | 41 ++++- .../img/group_FILL0_wght400_GRAD0_opsz24.svg | 1 + .../img/groups_FILL0_wght400_GRAD0_opsz24.svg | 1 + .../img/person_FILL0_wght400_GRAD0_opsz24.svg | 1 + .../img/quiz_FILL0_wght400_GRAD0_opsz24.svg | 1 + 14 files changed, 309 insertions(+), 40 deletions(-) create mode 100644 src/container/admin/FindMemId.js create mode 100644 src/container/admin/FindRole.js create mode 100644 src/resource/img/group_FILL0_wght400_GRAD0_opsz24.svg create mode 100644 src/resource/img/groups_FILL0_wght400_GRAD0_opsz24.svg create mode 100644 src/resource/img/person_FILL0_wght400_GRAD0_opsz24.svg create mode 100644 src/resource/img/quiz_FILL0_wght400_GRAD0_opsz24.svg diff --git a/src/container/admin/FindMemId.js b/src/container/admin/FindMemId.js new file mode 100644 index 0000000..1739654 --- /dev/null +++ b/src/container/admin/FindMemId.js @@ -0,0 +1,173 @@ +import { useState, useEffect, useCallback } from 'react'; +import styled from 'styled-components'; +import axios from 'axios'; +import Modal from 'react-modal'; // react-modal 라이브러리 import +import { MemberModalopen, MemberModalclose } from '../../modules/ProductSlice'; +import { useDispatch, useSelector } from 'react-redux'; +import _ from 'lodash'; + +export default function FindMemId() { + const dispatch = useDispatch(); + const memberModalOpen = useSelector( + (state) => state.product.memberModalOpen + ); + + const [data, setData] = useState([]); + + const [newMember, setNewMember] = useState(''); + + // 모달을 닫아주고 스크롤을 풀어줌. + const closeModal = () => { + dispatch(MemberModalclose()); + setData([]); + openScroll(); + }; + + const openScroll = useCallback(() => { + document.body.style.removeProperty('overflow'); + }, []); + + const findData = async () => { + try { + const newMemberEncode = encodeURIComponent(newMember); + const result = await axios.get( + `https://server.inuappcenter.kr/members/id/${newMemberEncode}` + ); + console.log('Success:', result.data); + + // POST 요청 성공 시, 새로운 동아리원을 data 상태 변수에 추가합니다. + setData(result.data); + + setNewMember(''); + } catch (error) { + console.error('Error adding data:', error); + } + }; + + return ( + <> + + 편성 추가 + 동아리원 + setNewMember(e.target.value)} + /> + + 이름 + 번호 + 이메일 + + <> + {data.map((member, index) => ( + + {member.name} + {member.member_id} + {member.email} + + ))} + + + 검색 + 취소 + + + ; + + ); +} +const ModalHeader = styled.div` + position: absolute; + + ${(props) => + props.type === 'name' + ? 'left: 10.2rem;' + : props.type === 'email' + ? 'left: 23.2rem;' + : ''} +`; + +const LabelWrapper = styled.div` + display: flex; + margin-top: 1rem; +`; + +const ModalTemplate = styled.div` + display: flex; + margin-bottom: 1rem; +`; +const ModalContainer = styled(Modal)` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background-color: #f7f7f8; + border-radius: 8px; + padding: 20px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); + width: 500px; + margin: 0 auto; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +`; + +const ModalTitle = styled.h2` + font-size: 1.5rem; + margin-bottom: 15px; + margin-right: auto; + margin-top: 0; + font-weight: 400; +`; + +const ModalLabel = styled.label` + font-size: 1rem; + margin-bottom: 5px; + margin-right: auto; + margin-left: 75px; +`; + +const ModalInput = styled.input` + width: 70%; + padding: 8px; + margin-bottom: 15px; + border: 1px solid black; + border-radius: 4px; + font-size: 1rem; + + &: (6) { + width: 80%; + } +`; + +const ModalButtonWrapper = styled.div` + display: flex; + justify-content: space-between; + margin-top: 15px; +`; + +const ModalButton = styled.button` + background-color: grey; + color: #fff; + border: none; + border-radius: 4px; + padding: 8px 16px; + font-size: 1rem; + cursor: pointer; + transition: background-color 0.2s ease-in-out; + + & + & { + margin: 0 10px; + } + + &:hover { + background-color: #8181f7; + } +`; diff --git a/src/container/admin/FindRole.js b/src/container/admin/FindRole.js new file mode 100644 index 0000000..e69de29 diff --git a/src/container/product/GenRegis.js b/src/container/product/GenRegis.js index 6786194..454ca18 100644 --- a/src/container/product/GenRegis.js +++ b/src/container/product/GenRegis.js @@ -2,9 +2,15 @@ import { useState, useEffect, useCallback } from 'react'; import styled from 'styled-components'; import axios from 'axios'; import Modal from 'react-modal'; // react-modal 라이브러리 import -import { RMopen, RMclose } from '../../modules/ProductSlice'; +import { + RMopen, + RMclose, + MemberModalopen, + RoleModalopen, +} from '../../modules/ProductSlice'; import { useDispatch, useSelector } from 'react-redux'; import _ from 'lodash'; +import FindMemId from '../admin/FindMemId'; export default function GenRegis() { const [data, setData] = useState([]); @@ -12,6 +18,10 @@ export default function GenRegis() { // 상태관리 관련 const dispatch = useDispatch(); const regisModalOpen = useSelector((state) => state.product.regisModalOpen); + const memberModalOpen = useSelector( + (state) => state.product.memberModalOpen + ); + const roleModalOpen = useSelector((state) => state.product.roleModalOpen); // 새 멤버 추가 입력받을 상태 변수 const [newRole, setNewRole] = useState({ @@ -54,6 +64,10 @@ export default function GenRegis() { document.body.style.removeProperty('overflow'); }, []); + const openMemberModal = () => { + dispatch(MemberModalopen()); + }; + return ( <> 편성 추가 - 동아리원 ID + 동아리원 setNewRole({ ...newRole, member_id: e.target.value }) } + onClick={() => openMemberModal()} /> - 역할 ID + {memberModalOpen && } + 역할 setNewRole({ ...newRole, role_id: e.target.value }) @@ -113,10 +129,10 @@ const ModalContainer = styled(Modal)` flex-direction: column; align-items: center; justify-content: center; - background-color: #fff; + background-color: #f7f7f8; border-radius: 8px; - border: 2px solid grey; padding: 20px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); width: 500px; margin: 0 auto; position: absolute; @@ -128,21 +144,29 @@ const ModalContainer = styled(Modal)` const ModalTitle = styled.h2` font-size: 1.5rem; margin-bottom: 15px; + margin-right: auto; + margin-top: 0; + font-weight: 400; `; const ModalLabel = styled.label` font-size: 1rem; margin-bottom: 5px; + margin-right: auto; + margin-left: 75px; `; const ModalInput = styled.input` width: 70%; padding: 8px; margin-bottom: 15px; - border: 1px solid #ccc; + border: 1px solid black; border-radius: 4px; font-size: 1rem; - text-align: center; + + &: (6) { + width: 80%; + } `; const ModalButtonWrapper = styled.div` diff --git a/src/container/product/ManageRegis.js b/src/container/product/ManageRegis.js index 1c8c874..36e5268 100644 --- a/src/container/product/ManageRegis.js +++ b/src/container/product/ManageRegis.js @@ -146,10 +146,10 @@ const ModalContainer = styled(Modal)` flex-direction: column; align-items: center; justify-content: center; - background-color: #fff; + background-color: #f7f7f8; border-radius: 8px; - border: 2px solid grey; padding: 20px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); width: 500px; margin: 0 auto; position: absolute; @@ -161,21 +161,25 @@ const ModalContainer = styled(Modal)` const ModalTitle = styled.h2` font-size: 1.5rem; margin-bottom: 15px; + margin-right: auto; + margin-top: 0; + font-weight: 400; `; const ModalLabel = styled.label` font-size: 1rem; margin-bottom: 5px; + margin-right: auto; + margin-left: 75px; `; const ModalInput = styled.input` width: 70%; padding: 8px; margin-bottom: 15px; - border: 1px solid #ccc; + border: 1px solid black; border-radius: 4px; font-size: 1rem; - text-align: center; &: (6) { width: 80%; diff --git a/src/container/product/QnARegis.js b/src/container/product/QnARegis.js index 73029ab..a3cf070 100644 --- a/src/container/product/QnARegis.js +++ b/src/container/product/QnARegis.js @@ -64,7 +64,7 @@ export default function QnARegis() { 파트 setNewQna({ ...newQna, part: e.target.value }) @@ -103,10 +103,10 @@ const ModalContainer = styled(Modal)` flex-direction: column; align-items: center; justify-content: center; - background-color: #fff; + background-color: #f7f7f8; border-radius: 8px; - border: 2px solid grey; padding: 20px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); width: 500px; margin: 0 auto; position: absolute; @@ -118,21 +118,29 @@ const ModalContainer = styled(Modal)` const ModalTitle = styled.h2` font-size: 1.5rem; margin-bottom: 15px; + margin-right: auto; + margin-top: 0; + font-weight: 400; `; const ModalLabel = styled.label` font-size: 1rem; margin-bottom: 5px; + margin-right: auto; + margin-left: 75px; `; const ModalInput = styled.input` width: 70%; padding: 8px; margin-bottom: 15px; - border: 1px solid #ccc; + border: 1px solid black; border-radius: 4px; font-size: 1rem; - text-align: center; + + &: (6) { + width: 80%; + } `; const ModalButtonWrapper = styled.div` diff --git a/src/container/product/RoleRegis.js b/src/container/product/RoleRegis.js index 09b1a4b..44f5961 100644 --- a/src/container/product/RoleRegis.js +++ b/src/container/product/RoleRegis.js @@ -89,10 +89,10 @@ const ModalContainer = styled(Modal)` flex-direction: column; align-items: center; justify-content: center; - background-color: #fff; + background-color: #f7f7f8; border-radius: 8px; - border: 2px solid grey; padding: 20px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); width: 500px; margin: 0 auto; position: absolute; @@ -104,21 +104,29 @@ const ModalContainer = styled(Modal)` const ModalTitle = styled.h2` font-size: 1.5rem; margin-bottom: 15px; + margin-right: auto; + margin-top: 0; + font-weight: 400; `; const ModalLabel = styled.label` font-size: 1rem; margin-bottom: 5px; + margin-right: auto; + margin-left: 75px; `; const ModalInput = styled.input` width: 70%; padding: 8px; margin-bottom: 15px; - border: 1px solid #ccc; + border: 1px solid black; border-radius: 4px; font-size: 1rem; - text-align: center; + + &: (6) { + width: 80%; + } `; const ModalButtonWrapper = styled.div` diff --git a/src/modules/ProductSlice.js b/src/modules/ProductSlice.js index 17abda9..ed8e5f9 100644 --- a/src/modules/ProductSlice.js +++ b/src/modules/ProductSlice.js @@ -3,6 +3,8 @@ import { createSlice } from '@reduxjs/toolkit'; const initialState = { regisModalOpen: false, modifyModalOpen: false, + memberModalOpen: false, + roleModalOpen: false, message: '', }; @@ -24,7 +26,30 @@ export const ProductSlice = createSlice({ MODclose(state) { state.modifyModalOpen = false; }, + MemberModalopen: (state, action) => { + state.memberModalOpen = true; + state.message = action.payload; + }, + MemberModalclose(state) { + state.memberModalOpen = false; + }, + RoleModalopen: (state, action) => { + state.roleModalOpen = true; + state.message = action.payload; + }, + RoleModalclose(state) { + state.roleModalOpen = false; + }, }, }); -export const { RMopen, RMclose, MODopen, MODclose } = ProductSlice.actions; +export const { + RMopen, + RMclose, + MODopen, + MODclose, + MemberModalopen, + MemberModalclose, + RoleModalopen, + RoleModalclose, +} = ProductSlice.actions; diff --git a/src/modules/rootReducer.js b/src/modules/rootReducer.js index 4b6b508..1cadf68 100644 --- a/src/modules/rootReducer.js +++ b/src/modules/rootReducer.js @@ -4,16 +4,6 @@ import { faqSlice } from './faqSlice'; import { ProductSlice } from './ProductSlice'; import { LoginSlice } from './LoginSlice'; -// redux-persist를 위해 localStorage를 불러옴 -import { persistReducer } from 'redux-persist'; -import storage from 'redux-persist/lib/storage'; - -const persistConfig = { - key: 'root', - storage, - whitelist: ['home', 'ourTeam', 'faq', 'product', 'login'], -}; - const rootReducer = { home: homeSlice.reducer, ourTeam: ourTeamSlice.reducer, diff --git a/src/page/AdminPage.js b/src/page/AdminPage.js index 7f462ec..232db39 100644 --- a/src/page/AdminPage.js +++ b/src/page/AdminPage.js @@ -62,8 +62,11 @@ export default function AdminPage() { ); } const PhotoBox = styled.div` + width: 7%; + height: 50%; background-color: white; margin-bottom: auto; + border-radius: 8px; `; const TextBox = styled.div` @@ -91,7 +94,8 @@ const MenuText = styled.div` ${(props) => props.type === 'title' ? css` - font-size: ${(props) => props.theme.fontSize.tablet.title}; + font-size: ${(props) => + props.theme.fontSize.smallTablet.title}; ` : css` font-size: ${(props) => props.theme.fontSize.tablet.caption}; diff --git a/src/page/CenterPage.js b/src/page/CenterPage.js index dabb6f2..88754d7 100644 --- a/src/page/CenterPage.js +++ b/src/page/CenterPage.js @@ -1,6 +1,10 @@ import styled, { css } from 'styled-components'; import { Link } from 'react-router-dom'; import InOut from '../component/common/InOut'; +import Generation from '../resource/img/groups_FILL0_wght400_GRAD0_opsz24.svg'; +import Member from '../resource/img/person_FILL0_wght400_GRAD0_opsz24.svg'; +import Role from '../resource/img/group_FILL0_wght400_GRAD0_opsz24.svg'; +import Question from '../resource/img/quiz_FILL0_wght400_GRAD0_opsz24.svg'; export default function AdminPage() { return ( @@ -15,19 +19,23 @@ export default function AdminPage() { + + + {'동아리원 관리'} - {'동아리를 추가, 삭제, 수정을 할 수 있어요'} + {'동아리원을 추가, 삭제, 수정을 할 수 있어요'} + + + {'기수 관리'} - { - '동아리원을 기수에 추가, 삭제, 수정을 할 수 있어요' - } + {'기수에 동아리원을 역할과 함께 편성할 수 있어요'} @@ -35,14 +43,22 @@ export default function AdminPage() { + + + {'역할 관리'} - {'역할군을 관리할 수 있어요'} + { + '센터장, 파트장, 파트원과 같은 역할을 추가할 수 있어요' + } + + + {'질문 관리'} {'질문과 답변을 추가, 삭제, 수정을 할 수 있어요'} @@ -54,6 +70,19 @@ export default function AdminPage() { ); } +const PhotoBox = styled.div` + display: flex; + background-color: white; + margin-bottom: auto; + width: 17%; + border-radius: 8px; +`; + +const PhotoImg = styled.img` + width: 40px; + height: 40px; +`; + const BoxContainer = styled.div` display: flex; justify-content: center; @@ -66,7 +95,7 @@ const InfoBox = styled.div` flex-direction: column; justify-content: flex-end; width: 250px; - height: 250px; + height: 200px; padding: 20px; background-color: #fff; border: none; diff --git a/src/resource/img/group_FILL0_wght400_GRAD0_opsz24.svg b/src/resource/img/group_FILL0_wght400_GRAD0_opsz24.svg new file mode 100644 index 0000000..dbc2c93 --- /dev/null +++ b/src/resource/img/group_FILL0_wght400_GRAD0_opsz24.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/resource/img/groups_FILL0_wght400_GRAD0_opsz24.svg b/src/resource/img/groups_FILL0_wght400_GRAD0_opsz24.svg new file mode 100644 index 0000000..998ff03 --- /dev/null +++ b/src/resource/img/groups_FILL0_wght400_GRAD0_opsz24.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/resource/img/person_FILL0_wght400_GRAD0_opsz24.svg b/src/resource/img/person_FILL0_wght400_GRAD0_opsz24.svg new file mode 100644 index 0000000..a3f6b24 --- /dev/null +++ b/src/resource/img/person_FILL0_wght400_GRAD0_opsz24.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/resource/img/quiz_FILL0_wght400_GRAD0_opsz24.svg b/src/resource/img/quiz_FILL0_wght400_GRAD0_opsz24.svg new file mode 100644 index 0000000..ae56d23 --- /dev/null +++ b/src/resource/img/quiz_FILL0_wght400_GRAD0_opsz24.svg @@ -0,0 +1 @@ + \ No newline at end of file From 821f96676ccbb0467091b74b8e33dd966b91af79 Mon Sep 17 00:00:00 2001 From: a1 Date: Tue, 6 Feb 2024 19:06:52 +0900 Subject: [PATCH 07/24] =?UTF-8?q?feat:=20=EA=B8=B0=EC=88=98=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EB=A9=A4=EB=B2=84ID,=20=EC=97=AD=ED=95=A0ID=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/common/Login.js | 13 ++ src/container/admin/FindMemId.js | 87 ++++++++---- src/container/admin/FindRole.js | 197 +++++++++++++++++++++++++++ src/container/product/GenRegis.js | 49 +++++-- src/container/product/ManageRegis.js | 32 +++-- src/container/product/QnARegis.js | 6 +- src/container/product/RoleRegis.js | 4 +- src/modules/idSlice.js | 22 +++ src/modules/rootReducer.js | 2 + 9 files changed, 353 insertions(+), 59 deletions(-) create mode 100644 src/modules/idSlice.js diff --git a/src/component/common/Login.js b/src/component/common/Login.js index eb245ff..29a0125 100644 --- a/src/component/common/Login.js +++ b/src/component/common/Login.js @@ -37,6 +37,7 @@ export default function Login() { type: 'login/setLogin', payload: { isLoggedIn: true }, }); + startLogoutTimer(); navigate(-1); }) .catch((err) => { @@ -46,6 +47,18 @@ export default function Login() { }); }; + const startLogoutTimer = () => { + const timer = setTimeout(() => { + window.sessionStorage.removeItem('token'); + dispatch({ + type: 'login/setLogin', + payload: { isLoggedIn: false }, + }); + alert('자동 로그아웃 되었습니다.'); + window.location.reload(); + }, 25 * 60 * 1000); + }; + return ( diff --git a/src/container/admin/FindMemId.js b/src/container/admin/FindMemId.js index 1739654..ec7f582 100644 --- a/src/container/admin/FindMemId.js +++ b/src/container/admin/FindMemId.js @@ -4,6 +4,7 @@ import axios from 'axios'; import Modal from 'react-modal'; // react-modal 라이브러리 import import { MemberModalopen, MemberModalclose } from '../../modules/ProductSlice'; import { useDispatch, useSelector } from 'react-redux'; +import { setMemberId } from '../../modules/idSlice'; import _ from 'lodash'; export default function FindMemId() { @@ -11,7 +12,6 @@ export default function FindMemId() { const memberModalOpen = useSelector( (state) => state.product.memberModalOpen ); - const [data, setData] = useState([]); const [newMember, setNewMember] = useState(''); @@ -44,6 +44,12 @@ export default function FindMemId() { } }; + const setId = (id) => { + console.log(id); + dispatch(setMemberId(id)); + closeModal(); + }; + return ( <> - 편성 추가 - 동아리원 + 이름으로 동아리원 찾기 + 이름 setNewMember(e.target.value)} /> + 검색 - 이름 - 번호 + 번호 + 이름 이메일 <> {data.map((member, index) => ( - + setId(member.member_id)}> + + {member.member_id} + {member.name} - {member.member_id} - {member.email} + {member.email} ))} - - 검색 - 취소 - ; ); } +const ModalName = styled.div` + margin-bottom: 5px; + margin-right: auto; + margin-left: 0.5rem; +`; + const ModalHeader = styled.div` position: absolute; ${(props) => props.type === 'name' - ? 'left: 10.2rem;' + ? 'left: 5.3rem;' : props.type === 'email' - ? 'left: 23.2rem;' - : ''} + ? 'left: 15rem;' + : 'left: 10rem;'} `; const LabelWrapper = styled.div` display: flex; margin-top: 1rem; + width: 70%; + height: 18px; + &:hover { + background-color: #f2f2f2; + transition: 0.5s ease-in-out; + } `; const ModalTemplate = styled.div` display: flex; - margin-bottom: 1rem; + margin-right: auto; + margin-left: 3rem; + width: 70%; + height: 40px; + background-color: white; + align-items: center; + border-radius: 10px; `; const ModalContainer = styled(Modal)` display: flex; @@ -111,7 +134,7 @@ const ModalContainer = styled(Modal)` border-radius: 8px; padding: 20px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); - width: 500px; + width: 400px; margin: 0 auto; position: absolute; top: 50%; @@ -129,9 +152,14 @@ const ModalTitle = styled.h2` const ModalLabel = styled.label` font-size: 1rem; - margin-bottom: 5px; - margin-right: auto; - margin-left: 75px; + position: absolute; + + ${(props) => + props.type === 'name' + ? 'left: 5.8rem;' + : props.type === 'email' + ? 'left: 14.7rem;' + : 'left: 9.7rem;'} `; const ModalInput = styled.input` @@ -141,16 +169,13 @@ const ModalInput = styled.input` border: 1px solid black; border-radius: 4px; font-size: 1rem; - - &: (6) { - width: 80%; - } + margin-right: auto; `; const ModalButtonWrapper = styled.div` display: flex; justify-content: space-between; - margin-top: 15px; + margin-top: 2rem; `; const ModalButton = styled.button` @@ -163,9 +188,13 @@ const ModalButton = styled.button` cursor: pointer; transition: background-color 0.2s ease-in-out; - & + & { - margin: 0 10px; - } + position: absolute; + left: 20.3rem; + top: 5.4rem; + + box-sizing: border-box; + width: 110px; + height: 38px; &:hover { background-color: #8181f7; diff --git a/src/container/admin/FindRole.js b/src/container/admin/FindRole.js index e69de29..3e02b61 100644 --- a/src/container/admin/FindRole.js +++ b/src/container/admin/FindRole.js @@ -0,0 +1,197 @@ +import { useState, useEffect, useCallback } from 'react'; +import styled from 'styled-components'; +import axios from 'axios'; +import Modal from 'react-modal'; // react-modal 라이브러리 import +import { RoleModalopen, RoleModalclose } from '../../modules/ProductSlice'; +import { useDispatch, useSelector } from 'react-redux'; +import { setRoleId } from '../../modules/idSlice'; +import _ from 'lodash'; + +export default function FindRole() { + const dispatch = useDispatch(); + const roleModalopen = useSelector((state) => state.product.roleModalOpen); + const roleId = useSelector((state) => state.id.role_id); + + const [data, setData] = useState([]); + + const [newMember, setNewMember] = useState(''); + + // 모달을 닫아주고 스크롤을 풀어줌. + const closeModal = () => { + dispatch(RoleModalclose()); + setData([]); + openScroll(); + }; + + const openScroll = useCallback(() => { + document.body.style.removeProperty('overflow'); + }, []); + + const findData = async () => { + try { + const newMemberEncode = encodeURIComponent(newMember); + const result = await axios.get( + `https://server.inuappcenter.kr/roles/id/${newMemberEncode}` + ); + console.log('Success:', result.data); + + // POST 요청 성공 시, 새로운 동아리원을 data 상태 변수에 추가합니다. + setData(result.data); + + setNewMember(''); + } catch (error) { + console.error('Error adding data:', error); + } + }; + + const setId = (id) => { + console.log(id); + dispatch(setRoleId(id)); + closeModal(); + }; + + return ( + <> + + 이름으로 역할 찾기 + 이름 + setNewMember(e.target.value)} + /> + 검색 + + 번호 + 이름 + + <> + {data.map((member, index) => ( + setId(member.roleId)}> + {member.roleId} + {member.roleName} + + ))} + + + + ); +} +const ModalName = styled.div` + margin-bottom: 5px; + margin-right: auto; + margin-left: 0.5rem; +`; + +const ModalHeader = styled.div` + position: absolute; + + ${(props) => + props.type === 'name' + ? 'left: 10.3rem;' + : props.type === 'email' + ? 'left: 15rem;' + : 'left: 15rem;'} +`; + +const LabelWrapper = styled.div` + display: flex; + margin-top: 1rem; + width: 40%; + height: 18px; + &:hover { + background-color: #f2f2f2; + transition: 0.5s ease-in-out; + } +`; + +const ModalTemplate = styled.div` + display: flex; + margin-right: auto; + margin-left: auto; + width: 35%; + height: 40px; + background-color: white; + align-items: center; + border-radius: 10px; +`; +const ModalContainer = styled(Modal)` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background-color: #f7f7f8; + border-radius: 8px; + padding: 20px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); + width: 400px; + margin: 0 auto; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +`; + +const ModalTitle = styled.h2` + font-size: 1.5rem; + margin-bottom: 15px; + margin-right: auto; + margin-top: 0; + font-weight: 400; +`; + +const ModalLabel = styled.label` + font-size: 1rem; + position: absolute; + + ${(props) => + props.type === 'name' + ? 'left:10.9rem;' + : props.type === 'email' + ? 'left: 14.7rem;' + : 'left: 14.8rem;'} +`; + +const ModalInput = styled.input` + width: 70%; + padding: 8px; + margin-bottom: 15px; + border: 1px solid black; + border-radius: 4px; + font-size: 1rem; + margin-right: auto; +`; + +const ModalButtonWrapper = styled.div` + display: flex; + justify-content: space-between; + margin-top: 2rem; +`; + +const ModalButton = styled.button` + background-color: grey; + color: #fff; + border: none; + border-radius: 4px; + padding: 8px 16px; + font-size: 1rem; + cursor: pointer; + transition: background-color 0.2s ease-in-out; + + position: absolute; + left: 20.3rem; + top: 5.4rem; + + box-sizing: border-box; + width: 110px; + height: 38px; + + &:hover { + background-color: #8181f7; + } +`; diff --git a/src/container/product/GenRegis.js b/src/container/product/GenRegis.js index 454ca18..10bd166 100644 --- a/src/container/product/GenRegis.js +++ b/src/container/product/GenRegis.js @@ -8,9 +8,11 @@ import { MemberModalopen, RoleModalopen, } from '../../modules/ProductSlice'; +import { setMemberId, setRoleId } from '../../modules/idSlice'; import { useDispatch, useSelector } from 'react-redux'; import _ from 'lodash'; import FindMemId from '../admin/FindMemId'; +import FindRole from '../admin/FindRole'; export default function GenRegis() { const [data, setData] = useState([]); @@ -23,12 +25,15 @@ export default function GenRegis() { ); const roleModalOpen = useSelector((state) => state.product.roleModalOpen); + const memberId = useSelector((state) => state.id.member_id); + const roleId = useSelector((state) => state.id.role_id); + // 새 멤버 추가 입력받을 상태 변수 const [newRole, setNewRole] = useState({ role_id: '', member_id: '', part: '', - year: 15, + year: '', }); const addData = async () => { @@ -46,16 +51,22 @@ export default function GenRegis() { role_id: '', member_id: '', part: '', - year: 16, + year: 15, }); dispatch(RMclose()); + dispatch(setMemberId('')); + dispatch(setRoleId('')); } catch (error) { + console.log(memberId); + console.log(newRole); console.error('Error adding data:', error); } }; // 모달을 닫아주고 스크롤을 풀어줌. const closeModal = () => { + dispatch(setMemberId('')); + dispatch(setRoleId('')); dispatch(RMclose()); openScroll(); }; @@ -68,6 +79,18 @@ export default function GenRegis() { dispatch(MemberModalopen()); }; + const openRoleModal = () => { + dispatch(RoleModalopen()); + }; + + useEffect(() => { + setNewRole({ ...newRole, member_id: memberId }); + }, [memberModalOpen]); + + useEffect(() => { + setNewRole({ ...newRole, role_id: roleId }); + }, [roleModalOpen]); + return ( <> - 편성 추가 + 기수 편성 동아리원 - setNewRole({ ...newRole, member_id: e.target.value }) - } + value={memberId} onClick={() => openMemberModal()} /> {memberModalOpen && } @@ -91,15 +111,14 @@ export default function GenRegis() { - setNewRole({ ...newRole, role_id: e.target.value }) - } + value={roleId} + onClick={() => openRoleModal()} /> - 파트명 + {roleModalOpen && } + 파트 setNewRole({ ...newRole, part: e.target.value }) @@ -107,8 +126,8 @@ export default function GenRegis() { /> 기수 setNewRole({ ...newRole, year: e.target.value }) diff --git a/src/container/product/ManageRegis.js b/src/container/product/ManageRegis.js index 36e5268..80ffbec 100644 --- a/src/container/product/ManageRegis.js +++ b/src/container/product/ManageRegis.js @@ -71,7 +71,7 @@ export default function ManageRegis() { 이름 setNewMember({ ...newMember, name: e.target.value }) @@ -80,7 +80,7 @@ export default function ManageRegis() { 이메일 setNewMember({ ...newMember, email: e.target.value }) @@ -89,7 +89,7 @@ export default function ManageRegis() { 블로그 URL setNewMember({ ...newMember, blogLink: e.target.value }) @@ -98,7 +98,7 @@ export default function ManageRegis() { Git URL setNewMember({ @@ -110,7 +110,7 @@ export default function ManageRegis() { 프로필 이미지 URL setNewMember({ @@ -120,9 +120,9 @@ export default function ManageRegis() { } /> 설명 - setNewMember({ @@ -141,6 +141,20 @@ export default function ManageRegis() { ); } +const InfoInput = styled.textarea` + height: 8rem; + box-shadow: 0 0 1px rgba(0, 0, 0, 0.3); + word-break: break-all; + resize: none; + + width: 70%; + padding: 8px; + margin-bottom: 15px; + border: 1px solid black; + border-radius: 4px; + font-size: 1rem; +`; + const ModalContainer = styled(Modal)` display: flex; flex-direction: column; @@ -180,10 +194,6 @@ const ModalInput = styled.input` border: 1px solid black; border-radius: 4px; font-size: 1rem; - - &: (6) { - width: 80%; - } `; const ModalButtonWrapper = styled.div` diff --git a/src/container/product/QnARegis.js b/src/container/product/QnARegis.js index a3cf070..c6a647a 100644 --- a/src/container/product/QnARegis.js +++ b/src/container/product/QnARegis.js @@ -64,7 +64,7 @@ export default function QnARegis() { 파트 setNewQna({ ...newQna, part: e.target.value }) @@ -73,7 +73,7 @@ export default function QnARegis() { 질문 setNewQna({ ...newQna, question: e.target.value }) @@ -82,7 +82,7 @@ export default function QnARegis() { 답변 setNewQna({ ...newQna, answer: e.target.value }) diff --git a/src/container/product/RoleRegis.js b/src/container/product/RoleRegis.js index 44f5961..fdde6f4 100644 --- a/src/container/product/RoleRegis.js +++ b/src/container/product/RoleRegis.js @@ -58,10 +58,11 @@ export default function RoleRegis() { contentLabel='Edit Role Modal' > 역할 등록 - 역할 + 역할 이름 setNewRole({ ...newRole, roleName: e.target.value }) } @@ -70,6 +71,7 @@ export default function RoleRegis() { setNewRole({ ...newRole, description: e.target.value }) } diff --git a/src/modules/idSlice.js b/src/modules/idSlice.js new file mode 100644 index 0000000..4d78a88 --- /dev/null +++ b/src/modules/idSlice.js @@ -0,0 +1,22 @@ +import { createSlice } from '@reduxjs/toolkit'; + +const initialState = { + member_id: '', + role_id: '', + message: '', +}; + +export const idSlice = createSlice({ + name: 'id', + initialState, + reducers: { + setMemberId: (state, action) => { + state.member_id = action.payload; + }, + setRoleId: (state, action) => { + state.role_id = action.payload; + }, + }, +}); + +export const { setMemberId, setRoleId } = idSlice.actions; diff --git a/src/modules/rootReducer.js b/src/modules/rootReducer.js index 1cadf68..26166a3 100644 --- a/src/modules/rootReducer.js +++ b/src/modules/rootReducer.js @@ -3,6 +3,7 @@ import { ourTeamSlice } from './ourTeamSlice'; import { faqSlice } from './faqSlice'; import { ProductSlice } from './ProductSlice'; import { LoginSlice } from './LoginSlice'; +import { idSlice } from './idSlice'; const rootReducer = { home: homeSlice.reducer, @@ -10,5 +11,6 @@ const rootReducer = { faq: faqSlice.reducer, product: ProductSlice.reducer, login: LoginSlice.reducer, + id: idSlice.reducer, }; export default rootReducer; From 3eee75f199d229b3b30bcdc176fbb68279e2ef87 Mon Sep 17 00:00:00 2001 From: a1 Date: Wed, 7 Feb 2024 01:34:27 +0900 Subject: [PATCH 08/24] =?UTF-8?q?style:=EC=A0=84=EC=B2=B4=EC=A0=81?= =?UTF-8?q?=EC=9D=B8=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=B0=8F=20eslint=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/common/Footer.js | 2 +- src/component/common/InOut.js | 3 +- src/component/common/Login.js | 8 +- src/component/home/ProductionDesktop.js | 3 +- src/component/ourteam/MemberList.js | 2 +- src/container/admin/FindMemId.js | 11 +- src/container/admin/FindRole.js | 12 +- src/container/faq/FAQDetailListContainer.js | 4 +- src/container/ourteam/PartContainer.js | 2 +- src/container/product/GenRegis.js | 6 +- src/container/product/ManageRegis.js | 7 +- src/container/product/ModifyModal.js | 18 ++- src/container/product/ProductRegis.js | 18 +-- src/container/product/QnARegis.js | 11 +- src/container/product/RoleRegis.js | 7 +- src/page/AdminPage.js | 43 +++--- src/page/CenterPage.js | 12 -- src/page/ManageGenPage.js | 100 +++----------- src/page/ManagePage.js | 138 +++++++++----------- src/page/ManageRolePage.js | 66 +--------- src/page/ProductPage.js | 27 +--- src/page/QnAPage.js | 60 +-------- 22 files changed, 171 insertions(+), 389 deletions(-) diff --git a/src/component/common/Footer.js b/src/component/common/Footer.js index 5063389..2d5ac20 100644 --- a/src/component/common/Footer.js +++ b/src/component/common/Footer.js @@ -25,7 +25,7 @@ export default function Footer() { /> - + 인천대앱센터 관리자 페이지 { @@ -44,7 +45,7 @@ export default function InOut() { ) : ( - + )} diff --git a/src/component/common/Login.js b/src/component/common/Login.js index 29a0125..5f3ab94 100644 --- a/src/component/common/Login.js +++ b/src/component/common/Login.js @@ -1,8 +1,8 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState } from 'react'; import styled from 'styled-components'; import axios from 'axios'; import { useNavigate } from 'react-router-dom'; -import { useSelector, useDispatch } from 'react-redux'; +import { useDispatch } from 'react-redux'; import LoginLogo from '../../resource/img/Login_logo.png'; export default function Login() { @@ -10,7 +10,6 @@ export default function Login() { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); - const isLoggedIn = useSelector((state) => state.login.isLoggedIn); const dispatch = useDispatch(); const handleUsernameChange = (e) => { @@ -32,6 +31,7 @@ export default function Login() { const { token } = res.data; axios.defaults.headers.common['X-AUTH-TOKEN'] = token; + console.log(response); window.sessionStorage.setItem('token', token); dispatch({ type: 'login/setLogin', @@ -55,6 +55,7 @@ export default function Login() { payload: { isLoggedIn: false }, }); alert('자동 로그아웃 되었습니다.'); + console.log(timer); window.location.reload(); }, 25 * 60 * 1000); }; @@ -129,6 +130,7 @@ const Label = styled.label` display: block; margin-bottom: 5px; margin-right: 2px; + color: white; &:nth-child(1) { margin-left: 8px; diff --git a/src/component/home/ProductionDesktop.js b/src/component/home/ProductionDesktop.js index 4d56fc0..a07ef80 100644 --- a/src/component/home/ProductionDesktop.js +++ b/src/component/home/ProductionDesktop.js @@ -17,7 +17,7 @@ export default function ProductionDesktop() { const [modalOpen, setModalOpen] = useState(false); const [appData, setAppData] = useState([]); const [imageData, setImageData] = useState([]); - const [firstImage, setFirstImage] = useState(''); + const breakPoint = { 680: { slidesPerView: 3, @@ -42,6 +42,7 @@ export default function ProductionDesktop() { ) .then((res) => { setData(res.data); + console.log(viewData); }); }; fetchData(); diff --git a/src/component/ourteam/MemberList.js b/src/component/ourteam/MemberList.js index c297853..81ba8db 100644 --- a/src/component/ourteam/MemberList.js +++ b/src/component/ourteam/MemberList.js @@ -21,7 +21,7 @@ export default function MemberList({ data }) { ) .then((res) => { setGroup(res.data); - console.log(res.data); + console.log(viewData); }); }; fetchData(); diff --git a/src/container/admin/FindMemId.js b/src/container/admin/FindMemId.js index ec7f582..b38d8b2 100644 --- a/src/container/admin/FindMemId.js +++ b/src/container/admin/FindMemId.js @@ -1,11 +1,10 @@ -import { useState, useEffect, useCallback } from 'react'; +import { useState, useCallback } from 'react'; import styled from 'styled-components'; import axios from 'axios'; import Modal from 'react-modal'; // react-modal 라이브러리 import -import { MemberModalopen, MemberModalclose } from '../../modules/ProductSlice'; +import { MemberModalclose } from '../../modules/ProductSlice'; import { useDispatch, useSelector } from 'react-redux'; import { setMemberId } from '../../modules/idSlice'; -import _ from 'lodash'; export default function FindMemId() { const dispatch = useDispatch(); @@ -172,12 +171,6 @@ const ModalInput = styled.input` margin-right: auto; `; -const ModalButtonWrapper = styled.div` - display: flex; - justify-content: space-between; - margin-top: 2rem; -`; - const ModalButton = styled.button` background-color: grey; color: #fff; diff --git a/src/container/admin/FindRole.js b/src/container/admin/FindRole.js index 3e02b61..1781e94 100644 --- a/src/container/admin/FindRole.js +++ b/src/container/admin/FindRole.js @@ -1,16 +1,14 @@ -import { useState, useEffect, useCallback } from 'react'; +import { useState, useCallback } from 'react'; import styled from 'styled-components'; import axios from 'axios'; import Modal from 'react-modal'; // react-modal 라이브러리 import -import { RoleModalopen, RoleModalclose } from '../../modules/ProductSlice'; +import { RoleModalclose } from '../../modules/ProductSlice'; import { useDispatch, useSelector } from 'react-redux'; import { setRoleId } from '../../modules/idSlice'; -import _ from 'lodash'; export default function FindRole() { const dispatch = useDispatch(); const roleModalopen = useSelector((state) => state.product.roleModalOpen); - const roleId = useSelector((state) => state.id.role_id); const [data, setData] = useState([]); @@ -167,12 +165,6 @@ const ModalInput = styled.input` margin-right: auto; `; -const ModalButtonWrapper = styled.div` - display: flex; - justify-content: space-between; - margin-top: 2rem; -`; - const ModalButton = styled.button` background-color: grey; color: #fff; diff --git a/src/container/faq/FAQDetailListContainer.js b/src/container/faq/FAQDetailListContainer.js index e6f44de..6ea87d8 100644 --- a/src/container/faq/FAQDetailListContainer.js +++ b/src/container/faq/FAQDetailListContainer.js @@ -1,6 +1,6 @@ import styled from 'styled-components'; import { useLocation } from 'react-router-dom'; -import { useEffect, useState, useMemo } from 'react'; +import { useEffect, useState } from 'react'; import axios from 'axios'; import { partInfo } from '../../resource/string/partInfo'; import { PageTitle } from '../../component/common/PageTitle'; @@ -19,6 +19,7 @@ export function FAQDetailListContainer() { 'https://server.inuappcenter.kr/faqs/public/all-faq-boards' ) .then((res) => { + console.log(viewData); setData( res.data.filter( (item) => item.part === pageInfo.partName @@ -32,6 +33,7 @@ export function FAQDetailListContainer() { }); }; fetchData(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [location]); useEffect(() => { diff --git a/src/container/ourteam/PartContainer.js b/src/container/ourteam/PartContainer.js index 9158450..f33ecfd 100644 --- a/src/container/ourteam/PartContainer.js +++ b/src/container/ourteam/PartContainer.js @@ -22,7 +22,7 @@ export default function PartContainer() { ) .then((res) => { setGroup(res.data); - console.log(res.data); + console.log(viewData); }); }; fetchData(); diff --git a/src/container/product/GenRegis.js b/src/container/product/GenRegis.js index 10bd166..a49ac75 100644 --- a/src/container/product/GenRegis.js +++ b/src/container/product/GenRegis.js @@ -3,14 +3,12 @@ import styled from 'styled-components'; import axios from 'axios'; import Modal from 'react-modal'; // react-modal 라이브러리 import import { - RMopen, RMclose, MemberModalopen, RoleModalopen, } from '../../modules/ProductSlice'; import { setMemberId, setRoleId } from '../../modules/idSlice'; import { useDispatch, useSelector } from 'react-redux'; -import _ from 'lodash'; import FindMemId from '../admin/FindMemId'; import FindRole from '../admin/FindRole'; @@ -85,10 +83,12 @@ export default function GenRegis() { useEffect(() => { setNewRole({ ...newRole, member_id: memberId }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [memberModalOpen]); useEffect(() => { setNewRole({ ...newRole, role_id: roleId }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [roleModalOpen]); return ( @@ -195,7 +195,7 @@ const ModalButtonWrapper = styled.div` `; const ModalButton = styled.button` - background-color: grey; + background-color: #1e88e5; color: #fff; border: none; border-radius: 4px; diff --git a/src/container/product/ManageRegis.js b/src/container/product/ManageRegis.js index 80ffbec..3bea199 100644 --- a/src/container/product/ManageRegis.js +++ b/src/container/product/ManageRegis.js @@ -1,10 +1,9 @@ -import { useState, useEffect, useCallback } from 'react'; +import { useState, useCallback } from 'react'; import styled from 'styled-components'; import axios from 'axios'; import Modal from 'react-modal'; // react-modal 라이브러리 import -import { RMopen, RMclose } from '../../modules/ProductSlice'; +import { RMclose } from '../../modules/ProductSlice'; import { useDispatch, useSelector } from 'react-redux'; -import _ from 'lodash'; export default function ManageRegis() { const [data, setData] = useState([]); @@ -203,7 +202,7 @@ const ModalButtonWrapper = styled.div` `; const ModalButton = styled.button` - background-color: grey; + background-color: #1e88e5; color: #fff; border: none; border-radius: 4px; diff --git a/src/container/product/ModifyModal.js b/src/container/product/ModifyModal.js index 8aa2104..d5e269b 100644 --- a/src/container/product/ModifyModal.js +++ b/src/container/product/ModifyModal.js @@ -94,6 +94,7 @@ export default function ModifyModal(props) { ); console.log('Member with ID', id, 'has been updated.'); console.log(response); + console.log(data); // 업데이트된 데이터를 data 상태에서 업데이트합니다. setData((prevData) => prevData.map((item) => @@ -135,8 +136,10 @@ export default function ModifyModal(props) { setImageData([firstValue]); // 디테일 이미지 setDetailImageData([secondValue, thirdValue, fourthValue]); + console.log(detailImageData); setShowImages([secondValue, thirdValue, fourthValue]); }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const onchangeImageUpload = (e) => { @@ -209,6 +212,7 @@ export default function ModifyModal(props) { setUpdateProduct({ ...updateProduct, @@ -221,6 +225,7 @@ export default function ModifyModal(props) { setUpdateProduct({ ...updateProduct, @@ -233,6 +238,7 @@ export default function ModifyModal(props) { setUpdateProduct({ ...updateProduct, @@ -243,6 +249,7 @@ export default function ModifyModal(props) { setUpdateProduct({ ...updateProduct, @@ -254,6 +261,7 @@ export default function ModifyModal(props) { setUpdateProduct({ @@ -265,7 +273,7 @@ export default function ModifyModal(props) { - + - + - +
- + ))} @@ -312,7 +311,8 @@ const CloseImg = styled.img` top: -0.5rem; border: 1px solid black; border-radius: 50%; - z-index: 1; + z-index: 1000; + background-color: white; &: hover { transition: 0.3s ease-in-out; @@ -350,6 +350,7 @@ const DetailImage = styled.img` height: 400px; z-index: 0; + margin-top: 1rem; margin-left: 0.01rem; margin-right: 0.72rem; `; @@ -360,6 +361,7 @@ const DeleteBtn = styled.button` background-color: transparent; border-radius: 15px; left: 10.8rem; + margin-top: 1rem; z-index: 1; ${({ regisModalOpen }) => !regisModalOpen && `display: none`} @@ -374,7 +376,7 @@ const NavBar = styled.div` box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); align-items: center; - top: 42.5rem; + top: 43.5rem; left: -1rem; `; @@ -390,7 +392,7 @@ const LinkBox = styled.div` const Regisbutton = styled.button` border: none; - background-color: grey; + background-color: #1e88e5; border-radius: 5px; color: white; width: 5rem; diff --git a/src/container/product/QnARegis.js b/src/container/product/QnARegis.js index c6a647a..82c8920 100644 --- a/src/container/product/QnARegis.js +++ b/src/container/product/QnARegis.js @@ -1,10 +1,9 @@ -import { useState, useEffect, useCallback } from 'react'; +import { useState, useCallback } from 'react'; import styled from 'styled-components'; import axios from 'axios'; import Modal from 'react-modal'; // react-modal 라이브러리 import -import { RMopen, RMclose } from '../../modules/ProductSlice'; +import { RMclose } from '../../modules/ProductSlice'; import { useDispatch, useSelector } from 'react-redux'; -import _ from 'lodash'; export default function QnARegis() { const [data, setData] = useState([]); @@ -28,16 +27,16 @@ export default function QnARegis() { newQna ); + console.log('Success:', result.data); // POST 요청 성공 시, 새로운 질문을 data 상태 변수에 추가합니다. setData([...data, newQna]); - setNewQna({ answer: '', id: '', part: '', question: '', }); - dispatch(RMclose()); + closeModal(); } catch (error) { console.error('Error adding data:', error); } @@ -150,7 +149,7 @@ const ModalButtonWrapper = styled.div` `; const ModalButton = styled.button` - background-color: grey; + background-color: #1e88e5; color: #fff; border: none; border-radius: 4px; diff --git a/src/container/product/RoleRegis.js b/src/container/product/RoleRegis.js index fdde6f4..7b8ba0c 100644 --- a/src/container/product/RoleRegis.js +++ b/src/container/product/RoleRegis.js @@ -1,10 +1,9 @@ -import { useState, useEffect, useCallback } from 'react'; +import { useState, useCallback } from 'react'; import styled from 'styled-components'; import axios from 'axios'; import Modal from 'react-modal'; // react-modal 라이브러리 import -import { RMopen, RMclose } from '../../modules/ProductSlice'; +import { RMclose } from '../../modules/ProductSlice'; import { useDispatch, useSelector } from 'react-redux'; -import _ from 'lodash'; export default function RoleRegis() { const [data, setData] = useState([]); @@ -138,7 +137,7 @@ const ModalButtonWrapper = styled.div` `; const ModalButton = styled.button` - background-color: grey; + background-color: #1e88e5; color: #fff; border: none; border-radius: 4px; diff --git a/src/page/AdminPage.js b/src/page/AdminPage.js index 232db39..c876420 100644 --- a/src/page/AdminPage.js +++ b/src/page/AdminPage.js @@ -1,12 +1,10 @@ import styled, { css } from 'styled-components'; import { Link } from 'react-router-dom'; -import { useSelector, useDispatch } from 'react-redux'; -import { useEffect } from 'react'; -import axios from 'axios'; import InOut from '../component/common/InOut'; import Phone from '../resource/img/phone.svg'; import Face from '../resource/img/face.svg'; import Camera from '../resource/img/camera.svg'; +import Question from '../resource/img/quiz_FILL0_wght400_GRAD0_opsz24.svg'; export default function AdminPage() { return ( @@ -58,15 +56,28 @@ export default function AdminPage() { + + + + + + + {'질문 관리'} + + {'질문과 답변을 추가, 삭제, 수정을 할 수 있어요'} + + + + ); } const PhotoBox = styled.div` width: 7%; - height: 50%; + height: 73%; background-color: white; - margin-bottom: auto; - border-radius: 8px; + margin-bottom: 2.2rem; + border-radius: 12px; `; const TextBox = styled.div` @@ -87,15 +98,14 @@ const MenuText = styled.div` text-transform: uppercase; color: ${(props) => (props.type === 'title' ? '#424242' : '#848484')}; font-weight: ${(props) => - props.type === 'top' ? 400 : props.type === 'title' ? 300 : 300}; + props.type === 'top' ? 400 : props.type === 'title' ? 600 : 300}; margin: auto 0; white-space: pre-line; ${(props) => props.type === 'title' ? css` - font-size: ${(props) => - props.theme.fontSize.smallTablet.title}; + font-size: 30px;}; ` : css` font-size: ${(props) => props.theme.fontSize.tablet.caption}; @@ -114,7 +124,7 @@ const MenuBox = styled.div` display: flex; padding: 2.5rem 0 0 1.4rem; width: 600px; - height: 8rem; + height: 6rem; background-color: #f2f2f2; margin: 0 auto 1.5rem auto; top: 20px; @@ -127,19 +137,6 @@ const MenuBox = styled.div` transition: 0.3s ease-in-out; } `; -const NavBar = styled.div` - position: absolute; - display: flex; - justify-content: center; - position: relative; - height: 25px; - width: 730px; - margin: 55px auto 10px auto; - - .menu { - margin-left: auto; - } -`; const IntroBox = styled.div` display: flex; diff --git a/src/page/CenterPage.js b/src/page/CenterPage.js index 88754d7..86f762f 100644 --- a/src/page/CenterPage.js +++ b/src/page/CenterPage.js @@ -4,7 +4,6 @@ import InOut from '../component/common/InOut'; import Generation from '../resource/img/groups_FILL0_wght400_GRAD0_opsz24.svg'; import Member from '../resource/img/person_FILL0_wght400_GRAD0_opsz24.svg'; import Role from '../resource/img/group_FILL0_wght400_GRAD0_opsz24.svg'; -import Question from '../resource/img/quiz_FILL0_wght400_GRAD0_opsz24.svg'; export default function AdminPage() { return ( @@ -54,17 +53,6 @@ export default function AdminPage() { - - - - - - {'질문 관리'} - - {'질문과 답변을 추가, 삭제, 수정을 할 수 있어요'} - - - ); diff --git a/src/page/ManageGenPage.js b/src/page/ManageGenPage.js index 632d08a..45ad7f1 100644 --- a/src/page/ManageGenPage.js +++ b/src/page/ManageGenPage.js @@ -1,4 +1,4 @@ -import styled, { css } from 'styled-components'; +import styled from 'styled-components'; import axios from 'axios'; import React, { useState, useEffect, useRef } from 'react'; import Modal from 'react-modal'; // react-modal 라이브러리 import @@ -65,6 +65,7 @@ export default function ManageGenPage() { setEditedRole(memberToEdit.role); setEditedGen(memberToEdit.generation); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedGroupId]); const closeEditModal = () => { @@ -88,6 +89,7 @@ export default function ManageGenPage() { ) .then((res) => { setData(res.data); + console.log(viewData); }); }; fetchData(); @@ -164,6 +166,7 @@ export default function ManageGenPage() { ); } catch (error) { console.error('Error deleting member:', error); + alert('삭제에 실패했습니다.'); } setContextMenuVisible(false); // 컨텍스트 메뉴 닫기 @@ -193,26 +196,6 @@ export default function ManageGenPage() { {content.member} {content.role} {content.year} - - - blog - - - - - github - - - {content.profileImage} - {content.email} ))} @@ -261,14 +244,15 @@ export default function ManageGenPage() { value={editedRole} onChange={(e) => setEditedRole(e.target.value)} /> + 기수 setEditedGen(e.target.value)} /> - 수정 완료 취소 + 수정 완료 @@ -288,9 +272,9 @@ const ModalContainer = styled(Modal)` justify-content: center; background-color: #fff; border-radius: 8px; - border: 2px solid #5858fa; + border: 2px solid grey; padding: 20px; - max-width: 400px; + width: 500px; margin: 0 auto; position: absolute; top: 50%; @@ -324,7 +308,7 @@ const ModalButtonWrapper = styled.div` `; const ModalButton = styled.button` - background-color: #5858fa; + background-color: #1e88e5; color: #fff; border: none; border-radius: 4px; @@ -373,7 +357,7 @@ const ContextMenu = styled.div` const Regisbutton = styled.button` position: absolute; border: none; - background-color: grey; + background-color: #1e88e5; border-radius: 5px; color: white; width: 5rem; @@ -387,38 +371,23 @@ const Regisbutton = styled.button` } `; -const AddMember = styled.input` - border-radius: 5px; - width: 80px; - height: 22px; - - :first-child { - margin-right: 5px; - width: 80px; - } - - & + & { - margin-right: 5px; - } - - ::placeholder { - text-align: center; - } -`; - const MemberTable = styled.table` - width: 700px; - border-collapse: collapse; + width: 600px; margin: 20px auto 20px auto; - th, td { - padding: 5px; + padding: 6px; text-align: center; - } + box-shadow: 0 0 3px rgba(0, 0, 0, 0.2); + border-right: 1px solid black; + border-radius: 8px; + overflow: hidden; + white-space: nowrap; th { font-weight: 700; + padding: 5px; + text-align: center; } a { @@ -435,37 +404,6 @@ const MemberTable = styled.table` } `; -const AddList = styled.div` - display: flex; - justify-content: center; - position: relative; - flex-wrap: wrap; - height: 25px; - width: 400px; - margin: 0 auto; - - font-size: 1.6rem; - padding-left: 2.5rem; - - .menu { - margin-left: auto; - } -`; - -const Addtitle = styled.div` - position: absolute; - display: flex; - position: relative; - height: 25px; - width: 730px; - margin: 0 auto 1.5rem auto; - font-size: 1.6rem; - - .menu { - margin-left: auto; - } -`; - const MemberList = styled.div` position: absolute; display: flex; diff --git a/src/page/ManagePage.js b/src/page/ManagePage.js index 396583b..459f2a2 100644 --- a/src/page/ManagePage.js +++ b/src/page/ManagePage.js @@ -1,4 +1,4 @@ -import styled, { css } from 'styled-components'; +import styled from 'styled-components'; import axios from 'axios'; import React, { useState, useEffect, useRef } from 'react'; import Modal from 'react-modal'; // react-modal 라이브러리 import @@ -13,7 +13,6 @@ import ManageRegis from '../container/product/ManageRegis'; export default function ManagePage() { const [data, setData] = useState([]); - const [loading, isLoading] = useState(false); const regisModalOpen = useSelector((state) => state.product.regisModalOpen); // prettier-ignore @@ -74,6 +73,7 @@ export default function ManagePage() { setEditedEmail(memberToEdit.email); setEditedGitRepositoryLink(memberToEdit.gitRepositoryLink); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedMemberId]); const closeEditModal = () => { @@ -91,12 +91,11 @@ export default function ManagePage() { useEffect(() => { const fetchData = async () => { - isLoading(true); const viewData = await axios .get('https://server.inuappcenter.kr/members/all-members') .then((res) => { - isLoading(false); setData(res.data); + console.log(viewData); }); }; fetchData(); @@ -185,6 +184,7 @@ export default function ManagePage() { ); } catch (error) { console.error('Error deleting member:', error); + alert('삭제에 실패했습니다.'); } setContextMenuVisible(false); // 컨텍스트 메뉴 닫기 @@ -211,27 +211,53 @@ export default function ManagePage() { }} > {content.name} - {content.email} - - Visit Blog - + {content.email ? ( + <>{content.email} + ) : ( + 'no Email' + )} - - github - + {content.blogLink ? ( + + Visit Blog + + ) : ( + 'no Blog' + )} + + + {content.gitRepositoryLink ? ( + + github + + ) : ( + 'no Github' + )} + + + {content.profileImage ? ( + <>{content.profileImage} + ) : ( + 'no profileImage' + )} + + + {content.description ? ( + <>{content.description} + ) : ( + 'no description' + )} - {content.profileImage} - {content.description} ))} @@ -279,7 +305,7 @@ export default function ManagePage() { value={editedName} onChange={(e) => setEditedName(e.target.value)} /> - 설명 + 자기 소개 setEditedGitRepositoryLink(e.target.value)} /> - 수정 완료 취소 + 수정 완료 @@ -367,7 +393,7 @@ const ModalButtonWrapper = styled.div` `; const ModalButton = styled.button` - background-color: grey; + background-color: #1e88e5; color: #fff; border: none; border-radius: 4px; @@ -416,7 +442,7 @@ const ContextMenu = styled.div` const Regisbutton = styled.button` position: absolute; border: none; - background-color: grey; + background-color: #1e88e5; border-radius: 5px; color: white; width: 5rem; @@ -430,38 +456,23 @@ const Regisbutton = styled.button` } `; -const AddMember = styled.input` - border-radius: 5px; - width: 112px; - height: 22px; - - :first-child { - margin-right: 5px; - width: 50px; - } - - & + & { - margin-right: 5px; - } - - ::placeholder { - text-align: center; - } -`; - const MemberTable = styled.table` - width: 700px; - border-collapse: collapse; + width: 900px; margin: 20px auto 20px auto; - th, td { - padding: 5px; + padding: 6px; text-align: center; - } + box-shadow: 0 0 3px rgba(0, 0, 0, 0.2); + border-right: 1px solid black; + border-radius: 8px; + overflow: hidden; + white-space: nowrap; th { font-weight: 700; + padding: 5px; + text-align: center; } a { @@ -478,35 +489,6 @@ const MemberTable = styled.table` } `; -const AddList = styled.div` - display: flex; - position: relative; - flex-wrap: wrap; - height: 25px; - width: 730px; - margin: 0 auto; - font-size: 1.6rem; - padding-left: 2.5rem; - - .menu { - margin-left: auto; - } -`; - -const Addtitle = styled.div` - position: absolute; - display: flex; - position: relative; - height: 25px; - width: 730px; - margin: 0 auto 1.5rem auto; - font-size: 1.6rem; - - .menu { - margin-left: auto; - } -`; - const MemberList = styled.div` position: absolute; display: flex; diff --git a/src/page/ManageRolePage.js b/src/page/ManageRolePage.js index 9345fa8..16572ca 100644 --- a/src/page/ManageRolePage.js +++ b/src/page/ManageRolePage.js @@ -1,4 +1,4 @@ -import styled, { css } from 'styled-components'; +import styled from 'styled-components'; import axios from 'axios'; import React, { useState, useEffect, useRef } from 'react'; import Modal from 'react-modal'; // react-modal 라이브러리 import @@ -63,6 +63,7 @@ export default function ManageRolePage() { setEditedRoleName(RoleToEdit.roleName); setEditedDesc(RoleToEdit.description); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedRoleId]); const closeEditModal = () => { @@ -84,7 +85,7 @@ export default function ManageRolePage() { .get('https://server.inuappcenter.kr/roles/all-roles') .then((res) => { setData(res.data); - console.log(data); + console.log(viewData); }); }; fetchData(); @@ -161,6 +162,7 @@ export default function ManageRolePage() { ); } catch (error) { console.error('Error deleting member:', error); + alert('삭제에 실패했습니다.'); } setContextMenuVisible(false); // 컨텍스트 메뉴 닫기 @@ -244,8 +246,8 @@ export default function ManageRolePage() { onChange={(e) => setEditedDesc(e.target.value)} /> - 수정 완료 취소 + 수정 완료 @@ -301,7 +303,7 @@ const ModalButtonWrapper = styled.div` `; const ModalButton = styled.button` - background-color: grey; + background-color: #1e88e5; color: #fff; border: none; border-radius: 4px; @@ -350,7 +352,7 @@ const ContextMenu = styled.div` const Regisbutton = styled.button` position: absolute; border: none; - background-color: grey; + background-color: #1e88e5; border-radius: 5px; color: white; width: 5rem; @@ -364,29 +366,6 @@ const Regisbutton = styled.button` } `; -const AddMember = styled.input` - border-radius: 5px; - width: 112px; - height: 22px; - - :first-child { - margin-right: 0.5rem; - width: 100px; - } - - :nth-child(2) { - width: 200px; - } - - & + & { - margin-right: 10px; - } - - ::placeholder { - text-align: center; - } -`; - const MemberTable = styled.table` width: 700px; border-collapse: collapse; @@ -416,37 +395,6 @@ const MemberTable = styled.table` } `; -const AddList = styled.div` - display: flex; - position: relative; - flex-wrap: wrap; - height: 25px; - width: 400px; - justify-content: center; - margin: 0 auto; - - font-size: 1.6rem; - padding-left: 3.9rem; - - .menu { - margin-left: auto; - } -`; - -const Addtitle = styled.div` - position: absolute; - display: flex; - position: relative; - height: 25px; - width: 730px; - margin: 0 auto 1.5rem auto; - font-size: 1.6rem; - - .menu { - margin-left: auto; - } -`; - const MemberList = styled.div` position: absolute; display: flex; diff --git a/src/page/ProductPage.js b/src/page/ProductPage.js index 0d382ff..47a8d9d 100644 --- a/src/page/ProductPage.js +++ b/src/page/ProductPage.js @@ -1,4 +1,4 @@ -import styled, { css } from 'styled-components'; +import styled from 'styled-components'; import axios from 'axios'; import React, { useState, useEffect, useRef } from 'react'; import Pagination from '../component/manage/Pagenation'; @@ -29,14 +29,6 @@ export default function ProductPage() { const contextMenuRef = useRef(null); const [productId, setProductId] = useState(''); - //* 수정 기능을 이용할 때 값을 저장하기 위해 사용합니다. */ - const [editedName, setEditedName] = useState(''); - const [editedDescription, setEditedDescription] = useState(''); - const [editedProfileImage, setEditedProfileImage] = useState(''); - const [editedBlogLink, setEditedBlogLink] = useState(''); - const [editedEmail, setEditedEmail] = useState(''); - const [editedGitRepositoryLink, setEditedGitRepositoryLink] = useState(''); - // 페이지네이션을 구현할때 사용합니다. const [currentPage, setCurrentPage] = useState(1); const itemsPerPage = 3; @@ -63,18 +55,6 @@ export default function ProductPage() { dispatch(MODopen()); }; - useEffect(() => { - const memberToEdit = data.find((item) => item.id === selectedProductId); - if (memberToEdit) { - setEditedName(memberToEdit.name); - setEditedDescription(memberToEdit.description); - setEditedProfileImage(memberToEdit.profileImage); - setEditedBlogLink(memberToEdit.blogLink); - setEditedEmail(memberToEdit.email); - setEditedGitRepositoryLink(memberToEdit.gitRepositoryLink); - } - }, [selectedProductId]); - const addData = () => { dispatch(RMopen()); scrollLock(); @@ -94,7 +74,7 @@ export default function ProductPage() { .then((res) => { isLoading(false); setData(res.data); - console.log(res.data); + console.log(viewData); }); }; fetchData(); @@ -141,6 +121,7 @@ export default function ProductPage() { ); } catch (error) { console.error('Error deleting member:', error); + alert('삭제에 실패했습니다.'); } setContextMenuVisible(false); // 컨텍스트 메뉴 닫기 @@ -273,7 +254,7 @@ const ContextMenu = styled.div` const Regisbutton = styled.button` position: absolute; border: none; - background-color: grey; + background-color: #1e88e5; border-radius: 5px; color: white; width: 5rem; diff --git a/src/page/QnAPage.js b/src/page/QnAPage.js index 985b9e4..5a3326c 100644 --- a/src/page/QnAPage.js +++ b/src/page/QnAPage.js @@ -1,4 +1,4 @@ -import styled, { css } from 'styled-components'; +import styled from 'styled-components'; import axios from 'axios'; import React, { useState, useEffect, useRef } from 'react'; import Modal from 'react-modal'; // react-modal 라이브러리 import @@ -66,6 +66,7 @@ export default function QnAPage() { setEditedQuestion(QnaToEdit.question); setEditedAnswer(QnaToEdit.answer); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedQnaId]); const closeEditModal = () => { @@ -91,6 +92,7 @@ export default function QnAPage() { .then((res) => { isLoading(false); setData(res.data); + console.log(viewData); }); }; fetchData(); @@ -315,7 +317,7 @@ const ModalButtonWrapper = styled.div` `; const ModalButton = styled.button` - background-color: grey; + background-color: #1e88e5; color: #fff; border: none; border-radius: 4px; @@ -364,7 +366,7 @@ const ContextMenu = styled.div` const Regisbutton = styled.button` position: absolute; border: none; - background-color: grey; + background-color: #1e88e5; border-radius: 5px; color: white; width: 5rem; @@ -378,29 +380,6 @@ const Regisbutton = styled.button` } `; -const AddMember = styled.input` - border-radius: 5px; - width: 112px; - height: 22px; - - :first-child { - margin-right: 5px; - width: 50px; - } - - :nth-child(2) { - width: 200px; - } - :nth-child(3) { - width: 350px; - margin-left: 5px; - } - - ::placeholder { - text-align: center; - } -`; - const MemberTable = styled.table` width: 700px; border-collapse: collapse; @@ -438,35 +417,6 @@ const MemberTable = styled.table` } `; -const AddList = styled.div` - display: flex; - position: relative; - flex-wrap: wrap; - height: 25px; - width: 730px; - margin: 0 auto; - font-size: 1.6rem; - padding-left: 2.5rem; - - .menu { - margin-left: auto; - } -`; - -const Addtitle = styled.div` - position: absolute; - display: flex; - position: relative; - height: 25px; - width: 730px; - margin: 0 auto 1.5rem auto; - font-size: 1.6rem; - - .menu { - margin-left: auto; - } -`; - const MemberList = styled.div` position: absolute; display: flex; From 3942358276e9ea148151a1648b0e1b77763db34c Mon Sep 17 00:00:00 2001 From: a1 Date: Wed, 7 Feb 2024 01:40:53 +0900 Subject: [PATCH 09/24] =?UTF-8?q?feat:=20eslint=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.js | 3 +- src/page/DetailPage.js | 638 +------------------------------------ src/page/ManageRolePage.js | 14 +- 3 files changed, 11 insertions(+), 644 deletions(-) diff --git a/src/index.js b/src/index.js index cf78d6c..ae5c082 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,5 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; -import { useEffect } from 'react'; import App from './App'; import reportWebVitals from './reportWebVitals'; import { StyledEngineProvider } from '@mui/material'; @@ -15,7 +14,7 @@ import logger from 'redux-logger'; import rootReducer from './modules/rootReducer'; import axios from 'axios'; -axios.defaults.baseURL = "https://server.inuappcenter.kr/"; +axios.defaults.baseURL = 'https://server.inuappcenter.kr/'; axios.defaults.withCredentials = true; const root = ReactDOM.createRoot(document.getElementById('root')); diff --git a/src/page/DetailPage.js b/src/page/DetailPage.js index c3d1336..9355e49 100644 --- a/src/page/DetailPage.js +++ b/src/page/DetailPage.js @@ -1,639 +1,3 @@ -import styled, { css } from 'styled-components'; -import { HiBars3 } from 'react-icons/hi2'; -import axios from 'axios'; -import React, { useState, useEffect, useRef } from 'react'; -import Modal from 'react-modal'; // react-modal 라이브러리 import -import Pagination from '../component/manage/Pagenation'; -import logo from '../resource/img/navbar_logo/logo_black.png'; -import InOut from '../component/common/InOut'; - export default function DetailPage() { - const [data, setData] = useState([]); - const [loading, isLoading] = useState(false); - - // 새 멤버를 추가할 때 사용합니다. - const [newMember, setNewMember] = useState({ - member_id: '', - name: '', - description: '', - profileImage: '', - blogLink: '', - email: '', - gitRepositoryLink: '', - }); - const [contextMenuVisible, setContextMenuVisible] = useState(false); - const [contextMenuPosition, setContextMenuPosition] = useState({ - x: 0, - y: 0, - }); - const [selectedMemberId, setSelectedMemberId] = useState(null); - const contextMenuRef = useRef(null); - const [isEditModalOpen, setEditModalOpen] = useState(false); - - //* 수정 기능을 이용할 때 값을 저장하기 위해 사용합니다. */ - const [editedName, setEditedName] = useState(''); - const [editedDescription, setEditedDescription] = useState(''); - const [editedProfileImage, setEditedProfileImage] = useState(''); - const [editedBlogLink, setEditedBlogLink] = useState(''); - const [editedEmail, setEditedEmail] = useState(''); - const [editedGitRepositoryLink, setEditedGitRepositoryLink] = useState(''); - - // 페이지네이션을 구현할때 사용합니다. - const [currentPage, setCurrentPage] = useState(1); - const itemsPerPage = 10; - - // 페이지당 데이터를 분할하는 함수입니다. - const paginateData = (data, currentPage, itemsPerPage) => { - const startIndex = (currentPage - 1) * itemsPerPage; - const endIndex = startIndex + itemsPerPage; - return data.slice(startIndex, endIndex); - }; - - const getCurrentPageData = () => { - return paginateData(data, currentPage, itemsPerPage); - }; - - // 페이지 변경 핸들러 - const handlePageChange = (pageNumber) => { - setCurrentPage(pageNumber); - }; - - const openEditModal = (selectedMemberId) => { - // 수정할 때 해당 memberId의 데이터를 가져와서 모달에 미리 채워넣을 수 있습니다. - setContextMenuVisible(false); - setEditModalOpen(true); - }; - - useEffect(() => { - const memberToEdit = data.find( - (item) => item.member_id === selectedMemberId - ); - if (memberToEdit) { - setEditedName(memberToEdit.name); - setEditedDescription(memberToEdit.description); - setEditedProfileImage(memberToEdit.profileImage); - setEditedBlogLink(memberToEdit.blogLink); - setEditedEmail(memberToEdit.email); - setEditedGitRepositoryLink(memberToEdit.gitRepositoryLink); - } - }, [selectedMemberId]); - - const closeEditModal = () => { - setEditModalOpen(false); - }; - - const addData = async () => { - try { - const result = await axios.post( - 'https://server.inuappcenter.kr/members', - newMember - ); - console.log('Success:', result.data); - - // POST 요청 성공 시, 새로운 동아리원을 data 상태 변수에 추가합니다. - setData([...data, result.data]); - - setNewMember({ - member_id: '', - name: '', - description: '', - profileImage: '', - blogLink: '', - email: '', - gitRepositoryLink: '', - }); - } catch (error) { - console.error('Error adding data:', error); - } - }; - - useEffect(() => { - const fetchData = async () => { - isLoading(true); - const viewData = await axios - .get('https://server.inuappcenter.kr/members/all-members') - .then((res) => { - isLoading(false); - setData(res.data); - }); - }; - fetchData(); - }, []); - - useEffect(() => { - const handleContextMenuClick = (e) => { - if ( - contextMenuRef.current && - !contextMenuRef.current.contains(e.target) - ) { - // 컨텍스트 메뉴 외의 영역을 클릭하면 메뉴를 닫습니다. - setContextMenuVisible(false); - } - }; - - window.addEventListener('click', handleContextMenuClick); - - return () => { - // 컴포넌트가 언마운트될 때 이벤트 리스너를 제거합니다. - window.removeEventListener('click', handleContextMenuClick); - }; - }, []); - - const handleEdit = async () => { - if (selectedMemberId === null) { - return; // 선택된 항목이 없으면 무시 - } - - // 수정할 데이터를 가져옵니다. - const updatedData = { - name: editedName, - description: editedDescription, - profileImage: editedProfileImage, - blogLink: editedBlogLink, - email: editedEmail, - gitRepositoryLink: editedGitRepositoryLink, - }; - - try { - // member_id를 사용하여 수정 요청을 보냅니다. - const response = await axios.patch( - `https://server.inuappcenter.kr/members?id=${selectedMemberId}`, - updatedData - ); - console.log( - 'Member with ID', - selectedMemberId, - 'has been updated.' - ); - console.log(response); - // 업데이트된 데이터를 data 상태에서 업데이트합니다. - setData((prevData) => - prevData.map((item) => - item.member_id === selectedMemberId - ? { ...item, ...updatedData } - : item - ) - ); - } catch (error) { - console.error('Error updating member:', error); - } - setEditModalOpen(false); - setContextMenuVisible(false); // 컨텍스트 메뉴 닫기 - }; - - const handleDelete = async () => { - if (selectedMemberId === null) { - return; // 선택된 항목이 없으면 무시 - } - - try { - // member_id를 사용하여 삭제 요청을 보냅니다. - await axios.delete( - `https://server.inuappcenter.kr/members/${selectedMemberId}` - ); - console.log( - 'Member with ID', - selectedMemberId, - 'has been deleted.' - ); - - // 삭제한 데이터를 data 상태에서 제거합니다. - setData((prevData) => - prevData.filter((item) => item.member_id !== selectedMemberId) - ); - } catch (error) { - console.error('Error deleting member:', error); - } - - setContextMenuVisible(false); // 컨텍스트 메뉴 닫기 - }; - return ( - <> - - - {'동아리원 관리'} - - {'동아리원 추가, 삭제, 수정을 할 수 있어요'} - - - 동아리원 목록 - - {loading &&
loading...
} - - {getCurrentPageData().map((content) => ( - { - e.preventDefault(); - setSelectedMemberId(content.member_id); - setContextMenuPosition({ - x: e.clientX, - y: e.clientY, - }); - setContextMenuVisible(true); - console.log(content.member_id); - }} - > - {content.member_id} - {content.name} - {content.email} - - - Visit Blog - - - - - github - - - {content.profileImage} - {content.description} - - ))} - -
- - {/* 페이지네이션 컨텐츠 */} - - - 동아리원 추가 - - {/* 사용자 입력을 받을 UI 요소들 */} - - setNewMember({ ...newMember, name: e.target.value }) - } - /> - - - setNewMember({ ...newMember, email: e.target.value }) - } - /> - - - setNewMember({ ...newMember, blogLink: e.target.value }) - } - /> - - - setNewMember({ - ...newMember, - gitRepositoryLink: e.target.value, - }) - } - /> - - - setNewMember({ - ...newMember, - profileImage: e.target.value, - }) - } - /> - - - setNewMember({ - ...newMember, - description: e.target.value, - }) - } - /> - 등록 - - {/* 컨텍스트 메뉴 */} - {contextMenuVisible && ( - - 수정 - 삭제 - - )} - - {/* 수정 팝업 모달 */} - - 회원 수정 - 이름 - setEditedName(e.target.value)} - /> - 설명 - setEditedDescription(e.target.value)} - /> - 프로필 이미지 - setEditedProfileImage(e.target.value)} - /> - 블로그 URL - setEditedBlogLink(e.target.value)} - /> - 이메일 - setEditedEmail(e.target.value)} - /> - Git URL - setEditedGitRepositoryLink(e.target.value)} - /> - - 수정 완료 - 취소 - - - - ); + return <>hello!; } - -const PaginationContainer = styled.div` - display: flex; - justify-content: center; - margin-top: 20px; /* 조정 가능한 마진 값 */ -`; - -const ModalContainer = styled(Modal)` - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - background-color: #fff; - border-radius: 8px; - padding: 20px; - max-width: 400px; - margin: 0 auto; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -`; - -const ModalTitle = styled.h2` - font-size: 1.5rem; - margin-bottom: 15px; -`; - -const ModalLabel = styled.label` - font-size: 1rem; - margin-bottom: 5px; -`; - -const ModalInput = styled.input` - width: 100%; - padding: 8px; - margin-bottom: 15px; - border: 1px solid #ccc; - border-radius: 4px; - font-size: 1rem; -`; - -const ModalButtonWrapper = styled.div` - display: flex; - justify-content: space-between; - margin-top: 15px; -`; - -const ModalButton = styled.button` - background-color: #5858fa; - color: #fff; - border: none; - border-radius: 4px; - padding: 8px 16px; - font-size: 1rem; - cursor: pointer; - transition: background-color 0.2s ease-in-out; - - & + & { - margin: 0 10px; - } - - &:hover { - background-color: #8181f7; - } -`; - -const MenuItem = styled.div` - padding: 5px 10px; - cursor: pointer; - user-select: none; - &:hover { - background-color: #f2f2f2; - } -`; - -const ContextMenu = styled.div` - position: absolute; - background-color: white; - border: 1px solid #ccc; - box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2); - z-index: 1000; - color: grey; -`; - -const Regisbutton = styled.button` - border: none; - background-color: #5858fa; - border-radius: 5px; - color: white; - width: 5rem; - height: 2rem; - margin: 1rem 3.5rem 0 auto; - - &:hover { - transition: 0.1s ease-in; - background-color: #8181f7; - } -`; - -const AddMember = styled.input` - border-radius: 5px; - width: 112px; - height: 22px; - - :first-child { - margin-right: 5px; - width: 50px; - } - - & + & { - margin-right: 5px; - } - - ::placeholder { - text-align: center; - } -`; - -const MemberTable = styled.table` - width: 700px; - border-collapse: collapse; - margin: 20px auto 20px auto; - - th, - td { - padding: 5px; - text-align: center; - } - - th { - font-weight: 700; - } - - a { - color: #0078d4; - text-decoration: none; - } - - tr { - border-radius: 20%; - } - - tr:hover { - background-color: #f2f2f2; - } -`; - -const AddList = styled.div` - display: flex; - position: relative; - flex-wrap: wrap; - height: 25px; - width: 730px; - margin: 0 auto; - font-size: 1.6rem; - padding-left: 2.5rem; - - .menu { - margin-left: auto; - } -`; - -const Addtitle = styled.div` - position: absolute; - display: flex; - position: relative; - height: 25px; - width: 730px; - margin: 0 auto 1.5rem auto; - font-size: 1.6rem; - - .menu { - margin-left: auto; - } -`; - -const MemberList = styled.div` - position: absolute; - display: flex; - position: relative; - height: 25px; - width: 730px; - margin: 0 auto 0 auto; - font-size: 1.6rem; - - .menu { - margin-left: auto; - } -`; - -const NavBar = styled.div` - position: absolute; - display: flex; - position: relative; - height: 25px; - width: 730px; - margin: 45px auto 0 auto; - - .menu { - margin-left: auto; - } -`; - -const IntroBox = styled.div` - position: relative; - width: 700px; - height: 130px; - background-color: #f2f2f2; - margin: 0 auto 2rem auto; - top: 20px; - border-radius: 20px; - padding-top: 50px; -`; - -const Text = styled.div` - font-style: normal; - text-align: center; - letter-spacing: 1px; - text-transform: uppercase; - color: ${(props) => (props.type === 'title' ? '#424242' : '#848484')}; - font-weight: ${(props) => - props.type === 'top' ? 100 : props.type === 'title' ? 600 : 100}; - margin-bottom: 3px; - white-space: pre-line; - - ${(props) => - props.type === 'title' - ? css` - font-size: ${(props) => props.theme.fontSize.tablet.title}; - ` - : css` - font-size: ${(props) => props.theme.fontSize.tablet.caption}; - `} -`; diff --git a/src/page/ManageRolePage.js b/src/page/ManageRolePage.js index 16572ca..ff3e87a 100644 --- a/src/page/ManageRolePage.js +++ b/src/page/ManageRolePage.js @@ -367,18 +367,22 @@ const Regisbutton = styled.button` `; const MemberTable = styled.table` - width: 700px; - border-collapse: collapse; + width: 600px; margin: 20px auto 20px auto; - th, td { - padding: 5px; + padding: 6px; text-align: center; - } + box-shadow: 0 0 3px rgba(0, 0, 0, 0.2); + border-right: 1px solid black; + border-radius: 8px; + overflow: hidden; + white-space: nowrap; th { font-weight: 700; + padding: 5px; + text-align: center; } a { From 57b3bde74a604e3c1dd681a53dafc782ca84f1b8 Mon Sep 17 00:00:00 2001 From: a1 Date: Wed, 7 Feb 2024 16:35:05 +0900 Subject: [PATCH 10/24] =?UTF-8?q?feat:=20prod-deploy=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/prod-deploy.yml | 119 +++++++++++++++--------------- 1 file changed, 60 insertions(+), 59 deletions(-) diff --git a/.github/workflows/prod-deploy.yml b/.github/workflows/prod-deploy.yml index 6b5729c..dd79ce8 100644 --- a/.github/workflows/prod-deploy.yml +++ b/.github/workflows/prod-deploy.yml @@ -1,73 +1,74 @@ name: Docker CD on: - push: - branches: - - 'production' - tags: - - 'v*' - pull_request: - branches: - - 'production' + push: + branches: + - 'production' + tags: + - 'v*' + pull_request: + branches: + - 'production' jobs: - docker: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 + docker: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 - - name: Use Node.js - uses: actions/setup-node@v3 - with: - node-version: '18.x' + - name: Use Node.js + uses: actions/setup-node@v3 + with: + node-version: '18.x' - - name: Make env - run: | - touch ./.env - echo "${{ secrets.ENV }}" > ./.env - echo .env - shell: sh + - name: Make env + run: | + touch ./.env + echo "${{ secrets.ENV }}" > ./.env + echo .env + shell: sh - - name: Install dependencies - run: yarn + - name: Install dependencies + run: yarn - - name: Build Project - run: yarn build + - name: Build Project + run: yarn build - - name: Docker meta - id: meta - uses: docker/metadata-action@v4 - with: - images: | - ${{ secrets.DOCKER_USERNAME }}/home-appcenter - tags: | - type=ref,event=branch - type=semver,pattern={{version}} + - name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + images: | + ${{ secrets.DOCKER_USERNAME }}/home-appcenter + tags: | + type=ref,event=branch + type=semver,pattern={{version}} - - name: Login to DockerHub - if: github.event_name != 'pull_request' - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} + - name: Login to DockerHub + if: github.event_name != 'pull_request' + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} - - name: Build and push - uses: docker/build-push-action@v4 - with: - context: . - push: ${{ github.event_name != 'pull_request' }} - tags: ${{ steps.meta.outputs.tags }} + - name: Build and push + uses: docker/build-push-action@v4 + with: + context: . + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + + - name: Deploy + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.HOST }} + username: ${{ secrets.USERNAME }} + password: ${{ secrets.PASSWORD }} + script: | + docker stop inu-hompage + docker rm inu-hompage + docker pull ${{ secrets.DOCKER_USERNAME }}/home-appcenter:production + docker run -d —name inu-hompage -p 3000:3000 ${{ secrets.DOCKER_USERNAME }}/home-appcenter:production - - name: Deploy - uses: appleboy/ssh-action@v0.1.7 - with: - host: ${{ secrets.HOST }} - username: ${{ secrets.USERNAME }} - password: ${{ secrets.PASSWORD }} - script: | - docker stop inu-hompage - docker rm inu-hompage - docker pull ${{ secrets.DOCKER_USERNAME }}/home-appcenter:production - docker run -d --name inu-hompage -p 3000:3000 ${{ secrets.DOCKER_USERNAME }}/home-appcenter:production # docker image prune -f From 180acbabf1e4eb69241cb3b7ec22a2d3b9e774c8 Mon Sep 17 00:00:00 2001 From: a1 Date: Wed, 7 Feb 2024 17:07:20 +0900 Subject: [PATCH 11/24] =?UTF-8?q?feat:gitignore=EC=B6=94=EA=B0=80,=20apple?= =?UTF-8?q?boy=20=EB=B2=84=EC=A0=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/prod-deploy.yml | 2 +- .gitignore | 3 + appcenter-homepage-renewal.git/HEAD | 1 - appcenter-homepage-renewal.git/config | 8 - appcenter-homepage-renewal.git/description | 1 - .../hooks/applypatch-msg.sample | 15 -- .../hooks/commit-msg.sample | 24 --- .../hooks/fsmonitor-watchman.sample | 174 ------------------ .../hooks/post-update.sample | 8 - .../hooks/pre-applypatch.sample | 14 -- .../hooks/pre-commit.sample | 49 ----- .../hooks/pre-merge-commit.sample | 13 -- .../hooks/pre-push.sample | 53 ------ .../hooks/pre-rebase.sample | 169 ----------------- .../hooks/pre-receive.sample | 24 --- .../hooks/prepare-commit-msg.sample | 42 ----- .../hooks/push-to-checkout.sample | 78 -------- .../hooks/update.sample | 128 ------------- appcenter-homepage-renewal.git/info/exclude | 6 - ...6978b4dae7bd103c36782e2e123bb97bded789.idx | Bin 76840 -> 0 bytes ...978b4dae7bd103c36782e2e123bb97bded789.pack | Bin 11483814 -> 0 bytes appcenter-homepage-renewal.git/packed-refs | 6 - src/page/ProductPage.js | 1 + 23 files changed, 5 insertions(+), 814 deletions(-) delete mode 100644 appcenter-homepage-renewal.git/HEAD delete mode 100644 appcenter-homepage-renewal.git/config delete mode 100644 appcenter-homepage-renewal.git/description delete mode 100755 appcenter-homepage-renewal.git/hooks/applypatch-msg.sample delete mode 100755 appcenter-homepage-renewal.git/hooks/commit-msg.sample delete mode 100755 appcenter-homepage-renewal.git/hooks/fsmonitor-watchman.sample delete mode 100755 appcenter-homepage-renewal.git/hooks/post-update.sample delete mode 100755 appcenter-homepage-renewal.git/hooks/pre-applypatch.sample delete mode 100755 appcenter-homepage-renewal.git/hooks/pre-commit.sample delete mode 100755 appcenter-homepage-renewal.git/hooks/pre-merge-commit.sample delete mode 100755 appcenter-homepage-renewal.git/hooks/pre-push.sample delete mode 100755 appcenter-homepage-renewal.git/hooks/pre-rebase.sample delete mode 100755 appcenter-homepage-renewal.git/hooks/pre-receive.sample delete mode 100755 appcenter-homepage-renewal.git/hooks/prepare-commit-msg.sample delete mode 100755 appcenter-homepage-renewal.git/hooks/push-to-checkout.sample delete mode 100755 appcenter-homepage-renewal.git/hooks/update.sample delete mode 100644 appcenter-homepage-renewal.git/info/exclude delete mode 100644 appcenter-homepage-renewal.git/objects/pack/pack-c86978b4dae7bd103c36782e2e123bb97bded789.idx delete mode 100644 appcenter-homepage-renewal.git/objects/pack/pack-c86978b4dae7bd103c36782e2e123bb97bded789.pack delete mode 100644 appcenter-homepage-renewal.git/packed-refs diff --git a/.github/workflows/prod-deploy.yml b/.github/workflows/prod-deploy.yml index dd79ce8..2c1e6a6 100644 --- a/.github/workflows/prod-deploy.yml +++ b/.github/workflows/prod-deploy.yml @@ -60,7 +60,7 @@ jobs: tags: ${{ steps.meta.outputs.tags }} - name: Deploy - uses: appleboy/ssh-action@v1.0.3 + uses: appleboy/ssh-action@latest with: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} diff --git a/.gitignore b/.gitignore index 511ec99..4577f1b 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,9 @@ # production /build +# git +appcenter-homepage-renewal.git/ + # misc .DS_Store .env diff --git a/appcenter-homepage-renewal.git/HEAD b/appcenter-homepage-renewal.git/HEAD deleted file mode 100644 index cb089cd..0000000 --- a/appcenter-homepage-renewal.git/HEAD +++ /dev/null @@ -1 +0,0 @@ -ref: refs/heads/master diff --git a/appcenter-homepage-renewal.git/config b/appcenter-homepage-renewal.git/config deleted file mode 100644 index 3c8acd0..0000000 --- a/appcenter-homepage-renewal.git/config +++ /dev/null @@ -1,8 +0,0 @@ -[core] - repositoryformatversion = 0 - filemode = true - bare = true - ignorecase = true - precomposeunicode = true -[remote "origin"] - url = https://github.com/Martinelli-3535/appcenter-homepage-renewal.git diff --git a/appcenter-homepage-renewal.git/description b/appcenter-homepage-renewal.git/description deleted file mode 100644 index 498b267..0000000 --- a/appcenter-homepage-renewal.git/description +++ /dev/null @@ -1 +0,0 @@ -Unnamed repository; edit this file 'description' to name the repository. diff --git a/appcenter-homepage-renewal.git/hooks/applypatch-msg.sample b/appcenter-homepage-renewal.git/hooks/applypatch-msg.sample deleted file mode 100755 index a5d7b84..0000000 --- a/appcenter-homepage-renewal.git/hooks/applypatch-msg.sample +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh -# -# An example hook script to check the commit log message taken by -# applypatch from an e-mail message. -# -# The hook should exit with non-zero status after issuing an -# appropriate message if it wants to stop the commit. The hook is -# allowed to edit the commit message file. -# -# To enable this hook, rename this file to "applypatch-msg". - -. git-sh-setup -commitmsg="$(git rev-parse --git-path hooks/commit-msg)" -test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"} -: diff --git a/appcenter-homepage-renewal.git/hooks/commit-msg.sample b/appcenter-homepage-renewal.git/hooks/commit-msg.sample deleted file mode 100755 index b58d118..0000000 --- a/appcenter-homepage-renewal.git/hooks/commit-msg.sample +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# -# An example hook script to check the commit log message. -# Called by "git commit" with one argument, the name of the file -# that has the commit message. The hook should exit with non-zero -# status after issuing an appropriate message if it wants to stop the -# commit. The hook is allowed to edit the commit message file. -# -# To enable this hook, rename this file to "commit-msg". - -# Uncomment the below to add a Signed-off-by line to the message. -# Doing this in a hook is a bad idea in general, but the prepare-commit-msg -# hook is more suited to it. -# -# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') -# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" - -# This example catches duplicate Signed-off-by lines. - -test "" = "$(grep '^Signed-off-by: ' "$1" | - sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || { - echo >&2 Duplicate Signed-off-by lines. - exit 1 -} diff --git a/appcenter-homepage-renewal.git/hooks/fsmonitor-watchman.sample b/appcenter-homepage-renewal.git/hooks/fsmonitor-watchman.sample deleted file mode 100755 index 23e856f..0000000 --- a/appcenter-homepage-renewal.git/hooks/fsmonitor-watchman.sample +++ /dev/null @@ -1,174 +0,0 @@ -#!/usr/bin/perl - -use strict; -use warnings; -use IPC::Open2; - -# An example hook script to integrate Watchman -# (https://facebook.github.io/watchman/) with git to speed up detecting -# new and modified files. -# -# The hook is passed a version (currently 2) and last update token -# formatted as a string and outputs to stdout a new update token and -# all files that have been modified since the update token. Paths must -# be relative to the root of the working tree and separated by a single NUL. -# -# To enable this hook, rename this file to "query-watchman" and set -# 'git config core.fsmonitor .git/hooks/query-watchman' -# -my ($version, $last_update_token) = @ARGV; - -# Uncomment for debugging -# print STDERR "$0 $version $last_update_token\n"; - -# Check the hook interface version -if ($version ne 2) { - die "Unsupported query-fsmonitor hook version '$version'.\n" . - "Falling back to scanning...\n"; -} - -my $git_work_tree = get_working_dir(); - -my $retry = 1; - -my $json_pkg; -eval { - require JSON::XS; - $json_pkg = "JSON::XS"; - 1; -} or do { - require JSON::PP; - $json_pkg = "JSON::PP"; -}; - -launch_watchman(); - -sub launch_watchman { - my $o = watchman_query(); - if (is_work_tree_watched($o)) { - output_result($o->{clock}, @{$o->{files}}); - } -} - -sub output_result { - my ($clockid, @files) = @_; - - # Uncomment for debugging watchman output - # open (my $fh, ">", ".git/watchman-output.out"); - # binmode $fh, ":utf8"; - # print $fh "$clockid\n@files\n"; - # close $fh; - - binmode STDOUT, ":utf8"; - print $clockid; - print "\0"; - local $, = "\0"; - print @files; -} - -sub watchman_clock { - my $response = qx/watchman clock "$git_work_tree"/; - die "Failed to get clock id on '$git_work_tree'.\n" . - "Falling back to scanning...\n" if $? != 0; - - return $json_pkg->new->utf8->decode($response); -} - -sub watchman_query { - my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty') - or die "open2() failed: $!\n" . - "Falling back to scanning...\n"; - - # In the query expression below we're asking for names of files that - # changed since $last_update_token but not from the .git folder. - # - # To accomplish this, we're using the "since" generator to use the - # recency index to select candidate nodes and "fields" to limit the - # output to file names only. Then we're using the "expression" term to - # further constrain the results. - my $last_update_line = ""; - if (substr($last_update_token, 0, 1) eq "c") { - $last_update_token = "\"$last_update_token\""; - $last_update_line = qq[\n"since": $last_update_token,]; - } - my $query = <<" END"; - ["query", "$git_work_tree", {$last_update_line - "fields": ["name"], - "expression": ["not", ["dirname", ".git"]] - }] - END - - # Uncomment for debugging the watchman query - # open (my $fh, ">", ".git/watchman-query.json"); - # print $fh $query; - # close $fh; - - print CHLD_IN $query; - close CHLD_IN; - my $response = do {local $/; }; - - # Uncomment for debugging the watch response - # open ($fh, ">", ".git/watchman-response.json"); - # print $fh $response; - # close $fh; - - die "Watchman: command returned no output.\n" . - "Falling back to scanning...\n" if $response eq ""; - die "Watchman: command returned invalid output: $response\n" . - "Falling back to scanning...\n" unless $response =~ /^\{/; - - return $json_pkg->new->utf8->decode($response); -} - -sub is_work_tree_watched { - my ($output) = @_; - my $error = $output->{error}; - if ($retry > 0 and $error and $error =~ m/unable to resolve root .* directory (.*) is not watched/) { - $retry--; - my $response = qx/watchman watch "$git_work_tree"/; - die "Failed to make watchman watch '$git_work_tree'.\n" . - "Falling back to scanning...\n" if $? != 0; - $output = $json_pkg->new->utf8->decode($response); - $error = $output->{error}; - die "Watchman: $error.\n" . - "Falling back to scanning...\n" if $error; - - # Uncomment for debugging watchman output - # open (my $fh, ">", ".git/watchman-output.out"); - # close $fh; - - # Watchman will always return all files on the first query so - # return the fast "everything is dirty" flag to git and do the - # Watchman query just to get it over with now so we won't pay - # the cost in git to look up each individual file. - my $o = watchman_clock(); - $error = $output->{error}; - - die "Watchman: $error.\n" . - "Falling back to scanning...\n" if $error; - - output_result($o->{clock}, ("/")); - $last_update_token = $o->{clock}; - - eval { launch_watchman() }; - return 0; - } - - die "Watchman: $error.\n" . - "Falling back to scanning...\n" if $error; - - return 1; -} - -sub get_working_dir { - my $working_dir; - if ($^O =~ 'msys' || $^O =~ 'cygwin') { - $working_dir = Win32::GetCwd(); - $working_dir =~ tr/\\/\//; - } else { - require Cwd; - $working_dir = Cwd::cwd(); - } - - return $working_dir; -} diff --git a/appcenter-homepage-renewal.git/hooks/post-update.sample b/appcenter-homepage-renewal.git/hooks/post-update.sample deleted file mode 100755 index ec17ec1..0000000 --- a/appcenter-homepage-renewal.git/hooks/post-update.sample +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh -# -# An example hook script to prepare a packed repository for use over -# dumb transports. -# -# To enable this hook, rename this file to "post-update". - -exec git update-server-info diff --git a/appcenter-homepage-renewal.git/hooks/pre-applypatch.sample b/appcenter-homepage-renewal.git/hooks/pre-applypatch.sample deleted file mode 100755 index 4142082..0000000 --- a/appcenter-homepage-renewal.git/hooks/pre-applypatch.sample +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh -# -# An example hook script to verify what is about to be committed -# by applypatch from an e-mail message. -# -# The hook should exit with non-zero status after issuing an -# appropriate message if it wants to stop the commit. -# -# To enable this hook, rename this file to "pre-applypatch". - -. git-sh-setup -precommit="$(git rev-parse --git-path hooks/pre-commit)" -test -x "$precommit" && exec "$precommit" ${1+"$@"} -: diff --git a/appcenter-homepage-renewal.git/hooks/pre-commit.sample b/appcenter-homepage-renewal.git/hooks/pre-commit.sample deleted file mode 100755 index e144712..0000000 --- a/appcenter-homepage-renewal.git/hooks/pre-commit.sample +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/sh -# -# An example hook script to verify what is about to be committed. -# Called by "git commit" with no arguments. The hook should -# exit with non-zero status after issuing an appropriate message if -# it wants to stop the commit. -# -# To enable this hook, rename this file to "pre-commit". - -if git rev-parse --verify HEAD >/dev/null 2>&1 -then - against=HEAD -else - # Initial commit: diff against an empty tree object - against=$(git hash-object -t tree /dev/null) -fi - -# If you want to allow non-ASCII filenames set this variable to true. -allownonascii=$(git config --type=bool hooks.allownonascii) - -# Redirect output to stderr. -exec 1>&2 - -# Cross platform projects tend to avoid non-ASCII filenames; prevent -# them from being added to the repository. We exploit the fact that the -# printable range starts at the space character and ends with tilde. -if [ "$allownonascii" != "true" ] && - # Note that the use of brackets around a tr range is ok here, (it's - # even required, for portability to Solaris 10's /usr/bin/tr), since - # the square bracket bytes happen to fall in the designated range. - test $(git diff --cached --name-only --diff-filter=A -z $against | - LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 -then - cat <<\EOF -Error: Attempt to add a non-ASCII file name. - -This can cause problems if you want to work with people on other platforms. - -To be portable it is advisable to rename the file. - -If you know what you are doing you can disable this check using: - - git config hooks.allownonascii true -EOF - exit 1 -fi - -# If there are whitespace errors, print the offending file names and fail. -exec git diff-index --check --cached $against -- diff --git a/appcenter-homepage-renewal.git/hooks/pre-merge-commit.sample b/appcenter-homepage-renewal.git/hooks/pre-merge-commit.sample deleted file mode 100755 index 399eab1..0000000 --- a/appcenter-homepage-renewal.git/hooks/pre-merge-commit.sample +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -# -# An example hook script to verify what is about to be committed. -# Called by "git merge" with no arguments. The hook should -# exit with non-zero status after issuing an appropriate message to -# stderr if it wants to stop the merge commit. -# -# To enable this hook, rename this file to "pre-merge-commit". - -. git-sh-setup -test -x "$GIT_DIR/hooks/pre-commit" && - exec "$GIT_DIR/hooks/pre-commit" -: diff --git a/appcenter-homepage-renewal.git/hooks/pre-push.sample b/appcenter-homepage-renewal.git/hooks/pre-push.sample deleted file mode 100755 index 4ce688d..0000000 --- a/appcenter-homepage-renewal.git/hooks/pre-push.sample +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/sh - -# An example hook script to verify what is about to be pushed. Called by "git -# push" after it has checked the remote status, but before anything has been -# pushed. If this script exits with a non-zero status nothing will be pushed. -# -# This hook is called with the following parameters: -# -# $1 -- Name of the remote to which the push is being done -# $2 -- URL to which the push is being done -# -# If pushing without using a named remote those arguments will be equal. -# -# Information about the commits which are being pushed is supplied as lines to -# the standard input in the form: -# -# -# -# This sample shows how to prevent push of commits where the log message starts -# with "WIP" (work in progress). - -remote="$1" -url="$2" - -zero=$(git hash-object --stdin &2 "Found WIP commit in $local_ref, not pushing" - exit 1 - fi - fi -done - -exit 0 diff --git a/appcenter-homepage-renewal.git/hooks/pre-rebase.sample b/appcenter-homepage-renewal.git/hooks/pre-rebase.sample deleted file mode 100755 index 6cbef5c..0000000 --- a/appcenter-homepage-renewal.git/hooks/pre-rebase.sample +++ /dev/null @@ -1,169 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2006, 2008 Junio C Hamano -# -# The "pre-rebase" hook is run just before "git rebase" starts doing -# its job, and can prevent the command from running by exiting with -# non-zero status. -# -# The hook is called with the following parameters: -# -# $1 -- the upstream the series was forked from. -# $2 -- the branch being rebased (or empty when rebasing the current branch). -# -# This sample shows how to prevent topic branches that are already -# merged to 'next' branch from getting rebased, because allowing it -# would result in rebasing already published history. - -publish=next -basebranch="$1" -if test "$#" = 2 -then - topic="refs/heads/$2" -else - topic=`git symbolic-ref HEAD` || - exit 0 ;# we do not interrupt rebasing detached HEAD -fi - -case "$topic" in -refs/heads/??/*) - ;; -*) - exit 0 ;# we do not interrupt others. - ;; -esac - -# Now we are dealing with a topic branch being rebased -# on top of master. Is it OK to rebase it? - -# Does the topic really exist? -git show-ref -q "$topic" || { - echo >&2 "No such branch $topic" - exit 1 -} - -# Is topic fully merged to master? -not_in_master=`git rev-list --pretty=oneline ^master "$topic"` -if test -z "$not_in_master" -then - echo >&2 "$topic is fully merged to master; better remove it." - exit 1 ;# we could allow it, but there is no point. -fi - -# Is topic ever merged to next? If so you should not be rebasing it. -only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` -only_next_2=`git rev-list ^master ${publish} | sort` -if test "$only_next_1" = "$only_next_2" -then - not_in_topic=`git rev-list "^$topic" master` - if test -z "$not_in_topic" - then - echo >&2 "$topic is already up to date with master" - exit 1 ;# we could allow it, but there is no point. - else - exit 0 - fi -else - not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` - /usr/bin/perl -e ' - my $topic = $ARGV[0]; - my $msg = "* $topic has commits already merged to public branch:\n"; - my (%not_in_next) = map { - /^([0-9a-f]+) /; - ($1 => 1); - } split(/\n/, $ARGV[1]); - for my $elem (map { - /^([0-9a-f]+) (.*)$/; - [$1 => $2]; - } split(/\n/, $ARGV[2])) { - if (!exists $not_in_next{$elem->[0]}) { - if ($msg) { - print STDERR $msg; - undef $msg; - } - print STDERR " $elem->[1]\n"; - } - } - ' "$topic" "$not_in_next" "$not_in_master" - exit 1 -fi - -<<\DOC_END - -This sample hook safeguards topic branches that have been -published from being rewound. - -The workflow assumed here is: - - * Once a topic branch forks from "master", "master" is never - merged into it again (either directly or indirectly). - - * Once a topic branch is fully cooked and merged into "master", - it is deleted. If you need to build on top of it to correct - earlier mistakes, a new topic branch is created by forking at - the tip of the "master". This is not strictly necessary, but - it makes it easier to keep your history simple. - - * Whenever you need to test or publish your changes to topic - branches, merge them into "next" branch. - -The script, being an example, hardcodes the publish branch name -to be "next", but it is trivial to make it configurable via -$GIT_DIR/config mechanism. - -With this workflow, you would want to know: - -(1) ... if a topic branch has ever been merged to "next". Young - topic branches can have stupid mistakes you would rather - clean up before publishing, and things that have not been - merged into other branches can be easily rebased without - affecting other people. But once it is published, you would - not want to rewind it. - -(2) ... if a topic branch has been fully merged to "master". - Then you can delete it. More importantly, you should not - build on top of it -- other people may already want to - change things related to the topic as patches against your - "master", so if you need further changes, it is better to - fork the topic (perhaps with the same name) afresh from the - tip of "master". - -Let's look at this example: - - o---o---o---o---o---o---o---o---o---o "next" - / / / / - / a---a---b A / / - / / / / - / / c---c---c---c B / - / / / \ / - / / / b---b C \ / - / / / / \ / - ---o---o---o---o---o---o---o---o---o---o---o "master" - - -A, B and C are topic branches. - - * A has one fix since it was merged up to "next". - - * B has finished. It has been fully merged up to "master" and "next", - and is ready to be deleted. - - * C has not merged to "next" at all. - -We would want to allow C to be rebased, refuse A, and encourage -B to be deleted. - -To compute (1): - - git rev-list ^master ^topic next - git rev-list ^master next - - if these match, topic has not merged in next at all. - -To compute (2): - - git rev-list master..topic - - if this is empty, it is fully merged to "master". - -DOC_END diff --git a/appcenter-homepage-renewal.git/hooks/pre-receive.sample b/appcenter-homepage-renewal.git/hooks/pre-receive.sample deleted file mode 100755 index a1fd29e..0000000 --- a/appcenter-homepage-renewal.git/hooks/pre-receive.sample +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# -# An example hook script to make use of push options. -# The example simply echoes all push options that start with 'echoback=' -# and rejects all pushes when the "reject" push option is used. -# -# To enable this hook, rename this file to "pre-receive". - -if test -n "$GIT_PUSH_OPTION_COUNT" -then - i=0 - while test "$i" -lt "$GIT_PUSH_OPTION_COUNT" - do - eval "value=\$GIT_PUSH_OPTION_$i" - case "$value" in - echoback=*) - echo "echo from the pre-receive-hook: ${value#*=}" >&2 - ;; - reject) - exit 1 - esac - i=$((i + 1)) - done -fi diff --git a/appcenter-homepage-renewal.git/hooks/prepare-commit-msg.sample b/appcenter-homepage-renewal.git/hooks/prepare-commit-msg.sample deleted file mode 100755 index 10fa14c..0000000 --- a/appcenter-homepage-renewal.git/hooks/prepare-commit-msg.sample +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/sh -# -# An example hook script to prepare the commit log message. -# Called by "git commit" with the name of the file that has the -# commit message, followed by the description of the commit -# message's source. The hook's purpose is to edit the commit -# message file. If the hook fails with a non-zero status, -# the commit is aborted. -# -# To enable this hook, rename this file to "prepare-commit-msg". - -# This hook includes three examples. The first one removes the -# "# Please enter the commit message..." help message. -# -# The second includes the output of "git diff --name-status -r" -# into the message, just before the "git status" output. It is -# commented because it doesn't cope with --amend or with squashed -# commits. -# -# The third example adds a Signed-off-by line to the message, that can -# still be edited. This is rarely a good idea. - -COMMIT_MSG_FILE=$1 -COMMIT_SOURCE=$2 -SHA1=$3 - -/usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE" - -# case "$COMMIT_SOURCE,$SHA1" in -# ,|template,) -# /usr/bin/perl -i.bak -pe ' -# print "\n" . `git diff --cached --name-status -r` -# if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;; -# *) ;; -# esac - -# SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') -# git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE" -# if test -z "$COMMIT_SOURCE" -# then -# /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE" -# fi diff --git a/appcenter-homepage-renewal.git/hooks/push-to-checkout.sample b/appcenter-homepage-renewal.git/hooks/push-to-checkout.sample deleted file mode 100755 index af5a0c0..0000000 --- a/appcenter-homepage-renewal.git/hooks/push-to-checkout.sample +++ /dev/null @@ -1,78 +0,0 @@ -#!/bin/sh - -# An example hook script to update a checked-out tree on a git push. -# -# This hook is invoked by git-receive-pack(1) when it reacts to git -# push and updates reference(s) in its repository, and when the push -# tries to update the branch that is currently checked out and the -# receive.denyCurrentBranch configuration variable is set to -# updateInstead. -# -# By default, such a push is refused if the working tree and the index -# of the remote repository has any difference from the currently -# checked out commit; when both the working tree and the index match -# the current commit, they are updated to match the newly pushed tip -# of the branch. This hook is to be used to override the default -# behaviour; however the code below reimplements the default behaviour -# as a starting point for convenient modification. -# -# The hook receives the commit with which the tip of the current -# branch is going to be updated: -commit=$1 - -# It can exit with a non-zero status to refuse the push (when it does -# so, it must not modify the index or the working tree). -die () { - echo >&2 "$*" - exit 1 -} - -# Or it can make any necessary changes to the working tree and to the -# index to bring them to the desired state when the tip of the current -# branch is updated to the new commit, and exit with a zero status. -# -# For example, the hook can simply run git read-tree -u -m HEAD "$1" -# in order to emulate git fetch that is run in the reverse direction -# with git push, as the two-tree form of git read-tree -u -m is -# essentially the same as git switch or git checkout that switches -# branches while keeping the local changes in the working tree that do -# not interfere with the difference between the branches. - -# The below is a more-or-less exact translation to shell of the C code -# for the default behaviour for git's push-to-checkout hook defined in -# the push_to_deploy() function in builtin/receive-pack.c. -# -# Note that the hook will be executed from the repository directory, -# not from the working tree, so if you want to perform operations on -# the working tree, you will have to adapt your code accordingly, e.g. -# by adding "cd .." or using relative paths. - -if ! git update-index -q --ignore-submodules --refresh -then - die "Up-to-date check failed" -fi - -if ! git diff-files --quiet --ignore-submodules -- -then - die "Working directory has unstaged changes" -fi - -# This is a rough translation of: -# -# head_has_history() ? "HEAD" : EMPTY_TREE_SHA1_HEX -if git cat-file -e HEAD 2>/dev/null -then - head=HEAD -else - head=$(git hash-object -t tree --stdin &2 - echo " (if you want, you could supply GIT_DIR then run" >&2 - echo " $0 )" >&2 - exit 1 -fi - -if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then - echo "usage: $0 " >&2 - exit 1 -fi - -# --- Config -allowunannotated=$(git config --type=bool hooks.allowunannotated) -allowdeletebranch=$(git config --type=bool hooks.allowdeletebranch) -denycreatebranch=$(git config --type=bool hooks.denycreatebranch) -allowdeletetag=$(git config --type=bool hooks.allowdeletetag) -allowmodifytag=$(git config --type=bool hooks.allowmodifytag) - -# check for no description -projectdesc=$(sed -e '1q' "$GIT_DIR/description") -case "$projectdesc" in -"Unnamed repository"* | "") - echo "*** Project description file hasn't been set" >&2 - exit 1 - ;; -esac - -# --- Check types -# if $newrev is 0000...0000, it's a commit to delete a ref. -zero=$(git hash-object --stdin &2 - echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 - exit 1 - fi - ;; - refs/tags/*,delete) - # delete tag - if [ "$allowdeletetag" != "true" ]; then - echo "*** Deleting a tag is not allowed in this repository" >&2 - exit 1 - fi - ;; - refs/tags/*,tag) - # annotated tag - if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 - then - echo "*** Tag '$refname' already exists." >&2 - echo "*** Modifying a tag is not allowed in this repository." >&2 - exit 1 - fi - ;; - refs/heads/*,commit) - # branch - if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then - echo "*** Creating a branch is not allowed in this repository" >&2 - exit 1 - fi - ;; - refs/heads/*,delete) - # delete branch - if [ "$allowdeletebranch" != "true" ]; then - echo "*** Deleting a branch is not allowed in this repository" >&2 - exit 1 - fi - ;; - refs/remotes/*,commit) - # tracking branch - ;; - refs/remotes/*,delete) - # delete tracking branch - if [ "$allowdeletebranch" != "true" ]; then - echo "*** Deleting a tracking branch is not allowed in this repository" >&2 - exit 1 - fi - ;; - *) - # Anything else (is there anything else?) - echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 - exit 1 - ;; -esac - -# --- Finished -exit 0 diff --git a/appcenter-homepage-renewal.git/info/exclude b/appcenter-homepage-renewal.git/info/exclude deleted file mode 100644 index a5196d1..0000000 --- a/appcenter-homepage-renewal.git/info/exclude +++ /dev/null @@ -1,6 +0,0 @@ -# git ls-files --others --exclude-from=.git/info/exclude -# Lines that start with '#' are comments. -# For a project mostly in C, the following would be a good set of -# exclude patterns (uncomment them if you want to use them): -# *.[oa] -# *~ diff --git a/appcenter-homepage-renewal.git/objects/pack/pack-c86978b4dae7bd103c36782e2e123bb97bded789.idx b/appcenter-homepage-renewal.git/objects/pack/pack-c86978b4dae7bd103c36782e2e123bb97bded789.idx deleted file mode 100644 index 8be995933aae657f064bc702a1be77bb510ca8cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76840 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

<{{aX+a|KU*bW;xDryslMycM1+I1^IxCQ7ivU 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

) : ( -
없음
+
-
)} @@ -224,10 +240,10 @@ export default function ManagePage() { target='_blank' rel='noopener noreferrer' > - 블로그 링크 + {content.blogLink} ) : ( -
없음
+
-
)} @@ -237,10 +253,10 @@ export default function ManagePage() { target='_blank' rel='noopener noreferrer' > - 깃허브 링크 + {content.gitRepositoryLink} ) : ( -
없음
+
-
)} @@ -250,17 +266,51 @@ export default function ManagePage() { alt='' type='link' > - 이미지 링크 + {content.profileImage} ) : ( -
없음
+
-
+ )} + + + {content.behanceLink ? ( + + {content.behanceLink} + + ) : ( +
-
)} {content.description ? (
{content.description}
) : ( -
없음
+
-
+ )} + + + {content.phoneNumber ? ( +
{content.phoneNumber}
+ ) : ( +
-
+ )} + + + {content.studentNumber ? ( +
{content.studentNumber}
+ ) : ( +
-
+ )} + + + {content.department ? ( +
{content.department}
+ ) : ( +
-
)} @@ -340,6 +390,30 @@ export default function ManagePage() { value={editedGitRepositoryLink} onChange={(e) => setEditedGitRepositoryLink(e.target.value)} /> + Behance URL + setEditedBehance(e.target.value)} + /> + 전화 번호 + setEditedPhoneNumber(e.target.value)} + /> + 학번 + setEditedStudentNumber(e.target.value)} + /> + 학부 + setEditedDepartment(e.target.value)} + /> 취소 수정 완료 @@ -371,14 +445,22 @@ const Cartegories = styled.div` props.type === 'first' ? 'left: 8rem; width: 60px;' : props.type === 'second' - ? 'left:11rem; width: 180px;' + ? 'left:11rem; width: 150px;' : props.type === 'third' - ? 'left: 21rem; width: 183px;' + ? 'left: 19.5rem; width: 120px;' : props.type === 'fourth' - ? 'left: 32.4rem; width: 150px;' + ? 'left: 26.5rem; width: 120px;' : props.type === 'fifth' - ? 'left: 45em; width: 220px;' - : 'left: 51rem; width: 190px;'} + ? 'left: 37.5em; width: 120px;' + : props.type === 'sixth' + ? 'left: 45.5em; width: 120px;' + : props.type === 'seventh' + ? 'left: 53.5em; width: 150px;' + : props.type === 'eighth' + ? 'left: 63em; width: 150px;' + : props.type === 'ninth' + ? 'left: 72.5em; width: 150px;' + : 'left: 81.5em; width: 150px;'} `; const PaginationContainer = styled.div` @@ -484,7 +566,7 @@ const Regisbutton = styled.button` color: white; width: 5rem; height: 2rem; - margin-left: 37rem; + margin-left: 55rem; margin-top: 0.6rem; &:hover { @@ -514,13 +596,15 @@ const MemberTable = styled.table` } div { - width: 150px; + width: 123px; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } a { + display: block; + width:100px; color: #0078d4; text-decoration: none; } From 873e4bd26caa9b07b8590282d5bdeefe6815e783 Mon Sep 17 00:00:00 2001 From: a1 Date: Thu, 15 Feb 2024 12:47:02 +0900 Subject: [PATCH 22/24] =?UTF-8?q?feat:=20=EA=B8=B0=EC=88=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/page/ManageGenPage.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/page/ManageGenPage.js b/src/page/ManageGenPage.js index 765e3ec..590bd01 100644 --- a/src/page/ManageGenPage.js +++ b/src/page/ManageGenPage.js @@ -24,6 +24,7 @@ export default function ManageGenPage() { y: 0, }); const [selectedGroupId, setSelectedGroupId] = useState(null); + const [selectedRoleId, setSelectedRoleId] = useState(null); const contextMenuRef = useRef(null); const [isEditModalOpen, setEditModalOpen] = useState(false); @@ -113,6 +114,12 @@ export default function ManageGenPage() { }; }, []); + const setRoleId = (content) => { + if (content === '센터장') setSelectedRoleId(1); + else if (content === '파트장') setSelectedRoleId(2); + else if (content === '파트원') setSelectedRoleId(3); + }; + const handleEdit = async () => { if (selectedGroupId === null) { return; // 선택된 항목이 없으면 무시 @@ -129,7 +136,7 @@ export default function ManageGenPage() { try { // group_id를 사용하여 수정 요청을 보냅니다. const response = await axios.patch( - `https://server.inuappcenter.kr/groups?id=${selectedGroupId}`, + `https://server.inuappcenter.kr/groups?groupId=${selectedGroupId}&roleId=${selectedRoleId}`, updatedData ); console.log('Member with ID', selectedGroupId, 'has been updated.'); @@ -193,12 +200,14 @@ export default function ManageGenPage() { onContextMenu={(e) => { e.preventDefault(); setSelectedGroupId(content.group_id); + setRoleId(content.role); setContextMenuPosition({ x: e.clientX, y: e.clientY, }); setContextMenuVisible(true); console.log(content.group_id); + console.log(content.role_id); }} > {content.member} @@ -401,7 +410,7 @@ const Regisbutton = styled.button` color: white; width: 5rem; height: 2rem; - margin-left: 37rem; + margin-left: 40rem; margin-top: 0.6rem; &:hover { From 2f2a18bba5f463891d178a81fa96949f044ebe6d Mon Sep 17 00:00:00 2001 From: Jungwoo Hong <79641160+Martinelli-3535@users.noreply.github.com> Date: Mon, 26 Feb 2024 16:05:08 +0900 Subject: [PATCH 23/24] =?UTF-8?q?style:=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=20UI=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/component/home/ProductionDesktop.js | 5 + src/page/ManageGenPage.js | 42 ++-- src/page/ManagePage.js | 21 +- src/page/ManageRolePage.js | 6 +- src/page/QnAPage.js | 272 +++++++----------------- yarn.lock | 5 - 6 files changed, 120 insertions(+), 231 deletions(-) diff --git a/src/component/home/ProductionDesktop.js b/src/component/home/ProductionDesktop.js index af9900b..ba17bb5 100644 --- a/src/component/home/ProductionDesktop.js +++ b/src/component/home/ProductionDesktop.js @@ -299,6 +299,7 @@ const DetailInfo = styled.div` width: 70%; margin: 0 auto; top: -1rem; + `; const InstallBtn = styled.button` @@ -361,6 +362,10 @@ const AppTitle = styled.h2` `; const AppDescription = styled.p` + width: 60%; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; font-size: 16px; color: #666; position: relative; diff --git a/src/page/ManageGenPage.js b/src/page/ManageGenPage.js index 590bd01..c2cd92c 100644 --- a/src/page/ManageGenPage.js +++ b/src/page/ManageGenPage.js @@ -24,7 +24,6 @@ export default function ManageGenPage() { y: 0, }); const [selectedGroupId, setSelectedGroupId] = useState(null); - const [selectedRoleId, setSelectedRoleId] = useState(null); const contextMenuRef = useRef(null); const [isEditModalOpen, setEditModalOpen] = useState(false); @@ -114,12 +113,6 @@ export default function ManageGenPage() { }; }, []); - const setRoleId = (content) => { - if (content === '센터장') setSelectedRoleId(1); - else if (content === '파트장') setSelectedRoleId(2); - else if (content === '파트원') setSelectedRoleId(3); - }; - const handleEdit = async () => { if (selectedGroupId === null) { return; // 선택된 항목이 없으면 무시 @@ -136,7 +129,7 @@ export default function ManageGenPage() { try { // group_id를 사용하여 수정 요청을 보냅니다. const response = await axios.patch( - `https://server.inuappcenter.kr/groups?groupId=${selectedGroupId}&roleId=${selectedRoleId}`, + `https://server.inuappcenter.kr/groups?id=${selectedGroupId}`, updatedData ); console.log('Member with ID', selectedGroupId, 'has been updated.'); @@ -200,21 +193,19 @@ export default function ManageGenPage() { onContextMenu={(e) => { e.preventDefault(); setSelectedGroupId(content.group_id); - setRoleId(content.role); setContextMenuPosition({ x: e.clientX, y: e.clientY, }); setContextMenuVisible(true); console.log(content.group_id); - console.log(content.role_id); }} > {content.member} {content.role} {content.year} {content.part ??
{content.part}
} - {content.email} + {content.email} ))} @@ -297,14 +288,14 @@ const Cartegories = styled.div` position: absolute; ${(props) => props.type === 'first' - ? 'left: 8rem; width: 90px;' + ? 'left: 8rem; width: 120px;' : props.type === 'second' - ? 'left:13rem; width: 100px;' + ? 'left:15rem; width: 100px;' : props.type === 'third' - ? 'left: 18rem; width: 100px;' + ? 'left: 21rem; width: 120px;' : props.type === 'fourth' - ? 'left: 22.5rem; width: 120px;' - : 'left: 28.5rem; width: 270px;'} + ? 'left: 28rem; width: 120px;' + : 'left: 34rem; width: 180px;'} `; const PaginationContainer = styled.div` @@ -410,7 +401,7 @@ const Regisbutton = styled.button` color: white; width: 5rem; height: 2rem; - margin-left: 40rem; + margin-left: 37rem; margin-top: 0.6rem; &:hover { @@ -424,12 +415,20 @@ const MemberTable = styled.table` margin: 20px auto 20px auto; td { + width: 90px; padding: 6px; text-align: center; box-shadow: 0 0 3px rgba(0, 0, 0, 0.1); border-radius: 4px; overflow: hidden; white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + + ${(props) => + props.type === 'email' + ? 'width: 150px;' : ''} th { font-weight: 700; @@ -442,6 +441,13 @@ const MemberTable = styled.table` text-decoration: none; } + div { + width: 90px; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + tr { border-radius: 20%; } @@ -463,4 +469,4 @@ const MemberList = styled.div` .menu { margin-left: auto; } -`; +`; \ No newline at end of file diff --git a/src/page/ManagePage.js b/src/page/ManagePage.js index b707e6d..1cec1c9 100644 --- a/src/page/ManagePage.js +++ b/src/page/ManagePage.js @@ -447,20 +447,20 @@ const Cartegories = styled.div` : props.type === 'second' ? 'left:11rem; width: 150px;' : props.type === 'third' - ? 'left: 19.5rem; width: 120px;' + ? 'left: 20rem; width: 140px;' : props.type === 'fourth' - ? 'left: 26.5rem; width: 120px;' + ? 'left: 28.5rem; width: 140px;' : props.type === 'fifth' - ? 'left: 37.5em; width: 120px;' + ? 'left: 41em; width: 150px;' : props.type === 'sixth' - ? 'left: 45.5em; width: 120px;' + ? 'left: 51em; width: 140px;' : props.type === 'seventh' - ? 'left: 53.5em; width: 150px;' + ? 'left: 59.5em; width: 150px;' : props.type === 'eighth' - ? 'left: 63em; width: 150px;' + ? 'left: 69.5em; width: 150px;' : props.type === 'ninth' - ? 'left: 72.5em; width: 150px;' - : 'left: 81.5em; width: 150px;'} + ? 'left: 79em; width: 150px;' + : 'left: 88em; width: 150px;'} `; const PaginationContainer = styled.div` @@ -607,6 +607,9 @@ const MemberTable = styled.table` width:100px; color: #0078d4; text-decoration: none; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; } tr { @@ -630,4 +633,4 @@ const MemberList = styled.div` .menu { margin-left: auto; } -`; +`; \ No newline at end of file diff --git a/src/page/ManageRolePage.js b/src/page/ManageRolePage.js index 5e94d00..bfbc417 100644 --- a/src/page/ManageRolePage.js +++ b/src/page/ManageRolePage.js @@ -282,8 +282,8 @@ const Cartegories = styled.div` props.type === 'first' ? 'left: 8rem; width: 180px;' : props.type === 'second' - ? 'left:19rem; width: 420px;' - : 'left: 41rem; width: 170px;'} + ? 'left:18rem; width: 440px;' + : 'left: 39rem; width: 170px;'} `; const PaginationContainer = styled.div` @@ -442,4 +442,4 @@ const MemberList = styled.div` .menu { margin-left: auto; } -`; +`; \ No newline at end of file diff --git a/src/page/QnAPage.js b/src/page/QnAPage.js index 3c9bea4..e2c2fc2 100644 --- a/src/page/QnAPage.js +++ b/src/page/QnAPage.js @@ -1,21 +1,22 @@ import styled from 'styled-components'; import axios from 'axios'; import React, { useState, useEffect, useRef } from 'react'; -import Modal from 'react-modal'; // react-modal 라이브러리 import import Pagination from '../component/manage/Pagenation'; +import { RMopen, MODopen } from '../modules/ProductSlice'; +import { useSelector, useDispatch } from 'react-redux'; +import { useCallback } from 'react'; +import ModifyModal from '../container/product/ModifyModal'; import InOut from '../component/common/InOut'; import IntroBox from '../component/admin/IntroBox'; import { introInfo } from '../resource/data/adminInfo'; -import { RMopen } from '../modules/ProductSlice'; -import { useSelector, useDispatch } from 'react-redux'; -import { useCallback } from 'react'; -import QnARegis from '../container/product/QnARegis'; +import ProductRegis from '../container/product/ProductRegis'; -export default function QnAPage() { +export default function ProductPage() { const [data, setData] = useState([]); const regisModalOpen = useSelector((state) => state.product.regisModalOpen); // prettier-ignore + const modifyModalOpen = useSelector((state) => state.product.modifyModalOpen); const dispatch = useDispatch(); const [contextMenuVisible, setContextMenuVisible] = useState(false); @@ -23,18 +24,13 @@ export default function QnAPage() { x: 0, y: 0, }); - const [selectedQnaId, setSelectedQnaId] = useState(null); + const [selectedProductId, setselectedProductId] = useState(null); const contextMenuRef = useRef(null); - const [isEditModalOpen, setEditModalOpen] = useState(false); - - //* 수정 기능을 이용할 때 값을 저장하기 위해 사용합니다. */ - const [editedPart, setEditedPart] = useState(''); - const [editedQuestion, setEditedQuestion] = useState(''); - const [editedAnswer, setEditedAnswer] = useState(''); + const [productId, setProductId] = useState(''); // 페이지네이션을 구현할때 사용합니다. const [currentPage, setCurrentPage] = useState(1); - const itemsPerPage = 10; + const itemsPerPage = 3; // 페이지당 데이터를 분할하는 함수입니다. const paginateData = (data, currentPage, itemsPerPage) => { @@ -52,24 +48,10 @@ export default function QnAPage() { setCurrentPage(pageNumber); }; - const openEditModal = (selectedQnaId) => { + const openEditModal = (selectedProductId) => { // 수정할 때 해당 memberId의 데이터를 가져와서 모달에 미리 채워넣을 수 있습니다. setContextMenuVisible(false); - setEditModalOpen(true); - }; - - useEffect(() => { - const QnaToEdit = data.find((item) => item.id === selectedQnaId); - if (QnaToEdit) { - setEditedPart(QnaToEdit.part); - setEditedQuestion(QnaToEdit.question); - setEditedAnswer(QnaToEdit.answer); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedQnaId]); - - const closeEditModal = () => { - setEditModalOpen(false); + dispatch(MODopen()); }; const addData = () => { @@ -85,14 +67,14 @@ export default function QnAPage() { const fetchData = async () => { const viewData = await axios //eslint-disable-line no-unused-vars .get( - 'https://server.inuappcenter.kr/faqs/public/all-faq-boards' + 'https://server.inuappcenter.kr/introduction-board/public/all-boards-contents' ) .then((res) => { setData(res.data); }); }; fetchData(); - }, [regisModalOpen]); + }, [regisModalOpen, modifyModalOpen]); useEffect(() => { const handleContextMenuClick = (e) => { @@ -113,56 +95,25 @@ export default function QnAPage() { }; }, []); - const handleEdit = async () => { - if (selectedQnaId === null) { - return; // 선택된 항목이 없으면 무시 - } - - // 수정할 데이터를 가져옵니다. - const updatedData = { - part: editedPart, - question: editedQuestion, - answer: editedAnswer, - }; - - try { - // member_id를 사용하여 수정 요청을 보냅니다. - const response = await axios.patch( - `https://server.inuappcenter.kr/faqs?id=${selectedQnaId}`, - updatedData - ); - console.log('Qna with ID', selectedQnaId, 'has been updated.'); - console.log(response); - // 업데이트된 데이터를 data 상태에서 업데이트합니다. - setData((prevData) => - prevData.map((item) => - item.id === selectedQnaId - ? { ...item, ...updatedData } - : item - ) - ); - } catch (error) { - console.error('Error updating member:', error); - } - setEditModalOpen(false); - setContextMenuVisible(false); // 컨텍스트 메뉴 닫기 - }; - const handleDelete = async () => { - if (selectedQnaId === null) { + if (selectedProductId === null) { return; // 선택된 항목이 없으면 무시 } try { - // member_id를 사용하여 삭제 요청을 보냅니다. + // id를 사용하여 삭제 요청을 보냅니다. await axios.delete( - `https://server.inuappcenter.kr/faqs/${selectedQnaId}` + `https://server.inuappcenter.kr/introduction-board/${selectedProductId}` + ); + console.log( + 'Member with ID', + selectedProductId, + 'has been deleted.' ); - console.log('Member with ID', selectedQnaId, 'has been deleted.'); // 삭제한 데이터를 data 상태에서 제거합니다. setData((prevData) => - prevData.filter((item) => item.id !== selectedQnaId) + prevData.filter((item) => item.id !== selectedProductId) ); } catch (error) { console.error('Error deleting member:', error); @@ -174,13 +125,13 @@ export default function QnAPage() { return ( <> - - 질문 및 답변 목록 + + 앱 목록 - 파트 - 질문 - 답변 + 썸네일 + 제목 + 부제목 {getCurrentPageData().map((content) => ( @@ -188,18 +139,29 @@ export default function QnAPage() { key={content.id} onContextMenu={(e) => { e.preventDefault(); - setSelectedQnaId(content.id); + setselectedProductId(content.id); setContextMenuPosition({ x: e.clientX, y: e.clientY, }); setContextMenuVisible(true); + setProductId(content.id); console.log(content.id); }} > - {content.part} - {content.question} - {content.answer} + +
+ +
+
+ {content.title} + {content.subTitle} ))} @@ -220,6 +182,8 @@ export default function QnAPage() { 등록 + {regisModalOpen && } + {modifyModalOpen && } {/* 컨텍스트 메뉴 */} {contextMenuVisible && ( 삭제 )} - {regisModalOpen && } - {/* 수정 팝업 모달 */} - - QnA 수정 - 파트 - setEditedPart(e.target.value)} - /> - 질문 - setEditedQuestion(e.target.value)} - /> - 답변 - setEditedAnswer(e.target.value)} - /> - - - 수정 완료 - 취소 - - ); } +const MemberBar = styled.div` + display: flex; + + justify-content: center; + align-items: center; + height: 40px; + transform: translate(-8rem); +`; + const Cartegories = styled.div` width: 80px; height: 20px; @@ -279,19 +220,26 @@ const Cartegories = styled.div` position: absolute; ${(props) => props.type === 'first' - ? 'left: 8rem;' + ? 'left: 8rem; width: 250px;' : props.type === 'second' - ? 'left:12rem; width: 250px;' - : 'left: 26rem; width: 410px;'} + ? 'left:19.5rem; width: 340px;' + : 'left: 36.5rem; width: 250px;'} `; -const MemberBar = styled.div` - display: flex; +const AppTd = styled.td` + width: 200px; + ${({ regisModalOpen }) => + regisModalOpen && + ` opacity: 0.1; +`} +`; - justify-content: center; - align-items: center; - height: 40px; - transform: translate(-8rem); +const AppImage = styled.img` + width: 7rem; + height: 7rem; + max-height: 200px; + object-fit: cover; + border-radius: 8px; `; const PaginationContainer = styled.div` @@ -300,67 +248,6 @@ const PaginationContainer = styled.div` margin-top: 20px; `; -const ModalContainer = styled(Modal)` - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - background-color: #fff; - border-radius: 8px; - box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); - padding: 20px; - width: 500px; - margin: 0 auto; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -`; - -const ModalTitle = styled.h2` - font-size: 1.5rem; - margin-bottom: 15px; -`; - -const ModalLabel = styled.label` - font-size: 1rem; - margin-bottom: 5px; -`; - -const ModalInput = styled.input` - width: 70%; - padding: 8px; - margin-bottom: 15px; - border: 1px solid #ccc; - border-radius: 4px; - font-size: 1rem; -`; - -const ModalButtonWrapper = styled.div` - display: flex; - justify-content: space-between; - margin-top: 15px; -`; - -const ModalButton = styled.button` - background-color: #1e88e5; - color: #fff; - border: none; - border-radius: 4px; - padding: 8px 16px; - font-size: 1rem; - cursor: pointer; - transition: background-color 0.2s ease-in-out; - - & + & { - margin: 0 10px; - } - - &:hover { - background-color: #8181f7; - } -`; - const MenuItem = styled.div` display: flex; align-items: center; @@ -397,7 +284,7 @@ const Regisbutton = styled.button` color: white; width: 5rem; height: 2rem; - margin-left: 37rem; + margin-left: 30rem; margin-top: 0.6rem; &:hover { @@ -413,17 +300,10 @@ const MemberTable = styled.table` th, td { + width: 200px; padding: 5px; text-align: center; box-shadow: 0 0 3px rgba(0, 0, 0, 0.1); - - :nth-child(2) { - width: 200px; - } - - :nth-child(3) { - width: 400px; - } } th { @@ -437,10 +317,10 @@ const MemberTable = styled.table` tr { border-radius: 20%; + } - &:hover { - background-color: #f2f2f2; - } + tr:hover { + background-color: #f2f2f2; } `; @@ -456,4 +336,4 @@ const MemberList = styled.div` .menu { margin-left: auto; } -`; +`; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 76c6321..191209e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5095,11 +5095,6 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.2, fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - function-bind@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" From 6d28e81edb03c4071d39acb80198e271619e0dd0 Mon Sep 17 00:00:00 2001 From: a1 Date: Mon, 26 Feb 2024 19:27:19 +0900 Subject: [PATCH 24/24] =?UTF-8?q?style:=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=20UI=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/page/ManageGenPage.js | 47 +++---- src/page/ProductPage.js | 5 +- src/page/QnAPage.js | 272 +++++++++++++++++++++++++++----------- 3 files changed, 217 insertions(+), 107 deletions(-) diff --git a/src/page/ManageGenPage.js b/src/page/ManageGenPage.js index c2cd92c..73cb35f 100644 --- a/src/page/ManageGenPage.js +++ b/src/page/ManageGenPage.js @@ -178,15 +178,15 @@ export default function ManageGenPage() { 편성 목록 + + 이름 + 역할명 + 기수 + 파트명 + 이메일 + - - 이름 - 역할명 - 기수 - 파트명 - 이메일 - {getCurrentPageData().map((content) => ( - props.type === 'first' - ? 'left: 8rem; width: 120px;' - : props.type === 'second' - ? 'left:15rem; width: 100px;' - : props.type === 'third' - ? 'left: 21rem; width: 120px;' - : props.type === 'fourth' - ? 'left: 28rem; width: 120px;' - : 'left: 34rem; width: 180px;'} `; const PaginationContainer = styled.div` @@ -401,8 +392,8 @@ const Regisbutton = styled.button` color: white; width: 5rem; height: 2rem; - margin-left: 37rem; - margin-top: 0.6rem; + margin-left: 38rem; + margin-top: 3rem; &:hover { transition: 0.1s ease-in; @@ -411,11 +402,11 @@ const Regisbutton = styled.button` `; const MemberTable = styled.table` - width: 600px; + width: 800px; margin: 20px auto 20px auto; td { - width: 90px; + width: 160px; padding: 6px; text-align: center; box-shadow: 0 0 3px rgba(0, 0, 0, 0.1); @@ -426,9 +417,7 @@ const MemberTable = styled.table` overflow: hidden; white-space: nowrap; - ${(props) => - props.type === 'email' - ? 'width: 150px;' : ''} + ${(props) => (props.type === 'email' ? 'width: 150px;' : '')} th { font-weight: 700; @@ -469,4 +458,4 @@ const MemberList = styled.div` .menu { margin-left: auto; } -`; \ No newline at end of file +`; diff --git a/src/page/ProductPage.js b/src/page/ProductPage.js index 6c8a937..07ae800 100644 --- a/src/page/ProductPage.js +++ b/src/page/ProductPage.js @@ -222,8 +222,8 @@ const Cartegories = styled.div` props.type === 'first' ? 'left: 8rem; width: 250px;' : props.type === 'second' - ? 'left:20rem; width: 340px;' - : 'left: 41rem; width: 170px;'} + ? 'left:19.5rem; width: 340px;' + : 'left: 36.5rem; width: 250px;'} `; const AppTd = styled.td` @@ -300,6 +300,7 @@ const MemberTable = styled.table` th, td { + width: 200px; padding: 5px; text-align: center; box-shadow: 0 0 3px rgba(0, 0, 0, 0.1); diff --git a/src/page/QnAPage.js b/src/page/QnAPage.js index e2c2fc2..3c9bea4 100644 --- a/src/page/QnAPage.js +++ b/src/page/QnAPage.js @@ -1,22 +1,21 @@ import styled from 'styled-components'; import axios from 'axios'; import React, { useState, useEffect, useRef } from 'react'; +import Modal from 'react-modal'; // react-modal 라이브러리 import import Pagination from '../component/manage/Pagenation'; -import { RMopen, MODopen } from '../modules/ProductSlice'; -import { useSelector, useDispatch } from 'react-redux'; -import { useCallback } from 'react'; -import ModifyModal from '../container/product/ModifyModal'; import InOut from '../component/common/InOut'; import IntroBox from '../component/admin/IntroBox'; import { introInfo } from '../resource/data/adminInfo'; -import ProductRegis from '../container/product/ProductRegis'; +import { RMopen } from '../modules/ProductSlice'; +import { useSelector, useDispatch } from 'react-redux'; +import { useCallback } from 'react'; +import QnARegis from '../container/product/QnARegis'; -export default function ProductPage() { +export default function QnAPage() { const [data, setData] = useState([]); const regisModalOpen = useSelector((state) => state.product.regisModalOpen); // prettier-ignore - const modifyModalOpen = useSelector((state) => state.product.modifyModalOpen); const dispatch = useDispatch(); const [contextMenuVisible, setContextMenuVisible] = useState(false); @@ -24,13 +23,18 @@ export default function ProductPage() { x: 0, y: 0, }); - const [selectedProductId, setselectedProductId] = useState(null); + const [selectedQnaId, setSelectedQnaId] = useState(null); const contextMenuRef = useRef(null); - const [productId, setProductId] = useState(''); + const [isEditModalOpen, setEditModalOpen] = useState(false); + + //* 수정 기능을 이용할 때 값을 저장하기 위해 사용합니다. */ + const [editedPart, setEditedPart] = useState(''); + const [editedQuestion, setEditedQuestion] = useState(''); + const [editedAnswer, setEditedAnswer] = useState(''); // 페이지네이션을 구현할때 사용합니다. const [currentPage, setCurrentPage] = useState(1); - const itemsPerPage = 3; + const itemsPerPage = 10; // 페이지당 데이터를 분할하는 함수입니다. const paginateData = (data, currentPage, itemsPerPage) => { @@ -48,10 +52,24 @@ export default function ProductPage() { setCurrentPage(pageNumber); }; - const openEditModal = (selectedProductId) => { + const openEditModal = (selectedQnaId) => { // 수정할 때 해당 memberId의 데이터를 가져와서 모달에 미리 채워넣을 수 있습니다. setContextMenuVisible(false); - dispatch(MODopen()); + setEditModalOpen(true); + }; + + useEffect(() => { + const QnaToEdit = data.find((item) => item.id === selectedQnaId); + if (QnaToEdit) { + setEditedPart(QnaToEdit.part); + setEditedQuestion(QnaToEdit.question); + setEditedAnswer(QnaToEdit.answer); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedQnaId]); + + const closeEditModal = () => { + setEditModalOpen(false); }; const addData = () => { @@ -67,14 +85,14 @@ export default function ProductPage() { const fetchData = async () => { const viewData = await axios //eslint-disable-line no-unused-vars .get( - 'https://server.inuappcenter.kr/introduction-board/public/all-boards-contents' + 'https://server.inuappcenter.kr/faqs/public/all-faq-boards' ) .then((res) => { setData(res.data); }); }; fetchData(); - }, [regisModalOpen, modifyModalOpen]); + }, [regisModalOpen]); useEffect(() => { const handleContextMenuClick = (e) => { @@ -95,25 +113,56 @@ export default function ProductPage() { }; }, []); + const handleEdit = async () => { + if (selectedQnaId === null) { + return; // 선택된 항목이 없으면 무시 + } + + // 수정할 데이터를 가져옵니다. + const updatedData = { + part: editedPart, + question: editedQuestion, + answer: editedAnswer, + }; + + try { + // member_id를 사용하여 수정 요청을 보냅니다. + const response = await axios.patch( + `https://server.inuappcenter.kr/faqs?id=${selectedQnaId}`, + updatedData + ); + console.log('Qna with ID', selectedQnaId, 'has been updated.'); + console.log(response); + // 업데이트된 데이터를 data 상태에서 업데이트합니다. + setData((prevData) => + prevData.map((item) => + item.id === selectedQnaId + ? { ...item, ...updatedData } + : item + ) + ); + } catch (error) { + console.error('Error updating member:', error); + } + setEditModalOpen(false); + setContextMenuVisible(false); // 컨텍스트 메뉴 닫기 + }; + const handleDelete = async () => { - if (selectedProductId === null) { + if (selectedQnaId === null) { return; // 선택된 항목이 없으면 무시 } try { - // id를 사용하여 삭제 요청을 보냅니다. + // member_id를 사용하여 삭제 요청을 보냅니다. await axios.delete( - `https://server.inuappcenter.kr/introduction-board/${selectedProductId}` - ); - console.log( - 'Member with ID', - selectedProductId, - 'has been deleted.' + `https://server.inuappcenter.kr/faqs/${selectedQnaId}` ); + console.log('Member with ID', selectedQnaId, 'has been deleted.'); // 삭제한 데이터를 data 상태에서 제거합니다. setData((prevData) => - prevData.filter((item) => item.id !== selectedProductId) + prevData.filter((item) => item.id !== selectedQnaId) ); } catch (error) { console.error('Error deleting member:', error); @@ -125,13 +174,13 @@ export default function ProductPage() { return ( <> - - 앱 목록 + + 질문 및 답변 목록 - 썸네일 - 제목 - 부제목 + 파트 + 질문 + 답변 {getCurrentPageData().map((content) => ( @@ -139,29 +188,18 @@ export default function ProductPage() { key={content.id} onContextMenu={(e) => { e.preventDefault(); - setselectedProductId(content.id); + setSelectedQnaId(content.id); setContextMenuPosition({ x: e.clientX, y: e.clientY, }); setContextMenuVisible(true); - setProductId(content.id); console.log(content.id); }} > - -
- -
-
- {content.title} - {content.subTitle} + {content.part} + {content.question} + {content.answer} ))} @@ -182,8 +220,6 @@ export default function ProductPage() { 등록 - {regisModalOpen && } - {modifyModalOpen && } {/* 컨텍스트 메뉴 */} {contextMenuVisible && ( 삭제 )} + {regisModalOpen && } + {/* 수정 팝업 모달 */} + + QnA 수정 + 파트 + setEditedPart(e.target.value)} + /> + 질문 + setEditedQuestion(e.target.value)} + /> + 답변 + setEditedAnswer(e.target.value)} + /> + + + 수정 완료 + 취소 + + ); } -const MemberBar = styled.div` - display: flex; - - justify-content: center; - align-items: center; - height: 40px; - transform: translate(-8rem); -`; - const Cartegories = styled.div` width: 80px; height: 20px; @@ -220,26 +279,19 @@ const Cartegories = styled.div` position: absolute; ${(props) => props.type === 'first' - ? 'left: 8rem; width: 250px;' + ? 'left: 8rem;' : props.type === 'second' - ? 'left:19.5rem; width: 340px;' - : 'left: 36.5rem; width: 250px;'} + ? 'left:12rem; width: 250px;' + : 'left: 26rem; width: 410px;'} `; -const AppTd = styled.td` - width: 200px; - ${({ regisModalOpen }) => - regisModalOpen && - ` opacity: 0.1; -`} -`; +const MemberBar = styled.div` + display: flex; -const AppImage = styled.img` - width: 7rem; - height: 7rem; - max-height: 200px; - object-fit: cover; - border-radius: 8px; + justify-content: center; + align-items: center; + height: 40px; + transform: translate(-8rem); `; const PaginationContainer = styled.div` @@ -248,6 +300,67 @@ const PaginationContainer = styled.div` margin-top: 20px; `; +const ModalContainer = styled(Modal)` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background-color: #fff; + border-radius: 8px; + box-shadow: 0 0 5px rgba(0, 0, 0, 0.3); + padding: 20px; + width: 500px; + margin: 0 auto; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +`; + +const ModalTitle = styled.h2` + font-size: 1.5rem; + margin-bottom: 15px; +`; + +const ModalLabel = styled.label` + font-size: 1rem; + margin-bottom: 5px; +`; + +const ModalInput = styled.input` + width: 70%; + padding: 8px; + margin-bottom: 15px; + border: 1px solid #ccc; + border-radius: 4px; + font-size: 1rem; +`; + +const ModalButtonWrapper = styled.div` + display: flex; + justify-content: space-between; + margin-top: 15px; +`; + +const ModalButton = styled.button` + background-color: #1e88e5; + color: #fff; + border: none; + border-radius: 4px; + padding: 8px 16px; + font-size: 1rem; + cursor: pointer; + transition: background-color 0.2s ease-in-out; + + & + & { + margin: 0 10px; + } + + &:hover { + background-color: #8181f7; + } +`; + const MenuItem = styled.div` display: flex; align-items: center; @@ -284,7 +397,7 @@ const Regisbutton = styled.button` color: white; width: 5rem; height: 2rem; - margin-left: 30rem; + margin-left: 37rem; margin-top: 0.6rem; &:hover { @@ -300,10 +413,17 @@ const MemberTable = styled.table` th, td { - width: 200px; padding: 5px; text-align: center; box-shadow: 0 0 3px rgba(0, 0, 0, 0.1); + + :nth-child(2) { + width: 200px; + } + + :nth-child(3) { + width: 400px; + } } th { @@ -317,10 +437,10 @@ const MemberTable = styled.table` tr { border-radius: 20%; - } - tr:hover { - background-color: #f2f2f2; + &:hover { + background-color: #f2f2f2; + } } `; @@ -336,4 +456,4 @@ const MemberList = styled.div` .menu { margin-left: auto; } -`; \ No newline at end of file +`;