diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 000000000..3a2021c21
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,28 @@
+{
+ "env": {
+ "browser": true,
+ "es2021": true,
+ "node": true
+ },
+ "extends": [
+ "eslint:recommended",
+ "airbnb-base",
+ "prettier"
+ ],
+ "parserOptions": {
+ "ecmaVersion": 2015,
+ "sourceType": "module"
+ },
+ "rules": {
+ "no-console": "off",
+ "no-plusplus": "off",
+ "class-methods-use-this": "off",
+ "radix": "off",
+ "no-restricted-properties": "off",
+ "max-depth" : ["error", 2],
+ "no-return-assign" : "off",
+ "no-continue" : "off",
+ "no-param-reassign" : "off",
+ "import/extensions" : "off"
+ }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..ae7194bd3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+.ieda
+.DS_Store
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 000000000..13a6d0595
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,8 @@
+{
+ "singleQuote": true,
+ "printWidth": 100,
+ "tabWidth": 2,
+ "useTabs": false,
+ "semi": true,
+ "arrowParens": "always"
+}
diff --git a/README.md b/README.md
index e97a1d649..a7a818659 100644
--- a/README.md
+++ b/README.md
@@ -1,114 +1,38 @@
# π μ§νμ² λ
Έμ λ λ―Έμ
+μ§νμ² μμ μΆκ° λ° μμ νκ³ , μμ±λ λ
Έμ μ μμ μΆκ°νκ³ μμ ν μ μλ νλ‘κ·Έλ¨μ΄λ€.
-## π κΈ°λ₯ μꡬμ¬ν
+## π κΈ°λ₯ ꡬν λͺ©λ‘
+1. μ¬μ©μλ λ²νΌ 4κ°(μ κ΄λ¦¬, λ
Έμ κ΄λ¦¬, κ΅¬κ° κ΄λ¦¬, μ§νμ² λ
Έμ λ μΆλ ₯)λ₯Ό μ νν μ μλ€.
### μ§νμ² μ κ΄λ ¨ κΈ°λ₯
-- μ§νμ² μμ λ±λ‘νκ³ μμ ν μ μλ€. (λ¨, λ
Έμ μ λ±λ‘λ μμ μμ ν μ μλ€)
-- μ€λ³΅λ μ§νμ² μ μ΄λ¦μ΄ λ±λ‘λ μ μλ€.
-- μ§νμ² μμ 2κΈμ μ΄μμ΄μ΄μΌ νλ€.
-- μ§νμ² μμ λͺ©λ‘μ μ‘°νν μ μλ€.
+2. μ¬μ©μλ μμ λ±λ‘ν μ μλ€.
+ - [μμΈμ²λ¦¬] 2κΈμ μ΄μμ μ΄λ¦μΈμ§ κ²μ¦ν΄μΌ νλ€.
+ - [μμΈμ²λ¦¬] μ€λ³΅λ μ΄λ¦μ΄ μλμ§ κ²μ¦ν΄μΌ νλ€.
+3. μ¬μ©μλ μμ μμ ν μ μλ€.
+ - [μμΈμ²λ¦¬] λ
Έμ μ μλμ§ κ²μ¦νκ³ , λ
Έμ μ μλ€λ©΄ μμ ν μ μλ€.
+4. μ¬μ©μ μ§νμ² μμ λͺ©λ‘μ μ‘°νν μ μλ€.
### μ§νμ² λ
Έμ κ΄λ ¨ κΈ°λ₯
-- μ§νμ² λ
Έμ μ λ±λ‘νκ³ μμ ν μ μλ€.
-- μ€λ³΅λ μ§νμ² λ
Έμ μ΄λ¦μ΄ λ±λ‘λ μ μλ€.
-- λ
Έμ λ±λ‘ μ μν μ’
μ μκ³Ό νν μ’
μ μμ μ
λ ₯λ°λλ€.
-- μ§νμ² λ
Έμ μ λͺ©λ‘μ μ‘°νν μ μλ€.
-
-### μ§νμ² κ΅¬κ° μΆκ° κΈ°λ₯
-- μ§νμ² λ
Έμ μ ꡬκ°μ μΆκ°νλ κΈ°λ₯μ λ
Έμ μ μμ μΆκ°νλ κΈ°λ₯μ΄λΌκ³ λ ν μ μλ€.
- - μκ³Ό μμ¬μ΄λ₯Ό ꡬκ°μ΄λΌ νκ³ μ΄ κ΅¬κ°λ€μ λͺ¨μμ΄ λ
Έμ μ΄λ€.
-- νλμ μμ μ¬λ¬κ°μ λ
Έμ μ μΆκ°λ μ μλ€.
-- μκ³Ό μ μ¬μ΄μ μλ‘μ΄ μμ΄ μΆκ° λ μ μλ€.
-- λ
Έμ μμ κ°λκΈΈμ μκΈΈ μ μλ€.
-
-
-
-### μ§νμ² κ΅¬κ° μμ κΈ°λ₯
-- λ
Έμ μ λ±λ‘λ μμ μ κ±°ν μ μλ€.
-- μ’
μ μ μ κ±°ν κ²½μ° λ€μ μμ΄ μ’
μ μ΄ λλ€.
-- λ
Έμ μ ν¬ν¨λ μμ΄ λκ° μ΄νμΌ λλ μμ μ κ±°ν μ μλ€.
-
-
+5. μ¬μ©μλ μ§νμ² λ
Έμ μ λ±λ‘ν μ μλ€.
+ - [쑰건] λ
Έμ λ±λ‘ μ μν μ’
μ μκ³Ό νν μ’
μ μμ μ
λ ₯λ°λλ€.
+ - [μμΈμ²λ¦¬] μ€λ³΅λ λ
Έμ μ΄λ¦μ΄ μλμ§ κ²μ¦ν΄μΌ νλ€.
+ - [μμΈμ²λ¦¬] ~μ ννμ μ΄λ¦μΈμ§ κ²μ¦ν΄μΌ νλ€.
+ - [μμΈμ²λ¦¬] μμ μ’
μ κ³Ό νν μ’
μ μ΄ κ°μ λ
Έμ μ λ±λ‘ν μ μλ€.
+ - [μμΈμ²λ¦¬] μν μ’
μ κ³Ό νν μ’
μ μ΄ λμΌν κΈ°μ‘΄μ λ
Έμ μ΄ μλμ§ κ²μ¦ν΄μΌ νλ€.
+ - [μμΈμ²λ¦¬] λ
Έμ μ΄λ¦μ 무쑰건 μ
λ ₯ν΄μΌ νλ€.
+6. μ¬μ©μλ μ§νμ² λ
Έμ μ μμ ν μ μλ€.
+7. μ¬μ©μλ μ§νμ² λ
Έμ μ λͺ©λ‘μ μ‘°νν μ μλ€.
+
+### μ§νμ² κ΅¬κ° κ΄λ ¨ κΈ°λ₯
+8. μ¬μ©μλ λ
Έμ μ μμ μΆκ°ν μ μλ€.
+ - [μμΈμ²λ¦¬] μ€λ³΅λ μμ΄ μλμ§ κ²μ¦ν΄μΌ νλ€.
+ - [쑰건] νλμ μμ μ¬λ¬κ°μ λ
Έμ μ μΆκ°λ μ μλ€.
+ - [쑰건] μκ³Ό μ μ¬μ΄μ μλ‘μ΄ μμ΄ μΆκ° λ μ μλ€.
+ - [쑰건] λ
Έμ μμ κ°λκΈΈμ μκΈΈ μ μλ€.
+9. μ¬μ©μλ λ
Έμ μ μμ μμ ν μ μλ€.
+ - [μμΈμ²λ¦¬] λ
Έμ μ μμ΄ 2κ° μ΄νμΈμ§ κ²μ¦νκ³ , 2κ° μ΄νλ©΄ μμ ν μ μλ€.
+ - [쑰건] μ’
μ μ μ κ±°ν κ²½μ° λ€μ μμ΄ μ’
μ μ΄ λλ€.
+10. μ¬μ©μλ λ
Έμ λ³λ‘ μμ λͺ©λ‘μ λ³Ό μ μλ€.
### μ§νμ² λ
Έμ μ λ±λ‘λ μ μ‘°ν κΈ°λ₯
-- λ
Έμ μ μν μ’
μ λΆν° νν μ’
μ κΉμ§ μ°κ²°λ μμλλ‘ μ λͺ©λ‘μ μ‘°νν μ μλ€.
-
-
-
-## π» νλ‘κ·Έλ¨ μ€ν κ²°κ³Ό
-
-### μκ΄λ¦¬
-
-
-### λ
Έμ κ΄λ¦¬
-
-
-### ꡬκ°κ΄λ¦¬
-
-
-### λ
Έμ λ μΆλ ₯
-
-
-
-## β
νλ‘κ·Έλλ° μꡬμ¬ν
-
-### λ©λ΄ λ²νΌ
-- μ κ΄λ¦¬ button νκ·Έλ `#station-manager-button` idκ°μ κ°μ§λ€.
-- λ
Έμ κ΄λ¦¬ button νκ·Έλ `#line-manager-button` idκ°μ κ°μ§λ€.
-- κ΅¬κ° κ΄λ¦¬ button νκ·Έλ `#section-manager-button` idκ°μ κ°μ§λ€.
-- μ§νμ² λ
Έμ λ μΆλ ₯ κ΄λ¦¬ button νκ·Έλ `#map-print-manager-button` idκ°μ κ°μ§λ€.
-
-### μ§νμ² μ κ΄λ ¨ κΈ°λ₯
-- μ§νμ² μμ μ
λ ₯νλ input νκ·Έλ `#station-name-input` idκ°μ κ°μ§λ€.
-- μ§νμ² μμ μΆκ°νλ button νκ·Έλ `#station-add-button` idκ°μ κ°μ§λ€.
-- μ§νμ² μμ μμ νλ button νκ·Έλ `.station-delete-button` classκ°μ κ°μ§λ€.
-
-### μ§νμ² λ
Έμ κ΄λ ¨ κΈ°λ₯
-- μ§νμ² λ
Έμ μ μ΄λ¦μ μ
λ ₯νλ input νκ·Έλ `#line-name-input` idκ°μ κ°μ§λ€.
-- μ§νμ² λ
Έμ μ μν μ’
μ μ μ ννλ select νκ·Έλ `#line-start-station-selector` idκ°μ κ°μ§λ€.
-- μ§νμ² λ
Έμ μ νν μ’
μ μ μ ννλ select νκ·Έλ `#line-end-station-selector` idκ°μ κ°μ§λ€.
-- μ§νμ² λ
Έμ μ μΆκ°νλ button νκ·Έλ `#line-add-button` idκ°μ κ°μ§λ€.
-- μ§νμ² λ
Έμ μ μμ νλ button νκ·Έλ `.line-delete-button` classκ°μ κ°μ§λ€.
-
-### μ§νμ² κ΅¬κ° μΆκ° κΈ°λ₯
-- μ§νμ² λ
Έμ μ μ ννλ button νκ·Έλ `.section-line-menu-button` classκ°μ κ°μ§λ€.
-- μ§νμ² κ΅¬κ°μ μ€μ ν μ select νκ·Έλ `#section-station-selector` idκ°μ κ°μ§λ€.
-- μ§νμ² κ΅¬κ°μ μμλ₯Ό μ
λ ₯νλ input νκ·Έλ `#section-order-input` idκ°μ κ°μ§λ€.
-- μ§νμ² κ΅¬κ°μ λ±λ‘νλ button νκ·Έλ `#section-add-button` idκ°μ κ°μ§λ€.
-- μ§νμ² κ΅¬κ°μ μ κ±°νλ button νκ·Έλ `.section-delete-button` classκ°μ κ°μ§λ€.
-
-### μ§νμ² λ
Έμ λ μΆλ ₯ κΈ°λ₯
-- μ§νμ² λ
Έμ λ μΆλ ₯ λ²νΌμ λλ₯΄λ©΄ `
` νκ·Έλ₯Ό λ§λ€κ³ ν΄λΉ νκ·Έ λ΄λΆμ λ
Έμ λλ₯Ό μΆλ ₯νλ€.
-
-### κΈ°μ‘΄ μꡬμ¬ν
-
-- μ¬μ©μκ° μλͺ»λ μ
λ ₯ κ°μ μμ±ν κ²½μ° `alert`μ μ΄μ©ν΄ λ©μμ§λ₯Ό 보μ¬μ£Όκ³ , μ¬μ
λ ₯ν μ μκ² νλ€.
-- μΈλΆ λΌμ΄λΈλ¬λ¦¬(jQuery, Lodash λ±)λ₯Ό μ¬μ©νμ§ μκ³ , μμ Vanilla JSλ‘λ§ κ΅¬ννλ€.
-- **μλ°μ€ν¬λ¦½νΈ μ½λ 컨벀μ
μ μ§ν€λ©΄μ νλ‘κ·Έλλ°** νλ€
- - [https://google.github.io/styleguide/jsguide.html](https://google.github.io/styleguide/jsguide.html)
- - [https://ui.toast.com/fe-guide/ko_CODING-CONVENSION/](https://ui.toast.com/fe-guide/ko_CODING-CONVENTION)
-- **indent(μΈλ΄νΈ, λ€μ¬μ°κΈ°) depthλ₯Ό 3μ΄ λμ§ μλλ‘ κ΅¬ννλ€. 2κΉμ§λ§ νμ©**νλ€.
- - μλ₯Ό λ€μ΄ whileλ¬Έ μμ ifλ¬Έμ΄ μμΌλ©΄ λ€μ¬μ°κΈ°λ 2μ΄λ€.
- - ννΈ: indent(μΈλ΄νΈ, λ€μ¬μ°κΈ°) depthλ₯Ό μ€μ΄λ μ’μ λ°©λ²μ ν¨μ(λλ λ©μλ)λ₯Ό λΆλ¦¬νλ©΄ λλ€.
-- **ν¨μ(λλ λ©μλ)μ κΈΈμ΄κ° 15λΌμΈμ λμ΄κ°μ§ μλλ‘ κ΅¬ννλ€.**
- - ν¨μ(λλ λ©μλ)κ° ν κ°μ§ μΌλ§ μ νλλ‘ κ΅¬ννλ€.
-- λ³μ μ μΈμ `var` λ₯Ό μ¬μ©νμ§ μλλ€. `const` μ `let` μ μ¬μ©νλ€.
- - [const](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/const)
- - [let](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/let)
-- `import` λ¬Έμ μ΄μ©ν΄ μ€ν¬λ¦½νΈλ₯Ό λͺ¨λννκ³ λΆλ¬μ¬ μ μκ² λ§λ λ€.
- - [https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/import](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Statements/import)
-- `template literal`μ μ΄μ©ν΄ λ°μ΄ν°μ html stringμ κ°λ
μ± μ’κ² νννλ€.
- - [https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Template_literals](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Template_literals)
-
-### μΆκ°λ μꡬμ¬ν
-- [data](https://developer.mozilla.org/ko/docs/Learn/HTML/Howto/%EB%8D%B0%EC%9D%B4%ED%84%B0_%EC%86%8D%EC%84%B1_%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0)μμ±μ νμ©νμ¬ html νκ·Έμ μ, λ
Έμ , ꡬκ°μ μ μΌν λ°μ΄ν° κ°λ€μ κ΄λ¦¬νλ€.
-- [localStorage](https://developer.mozilla.org/ko/docs/Web/API/Window/localStorage)λ₯Ό μ΄μ©νμ¬, μλ‘κ³ μΉ¨νλλΌλ κ°μ₯ μ΅κ·Όμ μμ
ν μ 보λ€μ λΆλ¬μ¬ μ μλλ‘ νλ€.
-
-
-
-## π λ―Έμ
μ μ₯μ λ° μ§ν μꡬμ¬ν
-
-- λ―Έμ
μ [https://github.com/woowacourse/javascript-subway-map-precours](https://github.com/woowacourse/javascript-subway-map-precourse) μ μ₯μλ₯Ό fork/cloneν΄ μμνλ€.
-- **κΈ°λ₯μ ꡬννκΈ° μ μ javascript-subway-precourse/docs/README.md νμΌμ ꡬνν κΈ°λ₯ λͺ©λ‘**μ μ λ¦¬ν΄ μΆκ°νλ€.
-- **gitμ commit λ¨μλ μ λ¨κ³μμ README.md νμΌμ μ 리ν κΈ°λ₯ λͺ©λ‘ λ¨μλ‘ μΆκ°**νλ€.
-- [ν리μ½μ€ κ³Όμ μ μΆ](https://github.com/woowacourse/woowacourse-docs/tree/master/precourse) λ¬Έμ μ μ°¨λ₯Ό λ°λΌ λ―Έμ
μ μ μΆνλ€.
+11. μ¬μ©μ λ
Έμ μ μν μ’
μ λΆν° νν μ’
μ κΉμ§ μ°κ²°λ μμλλ‘ μ 체 λ
Έμ κ³Ό μ λͺ©λ‘μ μ‘°νν μ μλ€.
diff --git a/index.html b/index.html
index fc99deac2..b0d5daab9 100644
--- a/index.html
+++ b/index.html
@@ -3,10 +3,52 @@
μ§νμ² λ
Έμ λ κ΄λ¦¬
+
π μ§νμ² λ
Έμ λ κ΄λ¦¬
+
+
diff --git a/src/components/template/lineManagerTemplate.js b/src/components/template/lineManagerTemplate.js
new file mode 100644
index 000000000..815ac02d3
--- /dev/null
+++ b/src/components/template/lineManagerTemplate.js
@@ -0,0 +1,57 @@
+import stationStorage from '../../storage/stationStorage.js';
+
+const LINE_MANAGER_PAGE_TEMPLATE = `
+
+
+ π μ§νμ² λ
Έμ λͺ©λ‘
+
+
+ | λ
Έμ μ΄λ¦ |
+ μν μ’
μ μ |
+ νν μ’
μ μ |
+ μ€μ |
+
+
+
+
+ `;
+
+const ALL_STATION_OPTION_LIST = (stations) => {
+ return stations
+ .map(
+ (station) =>
+ ``
+ )
+ .join('');
+};
+
+const LINE_TABLE_TEMPLATE = (line) => {
+ const lastStationId = line.stationIds[line.stationIds.length - 1];
+ const firstStationId = line.stationIds[0];
+
+ return `
+ | ${line.name} |
+ ${
+ stationStorage().getStationById(firstStationId).name
+ } |
+ ${
+ stationStorage().getStationById(lastStationId).name
+ } |
+ |
+
`;
+};
+
+export { LINE_MANAGER_PAGE_TEMPLATE, ALL_STATION_OPTION_LIST, LINE_TABLE_TEMPLATE };
diff --git a/src/components/template/mapPrintManagerTemplate.js b/src/components/template/mapPrintManagerTemplate.js
new file mode 100644
index 000000000..b96ac3494
--- /dev/null
+++ b/src/components/template/mapPrintManagerTemplate.js
@@ -0,0 +1,24 @@
+import stationStorage from '../../storage/stationStorage.js';
+import lineStorage from '../../storage/lineStorage.js';
+
+const STATION_LIST_TEMPLATE = (stationId) => {
+ const station = stationStorage().getStationById(stationId);
+ return `${station.name}`;
+};
+
+const MAP_MANAGER_PAGE_TEMPLATE = () => {
+ const lines = lineStorage().getLines();
+ return `
+ ${lines
+ .map(
+ (line) =>
+ `
${line.name}
+ ${line.stationIds
+ .map(STATION_LIST_TEMPLATE)
+ .join('')}`
+ )
+ .join('')}
+
`;
+};
+
+export default MAP_MANAGER_PAGE_TEMPLATE;
diff --git a/src/components/template/sectionManagerTemplate.js b/src/components/template/sectionManagerTemplate.js
new file mode 100644
index 000000000..7a5123daf
--- /dev/null
+++ b/src/components/template/sectionManagerTemplate.js
@@ -0,0 +1,52 @@
+import stationStorage from '../../storage/stationStorage.js';
+
+const SECTION_MANAGER_PAGE_MENU_TEMPLATE = `
+
+ `;
+
+const SECTION_LINE_MENU_BTN = (line) => {
+ return ``;
+};
+
+const SECTION_MANAGER_PAGE_SELECTOR_TEMPLATE = `
+ κ΅¬κ° λ±λ‘
+
+
+ `;
+
+const SECTION_MANAGER_PAGE_TABLE_TEMPLATE = `
+
+
+ | μμ |
+ μ΄λ¦ |
+ μ€μ |
+
+
+
+
`;
+
+const SECTION_TABLE_TEMPLATE = ({ id, stationIds }) => {
+ const stations = stationIds.map(stationStorage().getStationById);
+
+ return stations
+ .map(
+ (station, index) =>
+ `
+ | ${index} |
+ ${station.name} |
+ |
+
`
+ )
+ .join('');
+};
+
+export {
+ SECTION_MANAGER_PAGE_MENU_TEMPLATE,
+ SECTION_MANAGER_PAGE_SELECTOR_TEMPLATE,
+ SECTION_MANAGER_PAGE_TABLE_TEMPLATE,
+ SECTION_LINE_MENU_BTN,
+ SECTION_TABLE_TEMPLATE,
+};
diff --git a/src/components/template/stationManagerTemplate.js b/src/components/template/stationManagerTemplate.js
new file mode 100644
index 000000000..209f21d6b
--- /dev/null
+++ b/src/components/template/stationManagerTemplate.js
@@ -0,0 +1,24 @@
+const STATION_MANAGER_PAGE_TEMPLATE = `μ μ΄λ¦
+
+
+
+
+ π μ§νμ² μ λͺ©λ‘
+
+
+ | μ μ΄λ¦ |
+ μ€μ |
+
+
+
+
+
`;
+
+const STATION_TABLE_TEMPLATE = (station) => {
+ return `
+ | ${station.name} |
+ |
+
`;
+};
+
+export { STATION_MANAGER_PAGE_TEMPLATE, STATION_TABLE_TEMPLATE };
diff --git a/src/components/views/SectionManagerPages/sectionManagerPage.js b/src/components/views/SectionManagerPages/sectionManagerPage.js
new file mode 100644
index 000000000..d5f0cb7a1
--- /dev/null
+++ b/src/components/views/SectionManagerPages/sectionManagerPage.js
@@ -0,0 +1,26 @@
+import {
+ SECTION_MANAGER_PAGE_MENU_TEMPLATE,
+ SECTION_LINE_MENU_BTN,
+} from '../../template/sectionManagerTemplate.js';
+import lineStorage from '../../../storage/lineStorage.js';
+import singleSectionManager from './singleSectionManager.js';
+
+export default function sectionManagerPage($element) {
+ const lines = lineStorage().getLines();
+
+ $element.innerHTML = SECTION_MANAGER_PAGE_MENU_TEMPLATE;
+ const $listMenuBtn = $element.querySelector('.line-buttons');
+ $listMenuBtn.innerHTML = lines.map(SECTION_LINE_MENU_BTN).join('');
+ const $sectionSelectorContainer = $element.querySelector('#section-selector-container');
+ const $sectionTable = $element.querySelector('#section-table');
+
+ const onMenuFilterHandler = (e) => {
+ if (!e.target.classList.contains('section-line-menu-button')) {
+ return;
+ }
+ const currentLine = lineStorage().getOneLine(e.target.dataset.lineid);
+ singleSectionManager($sectionSelectorContainer, $sectionTable, currentLine);
+ };
+
+ $listMenuBtn.addEventListener('click', onMenuFilterHandler);
+}
diff --git a/src/components/views/SectionManagerPages/singleSectionManager.js b/src/components/views/SectionManagerPages/singleSectionManager.js
new file mode 100644
index 000000000..c6af11abe
--- /dev/null
+++ b/src/components/views/SectionManagerPages/singleSectionManager.js
@@ -0,0 +1,115 @@
+import {
+ sectionIndexValidator,
+ sectionStationNameValidator,
+ sectionDeleteValidator,
+} from '../../../utils/validator/sectionValidator.js';
+import {
+ SECTION_MANAGER_PAGE_SELECTOR_TEMPLATE,
+ SECTION_MANAGER_PAGE_TABLE_TEMPLATE,
+ SECTION_TABLE_TEMPLATE,
+} from '../../template/sectionManagerTemplate.js';
+import { ALL_STATION_OPTION_LIST } from '../../template/lineManagerTemplate.js';
+import stationStorage from '../../../storage/stationStorage.js';
+import lineStorage from '../../../storage/lineStorage.js';
+
+export default function singleSectionManager($container, $table, line) {
+ const stations = stationStorage().getStations();
+ const lines = lineStorage().getLines();
+
+ $container.innerHTML = SECTION_MANAGER_PAGE_SELECTOR_TEMPLATE;
+ const $lineTitle = $container.querySelector('#line-title');
+ $lineTitle.innerText = `${line.name} κ΄λ¦¬`;
+
+ const $sectionSelector = $container.querySelector('#section-station-selector');
+ $sectionSelector.innerHTML = ALL_STATION_OPTION_LIST(stations);
+
+ $table.innerHTML = SECTION_MANAGER_PAGE_TABLE_TEMPLATE;
+ const $sectionTableBody = $table.querySelector('.section-table-tbody');
+ $sectionTableBody.innerHTML = SECTION_TABLE_TEMPLATE(line);
+
+ const $userSectionIndexInput = $container.querySelector('#section-order-input');
+ const $userSelectStationBtn = $container.querySelector('#section-station-selector');
+ const $userSectionSubmitBtn = $container.querySelector('#section-add-button');
+
+ const renderSection = (newLine) => {
+ $sectionTableBody.innerHTML = SECTION_TABLE_TEMPLATE(newLine);
+ };
+
+ const getStationById = (stationIds) => {
+ return stations.filter((station) => station.id === stationIds)[0];
+ };
+
+ const removeStationInLine = (stationId) => {
+ const deleteIndex = line.stationIds.findIndex((id) => id === stationId);
+ line.stationIds.splice(deleteIndex, 1);
+ for (let i = 0; i < lines.length; i++) {
+ if (lines[i].id === line.id) {
+ lines[i] = line;
+ }
+ }
+ lineStorage().setLine(lines);
+ return line;
+ };
+
+ const removeLineInStation = (stationId, lineId) => {
+ const station = getStationById(stationId);
+ const deleteIndex = station.line.findIndex((lineNum) => lineNum === lineId);
+ station.line.splice(deleteIndex, 1);
+ stationStorage().setStation(stations);
+ };
+
+ const removeStationInSection = (stationId, lineId) => {
+ removeLineInStation(stationId, lineId);
+ renderSection(removeStationInLine(stationId));
+ };
+
+ const onRemoveSubmitHandler = (e) => {
+ const stationId = parseInt(e.target.dataset.stationid);
+ const lineId = parseInt(e.target.closest('tr').id); // line.id
+ if (Number.isNaN(stationId)) {
+ return;
+ }
+
+ if (sectionDeleteValidator(lineId) && confirm('μ λ§λ‘ λ
Έμ μμ μμ νμκ² μ΅λκΉ?')) {
+ removeStationInSection(stationId, lineId);
+ }
+ };
+
+ const updateLineState = (stationId, stationIndex) => {
+ line.stationIds.splice(stationIndex, 0, stationId);
+ for (let i = 0; i < lines.length; i++) {
+ if (lines[i].id === line.id) {
+ lines[i] = line;
+ }
+ }
+ lineStorage().setLine(lines);
+ return line;
+ };
+
+ const updateStationState = (stationId) => {
+ getStationById(stationId).line.push(line.id);
+ stationStorage().setStation(stations);
+ };
+
+ const updateSection = (stationName, stationIndex) => {
+ const stationId = stationStorage().getStationIdByName(stationName);
+ updateStationState(stationId);
+ renderSection(updateLineState(stationId, stationIndex));
+ };
+
+ const onSectionSubmitHandler = () => {
+ const userSelectStationName = $userSelectStationBtn.value;
+ const userSectionIndex = parseInt($userSectionIndexInput.value);
+
+ if (
+ sectionStationNameValidator(line, stations, userSelectStationName) &&
+ sectionIndexValidator(line.stationIds.length, userSectionIndex)
+ ) {
+ updateSection(userSelectStationName, userSectionIndex);
+ }
+ $userSectionIndexInput.value = '';
+ };
+
+ $userSectionSubmitBtn.addEventListener('click', onSectionSubmitHandler);
+ $sectionTableBody.addEventListener('click', onRemoveSubmitHandler);
+}
diff --git a/src/components/views/lineManagerPage.js b/src/components/views/lineManagerPage.js
new file mode 100644
index 000000000..6eea2a0fb
--- /dev/null
+++ b/src/components/views/lineManagerPage.js
@@ -0,0 +1,109 @@
+import {
+ LINE_MANAGER_PAGE_TEMPLATE,
+ ALL_STATION_OPTION_LIST,
+ LINE_TABLE_TEMPLATE,
+} from '../template/lineManagerTemplate.js';
+import { lineNameValidator, lineStationsValidator } from '../../utils/validator/lineValidator.js';
+import stationStorage from '../../storage/stationStorage.js';
+import lineStorage from '../../storage/lineStorage.js';
+import Line from '../../utils/Line.js';
+
+export default function stationManagerPage($element) {
+ $element.innerHTML = LINE_MANAGER_PAGE_TEMPLATE;
+ const $userInputLine = $element.querySelector('#line-name-input');
+ const $startStation = $element.querySelector('#line-start-station-selector');
+ const $endStation = $element.querySelector('#line-end-station-selector');
+ const $userLineSubmitBtn = $element.querySelector('#line-add-button');
+ const $lineTableBody = $element.querySelector('.line-table-tbody');
+
+ const stations = stationStorage().getStations();
+ let lines = lineStorage().getLines();
+
+ $startStation.innerHTML = ALL_STATION_OPTION_LIST(stations);
+ $endStation.innerHTML = ALL_STATION_OPTION_LIST(stations);
+
+ const renderLines = () => {
+ $lineTableBody.innerHTML = lines.map(LINE_TABLE_TEMPLATE).join('');
+ };
+
+ const getNewId = () => {
+ if (!lines || lines.length === 0) {
+ return 0;
+ }
+ return lines[lines.length - 1].id + 1;
+ };
+
+ const getStationById = (stationIds) => {
+ return stations.filter((station) => station.id === stationIds)[0];
+ };
+
+ const removeLineInStation = (currentLineId) => {
+ const stationsInLine = lines
+ .filter((line) => line.id === parseInt(currentLineId))[0]
+ .stationIds.map(getStationById);
+
+ for (let i = 0; i < stationsInLine.length; i++) {
+ const deleteIndex = stationsInLine[i].line.indexOf(parseInt(currentLineId));
+ stationsInLine[i].line.splice(deleteIndex, 1);
+ }
+ stationStorage().setStation(stations);
+ };
+
+ const removeLine = (currentLineId) => {
+ removeLineInStation(currentLineId);
+ lines = lines.filter((line) => line.id !== parseInt(currentLineId));
+ lineStorage().setLine(lines);
+ renderLines();
+ };
+
+ const onLineDeleteHandler = (e) => {
+ if (!e.target.classList.contains('line-delete-button')) {
+ return;
+ }
+ if (confirm('μ λ§λ‘ μμ νμκ² μ΅λκΉ?')) {
+ removeLine(e.target.closest('tr').id);
+ }
+ };
+
+ const addLine = (newLine) => {
+ lines.push(newLine);
+ lineStorage().setLine(lines);
+ renderLines();
+ };
+
+ const getStation = (stationName) => {
+ return stations.filter((station) => station.name === stationName)[0];
+ };
+
+ const addLineInStation = (lineId, stationName) => {
+ getStation(stationName).line.push(lineId);
+ stationStorage().setStation(stations);
+ };
+
+ const createLine = (newLineName, startStationName, endStationName) => {
+ const newLine = new Line(getNewId(), newLineName);
+ newLine.setLine(stationStorage().getStationIdByName(startStationName), 0);
+ newLine.setLine(stationStorage().getStationIdByName(endStationName), 1);
+
+ addLineInStation(newLine.id, startStationName);
+ addLineInStation(newLine.id, endStationName);
+ addLine(newLine);
+ };
+
+ const onLineSubmitHandler = () => {
+ const newLineName = $userInputLine.value;
+
+ if (
+ lineNameValidator(lines, newLineName) &&
+ lineStationsValidator($startStation.value, $endStation.value)
+ ) {
+ createLine(newLineName, $startStation.value, $endStation.value);
+ }
+
+ $userInputLine.value = '';
+ };
+
+ $userLineSubmitBtn.addEventListener('click', onLineSubmitHandler);
+ $lineTableBody.addEventListener('click', onLineDeleteHandler);
+ renderLines();
+}
diff --git a/src/components/views/mapPrintManagerPage.js b/src/components/views/mapPrintManagerPage.js
new file mode 100644
index 000000000..71938c49e
--- /dev/null
+++ b/src/components/views/mapPrintManagerPage.js
@@ -0,0 +1,5 @@
+import MAP_MANAGER_PAGE_TEMPLATE from '../template/mapPrintManagerTemplate.js';
+
+export default function mapPrintManagerPage($element) {
+ $element.innerHTML = MAP_MANAGER_PAGE_TEMPLATE();
+}
diff --git a/src/components/views/stationManagerPage.js b/src/components/views/stationManagerPage.js
new file mode 100644
index 000000000..17762faac
--- /dev/null
+++ b/src/components/views/stationManagerPage.js
@@ -0,0 +1,70 @@
+import {
+ STATION_MANAGER_PAGE_TEMPLATE,
+ STATION_TABLE_TEMPLATE,
+} from '../template/stationManagerTemplate.js';
+import {
+ stationNameValidator,
+ stationDeleteValidation,
+} from '../../utils/validator/stationValidator.js';
+import stationStorage from '../../storage/stationStorage.js';
+import Station from '../../utils/Station.js';
+
+export default function stationManagerPage($element) {
+ $element.innerHTML = STATION_MANAGER_PAGE_TEMPLATE;
+ const $userStationInput = $element.querySelector('#station-name-input');
+ const $userStationSubmit = $element.querySelector('#station-add-button');
+ const $stationTableBody = $element.querySelector('.station_manager_tbody');
+
+ let stations = stationStorage().getStations();
+
+ const renderStationTable = () => {
+ $stationTableBody.innerHTML = stations.map(STATION_TABLE_TEMPLATE).join('');
+ };
+
+ const deleteStation = (stationTag) => {
+ if (!stationDeleteValidation(stationTag)) {
+ return;
+ }
+ stations = stations.filter((station) => station.id !== parseInt(stationTag.id));
+ stationStorage().setStation(stations);
+ renderStationTable();
+ };
+
+ const onStationDeleteHandler = (e) => {
+ if (!e.target.classList.contains('station-delete-button')) {
+ return false;
+ }
+ if (confirm('μ λ§λ‘ μμ νμκ² μ΅λκΉ?')) {
+ deleteStation(e.target.closest('tr'));
+ }
+ };
+
+ const addStations = (newStations) => {
+ stations.push(newStations);
+ stationStorage().setStation(stations);
+ renderStationTable();
+ };
+
+ const getNewId = () => {
+ if (!stations || stations.length === 0) {
+ return 0;
+ }
+ return stations[stations.length - 1].id + 1;
+ };
+
+ const createStation = (newStationName) => {
+ addStations(new Station(getNewId(), newStationName));
+ };
+
+ const onStationSubmitHandler = () => {
+ const newStationName = $userStationInput.value;
+ if (stationNameValidator(stations, newStationName)) {
+ createStation(newStationName);
+ }
+ $userStationInput.value = '';
+ };
+
+ $userStationSubmit.addEventListener('click', onStationSubmitHandler);
+ $stationTableBody.addEventListener('click', onStationDeleteHandler);
+ renderStationTable();
+}
diff --git a/src/index.js b/src/index.js
index e69de29bb..646aeb386 100644
--- a/src/index.js
+++ b/src/index.js
@@ -0,0 +1,4 @@
+import subwayMapApp from './subwayMapApp.js';
+
+const $app = document.querySelector('#app');
+subwayMapApp($app);
diff --git a/src/storage/lineStorage.js b/src/storage/lineStorage.js
new file mode 100644
index 000000000..4d9fcdbec
--- /dev/null
+++ b/src/storage/lineStorage.js
@@ -0,0 +1,35 @@
+import { LINE_STORAGE_NAME } from '../utils/constant.js';
+
+export default function lineStorage() {
+ const getLines = () => {
+ if (!localStorage.getItem(LINE_STORAGE_NAME)) {
+ return [];
+ }
+ const storedItems = localStorage.getItem(LINE_STORAGE_NAME);
+ return JSON.parse(storedItems);
+ };
+
+ const setLine = (line) => {
+ localStorage.setItem(LINE_STORAGE_NAME, JSON.stringify(line));
+ };
+
+ const getStartPointsId = () => {
+ return getLines().map((line) => line.stationIds[0]);
+ };
+
+ const getEndPointsId = () => {
+ return getLines().map((line) => line.stationIds[line.stationIds.length - 1]);
+ };
+
+ const getOneLine = (lineId) => {
+ return getLines().filter((line) => line.id === parseInt(lineId))[0];
+ };
+
+ return {
+ getLines,
+ setLine,
+ getStartPointsId,
+ getEndPointsId,
+ getOneLine,
+ };
+}
diff --git a/src/storage/stationStorage.js b/src/storage/stationStorage.js
new file mode 100644
index 000000000..e6b1b9891
--- /dev/null
+++ b/src/storage/stationStorage.js
@@ -0,0 +1,30 @@
+import { STATION_STORAGE_NAME } from '../utils/constant.js';
+
+export default function stationStorage() {
+ const getStations = () => {
+ if (!localStorage.getItem(STATION_STORAGE_NAME)) {
+ return [];
+ }
+ const storedItems = localStorage.getItem(STATION_STORAGE_NAME);
+ return JSON.parse(storedItems);
+ };
+
+ const setStation = (station) => {
+ localStorage.setItem(STATION_STORAGE_NAME, JSON.stringify(station));
+ };
+
+ const getStationIdByName = (stationName) => {
+ return getStations().filter((station) => station.name === stationName)[0].id;
+ };
+
+ const getStationById = (stationId) => {
+ return getStations().filter((station) => station.id === stationId)[0];
+ };
+
+ return {
+ getStations,
+ setStation,
+ getStationIdByName,
+ getStationById,
+ };
+}
diff --git a/src/subwayMapApp.js b/src/subwayMapApp.js
new file mode 100644
index 000000000..31c00ff79
--- /dev/null
+++ b/src/subwayMapApp.js
@@ -0,0 +1,21 @@
+import stationManagerPage from './components/views/stationManagerPage.js';
+import lineManagerPage from './components/views/lineManagerPage.js';
+import sectionManagerPage from './components/views/SectionManagerPages/sectionManagerPage.js';
+import mapPrintManagerPage from './components/views/mapPrintManagerPage.js';
+
+export default function subwayMapApp($element) {
+ const init = () => {
+ const $stationManagerBtn = $element.querySelector('#station-manager-button');
+ const $lineManagerBtn = $element.querySelector('#line-manager-button');
+ const $sectionManagerBtn = $element.querySelector('#section-manager-button');
+ const $mapPrintManagerBtn = $element.querySelector('#map-print-manager-button');
+ const $contentSection = $element.querySelector('.content-container');
+
+ $stationManagerBtn.addEventListener('click', () => stationManagerPage($contentSection));
+ $lineManagerBtn.addEventListener('click', () => lineManagerPage($contentSection));
+ $sectionManagerBtn.addEventListener('click', () => sectionManagerPage($contentSection));
+ $mapPrintManagerBtn.addEventListener('click', () => mapPrintManagerPage($contentSection));
+ };
+
+ init();
+}
diff --git a/src/utils/Line.js b/src/utils/Line.js
new file mode 100644
index 000000000..c82eb4369
--- /dev/null
+++ b/src/utils/Line.js
@@ -0,0 +1,12 @@
+export default function Line(id, name) {
+ this.id = id;
+ this.name = name;
+ this.stationIds = [];
+
+ this.setLine = (stationId, index) => {
+ if (this.stationIds.includes(stationId)) {
+ alert('μ΄λ―Έ λ±λ‘λμ΄ μμ΅λλ€');
+ }
+ this.stationIds.splice(index, 0, stationId);
+ };
+}
diff --git a/src/utils/Station.js b/src/utils/Station.js
new file mode 100644
index 000000000..07c1baf13
--- /dev/null
+++ b/src/utils/Station.js
@@ -0,0 +1,5 @@
+export default function Station(id, name) {
+ this.id = id;
+ this.name = name;
+ this.line = [];
+}
diff --git a/src/utils/constant.js b/src/utils/constant.js
new file mode 100644
index 000000000..98c0788fe
--- /dev/null
+++ b/src/utils/constant.js
@@ -0,0 +1,6 @@
+const STATION_STORAGE_NAME = 'STATIONS';
+const LINE_STORAGE_NAME = 'LINES';
+const NAME_MIN_LENGTH = 2;
+const SECTION_MIN_LENGTH = 2;
+
+export { STATION_STORAGE_NAME, LINE_STORAGE_NAME, NAME_MIN_LENGTH, SECTION_MIN_LENGTH };
diff --git a/src/utils/validator/lineValidator.js b/src/utils/validator/lineValidator.js
new file mode 100644
index 000000000..bc338d0bd
--- /dev/null
+++ b/src/utils/validator/lineValidator.js
@@ -0,0 +1,64 @@
+import lineStorage from '../../storage/lineStorage.js';
+import stationStorage from '../../storage/stationStorage.js';
+
+const isEqualName = (lines, lineName) => {
+ return lines.some((line) => line.name === lineName) ? alert('μ€λ³΅λ λ
Έμ μ΄λ¦μ
λλ€.') : true;
+};
+
+const isNameNotNull = (lineName) => {
+ return !lineName ? alert('λ
Έμ μ΄λ¦μ μ
λ ₯ν΄μ£ΌμΈμ') : true;
+};
+
+const isCorrectName = (lineName) => {
+ return lineName.split('')[lineName.length - 1] !== 'μ '
+ ? alert("λ
Έμ μ μ΄λ¦μ 'μ 'μΌλ‘ λλμΌ ν©λλ€")
+ : true;
+};
+
+const isEqualPoints = (startStationName, endStationName) => {
+ return startStationName === endStationName
+ ? alert('μν μ’
μ κ³Ό νν μ’
μ μ μλ‘ λ¬λΌμΌ ν©λλ€')
+ : true;
+};
+
+const getStartStationIds = (stationName) => {
+ return lineStorage()
+ .getStartPointsId()
+ .map((stationId) => stationId === parseInt(stationStorage().getStationIdByName(stationName)));
+};
+
+const getEndStationIds = (stationName) => {
+ return lineStorage()
+ .getEndPointsId()
+ .map((stationId) => stationId === parseInt(stationStorage().getStationIdByName(stationName)));
+};
+
+const isEqualLine = (startStationName, endStationName) => {
+ const startStationIds = getStartStationIds(startStationName);
+ const endStationIds = getEndStationIds(endStationName);
+
+ const equalLine = startStationIds.filter((station, index) => station && endStationIds[index]);
+ return equalLine.length !== 0 ? alert('λμΌν μ’
μ μ κ°μ§ λ
Έμ μ΄ μμ΅λλ€') : true;
+};
+
+const isEqualReverseLine = (startStationName, endStationName) => {
+ const startStationIds = getStartStationIds(endStationName);
+ const endStationIds = getEndStationIds(startStationName);
+
+ const equalLine = startStationIds.filter((station, index) => station && endStationIds[index]);
+ return equalLine.length !== 0 ? alert('λμΌν μ’
μ μ κ°μ§ λ
Έμ μ΄ μμ΅λλ€') : true;
+};
+
+function lineNameValidator(lines, lineName) {
+ return isEqualName(lines, lineName) && isNameNotNull(lineName) && isCorrectName(lineName);
+}
+
+function lineStationsValidator(startStationName, endStationName) {
+ return (
+ isEqualPoints(startStationName, endStationName) &&
+ isEqualLine(startStationName, endStationName) &&
+ isEqualReverseLine(startStationName, endStationName)
+ );
+}
+
+export { lineNameValidator, lineStationsValidator };
diff --git a/src/utils/validator/sectionValidator.js b/src/utils/validator/sectionValidator.js
new file mode 100644
index 000000000..d5438036d
--- /dev/null
+++ b/src/utils/validator/sectionValidator.js
@@ -0,0 +1,45 @@
+import lineStorage from '../../storage/lineStorage.js';
+import { SECTION_MIN_LENGTH } from '../constant.js';
+
+const isNumber = (sectionIndex) => {
+ return Number.isNaN(sectionIndex) ? alert('μ νν μ«μλ₯Ό μ
λ ₯ν΄μ£ΌμΈμ') : true;
+};
+
+const isPositiveNumber = (sectionIndex) => {
+ return sectionIndex >= 0 ? true : alert('μμκ° μλ 0 μ΄μμ μ«μλ₯Ό μ
λ ₯ν΄μ£ΌμΈμ');
+};
+
+const isCorrectIndex = (sectionLength, sectionIndex) => {
+ return sectionIndex <= sectionLength ? true : alert('μ
λ ₯ν μμλ₯Ό νμΈν΄μ£ΌμΈμ');
+};
+
+const isLengthLongerThanTwo = (lineId) => {
+ const stationLength = lineStorage().getOneLine(lineId).stationIds.length;
+ return stationLength <= SECTION_MIN_LENGTH
+ ? alert('λ
Έμ μ μ΅μ 2κ°μ μμΌλ‘ μ΄λ£¨μ΄μ Έ μμ΅λλ€.')
+ : true;
+};
+
+const isNotRegistered = (id, stations, stationName) => {
+ return stations.filter((station) => station.name === stationName)[0].line.includes(id)
+ ? alert('μ΄λ―Έ λ±λ‘λ μμ
λλ€')
+ : true;
+};
+
+function sectionStationNameValidator({ id }, stations, stationName) {
+ return isNotRegistered(id, stations, stationName);
+}
+
+function sectionIndexValidator(sectionLength, sectionIndex) {
+ return (
+ isNumber(sectionIndex) &&
+ isPositiveNumber(sectionIndex) &&
+ isCorrectIndex(sectionLength, sectionIndex)
+ );
+}
+
+function sectionDeleteValidator(lineId) {
+ return isLengthLongerThanTwo(lineId);
+}
+
+export { sectionStationNameValidator, sectionIndexValidator, sectionDeleteValidator };
diff --git a/src/utils/validator/stationValidator.js b/src/utils/validator/stationValidator.js
new file mode 100644
index 000000000..12f8763b8
--- /dev/null
+++ b/src/utils/validator/stationValidator.js
@@ -0,0 +1,27 @@
+import { NAME_MIN_LENGTH } from '../constant.js';
+
+const isNameLengthLongerThanTwo = (stationName) => {
+ return stationName.length < NAME_MIN_LENGTH ? alert('μ΄λ¦μ λκΈμ μ΄μμ
λλ€') : true;
+};
+
+const isEqualName = (stations, stationName) => {
+ return stations && stations.map((value) => value.name).includes(stationName)
+ ? alert('μ€λ³΅λ μ΄λ¦μ
λλ€.')
+ : true;
+};
+
+const isIncludedLine = (stationTag) => {
+ return stationTag.dataset.lines.length !== 0
+ ? alert('λ
Έμ μ ν¬ν¨λμ΄ μλ λΌμΈμ μμ ν μ μμ΅λλ€')
+ : true;
+};
+
+function stationNameValidator(stations, stationName) {
+ return isNameLengthLongerThanTwo(stationName) && isEqualName(stations, stationName);
+}
+
+function stationDeleteValidation(stationTag) {
+ return isIncludedLine(stationTag);
+}
+
+export { stationNameValidator, stationDeleteValidation };