Skip to content

Commit

Permalink
Merge pull request #361 from boostcampwm2023/develop
Browse files Browse the repository at this point in the history
[Deploy] Ver1.0.0 릴리즈
  • Loading branch information
qkrwogk authored Dec 14, 2023
2 parents 7d676cc + 3b066fe commit 28e6946
Show file tree
Hide file tree
Showing 10 changed files with 179 additions and 108 deletions.
138 changes: 61 additions & 77 deletions README.md

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions packages/client/src/pages/Home/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,10 @@ export default function Home() {
<>
<Outlet />
{status === 'new' && <CoachMarker isFirst={true} />}
{isSwitching !== 'end' && (
<WarpScreen isSwitching={isSwitching} setIsSwitching={setIsSwitching} />
)}
{text && <Toast type={type}>{text}</Toast>}

<WarpScreen isSwitching={isSwitching} setIsSwitching={setIsSwitching} />

<UpperBar />
<UnderBar />

Expand Down
2 changes: 2 additions & 0 deletions packages/client/src/shared/lib/types/post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ export interface PostData {
images: string[];
like_cnt?: number;
}

export type TextStateTypes = 'DEFAULT' | 'INVALID' | 'OVER';
113 changes: 96 additions & 17 deletions packages/client/src/widgets/postModal/ui/PostModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import { useLocation, useNavigate, useParams } from 'react-router-dom';
import remarkGfm from 'remark-gfm';
import { instance } from 'shared/apis';
import { useCheckNickName, useFetch, useRefresh } from 'shared/hooks';
import { PostData } from 'shared/lib';
import { PostData, TextStateTypes } from 'shared/lib';
import { useCameraStore, useToastStore, useViewStore } from 'shared/store';
import { AlertDialog, Button, Input, Modal, TextArea } from 'shared/ui';
import { deletePost } from '../api/deletePost';
import ImageSlider from './ImageSlider';
import { Caption } from 'shared/styles';
import { css } from '@emotion/react';

export default function PostModal() {
const [deleteModal, setDeleteModal] = useState(false);
Expand All @@ -19,6 +21,8 @@ export default function PostModal() {
const [title, setTitle] = useState('');
const [isDeleteButtonDisabled, setIsDeleteButtonDisabled] = useState(false);
const [isSaveButtonDisabled, setIsSaveButtonDisabled] = useState(false);
const [titleState, setTitleState] = useState<TextStateTypes>('DEFAULT');
const [contentState, setContentState] = useState<TextStateTypes>('DEFAULT');

const { setToast } = useToastStore();
const { setView } = useViewStore();
Expand All @@ -44,7 +48,6 @@ export default function PostModal() {
const formData = new FormData();
formData.append('title', title);
formData.append('content', content);

try {
await instance({
url: `/post/${postId}`,
Expand Down Expand Up @@ -105,6 +108,8 @@ export default function PostModal() {
buttonType="CTA-icon"
type="button"
onClick={() => {
if (title === '') return setTitleState('INVALID');
if (content === '') return setContentState('INVALID');
setIsEdit(false);
handleEditSave();
}}
Expand Down Expand Up @@ -160,21 +165,44 @@ export default function PostModal() {
)}
{isEdit ? (
<TextContainer style={{ height: '100%' }}>
<Input
id={'postTitle'}
placeholder="제목"
style={{ marginBottom: '30px', height: '25%' }}
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
<TextArea
value={content}
onChange={(content) => {
setContent(content);
}}
width="100%"
height="75%"
/>
<TitleContainer>
<TitleInput
id={'postTitle'}
state={titleState}
placeholder="제목"
style={{ height: '25%' }}
value={title}
onChange={(e) => {
if (e.target.value.length > 20) {
setTitleState('OVER');
return;
}
setTitleState('DEFAULT');
setTitle(e.target.value);
}}
autoComplete="off"
/>
{titleState === 'INVALID' && (
<Message>제목을 입력해주세요.</Message>
)}
{titleState === 'OVER' && (
<Message>제목은 20자 까지 입력 가능합니다.</Message>
)}
</TitleContainer>
<ContentContainer state={contentState}>
<TextArea
value={content}
onChange={(content) => {
setContentState('DEFAULT');
setContent(content);
}}
width="100%"
height="100%"
/>
{contentState === 'INVALID' && (
<Message>내용을 입력해주세요.</Message>
)}
</ContentContainer>
</TextContainer>
) : (
<TextContainer>
Expand Down Expand Up @@ -203,6 +231,45 @@ export default function PostModal() {
);
}

const ContentContainer = styled.div<{ state: TextStateTypes }>`
border: 1px solid;
border-radius: 4px;
height: 75%;
${({ state, theme: { colors } }) => {
if (state === 'DEFAULT') return;
return css`
border-color: ${colors.text.warning};
&:focus {
border-color: ${colors.text.warning};
}
&:hover {
border-color: ${colors.text.warning};
}
`;
}};
`;

const TitleInput = styled(Input)<{ state: TextStateTypes }>`
${({ state, theme: { colors } }) => {
if (state === 'DEFAULT') return;
return css`
border-color: ${colors.text.warning};
&:focus {
border-color: ${colors.text.warning};
}
&:hover {
border-color: ${colors.text.warning};
}
`;
}};
`;

const PostModalLayout = styled(Modal)`
transform: translate(-10%, -50%);
`;
Expand Down Expand Up @@ -263,3 +330,15 @@ const ButtonContainer = styled.div`
display: flex;
gap: 8px;
`;

const Message = styled.p`
position: absolute;
margin: 4px 0 0 0;
color: ${({ theme: { colors } }) => colors.text.warning};
${Caption}
`;

const TitleContainer = styled.div`
margin-bottom: 30px;
`;
7 changes: 5 additions & 2 deletions packages/client/src/widgets/warpScreen/WarpScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ export default function WarpScreen({ isSwitching, setIsSwitching }: PropsType) {
zIndex: 999,
backgroundColor: theme.colors.background.bdp04,
};
if (isSwitching === 'fade') return <FadeoutScreen />;

if (isSwitching === 'end') return null;

if (isSwitching === 'fade')
return <FadeoutScreen onAnimationEnd={() => setIsSwitching('end')} />;

return (
<Canvas camera={camera} style={canvasStyle}>
Expand All @@ -50,7 +54,6 @@ export default function WarpScreen({ isSwitching, setIsSwitching }: PropsType) {
</EffectComposer>

<ambientLight intensity={AMBIENT_LIGHT_INTENSITY} />

<BrightSphere />
<SpaceWarp setIsSwitching={setIsSwitching} />
</Canvas>
Expand Down
6 changes: 3 additions & 3 deletions packages/client/src/widgets/warpScreen/ui/SpaceWarp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
} from '../lib/constants';
import { WarpStateType } from 'shared/lib';

const geSpaceWarpLinesInfo = () => {
const getSpaceWarpLinesInfo = () => {
const positions = Array.from({ length: SPACE_WARP_LINES_NUM }, () => {
const x = getRandomFloat(SPACE_WARP_XZ_MIN, SPACE_WARP_XZ_MAX);
const y = getRandomFloat(SPACE_WARP_Y_MIN, SPACE_WARP_Y_MAX);
Expand All @@ -38,10 +38,10 @@ interface PropsType {
}

export default function SpaceWarp({ setIsSwitching }: PropsType) {
const [positions, colors] = useMemo(() => geSpaceWarpLinesInfo(), []);
const [positions, colors] = useMemo(() => getSpaceWarpLinesInfo(), []);

useFrame((state, delta) => {
if (state.camera.position.y <= 0) {
if (state.camera.position.y <= SPACE_WARP_Y_MIN) {
state.scene.background = new THREE.Color(0xffffff);
setIsSwitching('fade');
return;
Expand Down
3 changes: 1 addition & 2 deletions packages/client/src/widgets/writingModal/ui/WritingModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import { usePostStore, useViewStore } from 'shared/store';
import { Caption } from 'shared/styles';
import { AlertDialog, Button, Input, Modal, TextArea } from 'shared/ui';
import Images from './Images';

type TextStateTypes = 'DEFAULT' | 'INVALID' | 'OVER';
import { TextStateTypes } from 'shared/lib';

export default function WritingModal() {
const [titleState, setTitleState] = useState<TextStateTypes>('DEFAULT');
Expand Down
4 changes: 2 additions & 2 deletions packages/server/src/board/dto/create-board.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { IsJSON, IsNotEmpty, IsString, MaxLength } from 'class-validator';
export class CreateBoardDto {
@IsNotEmpty({ message: '게시글 제목은 필수 입력입니다.' })
@IsString({ message: '게시글 제목은 문자열로 입력해야 합니다.' })
@MaxLength(20, { message: '게시글 제목은 20자 이내로 입력해야 합니다.' })
@MaxLength(40, { message: '게시글 제목은 20자 이내로 입력해야 합니다.' })
@ApiProperty({
description: '게시글 제목',
example: 'test title',
Expand All @@ -14,7 +14,7 @@ export class CreateBoardDto {

@IsNotEmpty({ message: '게시글 내용은 필수 입력입니다.' })
@IsString({ message: '게시글 내용은 문자열로 입력해야 합니다.' })
@MaxLength(1000, { message: '게시글 내용은 1000자 이내로 입력해야 합니다.' })
@MaxLength(2200, { message: '게시글 내용은 1000자 이내로 입력해야 합니다.' })
@ApiProperty({
description: '게시글 내용',
example: 'test content',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ export class HttpExceptionFilter {
const response = context.getResponse();
const method = request.method;
const status = exception.getStatus();
const errorResponse = exception.getResponse() as any;

const now = new Date();
const korTime = new Date(now.getTime() + 9 * 3600 * 1000);
const exceptionData = {
path: `${method} ${request.url}`,
error: `${status} ${exception.name}`,
message: exception.message,
message: errorResponse.message,
timestamp: korTime,
};
const saveException = new this.exceptionModel(exceptionData);
Expand Down
6 changes: 5 additions & 1 deletion packages/server/src/interceptor/log.interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ export class LogInterceptor implements NestInterceptor {
errLog += ` ${userString}`;
}
Logger.error(errLog);
Logger.error(error);
Logger.error(
`${error.getResponse()['statusCode']} ${
error.getResponse()['error']
} - ${error.getResponse()['message']}`,
);
throw error;
}),
tap(() => {
Expand Down

0 comments on commit 28e6946

Please sign in to comment.