Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
0f3ca2b
design: 만들기 버튼 영역 수정
e-sum-e Jun 23, 2025
e468df2
fix: 제목 최대 길이 수정(30->100)
e-sum-e Jun 23, 2025
ba4cb82
fix: 모집 정원을 자유롭게 입력하되 범위를 벗어날 경우 보정해주고 툴팁이 보이도록 수정
e-sum-e Jun 23, 2025
a83ea2a
feat: maxFileSize 디폴트 값 수정
rlarkdals1202 Jun 23, 2025
4554c43
feat: 서버에서 응답하는 메시지를 알리도록 변경
rlarkdals1202 Jun 23, 2025
b927009
feat: 쿼리 gcTime 옵션 추가
rlarkdals1202 Jun 23, 2025
26b110a
feat: 'Skills'와 'E-mail' 텍스트 수정
rlarkdals1202 Jun 23, 2025
f4cd186
fix: 내용이 길어지는 경우 스크롤
e-sum-e Jun 23, 2025
087960f
fix: 불필요 props 삭제
e-sum-e Jun 23, 2025
e5772e4
feat: 'refresh 만료' 메시지를 포함한 인증 에러 처리 추가
rlarkdals1202 Jun 23, 2025
0f8db42
fix: 참가 인원이 모두 채워진 경우 모집 종료 커버 씌워줌
e-sum-e Jun 23, 2025
d5c6bd4
Merge pull request #250 from moyeora-it/fix-sm
e-sum-e Jun 23, 2025
76093ed
Merge pull request #251 from moyeora-it/fix-my-page
e-sum-e Jun 23, 2025
925e1fc
feat: 모임 목록 조회 기능 일부 변경
rlarkdals1202 Jun 23, 2025
5941bb1
design: UI 깨지는 현상 수정
rlarkdals1202 Jun 23, 2025
cc1e674
feat: 신청자, 참여자 목록 로딩 컴포넌트 추가
rlarkdals1202 Jun 23, 2025
d777901
feat: 신청자 목록 로딩 UI 추가
rlarkdals1202 Jun 23, 2025
d5e2f98
feat: 참가자 목록 로딩 UI 추가
rlarkdals1202 Jun 23, 2025
c92eb31
Merge pull request #258 from moyeora-it/fix-my-page
rlarkdals1202 Jun 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/app/write/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
'use client';

import { WriteForm } from '@/components/organisms/write-form';
import useIsClient from '@/hooks/useIsClient';
import useAuthStore from '@/stores/useAuthStore';
import { routes } from '@/utils/routes';
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';
import useIsClient from '@/hooks/useIsClient';

export default function Page() {
const user = useAuthStore((state) => state.user);
Expand Down Expand Up @@ -34,7 +34,7 @@ export default function Page() {

return (
<div className="relative w-[300px] sm:w-[370px] md:w-[740px] m-auto mb-10">
<WriteForm userId={user.userId} />
<WriteForm />
</div>
);
}
4 changes: 3 additions & 1 deletion src/components/atoms/write-form/form-label.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ type WriteFormLabelProps = {
text: string;
className?: string;
info?: string;
isTooltipOpen?: boolean;
};

export const WriteFormLabel = ({
htmlFor,
text,
className,
info,
isTooltipOpen,
}: WriteFormLabelProps) => {
return (
<div className="flex gap-1">
Expand All @@ -28,7 +30,7 @@ export const WriteFormLabel = ({
{text}
</FormLabel>
{info && (
<Tooltip>
<Tooltip open={isTooltipOpen}>
<TooltipTrigger>
<CircleInfo />
</TooltipTrigger>
Expand Down
2 changes: 1 addition & 1 deletion src/components/error-boundary/error-handler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const handleError = ({
}

// 인증 에러
if (error.message.includes('401') || error.message.toLowerCase().includes('unauthorized')) {
if (error.message.includes('401') || error.message.toLowerCase().includes('unauthorized') || error.message.includes('refresh 만료')) {
return (
<ErrorFallback error={error} resetErrorBoundary={resetErrorBoundary}>
로그인이 필요합니다
Expand Down
6 changes: 4 additions & 2 deletions src/components/error-fallback/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ export const ErrorFallback = ({
const handleClick = () => {
if (
error?.message.includes('401') ||
error?.message.toLowerCase().includes('unauthorized')
error?.message.toLowerCase().includes('unauthorized') ||
error?.message.toLowerCase().includes('refresh 만료')
) {
clearUser();
console.log('401 에러 발생');
Expand All @@ -54,7 +55,8 @@ export const ErrorFallback = ({
className="mt-2 text-white text-sm cursor-pointer bg-green-600"
>
{error?.message.includes('401') ||
error?.message.toLowerCase().includes('unauthorized')
error?.message.toLowerCase().includes('unauthorized') ||
error?.message.toLowerCase().includes('refresh 만료')
? '로그인하기'
: '다시 시도'}
</Button>
Expand Down
2 changes: 1 addition & 1 deletion src/components/molecules/group-create-button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const WriteGroupButton = () => {
};

return (
<div className="fixed md:absolute bottom-0 md:top-0 right-0 my-6 z-10">
<div className="fixed md:absolute bottom-0 md:top-0 md:bottom-auto right-0 my-6 z-10">
<Button
onClick={writeButtonClickHandler}
className="cursor-pointer rounded-[50%] md:hidden"
Expand Down
4 changes: 3 additions & 1 deletion src/components/molecules/group/group-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ type GroupCardProps = {

// TODO : 섹션별로 component 나누기
export const GroupCard = ({ item }: GroupCardProps) => {
const isClosed = isBeforeToday(item.deadline);
const isClosed =
isBeforeToday(item.deadline) ||
item.participants.length >= item.maxParticipants;

return (
<div className="relative p-5 h-[280px] lg:h-[316px] bg-white shadow-sm ring-2 ring-gray-300/30 rounded-xl">
Expand Down
44 changes: 32 additions & 12 deletions src/components/molecules/write-form/maxParticipants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,47 @@ import {
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { WriteForm } from '@/types';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { UseFormReturn } from 'react-hook-form';

type TitleProps = {
form: UseFormReturn<WriteForm>;
};

export const MaxParticipants = ({ form }: TitleProps) => {
const [maxParticipants, setMaxParticipants] = useState(2);
const [isTooltipOpen, setIsTooltipOpen] = useState(false);

const inputChangeHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
const inputBlurHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = Number(e.target.value);
if (value > 30) {
setMaxParticipants(30);
return;
}
if (value < 2) {
setMaxParticipants(2);

if (value === 0) {
// 입력값이 없거나 0이면 2로 세팅
form.setValue('maxParticipants', 2);
return;
}

setMaxParticipants(value);
if (value < 2 || value > 30) {
const settedValue = Math.max(2, Math.min(30, value));
form.setValue('maxParticipants', settedValue);

setIsTooltipOpen(true);
}
};

/**
* 정원 보정 후 툴팁이 3초뒤에 사라지게 하기
*/

useEffect(() => {
if (isTooltipOpen) {
const timer = setTimeout(() => {
setIsTooltipOpen(false);
}, 3000); // 3초

return () => clearTimeout(timer);
}
}, [isTooltipOpen]);

return (
<>
<FormField
Expand All @@ -42,6 +59,7 @@ export const MaxParticipants = ({ form }: TitleProps) => {
htmlFor="maxParticipants"
text="정원"
info="최소 2명 ~ 최대 30명까지 가능합니다"
isTooltipOpen={isTooltipOpen}
/>
<FormControl>
<Input
Expand All @@ -51,8 +69,10 @@ export const MaxParticipants = ({ form }: TitleProps) => {
placeholder="정원을 입력해주세요"
{...field}
type="number"
onChange={inputChangeHandler}
value={maxParticipants}
onBlur={(e) => {
inputBlurHandler(e);
field.onBlur();
}}
/>
</FormControl>
<FormMessage />
Expand Down
2 changes: 1 addition & 1 deletion src/components/molecules/write-form/tiptap/desctiption.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export const Description = ({ form }: DescriptionProps) => {
editorProps: {
attributes: {
class: clsx(
'h-[500px] w-full mt-0 py-2 px-3 text-sm rounded-md border border-input bg-background ring-offset-background shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px]',
'h-[500px] overflow-y-auto w-full mt-0 py-2 px-3 text-sm rounded-md border border-input bg-background ring-offset-background shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px]',
hasError
? 'text-red-500 border-red-500 focus-visible:ring-red-500/20'
: 'focus-visible:border-ring focus-visible:ring-ring/50',
Expand Down
13 changes: 4 additions & 9 deletions src/components/organisms/write-form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ const formSchema = z
.string()
.trim()
.nonempty({ message: '제목을 입력해주세요.' })
.max(30, {
message: '제목이 너무 길어요. 30자 이내로 줄여주세요.',
.max(100, {
message: '제목이 너무 길어요. 100자 이내로 줄여주세요.',
}),
maxParticipants: z.coerce
.number({ message: '모임의 정원을 설정해주세요.' })
Expand Down Expand Up @@ -87,11 +87,7 @@ const formSchema = z
path: ['endDate'],
});

type WriteFormProps = {
userId: number;
};

export const WriteForm = ({ userId }: WriteFormProps) => {
export const WriteForm = () => {
const [isDeadlineCalendarOpen, setIsDeadlineCalendarOpen] = useState(false);
const [isStartDateCalendarOpen, setIsStartDateCalendarOpen] = useState(false);
const [isEndDateCalendarOpen, setIsEndDateCalendarOpen] = useState(false);
Expand Down Expand Up @@ -120,7 +116,6 @@ export const WriteForm = ({ userId }: WriteFormProps) => {
reValidateMode: 'onSubmit', // submit 시에만 유효성 검사
defaultValues: {
title: '',
maxParticipants: 2,
description: '',
autoAllow: false,
type: GroupType.STUDY,
Expand All @@ -145,7 +140,7 @@ export const WriteForm = ({ userId }: WriteFormProps) => {

try {
const result = await request.post(
`/v2/groups?userId=${userId}`,
`/v2/groups`,
{ 'Content-Type': 'application/json' },
JSON.stringify({ ...values, skills, position }),
{ credentials: 'include' },
Expand Down
4 changes: 2 additions & 2 deletions src/features/user/components/current-user-profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const CurrentUserProfile = () => {
</div>
<div className="flex flex-col gap-y-1 min-w-0">
<div className="flex gap-x-2 min-w-0">
<span className="text-sm font-medium shrink-0">Skills</span>
<span className="text-sm font-medium shrink-0">기술스택</span>
{skills && skills.length === 0 ? (
<p className="text-gray-700 text-sm">
설정된 기술스택이 없어요.
Expand All @@ -64,7 +64,7 @@ export const CurrentUserProfile = () => {
)}
</div>
<div className="flex gap-x-1.5">
<span className="text-sm font-medium">E-mail</span>
<span className="text-sm font-medium">이메일</span>
<span className="text-sm font-normal text-gray-700 line-clamp-1">
{email}
</span>
Expand Down
4 changes: 2 additions & 2 deletions src/features/user/components/other-user-profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ export const OtherUserProfile = () => {
/>
</div>
<div className="flex flex-col gap-y-9 mt-4 flex-1 min-w-0">
<div className="flex items-center justify-between md:gap-x-5 md:justify-start">
<span className="font-semibold">
<div className="flex items-center justify-between md:gap-x-5 md:justify-start flex-1">
<span className="font-semibold truncate">
{getDisplayNickname(nickname, email)}
</span>
<ToggleFollowButton
Expand Down
1 change: 1 addition & 0 deletions src/features/user/follow/components/followers-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export const FollowersList = () => {
refetchOnMount: true,
staleTime: 0,
retry: 0,
gcTime: 0,
},
});

Expand Down
1 change: 1 addition & 0 deletions src/features/user/follow/components/following-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const FollowingList = () => {
refetchOnMount: true,
staleTime: 0,
retry: 0,
gcTime: 0,
},
});

Expand Down
2 changes: 1 addition & 1 deletion src/features/user/group/components/group-list-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const GroupListItem = ({
</span>
) : (
<span
className={`text-sm inline-block font-medium px-3 py-1 rounded-lg bg-green-50 text-green-500`}
className={`text-sm inline-block font-medium px-3 py-1 rounded-lg bg-green-50 text-green-500 shrink-0`}
>
{type === 'study' ? '스터디' : '프로젝트'}
</span>
Expand Down
6 changes: 5 additions & 1 deletion src/features/user/group/components/group-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const GroupList = ({ status }: GroupListProps) => {
useFetchItems<Group>({
url: `/v2/groups/usergroup/${id}`,
queryParams: {
type: type ?? 'study',
...(status !== 'PARTICIPATING' && { type: type ?? 'study' }),
status: 'PARTICIPATING',
size: status !== 'ENDED' ? 10 : 50,
...(search && { search }),
Expand Down Expand Up @@ -65,6 +65,10 @@ export const GroupList = ({ status }: GroupListProps) => {
groupList = groupList.filter((group) => group.createUserId === Number(id));
}

if (status === 'PARTICIPATING') {
groupList = groupList.filter((group) => !isBeforeToday(group.endDate));
}

return (
<>
{groupList.length === 0 ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { MemberInfo } from '@/features/user/group/components/member-list-modal/m
import { useManageParticipation } from '@/features/user/group/hooks/useManageParticipation';
import { UserSummary } from '@/types';
import { useQuery } from '@tanstack/react-query';
import { MemberListLoading } from '@/features/user/group/components/member-list-modal/member-list-loading';

type ApplicantsListProps = {
groupId: string;
Expand Down Expand Up @@ -35,11 +36,12 @@ export const ApplicantsList = ({ groupId }: ApplicantsListProps) => {
},
staleTime: 0,
refetchOnWindowFocus: false,
gcTime: 0,
});

return (
<div className="flex-1">
{isLoading && <>Loading...</>}
{isLoading && <MemberListLoading />}
{isError && <>Error</>}
{applicantsList && applicantsList.length === 0 && (
<div className="flex flex-col items-center justify-center h-full ">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const MemberListDialog = ({
</DialogTrigger>
<DialogContent className="max-w-80 h-[25rem] overflow-y-auto scrollbar-hide flex flex-col">
<DialogHeader>
<DialogTitle>
<DialogTitle className="mt-2">
<span className="text-green-500">{`"${groupTitle}"`}</span> 모임의
참여/신청자 목록
</DialogTitle>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
'use client';

import { Skeleton } from '@/components/ui/skeleton';

export const MemberListLoading = () => {
return (
<ul className="flex flex-col gap-y-3">
{Array.from({ length: 3 }).map((_, index) => (
<li key={index} className="flex gap-x-6 pb-5">
<Skeleton className="size-14 rounded-full" />
<div className="flex flex-col gap-y-1">
<Skeleton className="w-20 h-6" />
<Skeleton className="w-40 h-4" />
</div>
</li>
))}
</ul>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { request } from '@/api/request';
import { MemberInfo } from '@/features/user/group/components/member-list-modal/member-info';
import { UserSummary } from '@/types';
import { useQuery } from '@tanstack/react-query';
import { MemberListLoading } from '@/features/user/group/components/member-list-modal/member-list-loading';

type ParticipantsListProps = {
groupId: string;
Expand Down Expand Up @@ -31,11 +32,12 @@ export const ParticipantsList = ({ groupId }: ParticipantsListProps) => {
},
staleTime: 0,
refetchOnWindowFocus: false,
gcTime: 0,
});

return (
<div>
{isLoading && <>Loading...</>}
{isLoading && <MemberListLoading />}
{isError && <>Error</>}
{participantsList && (
<ul className="flex flex-col gap-y-3">
Expand Down
5 changes: 3 additions & 2 deletions src/features/user/hooks/useChangePassword.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ export const useChangePassword = () => {
description: '비밀번호가 변경되었어요',
});
},
onError() {
onError(error) {
const errInfo = JSON.parse(error.message.split('-')[1]);
toast.error('비밀번호 변경 실패', {
description: '비밀번호 변경에 실패했어요. 잠시 후 다시 시도해주세요.',
description: errInfo.status.message,
});
},
});
Expand Down
2 changes: 1 addition & 1 deletion src/features/user/utils/validateImageFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
*/
export const validateImageFile = (
file: File,
maxFileSize = 5 * 1024 * 1024,
maxFileSize = 1024 * 1024,
): { isValid: boolean; errorMessage?: string } => {
if (!file.type.startsWith('image/')) {
return {
Expand Down