Skip to content

Commit

Permalink
Feat/#96 크루 상세 옵션 (#141)
Browse files Browse the repository at this point in the history
* Refactor: Tabs 컴포넌트 공통화

* Feat: 크루 헤더 및 탭 추가

* Fix: 수정된 Tabs 컴포넌트에 맞게 props 및 경로 수정

* Chore : 이미지 호스트 변경

* Feat: 크루 리스트 아이템 구현

* Feat : 정렬 옵션 구현

* Feat : 크루 페이지 구현

* Feat: 크루 api 수정

* Feat :  크루 상세페이지 조회

* Move :  BottomSheet 공용 컴포넌트로 이동 및 수정

내부에 사용하는 Form을 prop으로 받아 사용합니다

* Feat : 크루 가입신청 기능

* Feat : 가입 질문 렌더링

* Feat : 전체/내크루 리스트 아이템 컴포넌트 분리

* Feat : 가입요청 후 바텀시트 닫기

* Fix : 무한스크롤 페이징 수정

* Feat : 채팅 경로 및 UI

* Feat : 크루 생성 멀티스텝 폼

* Fix : 인풋 값 입력시 다음버튼 활성화

* Chore : 콘솔 제거

* Feat : 지역 선택 체크박스

* Fix : 타입수정

* Feat : 크루 생성 컴포넌트 구조 수정

* Refactor : 중복코드 수정

* Feat : 단계별 컴포넌트 정의

* Feat :  number input 디폴트 ui 삭제

* Feat : 크루 상세정보 데이터 추가

* Feat : 크루생성 폼 컴포넌트화

* Feat : 크루 이름, 소개, 태그 입력

* Feat : 크루 기반 지역 선택

* Feat : 가입형식 지정

* Feat : 인원, 연령대, 성별 선택

* Feat : 크루생성 api 설정

* Fix : 크루리스트 무한스크롤 로직 수정

* Feat : 크루 대표 이미지 추가 컴포넌트

* Fix : api 전송 형식 변경

* Feat : 렌더되는 정보 추가

* Feat : 옵션 배열 추가

* Feat : 태그선택 필수 및 최대 3개 제한

* Chore : 라벨 스타일 수정

* Feat : number 커스텀 입력 UX 개선

* Feat : 크루리스트 옵션 선택 추가

* Feat : 크루 생성 성공 후 리스트로 이동

* Fix : api 스키마에 일치

* Feat : 지역 배열 렌더링 수정

* Feat : 가입질문 UI 수정

* Feat : 엔드포인트 조건 수정

* Update .eslintrc.json

* Chore : EsLint Rule 재설정

* Fix: 빌드에러 수정

* Remove : 미사용 컴포넌트 삭제

* Feat : 가입정보 UI 수정

* Design : 간격 및 이미지 높이 수정

* Feat : 크루 생성 후 리스트 데이터 mutate

* Design : 크루목록 아이템 간격 및 렌더방식 수정

* Feat : 일정 옵션 버튼 컴포넌트 생성

* Feat : 멤버 목록 구현

* Fix : RESTful 규칙에 맞게 경로 수정

* Fix : 배열 map key값 수정

* Fix : RESTful 규칙에 맞게 경로 수정

* Chore : 상수 컨벤션 수정

* Feat : 크루멤버목록 api

* Feat : 크루 생성 후 크루목록 갱신 (mutate)

* Feat : 크루 수정 페이지

* Feat : 선택된 태그 유지

* Chore : 임포트 수정

* Chore : 상수 컨벤션으로 변경

* Fix : 컴포넌트명 파일명과 일치

* Feat : 챕터 변경 시 입력값 유지

* Chore : 임포트 수정

* Chore : 미완성 기능 비활성화

* Feat : 내 크루 API 응답 형식 변경 대응

* Feat : 크루 신청 목록 및 철회 구현

* Fix : 크루리스트 페이징 수정

* Feat : Option Drawer UI 정의

* Feat : 멤버관리 페이지 정의

* Fix : 크루정보 수정 데이터

* Fix : 충돌 해결

* Fix : Tab 갯수 대응

* Feat : 크루 설정

* Feat : 일정 생성 UI
  • Loading branch information
joanShim authored May 16, 2024
1 parent 3fe10e6 commit ca99222
Show file tree
Hide file tree
Showing 26 changed files with 691 additions and 142 deletions.
13 changes: 13 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"typescript": "^5.2.2",
"use-debounce": "^10.0.0",
"uuid": "^9.0.1",
"vaul": "^0.9.0",
"zod": "^3.22.4",
"zustand": "^4.4.7"
},
Expand Down
7 changes: 0 additions & 7 deletions src/app/_components/common/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
'use client';

import clsx from 'clsx';
import Modal from '@/app/_components/common/Modal';
import HeaderProps from '@/types/ui/common/header';
import deletePost from '@/app/service/community/[id]/api/deletePost';
import usePostListApi from '@/hooks/community/usePostList';
import Ellipsis from '@/app/_components/common/Ellipsis';
import * as M from '@/app/_components/ui/menubars';

import { useState } from 'react';
import { useRouter, useParams } from 'next/navigation';
Expand Down Expand Up @@ -85,11 +83,6 @@ function Header({ ...props }: Partial<HeaderProps>) {
<span className="font-medium">{title}</span>
<div className="absolute right-4">
{isEllipsis && <Ellipsis isPost onEditPost={onEdit} isMine />}
{isExit && (
<button type="button" onClick={onExit}>
<X />
</button>
)}
{categoryId !== 3 && isSearching && (
<button type="button" onClick={onSearch}>
<Search color={COLOR.primary} />
Expand Down
5 changes: 3 additions & 2 deletions src/app/_components/common/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ function Tabs({ tabs, useStateHook }: TabsProps) {
const { categoryId, setCategoryId } = useStateHook();

const liClassName = (id: number) => {
return clsx('flex rounded-xl transition duration-500 ease-in-out', {
return clsx('flex flex-1 rounded-xl transition duration-500 ease-in-out', {
'bg-white m-1': categoryId === id,
'bg-none': categoryId !== id,
});
Expand All @@ -23,7 +23,8 @@ function Tabs({ tabs, useStateHook }: TabsProps) {
const clickHandler = (id: number) => setCategoryId(id);

return (
<ul className="grid grid-cols-3 h-10 mx-4 bg-grey-100 rounded-xl">
<ul className="flex h-10 mx-4 bg-grey-100 rounded-xl">

{Object.values(tabs).map(tab => (
<li key={tab.title} className={liClassName(tab.id)}>
<button
Expand Down
118 changes: 118 additions & 0 deletions src/app/_components/ui/drawer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
"use client"

import * as React from "react"
import { Drawer as DrawerPrimitive } from "vaul"

import { cn } from "@/lib/utils"

const Drawer = ({
shouldScaleBackground = true,
...props
}: React.ComponentProps<typeof DrawerPrimitive.Root>) => (
<DrawerPrimitive.Root
shouldScaleBackground={shouldScaleBackground}
{...props}
/>
)
Drawer.displayName = "Drawer"

const DrawerTrigger = DrawerPrimitive.Trigger

const DrawerPortal = DrawerPrimitive.Portal

const DrawerClose = DrawerPrimitive.Close

const DrawerOverlay = React.forwardRef<
React.ElementRef<typeof DrawerPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<DrawerPrimitive.Overlay
ref={ref}
className={cn("fixed inset-0 z-50 bg-black/80", className)}
{...props}
/>
))
DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName

const DrawerContent = React.forwardRef<
React.ElementRef<typeof DrawerPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<DrawerPortal>
<DrawerOverlay />
<DrawerPrimitive.Content
ref={ref}
className={cn(
"fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background",
className
)}
{...props}
>
<div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" />
{children}
</DrawerPrimitive.Content>
</DrawerPortal>
))
DrawerContent.displayName = "DrawerContent"

const DrawerHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn("grid gap-1.5 p-4 text-center sm:text-left", className)}
{...props}
/>
)
DrawerHeader.displayName = "DrawerHeader"

const DrawerFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn("mt-auto flex flex-col gap-2 p-4", className)}
{...props}
/>
)
DrawerFooter.displayName = "DrawerFooter"

const DrawerTitle = React.forwardRef<
React.ElementRef<typeof DrawerPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title>
>(({ className, ...props }, ref) => (
<DrawerPrimitive.Title
ref={ref}
className={cn(
"text-lg font-semibold leading-none tracking-tight",
className
)}
{...props}
/>
))
DrawerTitle.displayName = DrawerPrimitive.Title.displayName

const DrawerDescription = React.forwardRef<
React.ElementRef<typeof DrawerPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Description>
>(({ className, ...props }, ref) => (
<DrawerPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
DrawerDescription.displayName = DrawerPrimitive.Description.displayName

export {
Drawer,
DrawerPortal,
DrawerOverlay,
DrawerTrigger,
DrawerClose,
DrawerContent,
DrawerHeader,
DrawerFooter,
DrawerTitle,
DrawerDescription,
}
6 changes: 2 additions & 4 deletions src/app/service/crew/[id]/_components/MemberList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ function MemberList({ setIsMemberListClicked, crewId }: MemberListProps) {
fetchCrewMembers();
}, [crewId]);

console.log(crewMembers);

const sortedCrewMembers = [...crewMembers].sort((a, b) => {
if (a.is_crew_creator && !b.is_crew_creator) {
return -1;
Expand All @@ -54,8 +52,8 @@ function MemberList({ setIsMemberListClicked, crewId }: MemberListProps) {
</header>
<ul className="p-[16px] flex flex-col gap-[20px]">
{sortedCrewMembers &&
sortedCrewMembers.map((member) => (
<li key={member.id}>
sortedCrewMembers.map(member => (
<li key={member.id} className="list-none">
<div className="flex w-full h-fit gap-[10px]">
<div className="size-[40px] relative shrink-0">
<Image
Expand Down
35 changes: 30 additions & 5 deletions src/app/service/crew/[id]/_components/Schedules.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { cn } from '@/lib/utils';
import React, { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';

type ButtonType = 'scheduled' | 'finished';

function Schedules() {
function Schedules({id}: { id: number }) {
const [selectedButton, setSelectedButton] = useState<ButtonType>('scheduled');

const router = useRouter();

const handleButtonClick = (button: ButtonType) => {
setSelectedButton(button);

Expand All @@ -17,20 +20,31 @@ function Schedules() {
};

const callScheduledApi = () => {
console.log('예정된 일정 API 호출');
const fetchCrewMeetings = () => {
// getCrewMeetings(crewId).then(response => setCrewMeetings(response));
console.log('예정된 일정 호출');
};
fetchCrewMeetings();
};

useEffect(() => {
callScheduledApi();
},[])
}, []);

const callFinishedApi = () => {
console.log('종료된 일정 API 호출');
};
return (
<div>

<div className="flex gap-[10px]">
<button
className="bg-white w-full drop-shadow-md py-[18px] rounded-[10px] text-violet-500 text-sm font-semibold"
onClick={() => {
router.push(`${id}/new-meeting`);
}}
>
+ 일정 추가하기
</button>
<div className="flex gap-[10px] mt-[12px]">
{/* 예정된 일정 버튼 */}
<button
className={cn(
Expand All @@ -57,6 +71,17 @@ function Schedules() {
종료된 일정
</button>
</div>
<div className="py-[16px]">
<div className="rounded-[10px] w-full overflow-hidden drop-shadow">
<div className="bg-[#E1D7FF] p-[12px] font-semibold">
3월 4일 (월) 오후 2:00
</div>
<div className="p-[12px] bg-white">
<div className="text-sm">강동클라이밍</div>
<div className="text-sm">참여인원</div>
</div>
</div>
</div>
</div>
);
}
Expand Down
11 changes: 11 additions & 0 deletions src/app/service/crew/[id]/api/crew.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { END_POINT } from '@/constants/api/end-point';
import { axiosInstance } from '@/lib/axios/axios-instance';

const crewApi = {
WithdrawCrew: async (crewId: number) =>
axiosInstance.delete(END_POINT.crewController.deleteMyApplication(crewId)),
DeleteCrew: async (crewId: number) =>
axiosInstance.delete(END_POINT.crewController.deleteMyCrew(crewId)),
};

export default crewApi;
16 changes: 14 additions & 2 deletions src/app/service/crew/[id]/api/getCrewMembers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { CrewMembersProps } from '@/types/crew/crew';

import CustomError from '@/error/CustomError';

const getCrewMembers = async (id: number) => {
export const getCrewMembers = async (id: number) => {
try {
const { data: crewMembers } = await axiosInstance<
TResponse<CrewMembersProps[]>
Expand All @@ -19,4 +19,16 @@ const getCrewMembers = async (id: number) => {
}
};

export default getCrewMembers;
export const getCrewApplicants = async (id: number) => {
try {
const { data: crewMembers } = await axiosInstance<
TResponse<CrewMembersProps[]>
>(END_POINT.crewController.getCrewApplicants(id));

return crewMembers.data;
} catch (error: unknown) {
if (error instanceof CustomError) {
throw new Error(error.message);
}
}
};
30 changes: 25 additions & 5 deletions src/app/service/crew/[id]/edit/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,29 @@
import React from 'react'
'use client';
import Header from '@/app/_components/common/Header';
import CreateCrewForm from '../../create/_components/CreateCrewForm';
import { useEffect, useState } from 'react';
import getCrewDetail from '../api/getCrewDetail';
import { CrewDetailProps } from '@/types/crew/crew';

function Page({ params }: { params: { id: string } }) {
const [crewDetail, setCrewDetail] = useState<CrewDetailProps>();
const { id } = params;
const crewId = Number(id);
useEffect(() => {
const fetchCrewDetail = () => {
getCrewDetail(crewId).then(response => setCrewDetail(response));
};

fetchCrewDetail();
}, [crewId]);


function page() {
return (
<div>page</div>
)
<div className="relative bg-[#F9F9F9] min-h-dvh flex flex-col max-h-full-size-omit-nav overflow-hidden">
<Header title="크루 수정" isExit />
<CreateCrewForm crewDetail={crewDetail} />
</div>
);
}

export default page
export default Page;
Loading

0 comments on commit ca99222

Please sign in to comment.