Skip to content

Commit

Permalink
feat: 메뉴 조회/등록 API 연결 pagers-org#20
Browse files Browse the repository at this point in the history
  • Loading branch information
xianeml committed Feb 6, 2022
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.