Skip to content

Commit

Permalink
feat: 메뉴 조회/등록 API 연결 pagers-org#20
Browse files Browse the repository at this point in the history
xianeml committed Feb 6, 2022

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent d2009be commit 3259f42
Showing 8 changed files with 65 additions and 57 deletions.
12 changes: 4 additions & 8 deletions packages/xianeml/src/components/MenuItem.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
import { Tstate } from '../types/store.js';
import { getCategoryMenus } from '../utils/helper.js';

const MenuItem = (state: Tstate) => {
const { menus, currentTab } = state;
const categoryMenus = getCategoryMenus(menus, currentTab);

return categoryMenus
const MenuItem = ({ menus }: Tstate) => {
return menus

This comment has been minimized.

Copy link
@SINHOLEE

SINHOLEE Feb 9, 2022

오우 그냥 아예 순수 ui가 되어버렸네요.

.map(
menu =>
(menu) =>
`<li id="${menu.id}" class="menu-list-item d-flex items-center py-2">
<span id="espresso-menu-name" class="w-100 pl-2 menu-name
${menu.inStock || 'sold-out'}">${menu.menuName}</span>
@@ -33,7 +29,7 @@ const MenuItem = (state: Tstate) => {
>
품절
</button>
</li>`,
</li>`
)
.join('');
};
8 changes: 5 additions & 3 deletions packages/xianeml/src/js/events.ts
Original file line number Diff line number Diff line change
@@ -15,21 +15,23 @@ export const handleNavigation = (store: Tstore) => (e: Event) => {
store.dispatch(setCurrentTab(categoryId));
};

export const handleSubmitMenuForm = (store: Tstore) => (e: Event) => {
export const handleSubmitMenuForm = (store: Tstore) => async (e: Event) => {
e.preventDefault();

const $menuInput = $('#espresso-menu-name') as HTMLInputElement;
if (!$menuInput.value) return;

const { menus, currentTab } = store.getState();
const state = await store.getState();
const { menus, currentTab } = state;

if (menus && menus.length === 20) {
return alert('메뉴는 20개까지 추가 가능합니다.');
}
store.dispatch(createMenuItem(currentTab.id, $menuInput.value.trim()));
$menuInput.value = '';
};

export const handleMenuList = (e: Event, store: Tstore) => {
export const handleMenuList = (store: Tstore) => (e: Event) => {
const target = e.target as HTMLElement;
const targetMenuId = (target.parentElement as HTMLElement).id;

6 changes: 3 additions & 3 deletions packages/xianeml/src/js/renderViews.ts
Original file line number Diff line number Diff line change
@@ -3,13 +3,13 @@ import layoutView from '../components/layout/index.js';
import { Tstore } from '../types/store.js';
import { handleNavigation, handleSubmitMenuForm, handleMenuList } from './events.js';

export const renderViews = (store: Tstore) => {
const state = store.getState();
export const renderViews = async (store: Tstore) => {
const state = await store.getState();
$('#app').innerHTML = layoutView(state);

const $menuForm = $('#espresso-menu-form');
const $menuList = $('#espresso-menu-list');
$('nav').addEventListener('click', handleNavigation(store));
$menuForm.addEventListener('submit', handleSubmitMenuForm(store));
$menuList.addEventListener('click', (e: Event) => handleMenuList(e, store));
$menuList.addEventListener('click', handleMenuList(store));
};
17 changes: 7 additions & 10 deletions packages/xianeml/src/store/helper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Treducer, TmenuAction, Tlistener, Tstate, Tstore } from '../types/store.js';
import { getMenus } from '../api/menu.js';

export const createStore = (reducer: Treducer): Tstore => {
const initialState: Tstate = {
@@ -15,19 +16,15 @@ export const createStore = (reducer: Treducer): Tstore => {

const listeners: Tlistener[] = [];

const getState = () => {
return JSON.parse(localStorage.getItem('state') || '{}') as Tstate;
const getState = async () => {
const menus = await getMenus(initialState.currentTab.id);
const state = { ...initialState, menus };

return state;
};

const dispatch = (action: TmenuAction) => {
const storageState = JSON.parse(localStorage.getItem('state') || '{}');

if (!storageState) {
localStorage.setItem('state', JSON.stringify(initialState));
} else {
const newState = reducer(storageState, action);
localStorage.setItem('state', JSON.stringify(newState));
}
reducer(initialState, action);
publish();
};

19 changes: 7 additions & 12 deletions packages/xianeml/src/store/menu.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Tcategory, TmenuAction } from '../types/store.js';
import { Tstate } from '../types/store.js';
import { createMenu } from '../api/menu.js';

/* 액션 타입 정의 */
const CREATE_MENU = 'CREATE_MENU' as const;
@@ -47,24 +48,18 @@ export const setCurrentTab = (categoryId: string) => ({
});

// 리듀서는 새로운 상태를 생성하는 함수.
export default function reducer(state: Tstate, action: TmenuAction) {
export default async function reducer(state: Tstate, action: TmenuAction) {

This comment has been minimized.

Copy link
@SINHOLEE

SINHOLEE Feb 9, 2022

async 를 리듀서에 붙이는게 나쁜건 아닙니다만 리덕스에서 리듀서는 순수 함수라서 비동기 통신을 넘기면 안대게끔 설계되어있어요. 그부분 염두하시고 구현하시면 좋을거같아요 ㅎㅎ 그래서 리덕스에서 thunk나 saga같은 비동기 미들웨어가 나타난거에요.

const { type, payload } = action;
const { categoryId = '', menuId = '', menuName = '' } = payload;
const { menus, categories } = state;

switch (type) {
case CREATE_MENU: {
const categoryMenus = menus.filter(menu => {
return menu.categoryId === categoryId;
});
// TODO: 중복 가능성 의심, UUID 적용
const id = `${categoryId}-menu-id-${categoryMenus.length}`;
const newMenu = { id, categoryId, menuName, inStock: true };
const newMenuList = [...menus, newMenu];
return { ...state, menus: newMenuList };
await createMenu({ category: categoryId, name: menuName });
break;
}
case EDIT_MENU: {
const newMenuList = menus.map(menu => {
const newMenuList = menus.map((menu) => {
if (menu.id === menuId) {
menu.menuName = menuName;
}
@@ -73,11 +68,11 @@ export default function reducer(state: Tstate, action: TmenuAction) {
return { ...state, menus: newMenuList };
}
case REMOVE_MENU: {
const newMenuList = menus.filter(menu => menu.id !== menuId);
const newMenuList = menus.filter((menu) => menu.id !== menuId);
return { ...state, menus: newMenuList };
}
case SOLD_OUT_MENU: {
const newMenuList = menus.map(menu => {
const newMenuList = menus.map((menu) => {
if (menu.id === menuId) {
menu.inStock = false;
}
14 changes: 13 additions & 1 deletion packages/xianeml/src/types/api.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
export type Tparams = {
category: string;
category?: string;
menuId?: string;
name?: string;
};

export type TmenuResponse = {
menuId: string;
name: string;
isSoldOut: boolean;
};

export type TrequestConfig = {
url: string;
method: string;
data?: { name?: string };
};
5 changes: 2 additions & 3 deletions packages/xianeml/src/types/store.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export type Treducer = (state: Tstate, action: TmenuAction) => Tstate;
export type Treducer = (state: Tstate, action: TmenuAction) => Promise<Tstate>;

export type Tstore = {
getState: () => Tstate;
getState: () => Promise<Tstate>;
dispatch: (action: TmenuAction) => void;
subscribe: (callback: Tlistener) => void;
};
@@ -23,7 +23,6 @@ export type TmenuAction = {

export type Tmenu = {
id: string;
categoryId: string;
menuName: string;
inStock: boolean;
};
41 changes: 24 additions & 17 deletions packages/xianeml/src/utils/request.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
import { SERVER_URL } from '../utils/constants/env.js';
import { TmenuResponse, TrequestConfig } from '../types/api.js';
import { Tmenu } from '../types/store.js';

type Tconfig = {
url: string;
method: string;
data?: { name?: string };
};

export default async (config: Tconfig) => {
export default async (config: TrequestConfig) => {
const { url, method, data } = config;

const requestUrl = SERVER_URL + url;
const headers = {
'Content-Type': 'application/json',
};

try {
const response = await fetch(requestUrl, {
method,
headers,
body: JSON.stringify(data),
});

if (response.status !== 200) throw Error('요청 에러');
if (method !== 'GET') return;

const response = await fetch(requestUrl, {
method,
body: JSON.stringify(data),
});
console.log('요청정보? >>>> ', response);
const resData = await response.json();
const resData = await response.json();

if (response.status === 200) {
return resData;
} else {
throw new Error('서버요청 에러!!');
return resData.map((data: TmenuResponse) => ({
id: data.menuId,
menuName: data.name,
inStock: !data.isSoldOut,
})) as Tmenu[];
} catch (e) {
console.error(e);
}
};

0 comments on commit 3259f42

Please sign in to comment.