+
+
+
${name}
+
캠퍼스부터 ${distance}분 내
+
${description}
+
${link}
+
+
+
+
+
+
+ `;
+
+ this.addEvent();
+ }
+}
diff --git a/src/pages/RestaurantListPage/RestaurantFilterContainer.ts b/src/pages/RestaurantListPage/RestaurantFilterContainer.ts
index 493a7b0c4..be366f7d2 100644
--- a/src/pages/RestaurantListPage/RestaurantFilterContainer.ts
+++ b/src/pages/RestaurantListPage/RestaurantFilterContainer.ts
@@ -6,48 +6,36 @@ import { FILTER_CATEGORY_OPTIONS, FILTER_SORT_BY_OPTIONS } from '../../utils/con
type RestaurantFilterContainerState = {
category: Category;
sortBy: SortBy;
- onChangeCategory: (e: Event) => void;
- onChangeSortBy: (e: Event) => void;
+ onChangeDropDown: (e: Event) => void;
};
type RestaurantFilterContainerProps = {
$parent: HTMLElement;
category: Category;
sortBy: SortBy;
- onChangeCategory: (e: Event) => void;
- onChangeSortBy: (e: Event) => void;
+ onChangeDropDown: (e: Event) => void;
};
-class RestaurantFilterContainer implements Component {
+export default class RestaurantFilterContainer
+ implements Component
+{
$target: HTMLElement;
state: RestaurantFilterContainerState;
- constructor({
- $parent,
- category,
- sortBy,
- onChangeCategory,
- onChangeSortBy,
- }: RestaurantFilterContainerProps) {
+ constructor({ $parent, category, sortBy, onChangeDropDown }: RestaurantFilterContainerProps) {
this.$target = document.createElement('section');
this.$target.classList.add('restaurant-filter-container');
this.state = {
category,
sortBy,
- onChangeCategory,
- onChangeSortBy,
+ onChangeDropDown,
};
$parent.append(this.$target);
}
- setState(newState: RestaurantFilterContainerState) {
- this.state = newState;
- this.render();
- }
-
- render() {
+ public render() {
this.$target.innerHTML = ``;
new DropDown({
$parent: this.$target,
@@ -56,7 +44,8 @@ class RestaurantFilterContainer implements Component void;
+ onOpenInfoDrawer: (e: Event) => void;
};
type RestaurantListProps = {
@@ -14,53 +17,84 @@ type RestaurantListProps = {
restaurants: Restaurant[];
category: Category;
sortBy: SortBy;
+ tabBarSelect: TabBarSelect;
+ fetchNewRestaurants: () => void;
+ onOpenInfoDrawer: (e: Event) => void;
};
-class RestaurantList implements Component {
+export default class RestaurantList implements Component {
$target: HTMLElement;
state: RestaurantListState;
- constructor({ $parent, restaurants, category, sortBy }: RestaurantListProps) {
- this.$target = document.createElement('div');
+ constructor({
+ $parent,
+ restaurants,
+ category,
+ sortBy,
+ tabBarSelect,
+ fetchNewRestaurants,
+ onOpenInfoDrawer,
+ }: RestaurantListProps) {
+ this.$target = document.createElement('ul');
+ this.$target.classList.add('restaurant-list');
+
this.state = {
restaurants,
category,
sortBy,
+ tabBarSelect,
+ fetchNewRestaurants,
+ onOpenInfoDrawer,
};
$parent.append(this.$target);
}
- setState(newState: RestaurantListState) {
- this.state = newState;
- this.render();
- }
-
- render() {
+ public getTemplate() {
const fragment = document.createDocumentFragment();
this.categorizeRestaurantByOption().forEach((restaurant) => {
- new RestaurantListItem({ $parent: fragment, restaurant }).render();
+ new RestaurantListItem({
+ $parent: fragment,
+ restaurant,
+ fetchNewRestaurants: this.state.fetchNewRestaurants,
+ onOpenInfoDrawer: this.state.onOpenInfoDrawer,
+ }).render();
});
- this.$target.append(fragment);
+ return fragment;
+ }
+
+ public render() {
+ this.$target.append(this.getTemplate());
+ }
+
+ private filterByTabBarSelect(restaurants: Restaurant[]) {
+ return restaurants.filter((restaurant) =>
+ this.state.tabBarSelect === 'favorite' ? restaurant.isFavorite : restaurant
+ );
}
- categorizeRestaurantByOption() {
- const { category, sortBy } = this.state;
- const filtered = this.state.restaurants.filter(
+ private filterByCategory(restaurants: Restaurant[], category: Category) {
+ return restaurants.filter(
(restaurant) => category === DEFAULT_CATEGORY || restaurant.category === category
);
+ }
+ private filterBySorting(restaurants: Restaurant[], sortBy: SortBy) {
const getPivot = (restaurant: Restaurant) =>
sortBy === 'name' ? restaurant.name : Number(restaurant.distance);
-
- return filtered.sort((a, b) => {
+ return restaurants.sort((a, b) => {
if (getPivot(a) > getPivot(b)) return 1;
if (getPivot(a) < getPivot(b)) return -1;
return 0;
});
}
-}
-export default RestaurantList;
+ private categorizeRestaurantByOption() {
+ return this.filterBySorting(
+ this.filterByCategory(this.filterByTabBarSelect(this.state.restaurants), this.state.category),
+ this.state.sortBy
+ );
+ }
+}
diff --git a/src/pages/RestaurantListPage/index.ts b/src/pages/RestaurantListPage/index.ts
index f552f6308..3f55eaaa9 100644
--- a/src/pages/RestaurantListPage/index.ts
+++ b/src/pages/RestaurantListPage/index.ts
@@ -1,91 +1,134 @@
import type { Component } from '../../interface';
-import type { Category, SortBy, Restaurant } from '../../type';
+import type { Category, SortBy, Restaurant, TabBarSelect } from '../../type';
import RestaurantList from './RestaurantList';
import RestaurantFilterContainer from './RestaurantFilterContainer';
-import { DEFAULT_CATEGORY, REQUEST_RASTAURANT_KEY } from '../../utils/constants';
-import GNB from '../../components/GNB';
+import { DEFAULT_CATEGORY } from '../../utils/constants';
+import TabBar from '../../components/TabBar';
+import RestaurantInfoDrawer from '../RestaurantInfoDrawer';
+import { getRestaurants } from '../../utils/api';
type RestaurantListPageState = {
category: Category;
sortBy: SortBy;
+ tabBarSelect: TabBarSelect;
restaurants: Restaurant[];
- toggleAddRestaurantDrawer: () => void;
+ isOpenDrawer: boolean;
+ selectId: number;
};
type RestaurantListPageProps = {
$parent: HTMLElement;
- toggleAddRestaurantDrawer: () => void;
};
-class RestaurantListPage implements Component {
+export default class RestaurantListPage implements Component {
$target: HTMLElement;
state: RestaurantListPageState;
- constructor({ $parent, toggleAddRestaurantDrawer }: RestaurantListPageProps) {
+ constructor({ $parent }: RestaurantListPageProps) {
this.$target = document.createElement('div');
this.state = {
+ isOpenDrawer: false,
category: DEFAULT_CATEGORY,
sortBy: 'name',
- restaurants: this.getRestaurants(),
- toggleAddRestaurantDrawer,
+ tabBarSelect: 'all',
+ restaurants: getRestaurants(),
+ selectId: 0,
};
$parent.append(this.$target);
}
- setState(newState: RestaurantListPageState) {
+ public setState(newState: RestaurantListPageState) {
this.state = newState;
this.render();
}
- render() {
+ public render() {
this.$target.innerHTML = '';
- new GNB({
+ new TabBar({
$parent: this.$target,
- toggleAddRestaurantDrawer: this.state.toggleAddRestaurantDrawer,
+ tabBarSelect: this.state.tabBarSelect,
+ onClickTabBar: this.onClickTabBar.bind(this),
}).render();
-
new RestaurantFilterContainer({
$parent: this.$target,
category: this.state.category,
sortBy: this.state.sortBy,
- onChangeCategory: this.onChangeCategory.bind(this),
- onChangeSortBy: this.onChangeSortBy.bind(this),
+ onChangeDropDown: this.onChangeDropDown.bind(this),
}).render();
new RestaurantList({
$parent: this.$target,
category: this.state.category,
sortBy: this.state.sortBy,
+ tabBarSelect: this.state.tabBarSelect,
restaurants: this.state.restaurants,
+ fetchNewRestaurants: this.fetchNewRestaurants.bind(this),
+ onOpenInfoDrawer: this.onOpenInfoDrawer.bind(this),
}).render();
+
+ if (this.state.isOpenDrawer) {
+ new RestaurantInfoDrawer({
+ $parent: this.$target,
+ selectId: this.state.selectId,
+ onToggleOpenDrawer: this.onToggleOpenDrawer.bind(this),
+ fetchNewRestaurants: this.fetchNewRestaurants.bind(this),
+ onDeleteRestaurant: this.onDeleteRestaurant.bind(this),
+ }).render();
+ }
}
- getRestaurants() {
- return JSON.parse(localStorage.getItem(REQUEST_RASTAURANT_KEY) ?? '[]');
+ public fetchNewRestaurants() {
+ const restaurants = getRestaurants();
+ this.setState({ ...this.state, restaurants });
}
- onChangeCategory(e: Event) {
+ public onChangeDropDown(e: Event) {
const $select = e.target as HTMLSelectElement;
- const category = $select.value as Category;
+ const key = $select.dataset.key as 'category' | 'sortBy';
+ this.setState({
+ ...this.state,
+ [key]: $select.value,
+ });
+ }
+
+ public onClickTabBar(e: Event) {
+ const target = e.target as HTMLButtonElement;
+ const tabBarSelect = target.dataset.type as TabBarSelect;
this.setState({
...this.state,
- category,
+ tabBarSelect,
});
}
- onChangeSortBy(e: Event) {
- const $select = e.target as HTMLSelectElement;
- const sortBy = $select.value as SortBy;
+ public onOpenInfoDrawer(e: Event) {
+ const currentTarget = e.currentTarget as HTMLElement;
+ const restaurantId = Number(currentTarget.dataset.restaurantId ?? '0');
this.setState({
...this.state,
- sortBy,
+ isOpenDrawer: true,
+ selectId: restaurantId,
});
}
-}
-export default RestaurantListPage;
+ public onToggleOpenDrawer() {
+ this.setState({
+ ...this.state,
+ isOpenDrawer: !this.state.isOpenDrawer,
+ });
+ }
+
+ public onDeleteRestaurant() {
+ const restaurants = getRestaurants();
+
+ this.setState({
+ ...this.state,
+ isOpenDrawer: !this.state.isOpenDrawer,
+ restaurants,
+ });
+ }
+}
diff --git a/src/type.ts b/src/type.ts
index 4eef4ca85..d57931109 100644
--- a/src/type.ts
+++ b/src/type.ts
@@ -1,5 +1,6 @@
export type Category = '전체' | '한식' | '중식' | '일식' | '양식' | '아시안' | '기타';
export type SortBy = 'name' | 'distance';
+export type TabBarSelect = 'all' | 'favorite';
export type Distance = 5 | 10 | 15 | 20 | 30;
export type SelectOption = {
@@ -8,9 +9,11 @@ export type SelectOption = {
};
export type Restaurant = {
+ id: number;
name: string;
category: Category;
distance: Distance;
description?: string;
link?: string;
+ isFavorite: boolean;
};
diff --git a/src/utils/api.js b/src/utils/api.js
new file mode 100644
index 000000000..25651b02f
--- /dev/null
+++ b/src/utils/api.js
@@ -0,0 +1,49 @@
+import { REQUEST_RASTAURANT_KEY, REQUEST_RESTAURANT_ID_KEY } from './constants';
+
+export const fetchFavoriteId = (id) => {
+ const restaurants = JSON.parse(localStorage.getItem(REQUEST_RASTAURANT_KEY) ?? '[]');
+
+ const newRestaurants = restaurants.map((restaurant) => {
+ if (restaurant.id === id) {
+ restaurant.isFavorite = !restaurant.isFavorite;
+ }
+ return restaurant;
+ });
+
+ localStorage.setItem(REQUEST_RASTAURANT_KEY, JSON.stringify(newRestaurants));
+};
+
+export const deleteById = (id) => {
+ const restaurants = JSON.parse(localStorage.getItem(REQUEST_RASTAURANT_KEY) ?? '[]');
+
+ const newRestaurants = restaurants.filter((restaurant) => restaurant.id !== id);
+
+ localStorage.setItem(REQUEST_RASTAURANT_KEY, JSON.stringify(newRestaurants));
+};
+
+export const getRestaurantById = (id) => {
+ const restaurants = JSON.parse(localStorage.getItem(REQUEST_RASTAURANT_KEY) ?? '[]');
+
+ return restaurants.filter((restaurant) => restaurant.id === id)[0];
+};
+
+export const getRestaurants = () => {
+ return JSON.parse(localStorage.getItem(REQUEST_RASTAURANT_KEY) ?? '[]');
+};
+
+export const getNewId = () => {
+ if (!localStorage.getItem(REQUEST_RESTAURANT_ID_KEY)) {
+ localStorage.setItem(REQUEST_RESTAURANT_ID_KEY, '0');
+ }
+
+ const currentId = Number(localStorage.getItem(REQUEST_RESTAURANT_ID_KEY));
+ localStorage.setItem(REQUEST_RESTAURANT_ID_KEY, `${currentId + 1}`);
+
+ return currentId + 1;
+};
+
+export const postRestaurant = (restaurant) => {
+ const restaurants = JSON.parse(localStorage.getItem(REQUEST_RASTAURANT_KEY) ?? '[]');
+ restaurants.push(restaurant);
+ localStorage.setItem(REQUEST_RASTAURANT_KEY, JSON.stringify(restaurants));
+};
diff --git a/src/utils/constants.ts b/src/utils/constants.ts
index 52a533a09..89d78b4b6 100644
--- a/src/utils/constants.ts
+++ b/src/utils/constants.ts
@@ -75,3 +75,6 @@ export const DEFAULT_CATEGORY: Category = CATEGORY_NAME.all;
export const DEFAULT_DISTANCE: Distance = 5;
export const OPTION_START_INDEX = 1;
export const REQUEST_RASTAURANT_KEY = 'restaurants';
+export const STAR_FILL_IMAGE_URL = './favorite-icon-filled.png';
+export const STAR_LINE_IMAGE_URL = './favorite-icon-lined.png';
+export const REQUEST_RESTAURANT_ID_KEY = 'restaurantId';
diff --git a/src/utils/util.js b/src/utils/util.js
new file mode 100644
index 000000000..78fb350e5
--- /dev/null
+++ b/src/utils/util.js
@@ -0,0 +1,5 @@
+import { STAR_FILL_IMAGE_URL, STAR_LINE_IMAGE_URL } from './constants';
+
+export const getImgSrcByFavorite = (isFavorite) => {
+ return isFavorite ? STAR_FILL_IMAGE_URL : STAR_LINE_IMAGE_URL;
+};