Skip to content

Commit

Permalink
[#414] [프로필] 프로필 수정 페이지 (#426)
Browse files Browse the repository at this point in the history
* chore: tailwind config에 디렉토리 옵션 추가

* chore: Storybook 프리뷰 내부 body에 padding 속성 제거

* chore: IconClose 기본 fill 속성 none으로 지정

* refactor: TopNavigation 컴포넌트 position 옵션 수정

* feat: 내 정보 수정 페이지 마크업

* feat: 내 정보 수정 페이지 Storybook 작성

* feat: 내 정보 수정 페이지 기능 구현

* chore: 주석 추가

* refactor: Storybook args 추가 및 props 타입 정리

* refactor: InputLength 컴포넌트 분리

* fix: Form에서 defaultValue가 안보이던 문제해결(스토리북 args 수정)

* fix: TopNavigation absolute 옵션 수정

* feat: Storybook에 Layout 추가

* feat: 기존 EditMyProfilePage 교체

* fix: TopNavigation.CenterItem padding값 수정

* refactor: InputLength 컴포넌트 props 수정

* refactor: InputLength 스토리북 수정

* refactor: InputLength 컴포넌트의 리액트-훅-폼 종속성 제거

* feat: ToastProvider 추가

* feat: API 연동 및 기능 구현(라우팅, 토스트UI)

* chore: 프로필 수정 페이지 스토리북 삭제

* fix: export default 컴포넌트 이름 수정

* refactor: InputLength 컴포넌트 currentLength props로 교체

* refactor: 아이콘 라우팅 방식 변경 (useRouter -> next/Link)

* chore: TopNavigation 스타일 코드 컨벤션 수정

* fix: InputLength Storybook 수정

* fix: InputLength props 타입에러 수정

* fix: InputLength 컴포넌트 undefined  처리방식 수정
  • Loading branch information
hanyugeon authored and gxxrxn committed Jun 17, 2024
1 parent b56d3d5 commit 91b8171
Show file tree
Hide file tree
Showing 8 changed files with 322 additions and 29 deletions.
5 changes: 4 additions & 1 deletion .storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from 'react';
import type { Preview } from '@storybook/react';
import '@/styles/global.css';

import Layout from '../src/v1/layout/Layout';
import ToastProvider from '../src/ui/Base/Toast/ToastProvider';

const preview: Preview = {
Expand All @@ -22,7 +23,9 @@ const preview: Preview = {
decorators: [
Story => (
<ToastProvider>
<Story />
<Layout>
<Story />
</Layout>
</ToastProvider>
),
],
Expand Down
4 changes: 2 additions & 2 deletions public/icons/close.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 16 additions & 22 deletions src/app/profile/me/edit/page.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,25 @@
'use client';

import { Suspense } from 'react';
import useAllJobQuery from '@/queries/job/useAllJobQuery';
import useMyProfileQuery from '@/queries/user/useMyProfileQuery';
import AuthRequired from '@/ui/AuthRequired';
import TopNavigation from '@/ui/common/TopNavigation';
import ProfileForm from '@/ui/Profile/ProfileForm';

import { isAuthed } from '@/utils/helpers';
import { Skeleton, VStack } from '@chakra-ui/react';
import { Suspense } from 'react';
import AuthRequired from '@/ui/AuthRequired';

import EditProfile from '@/v1/profile/EditProfile';

/**
* @todo
* Fallback UI 추가하기
*/

const EditMyPage = () => {
const EditProfilePage = () => {
return (
<AuthRequired>
<VStack justify="center" align="center">
<TopNavigation pageTitle="내 프로필 수정" />
<Suspense
fallback={
<VStack gap="2rem" align="stretch" w="100%">
<Skeleton w="100%" height="6rem" />
<Skeleton w="100%" height="6rem" />
<Skeleton w="100%" height="6rem" />
</VStack>
}
>
<Contents />
</Suspense>
</VStack>
<Suspense fallback={null}>
<Contents />
</Suspense>
</AuthRequired>
);
};
Expand All @@ -35,8 +29,8 @@ const Contents = () => {
const { data: profileData } = useMyProfileQuery();

return allJobQuery.isSuccess ? (
<ProfileForm profile={profileData} jobGroups={allJobQuery.data.jobGroups} />
<EditProfile profile={profileData} jobGroups={allJobQuery.data.jobGroups} />
) : null;
};

export default EditMyPage;
export default EditProfilePage;
5 changes: 4 additions & 1 deletion src/components/ContextProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import ChakraThemeProvider from '@/components/ChakraThemeProvider';
import ReactQueryProvider from '@/components/ReactQueryProvider';
import { ReactNode } from 'react';
import ErrorPage from '@/app/error';
import ToastProvider from '@/ui/Base/Toast/ToastProvider';

const ContextProvider = ({ children }: { children: ReactNode }) => {
return (
Expand All @@ -15,7 +16,9 @@ const ContextProvider = ({ children }: { children: ReactNode }) => {
<ReactQueryProvider>
<ChakraThemeProvider>
<ErrorBoundary fallbackRender={ErrorPage}>
<Layout>{children}</Layout>
<ToastProvider>
<Layout>{children}</Layout>
</ToastProvider>
</ErrorBoundary>
</ChakraThemeProvider>
</ReactQueryProvider>
Expand Down
76 changes: 76 additions & 0 deletions src/stories/Base/InputLength.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { Meta, StoryObj } from '@storybook/react';
import { SubmitHandler, useForm } from 'react-hook-form';

import Button from '@/ui/Base/Button';
import Input from '@/ui/Base/Input';
import InputLength from '@/ui/Base/InputLength';
import ErrorMessage from '@/ui/Base/ErrorMessage';

const meta: Meta<typeof InputLength> = {
title: 'Base/InputLength',
component: InputLength,
tags: ['autodocs'],
};

export default meta;

type Story = StoryObj<typeof InputLength>;

type DefaultValues = {
password: string;
};

const InputLengthUseWithForm = () => {
const {
register,
watch,
handleSubmit,
formState: { errors },
} = useForm<DefaultValues>({
mode: 'all',
});

const handleSubmitForm: SubmitHandler<DefaultValues> = ({ password }) => {
alert(`password: ${password}`);
};

return (
<form
onSubmit={handleSubmit(handleSubmitForm)}
className="flex w-full flex-col gap-[1.6rem]"
>
<div className="flex flex-col gap-[0.5rem]">
<Input
placeholder="비밀번호를 입력해주세요."
{...register('password', {
required: '필수 항목입니다.',
minLength: { value: 2, message: '2자 이상 입력해 주세요.' },
maxLength: { value: 10, message: '10자 이하 입력해 주세요.' },
})}
error={!!errors.password}
/>
<div className="flex flex-row-reverse justify-between gap-[0.4rem]">
<InputLength
currentLength={watch('password')?.length}
isError={!!errors.password}
maxLength={10}
/>
{errors.password && (
<ErrorMessage>{errors.password.message}</ErrorMessage>
)}
</div>
</div>
<Button
size="large"
type="submit"
onClick={handleSubmit(handleSubmitForm)}
>
Submit
</Button>
</form>
);
};

export const Default: Story = {
render: () => <InputLengthUseWithForm />,
};
22 changes: 22 additions & 0 deletions src/ui/Base/InputLength.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
type InputLengthProps = {
currentLength: number;
isError: boolean;
maxLength: number;
};

const InputLength = ({
currentLength,
isError,
maxLength,
}: InputLengthProps) => {
const textColor = isError ? 'text-warning-800 ' : 'text-main-900';

return (
<div>
<span className={textColor}>{currentLength ? currentLength : 0}</span>/
{maxLength}
</div>
);
};

export default InputLength;
8 changes: 5 additions & 3 deletions src/ui/Base/TopNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const TopNavigation = ({ children }: TopNavigationProps) => {

const LeftItem = ({ children }: ItemProps) => {
return (
<div className="absolute left-[2rem] [&_svg]:h-[2rem] [&_svg]:w-[2rem] [&_svg]:cursor-pointer">
<div className="absolute left-[0rem] [&_svg]:h-[2rem] [&_svg]:w-[2rem] [&_svg]:cursor-pointer">
{children}
</div>
);
Expand All @@ -32,13 +32,15 @@ const textAligns = {
const CenterItem = ({ children, textAlign = 'center' }: CenterItemProps) => {
const alignClassName = textAligns[textAlign];
return (
<div className={`w-full px-[3.5rem] ${alignClassName}`}>{children}</div>
<div className={`h-[2rem] w-full px-[1.5rem] ${alignClassName}`}>
{children}
</div>
);
};

const RightItem = ({ children }: ItemProps) => {
return (
<div className="absolute right-[2rem] flex gap-[1rem] [&_svg]:h-[2rem] [&_svg]:w-[2rem] [&_svg]:cursor-pointer">
<div className="absolute right-[0rem] flex gap-[1rem] [&_svg]:h-[2rem] [&_svg]:w-[2rem] [&_svg]:cursor-pointer">
{children}
</div>
);
Expand Down
Loading

0 comments on commit 91b8171

Please sign in to comment.