Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat, Fix] 더미 데이터 함수 수정, 프로필 액션 테스트 중 #102

Merged
merged 36 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
8ca9746
[Style] 반응형 스타일 추가
callmebyneon Jul 22, 2024
85bcb09
[Feat] tailwind mediaquery 사용을 위한 훅 추가
callmebyneon Jul 22, 2024
465dc50
[Feat] 팝오버(모달 등의 기본 구조) 사용 훅 추가
callmebyneon Jul 22, 2024
f26895b
[Feat] 팝오버 사용 훅과 props 타입 통일
callmebyneon Jul 22, 2024
104098d
[Style] z-modal 클래스 추가
callmebyneon Jul 22, 2024
72771cc
[Style, Feat] CloseIcon stroke?: string 속성 추가, MenuIcon 추가
callmebyneon Jul 22, 2024
e1e3c2e
[Style, Feat] 프로필 메뉴 반응형 추가, 리팩토링
callmebyneon Jul 22, 2024
b7e93e4
[Feat] ModalBackdrop을 활용하여 사이드 바 팝업 레이아웃 추가
callmebyneon Jul 22, 2024
cb1aad9
[Fix] postId값에 문자열을 넣어 unique key prop 경고 제거
callmebyneon Jul 22, 2024
d4178eb
[Feat] usePopover 사용시 document.body overflow hidden
callmebyneon Jul 22, 2024
3b52cac
[Style] 반응형 스타일 추가중
callmebyneon Jul 22, 2024
612ec88
[Feat] postId에 따라 LinkedStudyCard 표시 수정
callmebyneon Jul 22, 2024
d6b4c47
Merge branch 'dev' into feature/profile-api
callmebyneon Jul 22, 2024
5e79f47
[Style] mediaquery 훅 오류로 삭제
callmebyneon Jul 22, 2024
fbd7c9d
[Fix] 프로필 정보 POST 테스트
callmebyneon Jul 22, 2024
286c204
[Fix] 경로 수정
callmebyneon Jul 23, 2024
42d8544
[Fix] params 옵셔널 체이닝 추가
callmebyneon Jul 23, 2024
3a47e5f
[Fix] 변수명 수정
callmebyneon Jul 23, 2024
83ec6f0
[Fix] 옵셔널 추가
callmebyneon Jul 23, 2024
1735242
Merge branch 'dev' into feature/profile-api
callmebyneon Jul 23, 2024
dbe1c7e
Merge branch 'dev' into feature/profile-api
callmebyneon Jul 24, 2024
2b13609
[Feat] profile 정보 저장 테스트중
callmebyneon Jul 24, 2024
0d39524
[Chore[ 스키마 타입 업데이트
callmebyneon Jul 24, 2024
6b9f4a3
[Chore] 랜덤 이미지 지정
callmebyneon Jul 24, 2024
70ed75e
[Chore] 스타일/태그/링크 수정
callmebyneon Jul 24, 2024
3c25e53
[Feat] 커뮤니티 글 정렬, 카테고리, 검색어 키워드에 따라 필터링
callmebyneon Jul 24, 2024
180e144
[Refactor]
callmebyneon Jul 24, 2024
8a31bac
[Chore] 이동
callmebyneon Jul 24, 2024
c131b1a
[Refactor]
callmebyneon Jul 24, 2024
6741b8d
[Feat] session 체크
callmebyneon Jul 24, 2024
cdaf1cb
[Feat] 검색어 쿼리에 따라 커뮤니티 post 필터링 추가
callmebyneon Jul 24, 2024
e9a5764
[Feat] 댓글 업데이트
callmebyneon Jul 24, 2024
e381a8e
[Chore] 더미 데이터 추가
callmebyneon Jul 24, 2024
9b19553
[Chore] 경로 변경
callmebyneon Jul 24, 2024
9cd9284
[Fix] rollback and make optional index to reduce error
callmebyneon Jul 24, 2024
d2cbdd5
[Style] MobileMenu 반응형 수정
callmebyneon Jul 25, 2024
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/(route)/_components/SearchInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
import useQueryString from "@/hooks/useQueryString";
import { FormEvent } from "react";

export default function SearchInput() {
export default function SearchInput({ origin = "study" }: { origin?: string }) {
const onChangeQuery = useQueryString({
paramsKey: "q",
queryInclude: "search",
queryInclude: origin === "study" ? "search" : origin,
});

const onSubmit = (e: FormEvent<HTMLFormElement>) => {
Expand Down
30 changes: 20 additions & 10 deletions src/app/(route)/my/like-study/page.tsx
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
import NoneStudyComponent from "@/app/(route)/study/_components/NoneStudyComponent";
import Link from "next/link";
import NoneContentItemBase from "@/app/_components/NoneContentItemBase";
import SectionTitle from "@/common/Atoms/Text/SectionTitle";
import StudyCardItem from "@/common/Organisms/StudyCardItem";
import StudyCardList from "@/common/Templates/CardList";
import { getStudiesData } from "@/dummies/studies";
import { getStudiesData, TStudyCard } from "@/dummies/studies";

export default function MyStudyLiked() {
const studyCard = getStudiesData();
const studyCards: TStudyCard[] = getStudiesData();
return (
<>
<SectionTitle size="md" className="mb-6">
찜한 스터디
</SectionTitle>
<div>
{/* TODO: 스터디 카드에서 찜한 스터디 표시 추가 필요 */}
{studyCard.length === 0 ? (
<NoneStudyComponent />
{studyCards.length === 0 ? (
<NoneContentItemBase>
<p className="text-main-500 font-semibold">
찜한 스터디가 없습니다.
<br />첫 스터디를 찜해보세요!
</p>
<Link
href={"/study"}
className="block py-3 px-4 bg-main-500 text-white font-semibold rounded-2xl"
>
스터디 구경가기
</Link>
</NoneContentItemBase>
) : (
<div className="grid grid-cols-2 lg:grid-cols-3 gap-gutter-sm xl:gap-gutter-xl">
{studyCard.map((card) => (
<ul className="grid grid-cols-2 lg:grid-cols-3 gap-gutter-sm xl:gap-gutter-xl">
{studyCards.map((card) => (
<StudyCardItem key={card.id} card={card} />
))}
</div>
</ul>
)}
</div>
</>
Expand Down
29 changes: 17 additions & 12 deletions src/app/(route)/my/post/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import { getPosts } from "@/dummies/posts";
import SectionTitle from "@/common/Atoms/Text/SectionTitle";
import Dropdown from "@/common/Molecules/Dropdown";
import LinkButton from "@/common/Atoms/LinkButton";
import PostListItem from "@/common/Organisms/PostListItem";
import NotFound from "@/app/not-found";
import PostListWithPagination from "@/common/Templates/PostListWithPagination";
import { WriteIcon } from "@/common/Atoms/Image/Icon";
import { TPost } from "@/types/model/PostItem";
import NonePostItem from "../../post/_components/NonePostItem";

const POST_FROM = [
{ key: "community", label: "커뮤니티" },
Expand All @@ -16,7 +17,7 @@ const POST_FROM = [
type TQuery = { from?: string };

export default function MyPost({ searchParams }: { searchParams: TQuery }) {
const posts = getPosts();
const posts: TPost[] = []; //getPosts();
const from = searchParams?.from || "community";
const postFrom = POST_FROM.find((item) => item.key === from);
if (postFrom === undefined) {
Expand All @@ -28,7 +29,7 @@ export default function MyPost({ searchParams }: { searchParams: TQuery }) {
찜한 스터디
</SectionTitle>
<div className="flex flex-row items-start justify-between pb-6 border-b border-b-line-neutral">
<Dropdown
{/* <Dropdown
buttonLabel={postFrom.label}
items={POST_FROM.map((item) => (
<li
Expand All @@ -41,16 +42,20 @@ export default function MyPost({ searchParams }: { searchParams: TQuery }) {
</li>
))}
/>
{postFrom.key === "community" && (
<LinkButton href="/post/write">
<WriteIcon />
<span className="text-body-600">커뮤니티 글 작성하기</span>
</LinkButton>
)}
</div>
<div className="flex flex-col gap-0">
<PostListWithPagination posts={posts} />
{postFrom.key === "community" && ( */}
<LinkButton href="/post/write" className="self-end ml-auto">
<WriteIcon />
<span className="text-body-600">커뮤니티 글 작성하기</span>
</LinkButton>
{/* )} */}
</div>
{posts.length > 0 ? (
<div className="flex flex-col gap-0">
<PostListWithPagination posts={posts.slice(0, 1)} />
</div>
) : (
<NonePostItem />
)}
</>
);
}
78 changes: 40 additions & 38 deletions src/app/(route)/my/profile/_components/FormEditProfile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,34 +18,34 @@ import { profileAction } from "@/lib/action";
import handleAlert from "@/app/(auth)/_components/ErrorAlert";

export default function FormEditProfile({
data,
setData,
session,
userId,
profile,
sessionProvider,
}: {
data: TProfileData;
setData: Dispatch<SetStateAction<TProfileData>>;
session: Session | null;
userId: string;
profile: TProfileData;
sessionProvider: string;
}) {
const changeData = (
e: ChangeEvent<HTMLInputElement> | ChangeEvent<HTMLTextAreaElement>
) => {
console.log(e.target.name);
const name = e.target.name;
const value = e.target.value;
setData((p) => ({ ...p, [name]: value }));
};
const changeMultiSelect: (
newValue: unknown,
actionMeta: ActionMeta<unknown>
) => void = (newValue) => {
// console.log({ newValue });
if (Array.isArray(newValue)) setData((p) => ({ ...p, interest: newValue }));
};
// const changeData = (
// e: ChangeEvent<HTMLInputElement> | ChangeEvent<HTMLTextAreaElement>
// ) => {
// console.log(e.target.name);
// const name = e.target.name;
// const value = e.target.value;
// setData((p) => ({ ...p, [name]: value }));
// };
// const changeMultiSelect: (
// newValue: unknown,
// actionMeta: ActionMeta<unknown>
// ) => void = (newValue) => {
// // console.log({ newValue });
// if (Array.isArray(newValue)) setData((p) => ({ ...p, interest: newValue }));
// };

async function save(e: FormEvent<HTMLFormElement>) {
e.preventDefault();

const userId = "669bf4d4894a60fc9669e924";
// const userId = profile?.userId._id as string;
const formData = new FormData(e.currentTarget);

try {
Expand All @@ -62,25 +62,26 @@ export default function FormEditProfile({
<Input.Text
name="positionTag"
placeholder="이름 앞에 추가될 포지션 태그를 추가하세요"
value={data.positionTag}
onChange={changeData}
// value={data.positionTag}
// onChange={changeData}
/>
</ProfileInputArea>
<ProfileInputArea label="내 소개">
<Input.Textarea
name="introduce"
placeholder="나를 소개할 말을 추가하세요"
onChange={changeData}
// onChange={changeData}
/>
</ProfileInputArea>
{session?.account.provider === "credentials" && (
<ProfileInputArea label="이메일">
<Input.Email
name="email"
value={data.email}
onChange={changeData}
placeholder="이메일 주소를 입력하세요"
/>
<ProfileInputArea label="이메일">
<Input.Email
name="email"
// value={data.email}
// onChange={changeData}
placeholder="이메일 주소를 입력하세요"
readOnly={sessionProvider !== "credentials"}
/>
{false && (
<div className="flex gap-4 items-center">
<Button
variation="outline"
Expand All @@ -92,19 +93,20 @@ export default function FormEditProfile({
*변경 후 재인증이 필요합니다.
</span>
</div>
</ProfileInputArea>
)}
)}
</ProfileInputArea>
<ProfileInputArea label="관심 카테고리">
<Input.Select
name="interest"
placeholder="관심 카테고리를 추가하세요"
isMulti
options={CATEGORIES}
value={data.interest}
onChange={changeMultiSelect}
defaultValue={profile.interest}
// value={data.interest}
// onChange={changeMultiSelect}
/>
</ProfileInputArea>
<Button type="submit" variation="solid" className="self-start">
<Button type="submit" variation="solid" className="self-end">
프로필 저장
</Button>
</form>
Expand Down
64 changes: 44 additions & 20 deletions src/app/(route)/my/profile/_components/ProfileForms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import DeleteAccountConfirm from "./DeleteAccountConfirm";
import ProfilePreview from "./ProfilePreview";
import { Session } from "next-auth";
import { CategoryOption } from "@/types/model/Category";
import { ProfileSchema } from "@/types/model/Profile";
import { CATEGORIES_ALL_OPTIONS } from "@/constants/categories/job_category";

export type TProfileData = {
profileUrl: string;
Expand All @@ -20,32 +22,54 @@ export type TProfileData = {
interest: Array<CategoryOption>;
};

export default function ProfileForms({ session }: { session: Session | null }) {
const user = getUser();
const defaultData = {
profileUrl: session?.user.image || "",
positionTag: user.position,
introduce: "",
email: session?.user.email || "",
interest: user.interest,
export default function ProfileForms({
userId,
profile,
sessionProvider,
}: {
userId: string;
profile: ProfileSchema | null;
sessionProvider: string;
}) {
// const user = getUser();
const interestCategory: CategoryOption[] =
profile?.my_category && profile.my_category.length > 0
? profile?.my_category.map(
(value) =>
CATEGORIES_ALL_OPTIONS.find(
(opt) => opt.value === value
) as CategoryOption
)
: [];
const profileData = {
profileUrl: profile?.userId.profile_img || "",
positionTag: profile?.position_tag || "",
introduce: profile?.introduce || "",
email: profile?.userId.email || "",
interest: interestCategory || [],
};
const [data, setData] = useState<TProfileData>(defaultData);
console.log({ profile, profileData });

const setProfileImage = (image: string) => {
setData((prev) => ({ ...prev, profileUrl: image }));
};
// const [data, setData] = useState<TProfileData>(defaultData);
// const setProfileImage = (image: string) => {
// setData((prev) => ({ ...prev, profileUrl: image }));
// };

return (
<>
<div className="grid xl:grid-cols-[5fr_4fr] xl:items-start gap-gutter-xl">
<div className="flex flex-col gap-8">
<p className="text-H2 text-label-dimmed">{session?.user.name}</p>
<p className="text-H2 text-label-dimmed">{profile?.userId.name}</p>
<ProfileInputArea label="아바타 이미지">
<ProfileImageInput setProfileImage={setProfileImage} />
<ProfileImageInput />
</ProfileInputArea>
<div className="w-full h-[1px] border-t border-t-line-normal"></div>
<FormEditProfile data={data} setData={setData} session={session} />
{session?.account.provider === "credentials" && (
<FormEditProfile
userId={userId}
profile={profileData}
sessionProvider={sessionProvider}
/>
{sessionProvider === "credentials" && (
<>
<div className="w-full h-[1px] border-t border-t-line-normal"></div>
<SectionTitle size="md" className="mb-2">
Expand All @@ -58,14 +82,14 @@ export default function ProfileForms({ session }: { session: Session | null }) {
<SectionTitle size="md" className="mb-2">
연락처 수정
</SectionTitle>
<FormUpdatePhoneNumber defaultValue={user.phone} />
<FormUpdatePhoneNumber defaultValue={profile?.userId.phone} />
<div className="w-full h-[1px] border-t border-t-line-normal"></div>
<DeleteAccountConfirm />
</div>

<div className="previewBox rounded-2xl hidden xl:sticky xl:top-20 p-6 border border-line-normal xl:flex flex-col gap-4">
<ProfilePreview name={session?.user.name} data={data} />
</div>
{/* <div className="previewBox rounded-2xl hidden xl:sticky xl:top-20 p-6 border border-line-normal xl:flex flex-col gap-4">
<ProfilePreview name={profile?.userId.name} data={profileData} />
</div> */}
</div>
</>
);
Expand Down
18 changes: 9 additions & 9 deletions src/app/(route)/my/profile/_components/ProfileImageInput.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
"use client";
import { ChangeEvent, useEffect, useState } from "react";

import useModal from "@/hooks/useModal";
import ProfileImagePreviewModal from "./ProfileImagePreviewModal";
import Button from "@/common/Atoms/Form/Button";
import ImageInputWithButton from "@/common/Molecules/Form/ImageInputWithButton";

export default function ProfileImageInput({
setProfileImage,
}: {
setProfileImage: (image: string) => void;
}) {
export default function ProfileImageInput() {
const [imageUrl, setImageUrl] = useState("");
const { Modal, open, close } = useModal({
children: (
Expand Down Expand Up @@ -47,10 +44,13 @@ export default function ProfileImageInput({

function onSave() {
// TODO: DB에 저장
setProfileImage(imageUrl);
close();
}

function onDelete() {
setImageUrl("");
}

return (
<>
<div>
Expand All @@ -68,13 +68,13 @@ export default function ProfileImageInput({
>
이미지 변경하기
</ImageInputWithButton>
{/* <Button
<Button
variation="text"
colors={{ bg: "bg-label-neutral", text: "text-label-neutral" }}
onClick={() => setImageUrl("")}
onClick={onDelete}
>
이미지 제거
</Button> */}
</Button>
</div>
<p className="text-label-400 text-label-alt mt-2">
*권장 이미지 - 확장자: png, jpg, jpeg / 용량: 1MB 이하
Expand Down
Loading