diff --git a/src/main/java/cokr/xit/fims/crdn/parsing/EquipmentEnterprise.java b/src/main/java/cokr/xit/fims/crdn/parsing/EquipmentEnterprise.java index 3b97dd23..00e40e43 100644 --- a/src/main/java/cokr/xit/fims/crdn/parsing/EquipmentEnterprise.java +++ b/src/main/java/cokr/xit/fims/crdn/parsing/EquipmentEnterprise.java @@ -8,8 +8,16 @@ import cokr.xit.foundation.data.DataObject; public interface EquipmentEnterprise { + /**장비 단속 연계 파일에서 단속정보를 추출한다.
+ * @param fileList 연계파일리스트 + * @return 단속정보 + */ public List parsing(Stream fileList); + /**현재 연계 파일의 정보가 이전 연계 파일의 정보와 다른 단속건인지 판단한다.
+ * @param currentItem 현재 연계 파일 정보, beforeItem 이전 연계 파일 정보 + * @return 개별 단속자료 여부 + */ public boolean isChangeCrackdown(DataObject currentItem, DataObject beforeItem); } diff --git a/src/main/java/cokr/xit/fims/crdn/parsing/ParsingUtil.java b/src/main/java/cokr/xit/fims/crdn/parsing/ParsingUtil.java new file mode 100644 index 00000000..25f05c80 --- /dev/null +++ b/src/main/java/cokr/xit/fims/crdn/parsing/ParsingUtil.java @@ -0,0 +1,26 @@ +package cokr.xit.fims.crdn.parsing; + +import java.io.File; + +public class ParsingUtil { + + /**자치단체,과태료업무,장비업체,장비구분에 따른 연계 파일 작업 디렉토리 경로를 반환한다.
+ * @param institute 기관구분, taskSeCd 과태료구분, entType 업체구분, equipmentType 장비구분 + * @return 작업 디렉토리 경로 + */ + public static String getWorkDirectoryPath(String institute, String taskSeCd, String entType, String equipmentType) { + String workPath = ""; + + if(entType.equals("INO")) { + workPath = "files"+File.separator+"tempForIno"; + } + if(entType.equals("KNL")) { + workPath = "files"+File.separator+"tempForKnl"; + } + if(entType.equals("HITECOM")) { + workPath = "files"+File.separator+"tempForHitecom"; + } + + return workPath; + } +} diff --git a/src/main/java/cokr/xit/fims/crdn/service/CrdnService.java b/src/main/java/cokr/xit/fims/crdn/service/CrdnService.java index ea39b815..20175b35 100644 --- a/src/main/java/cokr/xit/fims/crdn/service/CrdnService.java +++ b/src/main/java/cokr/xit/fims/crdn/service/CrdnService.java @@ -77,6 +77,6 @@ public interface CrdnService { * @param entType 업체유형, fileName 파일명 * @return 저장 여부 */ - boolean removeEquipmentLinkFile(String entType, String fileName); + boolean removeEquipmentLinkFile(String workPath, String fileName); } diff --git a/src/main/java/cokr/xit/fims/crdn/service/ImportService.java b/src/main/java/cokr/xit/fims/crdn/service/ImportService.java index be7cc24f..001a11ea 100644 --- a/src/main/java/cokr/xit/fims/crdn/service/ImportService.java +++ b/src/main/java/cokr/xit/fims/crdn/service/ImportService.java @@ -2,6 +2,7 @@ package cokr.xit.fims.crdn.service; import java.util.HashMap; import java.util.List; +import java.util.Map; import cokr.xit.foundation.data.DataObject; @@ -19,5 +20,5 @@ public interface ImportService { * @param dataObject 연계 파일 정보 * @return 저장 여부 */ - HashMap createCrdnByEquipmentLinkFile(String taskSeCd, String entType, List linkFileInfoList); + HashMap createCrdnByEquipmentLinkFile(Map processInfo, List linkFileInfoList); } \ No newline at end of file diff --git a/src/main/java/cokr/xit/fims/crdn/service/bean/CrdnBean.java b/src/main/java/cokr/xit/fims/crdn/service/bean/CrdnBean.java index e396fa70..4f453537 100644 --- a/src/main/java/cokr/xit/fims/crdn/service/bean/CrdnBean.java +++ b/src/main/java/cokr/xit/fims/crdn/service/bean/CrdnBean.java @@ -344,35 +344,31 @@ public class CrdnBean extends AbstractComponent { } /** 장비 연계파일을 삭제한다. - * @param entType 업체유형, fileName 파일명 + * @param workPath 작업 디렉토리, fileName 파일명 * @return 저장 여부 */ - public boolean removeEquipmentLinkFile(String entType, String fileName) { + public boolean removeEquipmentLinkFile(String workPath, String fileName) { boolean saved = false; - if(entType.equals("INO")) { + try { - try { + Stream walk = Files.walk(Paths.get(workPath)); - Stream walk = Files.walk(Paths.get("files"+File.separator+"tempForIno")); + List deleteFilePaths = new ArrayList(); - List deleteFilePaths = new ArrayList(); + walk.filter(Files::isRegularFile) + .filter(p -> p.getFileName().toString().equalsIgnoreCase(fileName)) + .collect(Collectors.toList()) + .forEach(item -> deleteFilePaths.add(item.toFile().getPath())); - walk.filter(Files::isRegularFile) - .filter(p -> p.getFileName().toString().equalsIgnoreCase(fileName)) - .collect(Collectors.toList()) - .forEach(item -> deleteFilePaths.add(item.toFile().getPath())); - - for(String deleteFilePath : deleteFilePaths) { - saved = (new File(deleteFilePath)).delete(); - } - - //빈 디렉토리 삭제 - CmmnUtil.deleteEmptyDir(new File("files"+File.separator+"tempForIno"), false); - } catch (Exception e) { - throw new RuntimeException(); + for(String deleteFilePath : deleteFilePaths) { + saved = (new File(deleteFilePath)).delete(); } + //빈 디렉토리 삭제 + CmmnUtil.deleteEmptyDir(new File("files"+File.separator+"tempForIno"), false); + } catch (Exception e) { + throw new RuntimeException(); } return saved; diff --git a/src/main/java/cokr/xit/fims/crdn/service/bean/CrdnServiceBean.java b/src/main/java/cokr/xit/fims/crdn/service/bean/CrdnServiceBean.java index 9b8e4342..e5c1332f 100644 --- a/src/main/java/cokr/xit/fims/crdn/service/bean/CrdnServiceBean.java +++ b/src/main/java/cokr/xit/fims/crdn/service/bean/CrdnServiceBean.java @@ -242,7 +242,7 @@ public class CrdnServiceBean extends AbstractServiceBean implements CrdnService } @Override - public boolean removeEquipmentLinkFile(String entType, String fileName) { - return crdnBean.removeEquipmentLinkFile(entType, fileName); + public boolean removeEquipmentLinkFile(String workPath, String fileName) { + return crdnBean.removeEquipmentLinkFile(workPath, fileName); } } diff --git a/src/main/java/cokr/xit/fims/crdn/service/bean/ImportServiceBean.java b/src/main/java/cokr/xit/fims/crdn/service/bean/ImportServiceBean.java index cb9f9178..0591bddc 100644 --- a/src/main/java/cokr/xit/fims/crdn/service/bean/ImportServiceBean.java +++ b/src/main/java/cokr/xit/fims/crdn/service/bean/ImportServiceBean.java @@ -76,9 +76,14 @@ public class ImportServiceBean extends AbstractServiceBean implements ImportServ } @Override - public HashMap createCrdnByEquipmentLinkFile(String taskSeCd, String entType, List linkFileInfoList) { + public HashMap createCrdnByEquipmentLinkFile(Map processInfo, List linkFileInfoList) { HashMap resultMap = new HashMap<>(); boolean saved = false; + String institute = processInfo.get("institute"); + String taskSeCd = processInfo.get("taskSeCd"); + String entType = processInfo.get("entType"); + String equipmentType = processInfo.get("equipmentType"); + String workPath = processInfo.get("workPath"); Crdn crdn = new Crdn(); crdn.setCrdnRegSeCd("07"); @@ -149,7 +154,7 @@ public class ImportServiceBean extends AbstractServiceBean implements ImportServ if(saved) { for(DataObject delInfo : linkFileInfoList) { - crdnBean.removeEquipmentLinkFile(entType, delInfo.string("FILE_NAME")); + crdnBean.removeEquipmentLinkFile(workPath, delInfo.string("FILE_NAME")); } if(!ifEmpty(crdn.getErsrRegYmd(), () -> "").equals("")) { diff --git a/src/main/java/cokr/xit/fims/crdn/web/Crdn05Controller.java b/src/main/java/cokr/xit/fims/crdn/web/Crdn05Controller.java index 46a59f39..8e78e461 100644 --- a/src/main/java/cokr/xit/fims/crdn/web/Crdn05Controller.java +++ b/src/main/java/cokr/xit/fims/crdn/web/Crdn05Controller.java @@ -1,6 +1,5 @@ package cokr.xit.fims.crdn.web; -import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -29,6 +28,8 @@ import cokr.xit.fims.crdn.parsing.EquipmentEnterprise; import cokr.xit.fims.crdn.parsing.HiteCom; import cokr.xit.fims.crdn.parsing.Ino; import cokr.xit.fims.crdn.parsing.Knl; +import cokr.xit.fims.crdn.parsing.ParsingUtil; +import cokr.xit.fims.crdn.parsing.XitRecommend; import cokr.xit.fims.crdn.service.CrdnService; import cokr.xit.fims.crdn.service.CrdnStngService; import cokr.xit.fims.crdn.service.ImportService; @@ -48,8 +49,8 @@ public class Crdn05Controller extends ApplicationController { getTodayCrdnDataList = "/010/list.do", getFileRegistrationScreen = "/020/info.do", - importFileFromServer = "/020/importFileFromServer", - importFileFromClient = "/020/importFileFromClient", + importFileFromServer = "/020/importFileFromServer.do", + importFileFromClient = "/020/importFileFromClient.do", getEquipmentFileInfoList = "/020/list.do", removeLinkFile = "/020/remove.do", createCrdnByLinkFile = "/020/create.do", @@ -131,41 +132,31 @@ public class Crdn05Controller extends ApplicationController { case "INO": enterprise = new Ino(); break; case "KNL": enterprise = new Knl(); break; case "HITECOM": enterprise = new HiteCom(); break; - //case "": enterprise = new (); break; + case "XIT": enterprise = new XitRecommend(); break; //case "": enterprise = new (); break; } + String institute = currentUser().getInstitute(); + String taskSeCd = hReq.getParameter("taskSeCd"); + String equipmentType = hReq.getParameter("equipmentType"); - Stream fileList = null; - List dataObjectList = new ArrayList<>(); - - String downloadRoot = ""; //ftp나 usb에서 다운받는 파일 경로 + String workPath = ParsingUtil.getWorkDirectoryPath(institute, taskSeCd, entType, equipmentType); //ftp G클라우드 - if(entType.equals("INO")) { - - //RemoteSystemInfo rs = new RemoteSystemInfo(); - //rs.setIp("211.119.124.9"); - //rs.setId("xituser"); - //rs.setPw("xituser!@"); - //rs.setOsType("linux"); - //String remoteWorkPath = File.separator + "applications" - // + File.separator + "tempForFTPTest" - // + File.separator + "ino" - // + File.separator; - downloadRoot = "files"+File.separator+"tempForIno"; - } - if(entType.equals("KNL")) { - downloadRoot = "files"+File.separator+"tempForKnl"; - } - if(entType.equals("HITECOM")) { - downloadRoot = "files"+File.separator+"tempForHitecom"; - } + //RemoteSystemInfo rs = new RemoteSystemInfo(); + //rs.setIp("211.119.124.9"); + //rs.setId("xituser"); + //rs.setPw("xituser!@"); + //rs.setOsType("linux"); + //String remoteWorkPath = File.separator + "applications" + // + File.separator + "tempForFTPTest" + // + File.separator + "ino" + // + File.separator; try { - //boolean result = FTPUtil.fileDown(rs, remoteWorkPath, downloadRoot); + //boolean result = FTPUtil.fileDown(rs, remoteWorkPath, workPath); } catch (Exception e) { e.printStackTrace(); } @@ -189,26 +180,14 @@ public class Crdn05Controller extends ApplicationController { case "INO": enterprise = new Ino(); break; case "KNL": enterprise = new Knl(); break; case "HITECOM": enterprise = new HiteCom(); break; + case "XIT": enterprise = new XitRecommend(); break; //case "": enterprise = new (); break; - //case "": enterprise = new (); break; - } - - - Stream fileList = null; - List dataObjectList = new ArrayList<>(); - - String downloadRoot = ""; //ftp나 usb에서 다운받는 파일 경로 - - if(entType.equals("INO")) { - downloadRoot = "files"+File.separator+"tempForIno"; - } - if(entType.equals("KNL")) { - downloadRoot = "files"+File.separator+"tempForKnl"; - } - if(entType.equals("HITECOM")) { - downloadRoot = "files"+File.separator+"tempForHitecom"; } + String institute = currentUser().getInstitute(); + String taskSeCd = hReq.getParameter("taskSeCd"); + String equipmentType = hReq.getParameter("equipmentType"); + String workPath = ParsingUtil.getWorkDirectoryPath(institute, taskSeCd, entType, equipmentType); //TODO : @@ -229,37 +208,25 @@ public class Crdn05Controller extends ApplicationController { case "INO": enterprise = new Ino(); break; case "KNL": enterprise = new Knl(); break; case "HITECOM": enterprise = new HiteCom(); break; - //case "": enterprise = new (); break; + case "XIT": enterprise = new XitRecommend(); break; //case "": enterprise = new (); break; } - Stream fileList = null; - List dataObjectList = new ArrayList<>(); - - String downloadRoot = ""; - - if(entType.equals("INO") || entType.equals("KNL")) { - if(entType.equals("INO")) { - downloadRoot = "files"+File.separator+"tempForIno"; - } - if(entType.equals("KNL")) { - downloadRoot = "files"+File.separator+"tempForKnl"; - } - } - - if(entType.equals("HITECOM")) { - downloadRoot = "files"+File.separator+"tempForHitecom"; - } + String institute = currentUser().getInstitute(); + String taskSeCd = hReq.getParameter("taskSeCd"); + String equipmentType = hReq.getParameter("equipmentType"); + String workPath = ParsingUtil.getWorkDirectoryPath(institute, taskSeCd, entType, equipmentType); + Stream fileList = null; try { //폴더는 제외하고 파일만 필터링 - fileList = Files.walk(Paths.get(downloadRoot)).filter(Files::isRegularFile); + fileList = Files.walk(Paths.get(workPath)).filter(Files::isRegularFile); } catch (IOException e) { e.printStackTrace(); } - dataObjectList = enterprise.parsing(fileList); + List dataObjectList = enterprise.parsing(fileList); mav = setCollectionInfo(mav, dataObjectList, ""); return mav; } @@ -278,7 +245,12 @@ public class Crdn05Controller extends ApplicationController { String entType = hReq.getParameter("entType"); String fileName = hReq.getParameter("fileName"); - boolean saved = crdnService.removeEquipmentLinkFile(entType, fileName); + String institute = currentUser().getInstitute(); + String taskSeCd = hReq.getParameter("taskSeCd"); + String equipmentType = hReq.getParameter("equipmentType"); + String workPath = ParsingUtil.getWorkDirectoryPath(institute, taskSeCd, entType, equipmentType); + + boolean saved = crdnService.removeEquipmentLinkFile(workPath, fileName); mav.addObject("saved", saved); return mav; @@ -292,16 +264,25 @@ public class Crdn05Controller extends ApplicationController { * "saved": 등록되었으면 true, 그렇지 않으면 false * } */ - public ModelAndView createCrdnByLinkFile(String taskSeCd, String entType, String[] linkFileInfos) { + public ModelAndView createCrdnByLinkFile(String taskSeCd, String entType, String equipmentType, String[] linkFileInfos) { ModelAndView mav = new ModelAndView("jsonView"); - + String institute = currentUser().getInstitute(); List linkFileInfoList = new ArrayList(); for(String linkFileInfo : linkFileInfos) { linkFileInfoList.add(fromJson(linkFileInfo, DataObject.class)); } - HashMap resultMap = importService.createCrdnByEquipmentLinkFile(taskSeCd, entType, linkFileInfoList); + Map processInfo = new HashMap(); + processInfo.put("institute", institute); + processInfo.put("taskSeCd", taskSeCd); + processInfo.put("entType", entType); + processInfo.put("equipmentType", equipmentType); + + String workPath = ParsingUtil.getWorkDirectoryPath(institute, taskSeCd, entType, equipmentType); + processInfo.put("workPath", workPath); + + HashMap resultMap = importService.createCrdnByEquipmentLinkFile(processInfo, linkFileInfoList); boolean saved = (boolean) resultMap.get("saved"); mav.addObject("saved", saved); diff --git a/src/main/java/cokr/xit/fims/task/web/BpvController.java b/src/main/java/cokr/xit/fims/task/web/BpvController.java index 8c087f61..d1a2a8a2 100644 --- a/src/main/java/cokr/xit/fims/task/web/BpvController.java +++ b/src/main/java/cokr/xit/fims/task/web/BpvController.java @@ -240,6 +240,12 @@ public class BpvController { return super.removeLinkFile(hReq); } + @Override + @RequestMapping(name="전용차로과태료업무 장비업체 단속파일로 단속자료 생성", value=METHOD_URL.createCrdnByLinkFile) + public ModelAndView createCrdnByLinkFile(String taskSeCd, String entType, String equipmentType, String[] linkFileInfos) { + return super.createCrdnByLinkFile(taskSeCd, entType, equipmentType, linkFileInfos); + } + @Override @RequestMapping(name="전용차로과태료업무 단속자료 수기 등록 화면", value=METHOD_URL.getManualRegistrationScreen) public ModelAndView getManualRegistrationScreen(HttpServletRequest hReq) { diff --git a/src/main/java/cokr/xit/fims/task/web/DpvController.java b/src/main/java/cokr/xit/fims/task/web/DpvController.java index 1942c127..2ac959f5 100644 --- a/src/main/java/cokr/xit/fims/task/web/DpvController.java +++ b/src/main/java/cokr/xit/fims/task/web/DpvController.java @@ -267,8 +267,8 @@ public class DpvController { @Override @RequestMapping(name="장애인과태료업무 장비업체 단속파일로 단속자료 생성", value=METHOD_URL.createCrdnByLinkFile) - public ModelAndView createCrdnByLinkFile(String taskSeCd, String entType, String[] linkFileInfos) { - return super.createCrdnByLinkFile(taskSeCd, entType, linkFileInfos); + public ModelAndView createCrdnByLinkFile(String taskSeCd, String entType, String equipmentType, String[] linkFileInfos) { + return super.createCrdnByLinkFile(taskSeCd, entType, equipmentType, linkFileInfos); } @Override diff --git a/src/main/java/cokr/xit/fims/task/web/EcaController.java b/src/main/java/cokr/xit/fims/task/web/EcaController.java index 1e20304e..055e223c 100644 --- a/src/main/java/cokr/xit/fims/task/web/EcaController.java +++ b/src/main/java/cokr/xit/fims/task/web/EcaController.java @@ -243,8 +243,8 @@ public class EcaController { @Override @RequestMapping(name="전기차과태료업무 장비업체 단속파일로 단속자료 생성", value=METHOD_URL.createCrdnByLinkFile) - public ModelAndView createCrdnByLinkFile(String taskSeCd, String entType, String[] linkFileInfos) { - return super.createCrdnByLinkFile(taskSeCd, entType, linkFileInfos); + public ModelAndView createCrdnByLinkFile(String taskSeCd, String entType, String equipmentType, String[] linkFileInfos) { + return super.createCrdnByLinkFile(taskSeCd, entType, equipmentType, linkFileInfos); } @Override diff --git a/src/main/java/cokr/xit/fims/task/web/PvsController.java b/src/main/java/cokr/xit/fims/task/web/PvsController.java index a663c4e6..c7d1af44 100644 --- a/src/main/java/cokr/xit/fims/task/web/PvsController.java +++ b/src/main/java/cokr/xit/fims/task/web/PvsController.java @@ -253,6 +253,12 @@ public class PvsController { return super.removeLinkFile(hReq); } + @Override + @RequestMapping(name="주정차과태료업무 장비업체 단속파일로 단속자료 생성", value=METHOD_URL.createCrdnByLinkFile) + public ModelAndView createCrdnByLinkFile(String taskSeCd, String entType, String equipmentType, String[] linkFileInfos) { + return super.createCrdnByLinkFile(taskSeCd, entType, equipmentType, linkFileInfos); + } + @Override @RequestMapping(name="주정차과태료업무 단속자료 수기 등록 화면", value=METHOD_URL.getManualRegistrationScreen) public ModelAndView getManualRegistrationScreen(HttpServletRequest hReq) { diff --git a/src/main/webapp/WEB-INF/jsp/fims/crdn/crdn05020-info.jsp b/src/main/webapp/WEB-INF/jsp/fims/crdn/crdn05020-info.jsp index de674881..1e7e0d5a 100644 --- a/src/main/webapp/WEB-INF/jsp/fims/crdn/crdn05020-info.jsp +++ b/src/main/webapp/WEB-INF/jsp/fims/crdn/crdn05020-info.jsp @@ -30,8 +30,6 @@ - - @@ -41,9 +39,9 @@
- - @@ -58,7 +56,7 @@
- + @@ -220,6 +218,7 @@ $(document).ready(function(){ var $P = pageObject["${pageName}"]; $P.entType = ""; + $P.equipmentType = ""; $P.tempGroup = {}; @@ -313,10 +312,16 @@ $(document).ready(function(){ ajax.post({ url : wctx.url("/${taskSeCd}/crdn/crdn05/020/importFileFromServer.do"), - data : { entType : $("#entType--${pageName}").val() }, + data : { + taskSeCd : $("#taskSeCd--${pageName}").val(), + entType : $("#entType--${pageName}").val(), + equipmentType : $("#equipmentType--${pageName}").val() + }, success : (resp) => { if(resp.saved){ $P.searchFileList(); + } else { + dialog.alert("파일 조회에 실패하였습니다."); } } }); @@ -336,10 +341,16 @@ $(document).ready(function(){ ajax.post({ url : wctx.url("/${taskSeCd}/crdn/crdn05/020/importFileFromClient.do"), - data : { entType : $("#entType--${pageName}").val() }, + data : { + taskSeCd : $("#taskSeCd--${pageName}").val(), + entType : $("#entType--${pageName}").val(), + equipmentType : $("#equipmentType--${pageName}").val() + }, success : (resp) => { if(resp.saved){ $P.searchFileList(); + } else { + dialog.alert("파일 업로드에 실패하였습니다."); } } }); @@ -360,12 +371,18 @@ $(document).ready(function(){ $P.entType = ""; } + $P.equipmentType = $("#equipmentType--${pageName}").val(); + $("#table-responsive--${pageName}").find("span").attr("hidden","hidden"); $("#table-responsive--${pageName}").find("."+$P.entType).removeAttr("hidden"); ajax.post({ url : wctx.url("/${taskSeCd}/crdn/crdn05/020/list.do"), - data : { entType : $("#entType--${pageName}").val() }, + data : { + taskSeCd : $("#taskSeCd--${pageName}").val(), + entType : $("#entType--${pageName}").val(), + equipmentType : $("#equipmentType--${pageName}").val() + }, success : (resp) => { $P.parsedInfoControl.setData(resp); } @@ -442,6 +459,7 @@ $(document).ready(function(){ var formData = new FormData(document.getElementById("frmMultipart--${pageName}")); formData.append("taskSeCd", "${taskSeCd}"); formData.append("entType", $P.entType); + formData.append("equipmentType", $("#equipmentType--${pageName}").val()); for(var i=0; i<$P.tempGroup[firstGroupKey].length; i++){ formData.append("linkFileInfos", JSON.stringify($P.tempGroup[firstGroupKey][i])); } @@ -482,7 +500,9 @@ $(document).ready(function(){ ajax.get({ url : wctx.url("/${taskSeCd}/crdn/crdn05/020/remove.do"), data : { + taskSeCd : $("#taskSeCd--${pageName}").val(), entType : $P.entType, + equipmentType : $("#equipmentType--${pageName}").val(), fileName : fileName }, success : (resp) => { diff --git a/src/main/webapp/resources/css/fims/framework/common/common.css b/src/main/webapp/resources/css/fims/framework/common/common.css index cc49c204..0dcc0ee0 100644 --- a/src/main/webapp/resources/css/fims/framework/common/common.css +++ b/src/main/webapp/resources/css/fims/framework/common/common.css @@ -9,10 +9,6 @@ label.required:after { color: red; } -input[type=file] { - height: 24px; - padding: 0 5px; } - input[type=text]:disabled, input[type=text]:read-only { background-color: #eceef1; }