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 = ``; + 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); +};