Skip to content

Commit

Permalink
Merge pull request #158 from jooyong-boo/develop
Browse files Browse the repository at this point in the history
merge develop
  • Loading branch information
jooyong-boo authored Oct 11, 2024
2 parents 74a3407 + df5cd65 commit cc7f6e6
Show file tree
Hide file tree
Showing 14 changed files with 383 additions and 26 deletions.
66 changes: 66 additions & 0 deletions src/app/api/user/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { NextRequest, NextResponse } from 'next/server';
import { getToken } from 'next-auth/jwt';
import { auth } from '@/auth';
import { postImages } from '@/services/images';
import { oAuthProviders, SignUpRequestData } from '@/types/auth';
import prisma from '../../../../prisma/client';

Expand Down Expand Up @@ -101,3 +103,67 @@ export async function POST(req: NextRequest) {
);
}
}

export async function PATCH(req: NextRequest) {
try {
const session = await auth();

if (!session) {
return NextResponse.json(
{ message: '로그인이 필요합니다.' },
{ status: 401 },
);
}

const { id } = session.user;
// formData로 전달된 데이터를 추출
const formData = await req.formData();
const nickname = formData.get('nickname') as string | undefined;
const profile = formData.get('profile') as File | undefined;

// 필수 필드 확인
if (!id) {
return NextResponse.json(
{ message: '로그인이 필요합니다.' },
{ status: 400 },
);
}

// 사용자의 이미지 s3에 업로드
const { imageUrl } = profile
? await postImages({
file: profile,
folder: `users/${id}`,
})
: { imageUrl: '' };

console.log('imageUrl', imageUrl);

if (imageUrl) {
await prisma.users.update({
where: { id },
data: {
profile: imageUrl,
},
});
}

const updatedUser = await prisma.users.update({
where: { id },
data: {
nickname,
},
});

return NextResponse.json(
{ message: '회원 정보가 수정되었습니다.', userInfo: updatedUser },
{ status: 200 },
);
} catch (e) {
console.log(e);
return NextResponse.json(
{ message: '회원 정보 수정 중 오류가 발생했습니다.' },
{ status: 500 },
);
}
}
19 changes: 14 additions & 5 deletions src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ declare module 'next-auth' {
}
export interface Session {
user: User & {
name: string | null;
email: string | null;
image: string | null;
id: string;
name: string;
email: string;
image: string;
};
accessToken: string | JWT;
}
Expand All @@ -30,6 +31,7 @@ declare module 'next-auth/jwt' {
interface JWT {
accessToken: string;
user: {
id: string;
createdAt: string;
role: {
name: string;
Expand All @@ -38,6 +40,7 @@ declare module 'next-auth/jwt' {
name: string;
};
nickname: string;
profile: string;
};
}
}
Expand Down Expand Up @@ -82,8 +85,10 @@ const authOptions: NextAuthConfig = {
const user = await prisma.users.findUnique({
where: { email: token.email as string },
select: {
id: true,
createdAt: true,
nickname: true,
profile: true,
oauthProvider: {
select: {
name: true,
Expand All @@ -100,17 +105,21 @@ const authOptions: NextAuthConfig = {
Object.assign(token, { user });
}
if (trigger === 'update' && session) {
Object.assign(token, { user: session.user });
token.picture = session.user.image;
token.picture = session.profile;
token.user.profile = session.profile;
token.user.nickname = session.nickname;
return token;
}
return token;
},
session: async ({ session, token }) => {
session = { ...session, ...token.user };
if (token) {
session.user.id = token.user.id;
session.user.role = token.user.role;
session.user.oauthProvider = token.user.oauthProvider;
session.user.nickname = token.user.nickname;
session.user.image = token.user.profile;
session.accessToken = token.accessToken;
}
return session;
Expand Down
11 changes: 9 additions & 2 deletions src/components/Editor/extension/ImageUpload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@ import { ImageSvg } from '@/assets/svg/editor/index';
import { ToolbarProps } from '@/components/Editor/Toolbar';
import { postImages } from '@/services/images';

const ImageUpload = ({ editor }: ToolbarProps) => {
interface ImageUploadProps extends ToolbarProps {
path?: string;
}

const ImageUpload = ({
editor,
path = 'posts/temporary',
}: ImageUploadProps) => {
const imageRef = useRef<HTMLInputElement>(null);

const handleUploadImage = async (
Expand All @@ -15,7 +22,7 @@ const ImageUpload = ({ editor }: ToolbarProps) => {
return;
}

const result = await postImages({ file, folder: 'posts/temporary' });
const result = await postImages({ file, folder: path });

editor.chain().focus().setImage({ src: result.imageUrl }).run();
};
Expand Down
2 changes: 1 addition & 1 deletion src/components/ImageInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const ImageInput = ({ name, label, originSrc, id }: ImageInputProps) => {
};

return (
<div className="flex flex-col gap-2">
<div className="flex h-full w-full flex-col gap-2">
{label && (
<label htmlFor="image" className="text-lg font-bold">
{label}
Expand Down
33 changes: 33 additions & 0 deletions src/components/Modal/ModalContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { ReactNode, useRef } from 'react';
import ModalView from '@/components/Modal/ModalView';

interface ModalContainerProps {
onClose: () => void;
children: ReactNode;
isOpen: boolean;
displayClose?: string;
}

const ModalContainer = ({
onClose,
children,
isOpen,
displayClose,
}: ModalContainerProps) => {
const modalRef = useRef<HTMLDivElement>(null);

if (!isOpen) {
return null;
}
return (
<ModalView
onClose={onClose}
modalRef={modalRef}
displayClose={displayClose}
>
{children}
</ModalView>
);
};

export default ModalContainer;
19 changes: 19 additions & 0 deletions src/components/Modal/ModalPortal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import ReactDom from 'react-dom';

interface Props {
children: React.ReactNode;
modalKey: string;
}

const ModalPortal = ({ children, modalKey }: Props) => {
let el = document.getElementById(modalKey) as HTMLElement;
if (!el) {
const newPortal = document.createElement('div');
newPortal.setAttribute('id', modalKey);
document.body.appendChild(newPortal);
el = newPortal;
}
return ReactDom.createPortal(children, el);
};

export default ModalPortal;
61 changes: 61 additions & 0 deletions src/components/Modal/ModalView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { ReactNode, useEffect } from 'react';
import { cn } from '@/utils/cn';

interface ModalViewProps {
onClose: () => void;
children: ReactNode;
modalRef: React.RefObject<HTMLDivElement>;
displayClose?: string;
modalHeight?: string;
}

const ModalView = ({
onClose,
children,
modalRef,
displayClose = 'block',
}: ModalViewProps) => {
useEffect(() => {
const handleEscKey = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
onClose();
}
};

document.addEventListener('keydown', handleEscKey);

return () => {
document.removeEventListener('keydown', handleEscKey);
};
}, [onClose]);

const overlayClasses = `fixed inset-0 z-[99] w-full h-full backdrop-blur bg-black bg-opacity-50`;

const modalClasses = cn(
`fixed left-1/2 transition-all duration-500 ease-in-out overflow-y-auto sm:max-w-[500px] sm:mx-auto`,
`dark:bg-gray-800 bg-gray-200 p-4 border dark:bg-slate-800 dark:border-slate-700`,
'top-1/2 w-[calc(100%-32px)] -translate-x-1/2 -translate-y-1/2 rounded-lg animate-modalShow max-h-[90vh]',
);

const closeBtnClasses = `absolute bg-transparent -top-8 right-0 text-gray-50 ${displayClose === 'none' ? 'hidden' : 'block'}`;

return (
<div className={overlayClasses} onClick={onClose}>
<div
ref={modalRef}
className={modalClasses}
onClick={(e) => e.stopPropagation()}
>
{children}
<button
className={`${closeBtnClasses} material-symbols-rounded`}
onClick={onClose}
>
close
</button>
</div>
</div>
);
};

export default ModalView;
66 changes: 66 additions & 0 deletions src/components/Modal/hooks/useModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { useCallback, useState, ReactNode, useEffect } from 'react';
import ModalContainer from '@/components/Modal/ModalContainer';
import ModalPortal from '@/components/Modal/ModalPortal';

interface UseModalProps {
useBlur?: boolean;
}

const useModal = ({ useBlur = true }: UseModalProps = {}) => {
const [modals, setModals] = useState<string[]>([]);

const open = useCallback((modalId: string) => {
document.body.style.overflow = 'hidden';
setModals((prevModals) => [...prevModals, `${modalId}Modal`]);
}, []);

const close = useCallback((modalId: string) => {
const deleteModal = document.getElementById(`${modalId}Modal`);
if (deleteModal) {
document.body.style.overflow = 'visible';
setModals((prevModals) =>
prevModals.filter((id) => id !== `${modalId}Modal`),
);
document.body.removeChild(deleteModal);
}
}, []);

useEffect(() => {
return () => {
document.body.style.overflow = 'visible';
};
}, []);

const Modal = useCallback(
({
children,
modalKey,
displayClose,
}: {
children: ReactNode;
modalKey: string;
displayClose?: string;
}) => {
return modals.includes(`${modalKey}Modal`) ? (
<ModalPortal modalKey={`${modalKey}Modal`}>
<ModalContainer
isOpen
onClose={useBlur ? () => close(modalKey) : () => {}}
displayClose={displayClose}
>
{children}
</ModalContainer>
</ModalPortal>
) : null;
},
[modals, useBlur, close],
);

return {
Modal,
open,
close,
};
};

export default useModal;
2 changes: 1 addition & 1 deletion src/components/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Profile.Info = ({ src, alt, name, date }: ProfileInfoProps) => {
alt={alt}
width={60}
height={60}
className="h-10 w-10 rounded-full md:h-14 md:w-14"
className="h-10 w-10 rounded-full object-cover md:h-14 md:w-14"
/>
<div className="flex-col justify-center">
<p className="text-sm font-semibold md:text-lg">{name}</p>
Expand Down
Loading

0 comments on commit cc7f6e6

Please sign in to comment.