diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 000000000..56b8f8002
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,35 @@
+# π μ§νμ² λ
Έμ λ λ―Έμ
+
+## π κΈ°λ₯ μꡬμ¬ν
+
+### μ§νμ² μ κ΄λ ¨ κΈ°λ₯
+- [x] μ§νμ² μμ λ±λ‘νκ³ μμ ν μ μλ€.
+ - [x] λ¨, λ
Έμ μ λ±λ‘λ μμ μμ ν μ μλ€
+- [x] μ€λ³΅λ μ§νμ² μ μ΄λ¦μ΄ λ±λ‘λ μ μλ€.
+- [x] μ§νμ² μμ 2κΈμ μ΄μμ΄μ΄μΌ νλ€.
+- [x] μ§νμ² μμ λͺ©λ‘μ μ‘°νν μ μλ€.
+
+### μ§νμ² λ
Έμ κ΄λ ¨ κΈ°λ₯
+- [x] μ§νμ² λ
Έμ μ λ±λ‘νκ³ μμ ν μ μλ€.
+- [x] μ€λ³΅λ μ§νμ² λ
Έμ μ΄λ¦μ΄ λ±λ‘λ μ μλ€.
+- [x] λ
Έμ λ±λ‘ μ μν μ’
μ μκ³Ό νν μ’
μ μμ μ
λ ₯λ°λλ€.
+- [x] μ§νμ² λ
Έμ μ λͺ©λ‘μ μ‘°νν μ μλ€.
+
+### μ§νμ² κ΅¬κ° μΆκ° κΈ°λ₯
+- [x] μ§νμ² λ
Έμ μ ꡬκ°μ μΆκ°νλ κΈ°λ₯μ λ
Έμ μ μμ μΆκ°νλ κΈ°λ₯μ΄λΌκ³ λ ν μ μλ€.
+ - μκ³Ό μμ¬μ΄λ₯Ό ꡬκ°μ΄λΌ νκ³ μ΄ κ΅¬κ°λ€μ λͺ¨μμ΄ λ
Έμ μ΄λ€.
+- [x] νλμ μμ μ¬λ¬κ°μ λ
Έμ μ μΆκ°λ μ μλ€.
+- [x] μκ³Ό μ μ¬μ΄μ μλ‘μ΄ μμ΄ μΆκ° λ μ μλ€.
+- [ ] λ
Έμ μμ κ°λκΈΈμ μκΈΈ μ μλ€.
+
+### μ§νμ² κ΅¬κ° μμ κΈ°λ₯
+- [x] λ
Έμ μ λ±λ‘λ μμ μ κ±°ν μ μλ€.
+- [x] μ’
μ μ μ κ±°ν κ²½μ° λ€μ μμ΄ μ’
μ μ΄ λλ€.
+- [x] λ
Έμ μ ν¬ν¨λ μμ΄ λκ° μ΄νμΌ λλ μμ μ κ±°ν μ μλ€.
+
+### μ§νμ² λ
Έμ μ λ±λ‘λ μ μ‘°ν κΈ°λ₯
+- [x] λ
Έμ μ μν μ’
μ λΆν° νν μ’
μ κΉμ§ μ°κ²°λ μμλλ‘ μ λͺ©λ‘μ μ‘°νν μ μλ€.
+
+### μΆκ° κΈ°λ₯
+- `localStorage`λ₯Ό μ΄μ©νμ¬, μλ‘κ³ μΉ¨ νλλΌλ μμ
νλ μ 보λ€μ λΆλ¬μ¬ μ μκ² νλ€.
+- `data`μμ±μ μ΄μ©νμ¬ htmlνκ·Έμ μ, λ
Έμ , ꡬκ°μ μ μΌν λ°μ΄ν° κ°λ€μ 보κ΄νλ€.
diff --git a/index.html b/index.html
index fc99deac2..8a7194ac8 100644
--- a/index.html
+++ b/index.html
@@ -7,6 +7,43 @@
π μ§νμ² λ
Έμ λ κ΄λ¦¬
+
+
+
+
μ μ΄λ¦
+
+
+
π μ§νμ² μ λͺ©λ‘
+
+
+
+
+
λ
Έμ μ΄λ¦
+
+
+
+ μν μ’
μ
+
+ νν μ’
μ
+
+
+
+
+
π μ§νμ² λ
Έμ λͺ©λ‘
+
+
+
+
ꡬκ°μ μμ ν λ
Έμ μ μ νν΄μ£ΌμΈμ.
+
+
+
+
diff --git a/src/index.css b/src/index.css
new file mode 100644
index 000000000..4d2b60ecd
--- /dev/null
+++ b/src/index.css
@@ -0,0 +1,5 @@
+table,
+th,
+td {
+ border: 1px solid black;
+}
diff --git a/src/index.js b/src/index.js
index e69de29bb..5c3285e1f 100644
--- a/src/index.js
+++ b/src/index.js
@@ -0,0 +1,52 @@
+import stationContainer from "./station/stationContainer";
+import lineContainer from "./line/lineContainer";
+import sectionContainer from "./section/sectionContainer";
+import printMapContainer from "./printMap/printMapContainer";
+
+const init = () => {
+ const stationButton = document.querySelector("#station-manager-button");
+ const lineButton = document.querySelector("#line-manager-button");
+ const sectionButton = document.querySelector("#section-manager-button");
+ const printMapButton = document.querySelector("#map-print-manager-button");
+
+ stationButton.addEventListener("click", () => {
+ clearScreen();
+ stationContainer();
+ });
+
+ lineButton.addEventListener("click", () => {
+ clearScreen();
+ lineContainer();
+ });
+
+ sectionButton.addEventListener("click", () => {
+ clearScreen();
+ sectionContainer();
+ });
+ printMapButton.addEventListener("click", () => {
+ clearScreen();
+ printMapContainer();
+ });
+
+ const clearScreen = () => {
+ const stationManageContainer = document.querySelector(
+ "#station-manager-container",
+ );
+ const lineManageContainer = document.querySelector(
+ "#line-manager-container",
+ );
+ const sectionManageContainer = document.querySelector(
+ "#section-manager-container",
+ );
+ const printMapManageContainer = document.querySelector(
+ "#map-print-manager-container",
+ );
+
+ stationManageContainer.style.display = "none";
+ lineManageContainer.style.display = "none";
+ sectionManageContainer.style.display = "none";
+ printMapManageContainer.style.display = "none";
+ };
+};
+
+init();
diff --git a/src/line/lineConstant.js b/src/line/lineConstant.js
new file mode 100644
index 000000000..21f361943
--- /dev/null
+++ b/src/line/lineConstant.js
@@ -0,0 +1 @@
+export const INVALID_LINE_INFO = "μ§νμ² λ
Έμ μ΄ μ€λ³΅λμ§ μκ² μ
λ ₯ν΄μ£ΌμΈμ.";
diff --git a/src/line/lineContainer.js b/src/line/lineContainer.js
new file mode 100644
index 000000000..ccfa88273
--- /dev/null
+++ b/src/line/lineContainer.js
@@ -0,0 +1,105 @@
+import { isValidLineInfo } from "../utils";
+import { INVALID_LINE_INFO } from "./lineConstant";
+import { lineListTemplate, clearOption } from "./linePresenter";
+
+export default function lineContainer() {
+ let lineList = [];
+
+ const checkLineName = (lineName, startLine, endLine) => {
+ const lineInfo = [lineName, startLine, endLine];
+
+ isValidLineInfo(lineList, lineInfo)
+ ? addLine(lineInfo)
+ : alert(INVALID_LINE_INFO);
+ };
+
+ const addLine = lineInfo => {
+ lineList.push(lineInfo);
+ setLocalData(lineList);
+ lineListTemplate(lineList);
+ removeLineHandler();
+ };
+
+ const removeLineHandler = () => {
+ const lineRemoveButton = document.querySelectorAll("#line-remove-button");
+ if (lineRemoveButton !== null) {
+ for (const removeButton of lineRemoveButton) {
+ removeButton.addEventListener("click", event => {
+ removeLine(event);
+ });
+ }
+ }
+ };
+
+ const removeLine = event => {
+ const targetLine = event.target.parentNode.parentNode;
+ const lineName = targetLine.dataset.linename;
+ let lineIndex = 0;
+ for (const line of lineList) {
+ if (line[0] === lineName) {
+ lineIndex = lineList.indexOf(line);
+ }
+ }
+ lineList.splice(lineIndex, 1);
+ setLocalData(lineList);
+ lineListTemplate(lineList);
+ removeLineHandler();
+ };
+
+ const setLocalData = lineList => {
+ window.localStorage.setItem("lineList", JSON.stringify(lineList));
+ };
+
+ const getLocalData = () => {
+ let localData = window.localStorage.getItem("lineList");
+
+ if (localData) {
+ lineList = JSON.parse(localData);
+ }
+ };
+
+ const setSelectorOption = selector => {
+ const lineStartSelector = document.querySelector(
+ "#line-start-name-selector",
+ );
+ const stationData = JSON.parse(window.localStorage.getItem("stationList"));
+ if (selector === lineStartSelector) {
+ clearOption();
+ }
+ if (stationData) {
+ for (const station of stationData) {
+ const selectorOption = document.createElement("option");
+ selectorOption.setAttribute("id", "station-option");
+ selectorOption.textContent = station;
+ selector.appendChild(selectorOption);
+ }
+ }
+ };
+
+ const init = () => {
+ const lineNameContainer = document.querySelector("#line-manager-container");
+ const lineNameInput = document.querySelector("#line-name-input");
+ const lineStartSelector = document.querySelector(
+ "#line-start-name-selector",
+ );
+ const lineEndSelector = document.querySelector("#line-start-end-selector");
+ const lineNameButton = document.querySelector("#line-name-button");
+
+ getLocalData();
+ lineListTemplate(lineList);
+ removeLineHandler();
+ setSelectorOption(lineStartSelector);
+ setSelectorOption(lineEndSelector);
+ lineNameContainer.style.display = "block";
+ lineNameButton.addEventListener("click", () => {
+ checkLineName(
+ lineNameInput.value,
+ lineStartSelector.options[lineStartSelector.selectedIndex].value,
+ lineEndSelector.options[lineEndSelector.selectedIndex].value,
+ );
+ lineNameInput.value = "";
+ });
+ };
+
+ init();
+}
diff --git a/src/line/linePresenter.js b/src/line/linePresenter.js
new file mode 100644
index 000000000..bd3e84a10
--- /dev/null
+++ b/src/line/linePresenter.js
@@ -0,0 +1,33 @@
+export const lineListTemplate = lineList => {
+ let template = `
+
+ | λ
Έμ μ΄λ¦ |
+ μν μ’
μ μ |
+ νν μ’
μ μ |
+ μ€μ |
+
+ `;
+ for (const line of lineList) {
+ template += `
+
+ | ${line[0]} |
+ ${line[1]} |
+ ${line[line.length - 1]} |
+ |
+
+ `;
+ }
+ lineListPresenter(template);
+};
+
+export const lineListPresenter = template => {
+ const lineNameTable = document.querySelector("#line-name-table");
+ lineNameTable.innerHTML = template;
+};
+
+export const clearOption = () => {
+ const lineOptions = document.querySelectorAll("#station-option");
+ for (const option of lineOptions) {
+ option.remove();
+ }
+};
diff --git a/src/printMap/printMapContainer.js b/src/printMap/printMapContainer.js
new file mode 100644
index 000000000..8f49ea6ba
--- /dev/null
+++ b/src/printMap/printMapContainer.js
@@ -0,0 +1,20 @@
+import { listHeaderPresenter } from "./printMapPresenter";
+
+export default function printMapContainer() {
+ let lineData = [];
+
+ const getLocalData = () => {
+ lineData = JSON.parse(window.localStorage.getItem("lineList"));
+ };
+
+ const init = () => {
+ const printMapContainer = document.querySelector(
+ "#map-print-manager-container",
+ );
+ getLocalData();
+ listHeaderPresenter(lineData);
+ printMapContainer.style.display = "block";
+ };
+
+ init();
+}
diff --git a/src/printMap/printMapPresenter.js b/src/printMap/printMapPresenter.js
new file mode 100644
index 000000000..7132bc767
--- /dev/null
+++ b/src/printMap/printMapPresenter.js
@@ -0,0 +1,24 @@
+export const listHeaderPresenter = lineData => {
+ const printMapContainer = document.querySelector(
+ "#map-print-manager-container",
+ );
+ let listTemplate = "";
+ for (const line of lineData) {
+ listTemplate += `
+ ${line[0]}
+ `;
+ listTemplate += listStation(line);
+ }
+ printMapContainer.innerHTML = listTemplate;
+};
+
+const listStation = lineData => {
+ let stationTemplate = ``;
+ for (let i = 1; i < lineData.length; i++) {
+ stationTemplate += `
+ - ${lineData[i]}
+ `;
+ }
+ stationTemplate += `
`;
+ return stationTemplate;
+};
diff --git a/src/section/sectionConstant.js b/src/section/sectionConstant.js
new file mode 100644
index 000000000..8934761d6
--- /dev/null
+++ b/src/section/sectionConstant.js
@@ -0,0 +1,3 @@
+export const INVALID_SECTION_NAME = "ꡬκ°μ΄ μ€λ³΅λμ§ μκ² μ
λ ₯ν΄μ£ΌμΈμ.";
+export const CANNOT_REMOVE =
+ "λ
Έμ μ ν¬ν¨λ μμ΄ λ κ° μ΄νμΌ κ²½μ° μμ μ κ±°ν μ μμ΅λλ€.";
diff --git a/src/section/sectionContainer.js b/src/section/sectionContainer.js
new file mode 100644
index 000000000..a792a5b2b
--- /dev/null
+++ b/src/section/sectionContainer.js
@@ -0,0 +1,128 @@
+import {
+ lineMenuPresenter,
+ sectionManagePresenter,
+ lineListTemplate,
+} from "./sectionPresenter";
+import { hasDuplicatedName } from "../utils";
+import { INVALID_SECTION_NAME, CANNOT_REMOVE } from "./sectionConstant";
+
+export default function sectionContainer() {
+ let lineData = [];
+ let stationData = [];
+
+ const addSection = (station, order, line) => {
+ const sectionManageContainer = document.querySelector(
+ "#section-manage-container",
+ );
+ const lineListTable = document.querySelector("#line-list-table");
+ const prevLineDataIndex = lineData.indexOf(line);
+
+ if (!hasDuplicatedName(line, station)) {
+ line.splice(parseInt(order) + 1, 0, station);
+ lineData[prevLineDataIndex] = line;
+ sectionManageContainer.removeChild(lineListTable);
+
+ setLocalData(lineData);
+ lineListTemplate(line);
+ sectionHandler(line);
+ removeSectionHandler(line);
+ } else {
+ alert(INVALID_SECTION_NAME);
+ }
+ };
+
+ const removeSectionHandler = line => {
+ const sectionRemoveButton = document.querySelectorAll(
+ "#section-remove-button",
+ );
+ if (sectionRemoveButton !== null) {
+ for (const removeButton of sectionRemoveButton) {
+ removeButton.addEventListener("click", event => {
+ removeSection(event, line);
+ });
+ }
+ }
+ };
+
+ const removeSection = (event, line) => {
+ const sectionManageContainer = document.querySelector(
+ "#section-manage-container",
+ );
+ const lineListTable = document.querySelector("#line-list-table");
+ const lineDataIndex = lineData.indexOf(line);
+ const targetSection = event.target.parentNode.parentNode;
+ const targetName = targetSection.dataset.linename;
+ const targetIndex = line.indexOf(targetName);
+
+ if (line.length > 3) {
+ line.splice(targetIndex, 1);
+ lineData[lineDataIndex] = line;
+ sectionManageContainer.removeChild(lineListTable);
+
+ setLocalData(lineData);
+ lineListTemplate(line);
+ sectionHandler(line);
+ removeSectionHandler(line);
+ } else {
+ alert(CANNOT_REMOVE);
+ }
+ };
+
+ const getLocalData = () => {
+ stationData = JSON.parse(window.localStorage.getItem("stationList"));
+ lineData = JSON.parse(window.localStorage.getItem("lineList"));
+ };
+
+ const setLocalData = lineData => {
+ window.localStorage.setItem("lineList", JSON.stringify(lineData));
+ };
+
+ const sectionHandler = line => {
+ const sectionSelector = document.querySelector("#section-manage-selector");
+ const sectionInputNumber = document.querySelector("#section-manage-input");
+ const sectionButton = document.querySelector("#section-manage-button");
+ sectionButton.addEventListener("click", () => {
+ addSection(
+ sectionSelector.options[sectionSelector.selectedIndex].value,
+ sectionInputNumber.value,
+ line,
+ );
+ });
+ };
+
+ const MenuButtonHandler = () => {
+ const menuButtons = document.querySelectorAll("#line-menu-button");
+ if (menuButtons !== null) {
+ for (const menuButton of menuButtons) {
+ menuButton.addEventListener("click", event => {
+ manageLine(event);
+ });
+ }
+ }
+ };
+
+ const manageLine = event => {
+ const targetLine = event.target.dataset.linenumber;
+ for (const line of lineData) {
+ if (line[0] === targetLine) {
+ sectionManagePresenter(line, stationData);
+ lineListTemplate(line);
+ sectionHandler(line);
+ removeSectionHandler(line);
+ }
+ }
+ };
+
+ const init = () => {
+ const sectionContainer = document.querySelector(
+ "#section-manager-container",
+ );
+ getLocalData();
+ lineMenuPresenter(lineData);
+ MenuButtonHandler();
+ removeSectionHandler();
+ sectionContainer.style.display = "block";
+ };
+
+ init();
+}
diff --git a/src/section/sectionPresenter.js b/src/section/sectionPresenter.js
new file mode 100644
index 000000000..9a256946c
--- /dev/null
+++ b/src/section/sectionPresenter.js
@@ -0,0 +1,67 @@
+export const lineMenuPresenter = lineData => {
+ const lineMenuContainer = document.querySelector("#line-menu-container");
+ let lineMenu = "";
+ for (const line of lineData) {
+ lineMenu += `
+
+ `;
+ }
+ lineMenuContainer.innerHTML = lineMenu;
+};
+
+export const sectionManagePresenter = (lineData, stationData) => {
+ const sectionManageContainer = document.querySelector(
+ "#section-manage-container",
+ );
+ let sectionManageTemplate = `
+ ${lineData[0]} κ΄λ¦¬
+ κ΅¬κ° λ±λ‘
+ `;
+ sectionManageTemplate += stationSelector(stationData);
+ sectionManageTemplate += `
+
+
+ `;
+ sectionManageContainer.innerHTML = sectionManageTemplate;
+};
+
+const stationSelector = stationData => {
+ let selectTemplate = ``;
+ return selectTemplate;
+};
+
+export const lineListTemplate = line => {
+ let template = `
+
+ | μμ |
+ μ΄λ¦ |
+ μ€μ |
+
+ `;
+ for (let i = 1; i < line.length; i++) {
+ template += `
+
+ | ${i - 1} |
+ ${line[i]} |
+ |
+
+ `;
+ }
+ lineListPresenter(template);
+};
+
+export const lineListPresenter = template => {
+ const sectionManageContainer = document.querySelector(
+ "#section-manage-container",
+ );
+ const lineListTable = document.createElement("table");
+ lineListTable.setAttribute("id", "line-list-table");
+ lineListTable.innerHTML = template;
+ sectionManageContainer.appendChild(lineListTable);
+};
diff --git a/src/station/stationConstant.js b/src/station/stationConstant.js
new file mode 100644
index 000000000..2ca79fb33
--- /dev/null
+++ b/src/station/stationConstant.js
@@ -0,0 +1,3 @@
+export const INVALID_STATION_NAME =
+ "μ§νμ² μμ΄ μ€λ³΅λμ§ μκ² 2κΈμ μ΄μμΌλ‘ μ
λ ₯ν΄μ£ΌμΈμ.";
+export const CANNOT_REMOVE = "λ
Έμ μ λ±λ‘λ μμ μμ ν μ μμ΅λλ€.";
diff --git a/src/station/stationContainer.js b/src/station/stationContainer.js
new file mode 100644
index 000000000..67f62b89e
--- /dev/null
+++ b/src/station/stationContainer.js
@@ -0,0 +1,82 @@
+import { isValidStationName, inLine } from "../utils";
+import { stationListTemplate } from "./stationPresenter";
+import { INVALID_STATION_NAME, CANNOT_REMOVE } from "./stationConstant";
+
+export default function stationContainer() {
+ let stationList = [];
+
+ const checkStationName = stationName => {
+ isValidStationName(stationList, stationName)
+ ? addStation(stationName)
+ : alert(INVALID_STATION_NAME);
+ };
+
+ const addStation = stationName => {
+ stationList.push(stationName);
+ setLocalData(stationList);
+ stationListTemplate(stationList);
+ removeStationHandler();
+ };
+
+ const removeStationHandler = () => {
+ const stationRemoveButton = document.querySelectorAll(
+ "#station-remove-button",
+ );
+ if (stationRemoveButton !== null) {
+ for (const removeButton of stationRemoveButton) {
+ removeButton.addEventListener("click", event => {
+ removeStation(event);
+ });
+ }
+ }
+ };
+
+ const removeStation = event => {
+ const targetStation = event.target.parentNode.parentNode;
+ const stationName = targetStation.dataset.stationname;
+ const stationIndex = stationList.indexOf(stationName);
+
+ if (!inLine(stationName)) {
+ stationList.splice(stationIndex, 1);
+ setLocalData(stationList);
+ stationListTemplate(stationList);
+ removeStationHandler();
+ } else {
+ alert(CANNOT_REMOVE);
+ }
+ };
+
+ const setLocalData = stationList => {
+ window.localStorage.setItem("stationList", JSON.stringify(stationList));
+ };
+
+ const getLocalData = () => {
+ let localData = window.localStorage.getItem("stationList");
+
+ if (!localData) {
+ localData = [];
+ window.localStorage.setItem("stationList", JSON.stringify(localData));
+ } else {
+ stationList = JSON.parse(localData);
+ }
+ };
+
+ const init = () => {
+ const stationContainer = document.querySelector(
+ "#station-manager-container",
+ );
+ const submitButton = document.querySelector("#station-name-button");
+ const stationNameInput = document.querySelector("#station-name-input");
+
+ getLocalData();
+ stationListTemplate(stationList);
+ removeStationHandler();
+ stationContainer.style.display = "block";
+ submitButton.addEventListener("click", () => {
+ checkStationName(stationNameInput.value);
+ stationNameInput.value = "";
+ });
+ };
+
+ init();
+}
diff --git a/src/station/stationPresenter.js b/src/station/stationPresenter.js
new file mode 100644
index 000000000..a90f5f95b
--- /dev/null
+++ b/src/station/stationPresenter.js
@@ -0,0 +1,22 @@
+export const stationListTemplate = stationList => {
+ let template = `
+
+ | μ μ΄λ¦ |
+ μ€μ |
+
+ `;
+ for (const station of stationList) {
+ template += `
+
+ | ${station} |
+ |
+
+ `;
+ }
+ stationListPresenter(template);
+};
+
+export const stationListPresenter = template => {
+ const stationNameTable = document.querySelector("#station-name-table");
+ stationNameTable.innerHTML = template;
+};
diff --git a/src/utils.js b/src/utils.js
new file mode 100644
index 000000000..3dbaf7b11
--- /dev/null
+++ b/src/utils.js
@@ -0,0 +1,32 @@
+export const hasDuplicatedName = (stationList, stationName) =>
+ stationList.includes(stationName);
+
+const isLengthMoreOne = stationName => stationName.length > 1;
+
+export const isValidStationName = (stationList, stationName) => {
+ return (
+ !hasDuplicatedName(stationList, stationName) && isLengthMoreOne(stationName)
+ );
+};
+
+const hasDuplicatedLine = (lineList, lineInfo) => {
+ for (const line of lineList) {
+ if (line[0] === lineInfo[0]) {
+ return true;
+ }
+ }
+ return false;
+};
+
+const isSameStation = lineInfo => lineInfo[1] === lineInfo[2];
+
+export const isValidLineInfo = (lineList, lineInfo) => {
+ return !hasDuplicatedLine(lineList, lineInfo) && !isSameStation(lineInfo);
+};
+
+export const inLine = stationName => {
+ let lineList = JSON.parse(window.localStorage.getItem("lineList"));
+
+ lineList = lineList.flat();
+ return lineList.includes(stationName);
+};