Skip to content

Commit

Permalink
Merge pull request #166 from Ludo-SMP/feat/create-recruitment
Browse files Browse the repository at this point in the history
feat: 모집공고 작성 임시저장 기능 구현
  • Loading branch information
hyosin-Jang authored May 7, 2024
2 parents 933ccd6 + 31aa12a commit c94e65d
Show file tree
Hide file tree
Showing 64 changed files with 2,988 additions and 958 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ dist
dist-ssr
*.local

.yarn/cache/*

# Editor directories and files
.vscode/*
!.vscode/extensions.json
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ VITE_BASE_API_URL = Base API Endpoint
yarn start:windows
```

### 5. Storybook 실행

```sh
yarn storybook
````

## 📁 Directory Structure
```
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@types/react-datepicker": "^4.19.5",
"@types/styled-components": "^5.1.34",
"axios": "^1.6.7",
"date-fns": "^3.6.0",
"eslint-config-prettier": "^9.1.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-prettier": "^5.1.2",
Expand All @@ -32,8 +33,10 @@
"react-error-boundary": "^4.0.12",
"react-hook-form": "^7.50.1",
"react-router-dom": "^6.22.0",
"react-select": "^5.8.0",
"styled-components": "^6.1.8",
"styled-reset": "^4.5.2",
"uuid": "^9.0.1",
"vite-plugin-mkcert": "^1.17.3",
"zustand": "^4.5.0"
},
Expand All @@ -55,6 +58,7 @@
"@types/node": "^20.11.10",
"@types/react": "^18.2.43",
"@types/react-dom": "^18.2.17",
"@types/uuid": "^9.0.8",
"@typescript-eslint/eslint-plugin": "^6.14.0",
"@typescript-eslint/parser": "^6.14.0",
"@vitejs/plugin-react": "^4.2.1",
Expand Down
26 changes: 25 additions & 1 deletion src/Apis/recruitment.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { httpClient } from '@/utils/axios';
import { PopularRecruitments, Recruitments, FilterOptionParams, RecruitmentDetail } from '@/Types/study';
import {
PopularRecruitments,
Recruitments,
FilterOptionParams,
RecruitmentDetail,
RecruitmentForm,
} from '@/Types/study';
import { API_END_POINT } from '@/Constants/api';
import { getFilterOptions } from '@/utils/filter';

Expand All @@ -21,8 +27,26 @@ export const getRecruitments = (
});
};

// 모집공고 생성
export const createRecruitment = (
studyId: number,
body: RecruitmentForm,
): Promise<{ data: { data: RecruitmentDetail } }> => httpClient.post(API_END_POINT.CREATE_RECRUITMENT(studyId), body);

// TODO: 모집공고 수정 - 추후 data type 변경, location 헤더만 내리는 방식으로 개선 예정
export const editRecruitment = (
studyId: number,
body: RecruitmentForm,
): Promise<{ data: { data: RecruitmentDetail } }> => httpClient.put(API_END_POINT.EDIT_RECRUITMENT(studyId), body);

// 모집공고 조회
export const getRecruitmentDetail = (recruitmentId: number): Promise<{ data: { data: RecruitmentDetail } }> =>
httpClient.get(API_END_POINT.RECRUITMENT(recruitmentId));

// 모집공고 삭제
export const deleteRecruitment = (studyId: number): Promise<{ data: { data: RecruitmentDetail } }> =>
httpClient.get(API_END_POINT.DELETE_RECRUITMENT(studyId));

// ???
export const closeRecruitment = (studyId: number) =>
httpClient.patch(API_END_POINT.CLOSE_RECRUITMENT(studyId), null, { params: { status: 'RECRUITED' } });
2 changes: 1 addition & 1 deletion src/Assets/SearchIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const SearchIcon = () => {
clip-rule="evenodd"
d="M9.68274 16.3656C7.89774 16.3656 6.21954 15.6705 4.95744 14.4084C3.69534 13.1463 3.00024 11.4681 3.00024 9.68311C3.00024 7.89811 3.69534 6.21991 4.95744 4.95781C6.21954 3.69571 7.89774 3.00061 9.68274 3.00061C11.4677 3.00061 13.1459 3.69571 14.408 4.95781C15.6701 6.21991 16.3652 7.89811 16.3652 9.68311C16.3652 11.2665 15.8183 12.7659 14.8138 13.9653L15.5256 14.6772L15.8504 14.3523C16.3103 13.8924 17.0585 13.8924 17.5181 14.3523L17.9774 14.8116L17.9773 14.8118L18.0974 14.9319C18.32 15.1545 18.4427 15.4509 18.4427 15.7659C18.4427 15.8803 18.4266 15.9921 18.3953 16.0989C19.5324 17.2361 20.1883 17.9037 20.5202 18.2619C20.8355 18.6021 20.9999 18.7938 20.9999 19.0968C20.9999 19.3506 20.9012 19.5888 20.7218 19.7682L20.2448 20.2452V20.2449L19.7679 20.7219C19.5885 20.9013 19.35 21 19.0965 21C18.8427 21 18.6045 20.9013 18.4251 20.7219L16.0984 18.3953C15.9915 18.4266 15.8794 18.4428 15.7649 18.4428H15.7655C15.4505 18.4428 15.1544 18.3201 14.9315 18.0975L14.8115 17.9775L14.4722 17.6382L14.3522 17.5182C13.8923 17.0583 13.8923 16.3101 14.3522 15.8505L14.6771 15.5257L13.9653 14.8139C12.7658 15.8186 11.2663 16.3656 9.68274 16.3656ZM15.3206 16.7895L15.3207 16.7894L15.7655 17.2342L16.4996 16.5L17.348 17.3484L17.0482 17.6482L19.0965 19.6965L19.3964 19.3965L19.3965 19.3965L19.6766 19.1163C19.2969 18.7038 18.4377 17.8386 17.6485 17.0489L17.3483 17.349V17.3484L16.4999 16.5L17.2343 15.7656L16.7897 15.321L16.7899 15.3209L16.6844 15.2154L15.95 15.9498H15.9497L15.2153 16.6842L15.3206 16.7895ZM9.68274 4.20001C6.65964 4.20001 4.19994 6.65971 4.19994 9.68281C4.19994 12.7059 6.65964 15.1656 9.68274 15.1656C12.7058 15.1656 15.1655 12.7062 15.1655 9.68281C15.1655 6.65941 12.7061 4.20001 9.68274 4.20001Z"
fill="black"
fill-opacity="0.45"
fillOpacity="0.45"
/>
</g>
</g>
Expand Down
2 changes: 1 addition & 1 deletion src/Assets/SelectArrow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const SelectArrow = () => {
id="Vector"
d="M20.9999 7.92031C20.9999 8.07391 20.9414 8.22751 20.8241 8.34451L12.8339 16.335C12.374 16.7949 11.6258 16.7949 11.1662 16.335L3.17572 8.34451C2.94142 8.11021 2.94142 7.73041 3.17572 7.49611C3.41002 7.26181 3.78982 7.26181 4.02412 7.49611L11.9999 15.4719L19.9757 7.49611C20.21 7.26181 20.5898 7.26181 20.8241 7.49611C20.9414 7.61341 20.9999 7.76671 20.9999 7.92031Z"
fill="black"
fill-opacity="0.45"
fillOpacity="0.45"
/>
</svg>
);
Expand Down
93 changes: 50 additions & 43 deletions src/Components/Calendar/EndDate.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,61 @@
// react-datepicker를 사용해서 마감날짜 구현 328px, 44px, ex) 24.01.23
import DatePicker from 'react-datepicker';
import { useState } from 'react';
import React, { useState } from 'react';
import { ControllerRenderProps } from 'react-hook-form';

// date-picker
import DatePicker, { ReactDatePicker } from 'react-datepicker';
import { registerLocale } from 'react-datepicker';
import ko from 'date-fns/locale/ko';
import 'react-datepicker/dist/react-datepicker.css';
import { parseISOString } from '@/utils/date';
import styled from 'styled-components';
import { OptionalCreates } from '@/Pages/Studies/CreateRecruitment';
import { Creates } from '@/Types/studies';

export type Props = {
onClick?: () => void;
children?: React.ReactNode;
// onChange?: (event: string) => void;
setForm: (any: OptionalCreates) => void;
useForm: Creates;
value?: string;
type?: string;
name?: string;
maxlength?: number;
id?: string;
formData?: number | string;
ref?: string;
};

export const EndDate = ({ useForm }: Props) => {
const [startDateTime, setForms] = useState(new Date());

return (
<DateContainer
value={(useForm.recruitmentEndDateTime = startDateTime.toISOString().slice(0, -5))}
selected={startDateTime}
dateFormat="yy.MM.dd"
onChange={(date: Date) => setForms(date)}
placeholderText="ex)24.01.07"
isClearable={true}
/>
);
};

registerLocale('ko', ko);

interface IFormValues {
recruitmentEndDateTime: string;
}

interface Props {
defaultValue?: string;
}

export const EndDate = React.forwardRef<ReactDatePicker<string, boolean>, ControllerRenderProps<IFormValues> & Props>(
({ onChange, name, defaultValue }, ref) => {
const today = new Date();
const [startDate, setStartDate] = useState<Date>(defaultValue && new Date(defaultValue));

return (
<DateContainer
name={name}
locale="ko"
selected={startDate}
dateFormat="yy.MM.dd"
minDate={today}
onChange={(date) => {
onChange(parseISOString(date));
if (date instanceof Array) setStartDate(date[1]);
else setStartDate(date);
}}
placeholderText="ex)24.01.07"
isClearable={false}
ref={ref}
shouldCloseOnSelect // 날짜를 선택하면 자동으로 닫힌다
autoComplete="off"
/>
);
},
);

const DateContainer = styled(DatePicker)`
width: 328px;
width: 100%;
height: 24px;
background-color: ${(props) => props.theme.color.gray3};
align-items: center;
align-self: stretch;
border: 1px solid #cbcdd1;
border-width: 0;
background: ${(props) => props.theme.color.gray1};
resize: none;
flex: 1 0 0;
margin-top: 10px;
padding-bottom: 10px;
padding-right: 16px;
padding-left: 16px;
&::placeholder {
color: ${(props) => props.theme.color.black2};
}
`;
17 changes: 8 additions & 9 deletions src/Components/Common/Button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,15 @@ const Button = ({
children,
className,
size = 'normal',
}: ButtonProps) => (
<ButtonContainer {...{ onClick, type, scheme, disabled, className, size }}>
<>{children}</>
</ButtonContainer>
);
}: ButtonProps) => {
return (
<ButtonContainer {...{ onClick, type, scheme, disabled, className, size }}>
<>{children}</>
</ButtonContainer>
);
};

const ButtonContainer = styled.button<{
scheme: 'primary' | 'secondary' | 'third' | 'normal';
size: 'normal' | 'fullWidth';
}>`
const ButtonContainer = styled.button<ButtonProps>`
display: inline-flex;
justify-content: center;
align-items: center;
Expand Down
34 changes: 34 additions & 0 deletions src/Components/Common/FormSection/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import Heading from '@/Components/Heading';
import React from 'react';
import styled from 'styled-components';

export interface FormSectionProps {
icon?: any;
title?: string;
children: React.ReactNode;
}
export const FormSection = ({ icon, title, children }: FormSectionProps) => {
return (
<FormSectionWrap>
<Heading type={'Title'} component={'Page'}>
{icon && <AssetContainer>{icon}</AssetContainer>}
{title}
</Heading>
{children}
</FormSectionWrap>
);
};

const AssetContainer = styled.image`
padding-right: 12px;
`;

const FormSectionWrap = styled.section`
display: flex;
flex-direction: column;
margin: 24px 0;
& ~ & {
margin-top: 20px;
}
`;
7 changes: 7 additions & 0 deletions src/Components/Common/InputText/InputText.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,10 @@ export const Member: Story = {
placeholder: '멤버를 입력하세요',
},
};

export const Label: Story = {
args: {
label: '포지션',
placeholder: '포지션을 입력하세요',
},
};
32 changes: 29 additions & 3 deletions src/Components/Common/InputText/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,29 @@ interface InputTextProps extends React.InputHTMLAttributes<HTMLInputElement> {
defaultValue?: string;
currentLength?: number;
maxLength?: number;
label?: string;
icon?: ReactNode;
}

const InputText = forwardRef<HTMLInputElement, ComponentProps<'input'> & InputTextProps>(
(
{ name, placeholder, defaultValue, inputType, onChange, maxLength, currentLength, icon, ...props }: InputTextProps,
{
name,
placeholder,
defaultValue,
inputType,
onChange,
maxLength,
currentLength,
label,
icon,
...props
}: InputTextProps,
ref: ForwardedRef<HTMLInputElement>,
) => {
return (
<Box>
{label && <Label>{label}</Label>}
<InputWrapper
placeholder={placeholder}
defaultValue={defaultValue}
Expand All @@ -37,6 +50,13 @@ const InputText = forwardRef<HTMLInputElement, ComponentProps<'input'> & InputTe
},
);

export const Label = styled.div`
font-size: 18px;
line-height: 24px;
color: #000000f2;
margin-bottom: 12px;
`;

const InputWrapper = styled.input`
width: 100%;
padding: 10px 16px;
Expand All @@ -45,7 +65,12 @@ const InputWrapper = styled.input`
font-size: ${({ theme }) => theme.font.small};
line-height: 1.5;
color: ${({ theme }) => theme.color.black};
::placeholder {
text-overflow: ellipsis;
display: block;
white-space: nowrap;
overflow: hidden;
&::placeholder {
color: ${({ theme }) => theme.color.black2};
font-family: 'Pretendard400';
font-size: ${({ theme }) => theme.font.small};
Expand All @@ -58,6 +83,7 @@ const InputWrapper = styled.input`
const Box = styled.div`
position: relative;
display: flex;
flex-direction: column;
`;

const IconWrapper = styled.div`
Expand All @@ -71,7 +97,7 @@ const IconWrapper = styled.div`
`;
const LengthIndicator = styled.div`
position: absolute;
top: 10px;
bottom: 13px;
right: 16px;
color: #00000073;
font-family: Pretendard400;
Expand Down
26 changes: 26 additions & 0 deletions src/Components/Common/LabelForm/LabelForm.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { Meta, StoryObj } from '@storybook/react';
import React from 'react';
import { LabelForm } from '.';

const meta = {
component: LabelForm,
args: {
label: '포지션',
children: React.createElement('input', { placeholder: 'input' }, null),
},
} satisfies Meta<typeof LabelForm>;

export default meta;
type Story = StoryObj<typeof meta>;

/** 유효하지 않을 경우, 에러메시지를 표시합니다. */
export const Errors: Story = {
args: {
errors: {
positionIds: {
message: '포지션을 선택해주세요.',
},
},
name: 'positionIds',
},
};
Loading

0 comments on commit c94e65d

Please sign in to comment.