Skip to content

Commit

Permalink
Merge branch 'develop' into server
Browse files Browse the repository at this point in the history
  • Loading branch information
kangjuhyup committed Dec 27, 2024
2 parents d59e12f + d5ae09b commit d95d36f
Show file tree
Hide file tree
Showing 14 changed files with 296 additions and 89 deletions.
32 changes: 32 additions & 0 deletions packages/page/src/api/dto/letter.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export interface MetaDefault {
width: string;
height: string;
}

export interface MetaDetail extends MetaDefault {
x?: string;
y?: string;

z?: string;

angle?: string;
}

export interface PrepareRequest {
thumbnailMeta: MetaDefault;

letterMeta: MetaDefault;

backgroundMeta: MetaDefault;

componentMetas: MetaDetail[];
}

export interface PrepareResponse {
thumbnailUrl: string;
letterUrl: string;
backgroundUrl: string;
componentUrls: string[];
expires: number;
sessionKey: string;
}
77 changes: 52 additions & 25 deletions packages/page/src/api/letter.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,63 @@ import { useState } from 'react';
import apiClient from '../common/http.client';
import ApiResponse from '../common/response';
import useErrorStore from '../store/error.store';

import { PrepareRequest, PrepareResponse } from './dto/letter.dto';
import useLoginStore from '@/store/login.store';

interface GetLetterResponse {
id : number;
img : string
comments : {
name : string,
body : string
}[]
id: number;
img: string;
comments: {
name: string;
body: string;
}[];
}

const useLetterApi = () => {
const {setError} = useErrorStore()
const [letter,setLetter] = useState<GetLetterResponse>()
const getLetter = async (letterId : number) => {
const response = await apiClient.get<ApiResponse<GetLetterResponse>>(`/${letterId}`)
if(!response.result) {
setError(response.error)
} else {
setLetter(response.data)
}
const { setError } = useErrorStore();
const { access } = useLoginStore();
const [letter, setLetter] = useState<GetLetterResponse>();
const [prepareUrls, setPrepareUrls] = useState<PrepareResponse>();
const getLetter = async (letterId: number) => {
const response = await apiClient.get<ApiResponse<GetLetterResponse>>(
`/${letterId}`,
);
if (!response.result) {
setError(response.error);
} else {
setLetter(response.data);
}
const getLetterMock = async (letterId:number) => {
setLetter({
id : 1,
img : 'https://yimgf-thinkzon.yesform.com/docimgs/public/1/62/61676/61675904.png',
comments : [{name:'강주협',body:'무조건 참석합니다!'}]
})
};
const getPrepareUrls = async (request: PrepareRequest) => {
const response = await apiClient.post<ApiResponse<PrepareResponse>>(
`/letter/prepare-add`,
request,
{
headers: {
Authorization: `Bearer ${access ?? ''}`,
},
},
);
if (!response.result) {
setError(response.error);
} else {
setPrepareUrls(response.data);
}
return {letter,getLetter,getLetterMock}
}
};
const getLetterMock = async (letterId: number) => {
setLetter({
id: 1,
img: 'https://yimgf-thinkzon.yesform.com/docimgs/public/1/62/61676/61675904.png',
comments: [{ name: '강주협', body: '무조건 참석합니다!' }],
});
};
return {
letter,
getLetter,
getLetterMock,
prepareUrls,
getPrepareUrls,
};
};

export default useLetterApi;
export default useLetterApi;
36 changes: 36 additions & 0 deletions packages/page/src/api/user.api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { useState } from 'react';
import apiClient from '../common/http.client';
import ApiResponse from '../common/response';
import useErrorStore from '../store/error.store';
import useLoginStore from '@/store/login.store';

interface ProfileResponse {
userId: string;
email: string;
nickName: string;
}

const useUserApi = () => {
const { setError } = useErrorStore();
const { access } = useLoginStore();
const [profile, setProfile] = useState<ProfileResponse>();
const getProfile = async () => {
const response = await apiClient.get<ApiResponse<ProfileResponse>>(
`/user`,
{
headers: {
Authorization: `Bearer ${access ?? ''}`,
},
},
);
if (!response.result) {
setError(response.error);
} else {
setProfile(response.data);
}
};

return { profile, getProfile };
};

export default useUserApi;
6 changes: 6 additions & 0 deletions packages/page/src/common/http.client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,13 @@ class HttpClient {
): Promise<T> {
return this.request<T>(endpoint, 'POST', {
...options,
headers: {
'Content-Type': 'application/json',
...(options?.headers || {}),
},
body: JSON.stringify(body),
}).catch((err) => {
throw err;
});
}

Expand Down
32 changes: 32 additions & 0 deletions packages/page/src/common/login.provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React, { createContext, useContext, ReactNode, useEffect } from 'react';
import { useRouter } from 'next/router';
import useLoginStore from '@/store/login.store';

const LoginContext = createContext<ReturnType<typeof useLoginStore> | null>(
null,
);

export const LoginProvider = ({ children }: { children: ReactNode }) => {
const router = useRouter();
const store = useLoginStore();

useEffect(() => {
const { access } = store;

if (!access && router.pathname !== '/page/login') {
router.replace('/page/login');
}
}, [store.access, router]);

return (
<LoginContext.Provider value={store}>{children}</LoginContext.Provider>
);
};

export const useLogin = () => {
const context = useContext(LoginContext);
if (!context) {
throw new Error('useLogin must be used within a LoginProvider');
}
return context;
};
18 changes: 18 additions & 0 deletions packages/page/src/components/header/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Button, Grid } from '@mantine/core';
import { useRouter } from 'next/router';

const Header = () => {
const router = useRouter();
return (
<Grid>
<Grid.Col span={4}>
<Button onClick={() => router.replace('/page/my')}>MY</Button>
</Grid.Col>
<Grid.Col span={4}>
<Button onClick={() => router.replace('/page/create')}>CREATE</Button>
</Grid.Col>
</Grid>
);
};

export default Header;
20 changes: 18 additions & 2 deletions packages/page/src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,33 @@

import '@mantine/core/styles.css';
import '@mantine/dropzone/styles.css';
import { MantineProvider } from '@mantine/core';
import { AppShell, MantineProvider } from '@mantine/core';
import { GoogleOAuthProvider } from '@react-oauth/google';
import { AppProps } from 'next/app';
import { LoginProvider } from '@/common/login.provider';
import Header from '@/components/header';

const googleClientId = process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID || '';

export default function App({ Component, pageProps }: AppProps) {
return (
<MantineProvider>
<GoogleOAuthProvider clientId={googleClientId}>
<Component {...pageProps} />
<LoginProvider>
<AppShell
header={{ height: 60 }}
footer={{ height: 50 }}
navbar={{ width: 300, breakpoint: 'sm' }}
padding="md"
>
<AppShell.Header>
<Header />
</AppShell.Header>
<AppShell.Main>
<Component {...pageProps} />
</AppShell.Main>
</AppShell>
</LoginProvider>
</GoogleOAuthProvider>
</MantineProvider>
);
Expand Down
50 changes: 38 additions & 12 deletions packages/page/src/pages/page/create.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
'use client';

import { useState } from 'react';
import { AppShell, Button, Container, Grid } from '@mantine/core';
import { useEffect, useState } from 'react';
import { ActionIcon, AppShell, Button, Container, Grid } from '@mantine/core';
import { DropzoneButton } from '../../components/button/dropzone/dropzone.button';
import { FileWithPath, MIME_TYPES } from '@mantine/dropzone';
import MoveResizeImage from '../../components/image/move/move.resize.image';
import {
IconDeviceFloppy,
IconPencil,
IconSticker,
IconTextGrammar,
} from '@tabler/icons-react';
import { useDisclosure } from '@mantine/hooks';
import MoveResizeText from '../../components/text/move/move.resize.text';
import useLetterApi from '@/api/letter.api';

const CreatePage = () => {
const { prepareUrls, getPrepareUrls } = useLetterApi();
const [files, setFiles] = useState<
{
file: FileWithPath;
Expand Down Expand Up @@ -70,28 +71,51 @@ const CreatePage = () => {
texts: textData,
};

getPrepareUrls({
thumbnailMeta: {
width: '0',
height: '0',
},
letterMeta: {
width: '0',
height: '0',
},
backgroundMeta: {
width: '200',
height: '400',
},
componentMetas: imageData.map((image, idx) => {
return {
width: image.size.width.toString(),
height: image.size.height.toString(),
x: image.position.x.toString(),
y: image.position.y.toString(),
z: idx.toString(),
angle: '0',
};
}),
});
console.log(result);
return result;
};

useEffect(() => {
console.log(prepareUrls);
}, [prepareUrls]);

return (
<AppShell
header={{ height: 60 }}
footer={{ height: 50 }}
navbar={{ width: 300, breakpoint: 'sm', collapsed: { mobile: !opened } }}
padding="md"
>
<AppShell.Header>
<Button onClick={handleSave}>
<IconDeviceFloppy />
</Button>
</AppShell.Header>
<AppShell.Main>
<Container
style={{
backgroundColor: 'blue',
flex: 1,
overflow: 'hidden', // 이미지가 Container를 초과하지 않도록 설정
overflow: 'hidden',
}}
>
{files.map((fileInfo, index) => (
Expand Down Expand Up @@ -121,7 +145,7 @@ const CreatePage = () => {
/>
</Grid.Col>
<Grid.Col h={'100%'} span={3} bg={'blue'}>
<Button
<ActionIcon
onClick={() =>
setTexts((prevTexts) => [
...prevTexts,
Expand All @@ -134,13 +158,15 @@ const CreatePage = () => {
}
>
<IconTextGrammar />
</Button>
</ActionIcon>
</Grid.Col>
<Grid.Col h={'100%'} span={3} bg={'green'}>
<IconSticker />
</Grid.Col>
<Grid.Col h={'100%'} span={3} bg={'yellow'}>
<IconPencil />
<ActionIcon onClick={() => handleSave()}>
<IconDeviceFloppy></IconDeviceFloppy>
</ActionIcon>
</Grid.Col>
</Grid>
</AppShell.Footer>
Expand Down
Loading

0 comments on commit d95d36f

Please sign in to comment.