diff --git a/src/App.jsx b/src/App.jsx
index d509b19..c1fcba1 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -14,6 +14,8 @@ import ProjectRecruitment from './components/project-recruitment';
import ProjectCollaboration from './components/project-collaboration';
import ProjectPromotion from './components/project-promotion';
import Community from './components/community/Top10-rank';
+import ContactForm from './components/contact/contactForm';
+import TeamMemberSearch from './components/contact/member-registration';
const router = createBrowserRouter([
@@ -86,7 +88,10 @@ const router = createBrowserRouter([
},
{
path: 'community',
+ //element:
+ //element:
element:
+
},
{ //마이페이지 경로
path: 'mypage',
diff --git a/src/components/contact/contactForm.jsx b/src/components/contact/contactForm.jsx
new file mode 100644
index 0000000..df8dc2a
--- /dev/null
+++ b/src/components/contact/contactForm.jsx
@@ -0,0 +1,104 @@
+import React, { useState } from 'react';
+
+import {
+ MainContainer,
+ Title,
+ Description,
+ ContactsContainer,
+ InputContainer,
+ InputWrapper,
+ InputGroup,
+ Input,
+ RegisterButton,
+ RegisteredContact,
+ ContactInfo,
+ DeleteButton
+} from "../../styled-components/contact/styled-contactForm";
+
+const ContactForm = () => {
+ const [contacts, setContacts] = useState([]);
+ const [currentContact, setCurrentContact] = useState({
+ method: '',
+ link: '',
+ isRegistered: false
+ });
+
+ const handleRegister = () => {
+ if (currentContact.method && currentContact.link) {
+ setContacts([...contacts, { ...currentContact, isRegistered: true }]);
+ setCurrentContact({ method: '', link: '', isRegistered: false });
+ }
+ };
+
+ const handleDelete = (index) => {
+ const newContacts = contacts.filter((_, i) => i !== index);
+ setContacts(newContacts);
+ };
+
+ return (
+
+ 연락 방법
+ 이메일, 오픈채팅방, 인스타그램 등 연락 방법을 입력해주세요
+
+
+ {contacts.length < 10 && (
+
+
+
+ setCurrentContact({
+ ...currentContact,
+ method: e.target.value
+ })}
+ className="method"
+ />
+
+
+ setCurrentContact({
+ ...currentContact,
+ link: e.target.value
+ })}
+ className="link"
+ />
+
+
+ 등록하기
+
+ )}
+
+ {contacts.map((contact, index) => (
+
+
+
+
+
+ handleDelete(index)}>
+
+
+
+ ))}
+
+
+ );
+};
+
+
+export default ContactForm;
diff --git a/src/components/contact/member-registration.jsx b/src/components/contact/member-registration.jsx
new file mode 100644
index 0000000..44325c2
--- /dev/null
+++ b/src/components/contact/member-registration.jsx
@@ -0,0 +1,129 @@
+import React, { useState } from 'react';
+
+import {
+ Container,
+ Title,
+ Description,
+ MainSection,
+ SearchSection,
+ SearchInputWrapper,
+ SearchInput,
+ SearchButton,
+ TagsSection,
+ TagGroup,
+ TagLabel,
+ TagsWrapper,
+ LeaderContainer,
+ MemberContainer,
+ LeaderTag,
+ MemberTag,
+ DeleteButton,
+ SearchResultsList,
+ SearchResultItem,
+ ProfileImage,
+ ResultNickname
+ } from '../../styled-components/contact/styled-member-registration';
+
+export const TeamMemberSearch = () => {
+ // 임시 데이터
+ const [searchQuery, setSearchQuery] = useState('');
+ const [searchResults, setSearchResults] = useState([]);
+ const [teamMembers, setTeamMembers] = useState([
+ { id: 1, nickname: '도도', isLeader: false },
+ { id: 2, nickname: '이노', isLeader: false }
+ ]);
+
+ // 임시 사용자 데이터베이스
+ const userDatabase = [
+ { id: 3, nickname: '에이치', profileImage: '프로필이미지URL' },
+ { id: 4, nickname: '에이호', profileImage: '프로필이미지URL' },
+ { id: 5, nickname: '에이든', profileImage: '프로필이미지URL' },
+ ];
+
+ const handleSearch = (e) => {
+ setSearchQuery(e.target.value);
+ if (e.target.value.trim()) {
+ const results = userDatabase.filter(user =>
+ user.nickname.toLowerCase().includes(e.target.value.toLowerCase())
+ );
+ setSearchResults(results);
+ } else {
+ setSearchResults([]);
+ }
+ };
+
+ const handleAddMember = (member) => {
+ if (!teamMembers.find(m => m.id === member.id)) {
+ setTeamMembers([...teamMembers, { ...member, isLeader: false }]);
+ }
+ setSearchResults([]);
+ setSearchQuery('');
+ };
+
+ const handleRemoveMember = (memberId) => {
+ setTeamMembers(teamMembers.filter(member => member.id !== memberId));
+ };
+
+ return (
+
+ 함께 한 팀원
+ 프로젝트를 함께하고 있는 팀원이 있다면 추가해주세요.
+
+
+
+
+
+
+
+
+
+
+ {searchResults.length > 0 && (
+
+ {searchResults.map(result => (
+ handleAddMember(result)}>
+
+ {result.nickname}
+
+ ))}
+
+ )}
+
+
+
+
+ 리더
+ 노브
+
+
+
+ 팀원
+
+ {teamMembers.map(member => (
+
+ {member.nickname}
+ handleRemoveMember(member.id)}>
+
+
+
+ ))}
+
+
+
+
+
+ );
+};
+
+
+export default TeamMemberSearch;
diff --git a/src/components/contact/permission-registration.jsx b/src/components/contact/permission-registration.jsx
new file mode 100644
index 0000000..e69de29
diff --git a/src/components/partnerd-search.jsx b/src/components/partnerd-search.jsx
index ba72107..46e2d10 100644
--- a/src/components/partnerd-search.jsx
+++ b/src/components/partnerd-search.jsx
@@ -76,7 +76,21 @@ const PartnerSearch = () => {
key="prev"
onClick={() => setCurrentPage(prev => prev === 1 ? totalPages : prev - 1)}
>
-
+
);
@@ -111,7 +125,21 @@ const PartnerSearch = () => {
key="next"
onClick={() => setCurrentPage(prev => prev === totalPages ? 1 : prev + 1)}
>
-
+
);
diff --git a/src/components/project-collaboration.jsx b/src/components/project-collaboration.jsx
index 04eaadc..f7baa83 100644
--- a/src/components/project-collaboration.jsx
+++ b/src/components/project-collaboration.jsx
@@ -73,7 +73,21 @@ const ProjectCollaboration = () => {
key="prev"
onClick={() => setCurrentPage(prev => prev === 1 ? totalPages : prev - 1)}
>
-
+
);
@@ -106,7 +120,21 @@ const ProjectCollaboration = () => {
key="next"
onClick={() => setCurrentPage(prev => prev === totalPages ? 1 : prev + 1)}
>
-
+
);
diff --git a/src/components/project-promotion.jsx b/src/components/project-promotion.jsx
index c629803..c50010e 100644
--- a/src/components/project-promotion.jsx
+++ b/src/components/project-promotion.jsx
@@ -95,7 +95,21 @@ const ProjectPromotion = () => {
key="prev"
onClick={() => setCurrentPage(prev => prev === 1 ? totalPages : prev - 1)}
>
-
+
);
@@ -142,7 +156,21 @@ const ProjectPromotion = () => {
key="next"
onClick={() => setCurrentPage(prev => prev === totalPages ? 1 : prev + 1)}
>
-
+
);
diff --git a/src/components/project-recruitment.jsx b/src/components/project-recruitment.jsx
index 625ef1a..7993944 100644
--- a/src/components/project-recruitment.jsx
+++ b/src/components/project-recruitment.jsx
@@ -103,7 +103,21 @@ const ProjectRecruitment = () => {
}
}}
>
-
+
);
@@ -154,7 +168,21 @@ const ProjectRecruitment = () => {
}
}}
>
-
+
);
diff --git a/src/styled-components/contact/styled-contactForm.jsx b/src/styled-components/contact/styled-contactForm.jsx
new file mode 100644
index 0000000..8f81d6d
--- /dev/null
+++ b/src/styled-components/contact/styled-contactForm.jsx
@@ -0,0 +1,159 @@
+import styled from "styled-components";
+
+export const MainContainer = styled.div`
+ background: #FFFFFF;
+ padding: 24px;
+ border-radius: 8px;
+ width: 1180px;
+ height: 886px;
+ flex-shrink: 0;
+`;
+
+export const Title = styled.h2`
+ color: #212121;
+ font-family: Pretendard;
+ font-size: 32px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: normal;
+ letter-spacing: -0.64px;
+ margin-bottom: 8px;
+`;
+
+export const Description = styled.p`
+ color: #707070;
+ font-family: Pretendard;
+ font-size: 16px;
+ font-style: normal;
+ font-weight: 500;
+ line-height: normal;
+ letter-spacing: -0.32px;
+ margin-bottom: 20px;
+`;
+
+export const ContactsContainer = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ width: 1028px;
+ height: 229px;
+ flex-shrink: 0;
+`;
+
+export const InputContainer = styled.div`
+ background: #F3F3F3;
+ padding: 16px;
+ border-radius: 8px;
+ display: flex;
+ gap: 12px;
+ align-items: flex-start;
+`;
+
+export const InputWrapper = styled.div`
+ display: grid;
+ grid-template-columns: 210px 632px;
+ gap: 16px;
+ flex: 1;
+`;
+
+export const InputGroup = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+`;
+
+export const Input = styled.input`
+ padding: 22px 28px;
+ border: 2px solid #E1E1E1;
+ border-radius: 4px;
+ font-size: 14px;
+ color: #212121;
+ background: #FFFFFF;
+ box-sizing: border-box;
+ display: flex;
+ align-items: center;
+ flex-shrink: 0;
+
+ &.method {
+ width: 210px;
+ height: 64px;
+ }
+
+ &.link {
+ width: 632px;
+ height: 64px;
+ }
+
+ &:focus {
+ border-color: #C2C2C2;
+ outline: none;
+ }
+
+ &::placeholder {
+ color: #E1E1E1;
+ }
+
+ &:disabled {
+ background: #FFFFFF;
+ border-color: #E1E1E1;
+ color: #212121;
+ }
+`;
+
+export const RegisterButton = styled.button`
+ display: inline-flex;
+ padding: 20px 24px;
+ align-items: center;
+ gap: 8px;
+ border-radius: 8px;
+ border: 1px solid #0D29B7;
+ background: #FFF;
+ color: #0D29B7;
+ font-family: Pretendard;
+ font-size: 20px;
+ font-style: normal;
+ font-weight: 600;
+ line-height: normal;
+ letter-spacing: -0.4px;
+ cursor: pointer;
+
+ &:hover {
+ background: #F8F9FF;
+ }
+`;
+
+export const RegisteredContact = styled.div`
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ background: #F3F3F3;
+ padding: 16px;
+ border-radius: 8px;
+`;
+
+export const ContactInfo = styled.div`
+ display: grid;
+ grid-template-columns: 210px 632px;
+ gap: 16px;
+ flex: 1;
+`;
+
+export const DeleteButton = styled.button`
+ width: 40px;
+ height: 40px;
+ flex-shrink: 0;
+ background: none;
+ border: none;
+ padding: 0;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-right: 82px;
+
+ &:hover {
+ svg path {
+ stroke: #666666;
+ }
+ }
+`;
\ No newline at end of file
diff --git a/src/styled-components/contact/styled-member-registration.jsx b/src/styled-components/contact/styled-member-registration.jsx
new file mode 100644
index 0000000..5299e56
--- /dev/null
+++ b/src/styled-components/contact/styled-member-registration.jsx
@@ -0,0 +1,191 @@
+import styled from "styled-components";
+
+export const Container = styled.div`
+ width: 863px;
+`;
+
+export const Title = styled.h2`
+ color: #212121;
+ font-family: Pretendard;
+ font-size: 32px;
+ font-weight: 700;
+ letter-spacing: -0.64px;
+ margin-bottom: 16px;
+`;
+
+export const Description = styled.p`
+ color: #707070;
+ font-family: Pretendard;
+ font-size: 24px;
+ font-weight: 500;
+ letter-spacing: -0.48px;
+ margin-bottom: 32px;
+`;
+
+export const MainSection = styled.div`
+ display: flex;
+ gap: 120px;
+`;
+
+export const SearchSection = styled.div`
+ position: relative;
+ width: 500px;
+`;
+
+export const SearchInputWrapper = styled.div`
+ position: relative;
+ width: 500px;
+`;
+
+export const SearchInput = styled.input`
+ width: 500px;
+ height: 52px;
+ padding: 14px 24px;
+ border-radius: 4px;
+ border: 1px solid #E0E0E0;
+ background: #FFF;
+ box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.1);
+ color: #212121;
+ font-family: Pretendard;
+ font-size: 16px;
+ font-weight: 500;
+
+ &::placeholder {
+ color: #C2C2C2;
+ }
+`;
+
+export const SearchButton = styled.button`
+ position: absolute;
+ margin-left: 500px;
+ top: 50%;
+ transform: translateY(-50%);
+ background: none;
+ border: none;
+ cursor: pointer;
+ padding: 0;
+ width: 20px;
+ height: 20px;
+`;
+
+export const TagsSection = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 24px;
+`;
+
+export const TagGroup = styled.div`
+ display: flex;
+ align-items: center;
+`;
+
+export const TagLabel = styled.span`
+ color: #212121;
+ font-family: Pretendard;
+ font-size: 20px;
+ font-weight: 700;
+ letter-spacing: -0.4px;
+ white-space: nowrap;
+ position: sticky;
+ top: 0;
+`;
+
+export const TagsWrapper = styled.div`
+ display: grid;
+ grid-template-columns: repeat(3, auto);
+ gap: 8px;
+ width: fit-content;
+ align-items: flex-start;
+`;
+
+export const LeaderContainer = styled.div`
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 16px;
+`;
+
+export const MemberContainer = styled.div`
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ gap: 16px;
+`;
+
+export const LeaderTag = styled.div`
+ display: flex;
+ padding: 4px 20px;
+ align-items: center;
+ border-radius: 100px;
+ border: 1px solid #0D29B7;
+ background: #FFF;
+ color: #0D29B7;
+ font-family: Pretendard;
+ font-size: 20px;
+ font-weight: 600;
+ letter-spacing: -0.4px;
+`;
+
+export const MemberTag = styled.div`
+ display: inline-flex;
+ padding: 4px 10px 4px 20px;
+ align-items: center;
+ gap: 10px;
+ border-radius: 100px;
+ border: 1px solid #0D29B7;
+ background: #EAF1FF;
+ color: #0D29B7;
+ font-family: Pretendard;
+ font-size: 20px;
+ font-weight: 600;
+ letter-spacing: -0.4px;
+ white-space: nowrap;
+`;
+
+export const DeleteButton = styled.button`
+ background: none;
+ border: none;
+ padding: 0;
+ cursor: pointer;
+ width: 16px;
+ height: 16px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+`;
+
+export const SearchResultsList = styled.div`
+ position: absolute;
+ top: 100%;
+ left: 0;
+ width: 100%;
+ background: #FFFFFF;
+ border-radius: 4px;
+ box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.1);
+ z-index: 1000;
+`;
+
+export const SearchResultItem = styled.div`
+ display: flex;
+ align-items: center;
+ padding: 16px;
+ cursor: pointer;
+
+ &:hover {
+ background: #F3F3F3;
+ }
+`;
+
+export const ProfileImage = styled.img`
+ width: 50px;
+ height: 50px;
+ border-radius: 50%;
+ margin-right: 12px;
+`;
+
+export const ResultNickname = styled.span`
+ color: #212121;
+ font-family: Pretendard;
+ font-size: 16px;
+ font-weight: 600;
+`;
\ No newline at end of file
diff --git a/src/styled-components/styled-common.jsx b/src/styled-components/styled-common.jsx
index 0777cf6..096982d 100644
--- a/src/styled-components/styled-common.jsx
+++ b/src/styled-components/styled-common.jsx
@@ -19,60 +19,18 @@ export const ArrowButton = styled.button`
justify-content: center;
`;
-export const ArrowIcon = styled.span`
+export const ArrowIcon = styled.svg`
position: relative;
display: inline-block;
width: 24px;
height: 24px;
&.right {
- &::before {
- content: '';
- position: absolute;
- left: 0;
- top: 50%;
- width: 16px;
- height: 2px;
- background: #4B48DF;
- transform: translateY(-50%);
- }
-
- &::after {
- content: '';
- position: absolute;
- left: 10px;
- top: 50%;
- width: 8px;
- height: 8px;
- border-top: 2px solid #4B48DF;
- border-right: 2px solid #4B48DF;
- transform: translateY(-50%) rotate(45deg);
- }
+ transform: rotate(180deg);
}
- &.left {
- &::before {
- content: '';
- position: absolute;
- right: 0;
- top: 50%;
- width: 16px;
- height: 2px;
- background: #4B48DF;
- transform: translateY(-50%);
- }
-
- &::after {
- content: '';
- position: absolute;
- right: 10px;
- top: 50%;
- width: 8px;
- height: 8px;
- border-top: 2px solid #4B48DF;
- border-left: 2px solid #4B48DF;
- transform: translateY(-50%) rotate(-45deg);
- }
+ path {
+ stroke: #4B48DF;
}
`;