Skip to content

Commit

Permalink
[Feature]: 스터디 개설하기 api 연동 (#30)
Browse files Browse the repository at this point in the history
* chore: lock 패키지 변경 사항 반영

* chore: 중복된 Navbar 컴포넌트 삭제

* feat: 개설 스터디 기본 잡기

* feat: 스터디 개설 페이지 레이아웃

* fix: 찌그러짐 방지용 minWidth 추가

* feat: 스터디 개설하기 뷰 생성

* fix: 개설된 스터디로 화면 옮기기

* feat: auth 체크용 middleWare 생성

* fix: utils fetcher 함수 export 경로 변경

* feat: 스터디 생성하기 페이지 생성

* fix: router handler 삭제

* refactor: api path 상수로 수정

* feat: isAdmin 함수 추가

* fix: 코드리뷰 반영

* fix: admin여부에 따라 스터디 생성 박스 보이지 않도록

* chore: 패키지 설치

* fix: 빌드에러 고치기

* fix: isadmin 판별 로직 미들웨어에서 진행

* feat: 역할 논리 다시

* fix: 폴더명 변경

* fix: createStudy 경로 변경

* feat:wow-icons 추가

* refactor: 컴포넌트 폴더위치 변경

* chore:react-hook-form 설치

* feat: 복잡한 UI 설계 react-hook-form

* feat: 스터디 수강 시작 날짜 지정

* chore: timepicker 패키지 설치

* feat: 스터디 신청 UI 완성

* fix: 쓸데없는 콘솔로그 제거

* feat: api 연결

* feat:api 호출 성공시 redirect

* fix: 캐시 지워서 빌드에러 해결

* chore: 패키지 설치

* fix: api 관련 코드리뷰 반영

* fix: 코드리뷰 반영

* fix: navbar 두개인것 고치기

* fix: 코드리뷰 반영

* fix: 코드리뷰 반영

* fix: 코드리뷰 반영

* fix: 멘토 id 변경

---------

Co-authored-by: ghdtjgus76 <[email protected]>
  • Loading branch information
eugene028 and ghdtjgus76 authored Aug 21, 2024
1 parent eff088b commit 7672ab6
Show file tree
Hide file tree
Showing 28 changed files with 1,180 additions and 12 deletions.
11 changes: 11 additions & 0 deletions apps/admin/apis/form/createStudyApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { fetcher } from "@wow-class/utils";
import { apiPath } from "constants/apiPath";
import type { CreateStudyApiRequestDto } from "types/dtos/createStudy";

export const createStudyApi = {
postCreateStudy: async (data: CreateStudyApiRequestDto) => {
const response = await fetcher.post(apiPath.createStudy, data);

return { success: response.ok };
},
};
1 change: 0 additions & 1 deletion apps/admin/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ const RootLayout = ({
<html lang="ko">
<body>
<JotaiProvider>
<Navbar />
{children}
{modal}
</JotaiProvider>
Expand Down
173 changes: 173 additions & 0 deletions apps/admin/app/studies/[study]/_components/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
"use client";

import { css } from "@styled-system/css";
import { Flex } from "@styled-system/jsx";
import { Space, Text } from "@wow-class/ui";
import { padWithZero, parseISODate } from "@wow-class/utils";
import { dayToKorean } from "constants/dayToKorean";
import { headerMockData } from "constants/mockData";
import Image from "next/image";
import Link from "next/link";
import { useState } from "react";
import { space } from "wowds-tokens";
import TextButton from "wowds-ui/TextButton";

const Header = () => {
const [showIntro, setShowIntro] = useState(false);

const handleClickShowIntro = () => {
setShowIntro((prev) => !prev);
};

const introSectionButtonAriaLabel = showIntro
? "Collapse introduction"
: "Expand introduction";
const introSectionImageAriaLabel = showIntro
? "Collapse introduction icon"
: "Expand introduction icon";

const {
title,
academicYear,
semester,
mentorName,
studyType,
dayOfWeek,
startTime: { hour: startHour, minute: startMinute },
endTime: { hour: endHour, minute: endMinute },
totalWeek,
period: { startDate, endDate },
introduction,
notionLink,
} = headerMockData;

const { month: startMonth, day: startDay } = parseISODate(startDate);
const { month: endMonth, day: endDay } = parseISODate(endDate);

const studySemester = `${academicYear}-${semester === "FIRST" ? 1 : 2}`;
const studySchedule = `${dayToKorean[dayOfWeek]} ${startHour}:${padWithZero(startMinute)}-
${endHour}:${padWithZero(endMinute)}`;
const studyPeriod = `${padWithZero(startMonth)}.${padWithZero(startDay)}-
${padWithZero(endMonth)}.${padWithZero(endDay)}`;

return (
<header>
<section aria-label="my-study-header">
<Flex alignItems="center" gap={8}>
<Text as="h1" typo="h1">
{title}
</Text>
<button
aria-controls="intro-section"
aria-expanded={showIntro}
aria-label={introSectionButtonAriaLabel}
tabIndex={0}
onClick={handleClickShowIntro}
>
<Image
alt={introSectionImageAriaLabel}
className={downArrowIconStyle}
height={20}
src="/images/arrow.svg"
style={{ rotate: showIntro ? "0deg" : "180deg" }}
width={20}
/>
</button>
</Flex>
</section>
<section>
<Space height={8} />
<Flex gap="xs">
<Text as="h5" color="sub">
{studySemester}
</Text>
<ItemSeparator />
<Text as="h5" color="sub">
{mentorName} 멘토
</Text>
<ItemSeparator />
<Text as="h5" color="sub">
{studyType}
</Text>
</Flex>
</section>
{showIntro && (
<section id="intro-section">
<section aria-labelledby="study-schedule-heading">
<Space height={24} />
<Flex direction="column" gap="4">
<Text as="h3" typo="h3">
스터디 일정
</Text>
<Flex gap="xs">
<Text as="h5" color="sub">
{studySchedule}
</Text>
<ItemSeparator />
<Text as="h5" color="sub">
{totalWeek}주 코스
</Text>
<ItemSeparator />
<Text as="h5" color="sub">
{studyPeriod}
</Text>
</Flex>
</Flex>
</section>
<section aria-labelledby="study-intro-heading">
<Space height={28} />
<Flex direction="column" gap="4">
<Text as="h3" typo="h3">
스터디 소개
</Text>
<Flex alignItems="center" gap="sm">
<Text as="h5" color="sub">
{introduction}
</Text>
<Link
className={introduceLinkStyle}
href={notionLink}
role="button"
tabIndex={0}
>
<Image
alt="link-icon"
height={24}
src="/images/link.svg"
width={24}
/>
<TextButton
size="lg"
style={textButtonStyle}
text="소개 링크 바로가기"
/>
</Link>
</Flex>
</Flex>
</section>
</section>
)}
</header>
);
};

export default Header;

const ItemSeparator = () => (
<Image alt="item separator" height={4} src="/images/dot.svg" width={4} />
);

const downArrowIconStyle = css({
cursor: "pointer",
});

const introduceLinkStyle = css({
display: "flex",
alignItems: "center",
cursor: "pointer",
gap: "4px",
});

const textButtonStyle = {
padding: `${space.sm} 0`,
};
3 changes: 2 additions & 1 deletion apps/admin/app/studies/[study]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Header from "./_components/Header";
const Study = () => {
return <div>Study</div>;
return <Header />;
};

export default Study;
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { css } from "@styled-system/css";
import { Flex } from "@styled-system/jsx";
import { Text } from "@wow-class/ui";
import { studyRouterPath } from "constants/router/study";
import Link from "next/link";
import isAdmin from "utils/isAdmin";
import { Plus } from "wowds-icons";
Expand All @@ -10,12 +12,12 @@ const CreateStudyButton = async () => {
if (!adminStatus) return null;

return (
<Link href="studies/create-study">
<Link href={studyRouterPath.createStudy.href}>
<button className={createStudyButtonStyle}>
<Flex gap="xs">
<p className={css({ textStyle: "label1", color: "sub" })}>
<Text color="sub" typo="label1">
새로운 스터디 개설하기
</p>
</Text>
<div className={PlusIconStyle}>
<Plus height={14} width={14} />
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
"use client";
import { Flex } from "@styled-system/jsx";
import { Text } from "@wow-class/ui";
import { Controller, useFormContext } from "react-hook-form";
import DropDown from "wowds-ui/DropDown";
import DropDownOption from "wowds-ui/DropDownOption";

const StudyMentorSelect = () => {
const { control } = useFormContext();
return (
<Controller
control={control}
defaultValue={0}
name="mentorId"
render={({ field }) => (
<Flex direction="column" gap="xl">
<Text typo="h2">스터디 멘토</Text>
<DropDown
{...field}
placeholder="선택하세요"
style={{ width: "270px" }}
value={field.value ? String(field.value) : ""}
onChange={({ selectedValue }) => {
field.onChange(Number(selectedValue));
}}
>
<DropDownOption text="김유진" value="3" />
</DropDown>
</Flex>
)}
rules={{
required: true,
}}
/>
);
};

export default StudyMentorSelect;
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"use client";
import { css } from "@styled-system/css";
import { Flex } from "@styled-system/jsx";
import { Text } from "@wow-class/ui";
import { Controller, useFormContext } from "react-hook-form";

const StudyNameTextField = () => {
const { control } = useFormContext();
return (
<Controller
control={control}
defaultValue=""
name="title"
render={({ field }) => (
<Flex direction="column" gap="sm">
<Text color="sub" typo="h3">
새로 개설할 스터디 정보를 입력해주세요
</Text>
<textarea
{...field}
className={TextFieldStyle}
placeholder="새로운 스터디"
onChange={field.onChange}
/>
</Flex>
)}
rules={{
required: true,
}}
/>
);
};

export default StudyNameTextField;

const TextFieldStyle = css({
width: "250px",
height: "31px",
border: "none",
color: "textBlack",

textStyle: "h1",
_placeholder: {
color: "darkDisabled",
},
_focus: {
outline: "none",
},
resize: "none",
});
4 changes: 4 additions & 0 deletions apps/admin/app/studies/create-study/_components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { default as CreateStudyButton } from "./CreateStudyButton";
export { default as StudyBasicInfo } from "./studyBasicInfo";
export { default as StudyMentorSelect } from "./StudyMentorSelect";
export { default as StudyNameTextField } from "./StudyNameTextField";
Loading

0 comments on commit 7672ab6

Please sign in to comment.