diff --git a/src/main/java/com/itn/admin/cmn/msg/RestResponse.java b/src/main/java/com/itn/admin/cmn/msg/RestResponse.java index 99dbf2b..88fcb0a 100644 --- a/src/main/java/com/itn/admin/cmn/msg/RestResponse.java +++ b/src/main/java/com/itn/admin/cmn/msg/RestResponse.java @@ -37,6 +37,11 @@ public class RestResponse { this.msg = msg; this.data = data; } + @Builder + public RestResponse(HttpStatus status, String msg) { + this.status = status; + this.msg = msg; + } } diff --git a/src/main/java/com/itn/admin/commute/mapper/domain/ItnCommuteVO.java b/src/main/java/com/itn/admin/commute/mapper/domain/ItnCommuteVO.java index 42f8b42..a1b2521 100644 --- a/src/main/java/com/itn/admin/commute/mapper/domain/ItnCommuteVO.java +++ b/src/main/java/com/itn/admin/commute/mapper/domain/ItnCommuteVO.java @@ -18,7 +18,7 @@ public class ItnCommuteVO implements Serializable { private Integer commuteGroupId; // 그룹 아이디 private String uniqId; private String userName; // 이름 - private String userRank; // 직위 + private String rankCd; // 직위 private String category; // 구분 private String workDt; // 근무일자 private String startTime; // 출근시간 diff --git a/src/main/java/com/itn/admin/commute/service/impl/CommuteServiceImpl.java b/src/main/java/com/itn/admin/commute/service/impl/CommuteServiceImpl.java index 0c26918..52b0950 100644 --- a/src/main/java/com/itn/admin/commute/service/impl/CommuteServiceImpl.java +++ b/src/main/java/com/itn/admin/commute/service/impl/CommuteServiceImpl.java @@ -109,7 +109,7 @@ public class CommuteServiceImpl implements CommuteService { if( matchedUser != null ){ t.setUsrid(matchedUser.getUserName()); - t.setPstn(matchedUser.getUserRank()); + t.setPstn(matchedUser.getRankCd()); t.setUniqId(matchedUser.getUniqId()); } diff --git a/src/main/java/com/itn/admin/itn/bizTrip/mapper/BizTripMapper.java b/src/main/java/com/itn/admin/itn/bizTrip/mapper/BizTripMapper.java new file mode 100644 index 0000000..674d325 --- /dev/null +++ b/src/main/java/com/itn/admin/itn/bizTrip/mapper/BizTripMapper.java @@ -0,0 +1,19 @@ +package com.itn.admin.itn.bizTrip.mapper; + +import com.itn.admin.itn.bizTrip.mapper.domain.BizTripApprovalVO; +import com.itn.admin.itn.bizTrip.mapper.domain.BizTripMemberVO; +import com.itn.admin.itn.bizTrip.mapper.domain.BizTripVO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface BizTripMapper { + void insertBizTrip(BizTripVO trip); + + void insertTripMember(BizTripMemberVO member); + + void insertApprovalLine(BizTripApprovalVO approval); + + List selectTripList(); +} diff --git a/src/main/java/com/itn/admin/itn/bizTrip/mapper/domain/BizTripApprovalVO.java b/src/main/java/com/itn/admin/itn/bizTrip/mapper/domain/BizTripApprovalVO.java new file mode 100644 index 0000000..5341545 --- /dev/null +++ b/src/main/java/com/itn/admin/itn/bizTrip/mapper/domain/BizTripApprovalVO.java @@ -0,0 +1,29 @@ +package com.itn.admin.itn.bizTrip.mapper.domain; + +import com.itn.admin.cmn.vo.CmnVO; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; + + +@Getter +@Setter +@ToString +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class BizTripApprovalVO extends CmnVO { + + private Integer id; + private Integer tripId; // 출장 ID (FK) + private String approverId; // 결재자 uniq_id + private Integer orderNo; // 결재 순서 + private String approveStatus; // 결재 상태 (WAIT, APPROVED, REJECTED) + private LocalDateTime approveDt; // 결재 일시 + private String comment; // 결재 의견 + + +} diff --git a/src/main/java/com/itn/admin/itn/bizTrip/mapper/domain/BizTripMemberVO.java b/src/main/java/com/itn/admin/itn/bizTrip/mapper/domain/BizTripMemberVO.java new file mode 100644 index 0000000..9da0b60 --- /dev/null +++ b/src/main/java/com/itn/admin/itn/bizTrip/mapper/domain/BizTripMemberVO.java @@ -0,0 +1,25 @@ +package com.itn.admin.itn.bizTrip.mapper.domain; + +import com.itn.admin.cmn.vo.CmnVO; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import java.io.Serializable; +import java.time.LocalDate; +import java.time.LocalTime; + + +@Getter +@Setter +@ToString +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class BizTripMemberVO extends CmnVO { + + private Integer id; + private Integer tripId; // 출장 ID (FK) + private String uniqId; // 유저 고유 ID (FK) + private String role; // 역할 (기안자: 0, 동행자: 1 등) + +} diff --git a/src/main/java/com/itn/admin/itn/bizTrip/mapper/domain/BizTripRequestDTO.java b/src/main/java/com/itn/admin/itn/bizTrip/mapper/domain/BizTripRequestDTO.java new file mode 100644 index 0000000..4e91e73 --- /dev/null +++ b/src/main/java/com/itn/admin/itn/bizTrip/mapper/domain/BizTripRequestDTO.java @@ -0,0 +1,17 @@ +package com.itn.admin.itn.bizTrip.mapper.domain; + +import lombok.*; + +import java.util.List; + +@Getter +@Setter +@ToString +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class BizTripRequestDTO { + private BizTripVO tripInfo; + private List tripMembers; + private List approvalLines; +} diff --git a/src/main/java/com/itn/admin/itn/bizTrip/mapper/domain/BizTripVO.java b/src/main/java/com/itn/admin/itn/bizTrip/mapper/domain/BizTripVO.java new file mode 100644 index 0000000..936ae6b --- /dev/null +++ b/src/main/java/com/itn/admin/itn/bizTrip/mapper/domain/BizTripVO.java @@ -0,0 +1,33 @@ +package com.itn.admin.itn.bizTrip.mapper.domain; + +import com.itn.admin.cmn.vo.CmnVO; +import lombok.*; +import lombok.experimental.SuperBuilder; + +import java.time.LocalDate; +import java.time.LocalTime; + + +@Getter +@Setter +@ToString +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +public class BizTripVO extends CmnVO { + + private Integer tripId; // 출장 고유 ID + private String tripTypeCd; // 출장 구분 (TRIP_TYPE 공통코드) + private String locationCd; // 출장지 (TRIP_LOCATION 공통코드) + private String locationTxt; // 출장지 (TRIP_LOCATION 공통코드) + private String purpose; // 출장 목적 + private String moveCd; // 이동 수단 (TRIP_MOVE 공통코드) + private LocalDate tripDt; // 출장일자 + private LocalTime startTime; // 출장 시작시간 + private LocalTime endTime; // 출장 종료시간 + private String status; // 결재 상태 (ING, DONE 등) + + + private String latestApproveStatus; // 최신 결재 상태 + +} diff --git a/src/main/java/com/itn/admin/itn/bizTrip/service/BizTripService.java b/src/main/java/com/itn/admin/itn/bizTrip/service/BizTripService.java new file mode 100644 index 0000000..def276e --- /dev/null +++ b/src/main/java/com/itn/admin/itn/bizTrip/service/BizTripService.java @@ -0,0 +1,13 @@ +package com.itn.admin.itn.bizTrip.service; + +import com.itn.admin.cmn.msg.RestResponse; +import com.itn.admin.itn.bizTrip.mapper.domain.BizTripRequestDTO; +import com.itn.admin.itn.bizTrip.mapper.domain.BizTripVO; + +import java.util.List; + +public interface BizTripService { + RestResponse register(BizTripRequestDTO dto); + + List selectTripList(); +} diff --git a/src/main/java/com/itn/admin/itn/bizTrip/service/impl/BizTripServiceImpl.java b/src/main/java/com/itn/admin/itn/bizTrip/service/impl/BizTripServiceImpl.java new file mode 100644 index 0000000..d0e585e --- /dev/null +++ b/src/main/java/com/itn/admin/itn/bizTrip/service/impl/BizTripServiceImpl.java @@ -0,0 +1,60 @@ +package com.itn.admin.itn.bizTrip.service.impl; + +import com.itn.admin.cmn.msg.RestResponse; +import com.itn.admin.itn.bizTrip.mapper.BizTripMapper; +import com.itn.admin.itn.bizTrip.mapper.domain.BizTripApprovalVO; +import com.itn.admin.itn.bizTrip.mapper.domain.BizTripMemberVO; +import com.itn.admin.itn.bizTrip.mapper.domain.BizTripRequestDTO; +import com.itn.admin.itn.bizTrip.mapper.domain.BizTripVO; +import com.itn.admin.itn.bizTrip.service.BizTripService; +import com.itn.admin.itn.code.mapper.CodeMapper; +import com.itn.admin.itn.code.mapper.domain.CodeVO; +import com.itn.admin.itn.code.server.CodeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class BizTripServiceImpl implements BizTripService { + + @Autowired + private BizTripMapper bizTripMapper; + + @Override + public RestResponse register(BizTripRequestDTO dto) { + // 1. BizTripVO insert (자동 생성된 tripId 얻기) + BizTripVO trip = dto.getTripInfo(); + bizTripMapper.insertBizTrip(trip); // insert 후 trip.tripId에 PK 자동 세팅됨 + + Integer tripId = trip.getTripId(); + + // 2. 출장 참여 인원 등록 + List memberList = dto.getTripMembers(); + if (memberList != null && !memberList.isEmpty()) { + for (BizTripMemberVO member : memberList) { + member.setTripId(tripId); + bizTripMapper.insertTripMember(member); + } + } + + // 3. 결재 라인 등록 + List approvalList = dto.getApprovalLines(); + if (approvalList != null && !approvalList.isEmpty()) { + for (BizTripApprovalVO approval : approvalList) { + approval.setTripId(tripId); + bizTripMapper.insertApprovalLine(approval); + } + } + + + return new RestResponse(HttpStatus.OK, "등록되었습니다"); + } + + @Override + public List selectTripList() { + return bizTripMapper.selectTripList(); + + } +} diff --git a/src/main/java/com/itn/admin/itn/trip/web/TripController.java b/src/main/java/com/itn/admin/itn/bizTrip/web/BizTripController.java similarity index 53% rename from src/main/java/com/itn/admin/itn/trip/web/TripController.java rename to src/main/java/com/itn/admin/itn/bizTrip/web/BizTripController.java index 9c997a0..4710e5c 100644 --- a/src/main/java/com/itn/admin/itn/trip/web/TripController.java +++ b/src/main/java/com/itn/admin/itn/bizTrip/web/BizTripController.java @@ -1,42 +1,42 @@ -package com.itn.admin.itn.trip.web; +package com.itn.admin.itn.bizTrip.web; import com.itn.admin.cmn.config.CustomUserDetails; +import com.itn.admin.itn.bizTrip.mapper.domain.BizTripVO; +import com.itn.admin.itn.bizTrip.service.BizTripService; import com.itn.admin.itn.user.mapper.domain.UserVO; import com.itn.admin.itn.user.service.UserService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; -import java.util.Map; +import java.util.List; @Slf4j @Controller -public class TripController { +public class BizTripController { @Autowired - private UserService userService; + private BizTripService bizTripService; - @GetMapping("/itn/trip/reg") - public String list(@ModelAttribute("userVO") UserVO userVO - ,@AuthenticationPrincipal CustomUserDetails loginUser + @GetMapping("/itn/bizTrip/reg") + public String list(@AuthenticationPrincipal CustomUserDetails loginUser ,Model model ) { log.info(" + loginUser :: [{}]", loginUser.getUser()); - -/* - Map resultMap = userService.getList(userVO); - - model.addAttribute("list", resultMap.get("resultList")); -*/ model.addAttribute("loginUser", loginUser.getUser()); - return "itn/trip/reg"; + return "itn/bizTrip/reg"; + } + @GetMapping("/itn/bizTrip/list") + public String bizTripList(Model model) { + List list = bizTripService.selectTripList(); + model.addAttribute("list", list); + return "itn/bizTrip/list"; // Thymeleaf HTML 파일 경로 } } diff --git a/src/main/java/com/itn/admin/itn/bizTrip/web/BizTripRestController.java b/src/main/java/com/itn/admin/itn/bizTrip/web/BizTripRestController.java new file mode 100644 index 0000000..bb40d52 --- /dev/null +++ b/src/main/java/com/itn/admin/itn/bizTrip/web/BizTripRestController.java @@ -0,0 +1,37 @@ +package com.itn.admin.itn.bizTrip.web; + +import com.itn.admin.cmn.config.CustomUserDetails; +import com.itn.admin.cmn.msg.RestResponse; +import com.itn.admin.itn.bizTrip.mapper.domain.BizTripRequestDTO; +import com.itn.admin.itn.bizTrip.service.BizTripService; +import com.itn.admin.itn.code.mapper.domain.CodeDetailVO; +import com.itn.admin.itn.user.mapper.domain.UserVO; +import com.itn.admin.itn.user.service.UserService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; + +@Slf4j +@RestController +public class BizTripRestController { + + @Autowired + private BizTripService bizTripService; + + @PostMapping("/api/bizTrip/register") + public ResponseEntity registerBizTrip(@RequestBody BizTripRequestDTO dto + , @AuthenticationPrincipal CustomUserDetails loginUser) { + dto.getTripInfo().setFrstRegisterId(loginUser.getUser().getUniqId()); + log.info("dto: [{}]", dto); +// bizTripService.register(dto); + return ResponseEntity.ok().body(bizTripService.register(dto)); + } + + + + +} diff --git a/src/main/java/com/itn/admin/itn/user/mapper/UserMapper.java b/src/main/java/com/itn/admin/itn/user/mapper/UserMapper.java index 150f717..b7619df 100644 --- a/src/main/java/com/itn/admin/itn/user/mapper/UserMapper.java +++ b/src/main/java/com/itn/admin/itn/user/mapper/UserMapper.java @@ -41,6 +41,10 @@ public interface UserMapper { void changepassword(UserVO userVO); + List selectUsersByNameAndRank(String userName); + + @Select("SELECT * FROM users WHERE user_name LIKE CONCAT('%', #{userName}, '%')") List findByUniqUserName(String userName); + } diff --git a/src/main/java/com/itn/admin/itn/user/mapper/domain/UserVO.java b/src/main/java/com/itn/admin/itn/user/mapper/domain/UserVO.java index a2bdc24..01ae02f 100644 --- a/src/main/java/com/itn/admin/itn/user/mapper/domain/UserVO.java +++ b/src/main/java/com/itn/admin/itn/user/mapper/domain/UserVO.java @@ -21,7 +21,7 @@ public class UserVO extends CmnVO { private String userId; private String password; private String userName; - private String userRank; + private String rankCd; private String mobilePhone; private String gwId; private String biostarId; diff --git a/src/main/java/com/itn/admin/itn/user/service/UserService.java b/src/main/java/com/itn/admin/itn/user/service/UserService.java index 0719abf..f2b0148 100644 --- a/src/main/java/com/itn/admin/itn/user/service/UserService.java +++ b/src/main/java/com/itn/admin/itn/user/service/UserService.java @@ -26,4 +26,5 @@ public interface UserService { RestResponse changepassword(UserVO userVO); RestResponse findByUniqUserName(String userName); + RestResponse findByUniqApprovalUser(String userName); } diff --git a/src/main/java/com/itn/admin/itn/user/service/impl/UserServiceImpl.java b/src/main/java/com/itn/admin/itn/user/service/impl/UserServiceImpl.java index 0654d60..8019aab 100644 --- a/src/main/java/com/itn/admin/itn/user/service/impl/UserServiceImpl.java +++ b/src/main/java/com/itn/admin/itn/user/service/impl/UserServiceImpl.java @@ -107,6 +107,27 @@ public class UserServiceImpl implements UserService { users.forEach(user -> { String deptNm = tCodeUtils.getCodeName("DEPT", user.getDeptCd()); user.setDeptNm(deptNm); // UserVO에 deptCdName 필드 있어야 함 + String rankNm = tCodeUtils.getCodeName("RANK", user.getRankCd()); + user.setDeptNm(rankNm); // UserVO에 deptCdName 필드 있어야 함 + }); + + + return RestResponse.builder() + .status(HttpStatus.OK) // 200 성공 + .data(users) +// .msg("수정되었습니다.") + .build(); + + } @Override + public RestResponse findByUniqApprovalUser(String userName) { + + List users = userMapper.selectUsersByNameAndRank(userName); + // 코드 이름을 붙여주는 처리 (가공) + users.forEach(user -> { + String deptNm = tCodeUtils.getCodeName("DEPT", user.getDeptCd()); + user.setDeptNm(deptNm); // UserVO에 deptCdName 필드 있어야 함 + String rankNm = tCodeUtils.getCodeName("RANK", user.getRankCd()); + user.setDeptNm(rankNm); // UserVO에 deptCdName 필드 있어야 함 }); return RestResponse.builder() diff --git a/src/main/java/com/itn/admin/itn/user/web/UserRestController.java b/src/main/java/com/itn/admin/itn/user/web/UserRestController.java index 9e24aab..2879e87 100644 --- a/src/main/java/com/itn/admin/itn/user/web/UserRestController.java +++ b/src/main/java/com/itn/admin/itn/user/web/UserRestController.java @@ -49,6 +49,11 @@ public class UserRestController { log.info("userName: {}", userName); return ResponseEntity.ok(userService.findByUniqUserName(userName)); } + @GetMapping("/api/admin/approval/search/name") + public ResponseEntity findByUniqApprovalUser(@RequestParam String userName) { + log.info("userName: {}", userName); + return ResponseEntity.ok(userService.findByUniqApprovalUser(userName)); + } // 코드 그룹 수정 메서드 @PutMapping("/api/admin/user/{uniqId}") diff --git a/src/main/resources/mapper/itn/bizTrip/BizTripMapper.xml b/src/main/resources/mapper/itn/bizTrip/BizTripMapper.xml new file mode 100644 index 0000000..8775c1c --- /dev/null +++ b/src/main/resources/mapper/itn/bizTrip/BizTripMapper.xml @@ -0,0 +1,104 @@ + + + + + + + + INSERT INTO biz_trip ( + trip_type_cd, location_cd, location_txt, purpose, + move_cd, trip_dt, start_time, end_time, status, + frst_register_id + ) VALUES ( + #{tripTypeCd}, #{locationCd}, #{locationTxt}, #{purpose}, + #{moveCd}, #{tripDt}, #{startTime}, #{endTime}, #{status}, + #{frstRegisterId} + ) + + + + INSERT INTO biz_trip_member (trip_id, uniq_id, role) + VALUES (#{tripId}, #{uniqId}, #{role}) + + + + INSERT INTO biz_trip_approval (trip_id, approver_id, order_no, approve_status) + VALUES (#{tripId}, #{approverId}, #{orderNo}, #{approveStatus}) + + + + + + \ No newline at end of file diff --git a/src/main/resources/mapper/itn/user/UserMapper.xml b/src/main/resources/mapper/itn/user/UserMapper.xml index cf5ecc3..c36e798 100644 --- a/src/main/resources/mapper/itn/user/UserMapper.xml +++ b/src/main/resources/mapper/itn/user/UserMapper.xml @@ -12,7 +12,7 @@ , user_id , user_pw as password , user_name - , user_rank + , rank_cd , role , gw_id , biostar_id @@ -33,7 +33,7 @@ , user_id , user_pw AS password , user_name - , user_rank + , rank_cd , mobile_phone , dept_cd , role @@ -57,8 +57,8 @@ UPDATE users SET - user_name = #{username} - , user_rank = #{userRank} + user_name = #{userName} + , rank_cd = #{rankCd} , dept_cd = #{deptCd} ,role = #{role} @@ -66,6 +66,9 @@ ,gw_id = #{gwId} + + ,mobile_phone = #{mobilePhone} + ,biostar_id = #{biostarId} @@ -83,4 +86,20 @@ WHERE uniq_id = #{uniqId} + \ No newline at end of file diff --git a/src/main/resources/mybatis-config.xml b/src/main/resources/mybatis-config.xml index 7229104..fbef0c5 100644 --- a/src/main/resources/mybatis-config.xml +++ b/src/main/resources/mybatis-config.xml @@ -25,6 +25,10 @@ + + + + diff --git a/src/main/resources/static/cmn/js/agent/init.js b/src/main/resources/static/cmn/js/agent/init.js deleted file mode 100644 index d74cb4f..0000000 --- a/src/main/resources/static/cmn/js/agent/init.js +++ /dev/null @@ -1,177 +0,0 @@ - -$(function () { - - $(".slider").each(function () { - var $slider = $(this); // 현재 슬라이더 요소 - var $input = $slider.closest('.input-group').find('.sliderValue'); // 해당 슬라이더의 input 요소 - - // 슬라이더 초기화 - $slider.slider({ - range: "max", - min: 1, - max: 1000000, - value: 1, - slide: function (event, ui) { - $input.val(ui.value); // 슬라이더 이동 시 input 값 업데이트 - } - }); - - // 슬라이더의 초기 값을 input에 설정 - $input.val($slider.slider("value")); - - // input 변경 시 슬라이더 값 업데이트 (실시간) - $input.on("input", function () { - var value = $(this).val(); - // 숫자 범위 확인 후 슬라이더 값 업데이트 - if ($.isNumeric(value) && value >= 1 && value <= 1000000) { - $slider.slider("value", value); - } - }); - }); - - $("form").on("reset", function () { - setTimeout(function () { - $("#divTwoSms #sendCnt").val(1); // 건수 필드 값을 1로 설정 - $("#divTwoSms #slider").slider("value", 1); // 슬라이더 값도 1로 설정 - }, 0); // setTimeout 사용 이유: reset 이벤트 후에 값 설정 - }); - - /* - * 예시 버튼 - * */ - $(".examBtn").on("click", function () { - - var tagId = getParentsId($(this)); - var $recvPhone = $(tagId + ' .recvPhone'); - var $sendPhone = $(tagId + ' .sendPhone'); - var $msgType = $(tagId + ' .msgType'); - var $message = $(tagId + ' .message'); - var $subject = $(tagId + ' .subject'); - - // 기본 전화번호 설정 - $recvPhone.val('01083584250'); - $sendPhone.val('01083584250'); - - // 메시지 타입에 따른 메시지 설정 - var msgType = $msgType.val(); - var msg = generateMessage(msgType); - - // 내용 - $message.val(msg); - updateByteCount($message); - - if (msgType === 'L' - ||msgType === 'M' - ||msgType === 'A' - ||msgType === 'F' - ) { - $subject.val('ITN SUBJECT'); - } - - }); - - function generateMessage(msgType) { - var messages = { - 'S': 'ITN SMS test ', - 'L': 'ITN LMS test ', - 'M': 'ITN MMS message test ', - 'A': 'ITN ', - 'F': 'ITN ' - }; - // 타입이 위 값들고 같이 않으면 null 반환 - return (messages[msgType] || '') + getNowDate(); - } - $('.msgType').on('change', function() { - - var msgType = $(this).val(); - var tagId = getParentsId($(this)); - - // 제목 - $(tagId+' .subject').closest('.form-group').hide(); - - // 파일 그룹 - $(tagId+' .fileUploadGroup').hide(); - $(tagId+' .fileUploadGroup input[type="file"]').val(""); - - if(msgType === 'L' - ||msgType === 'A' - ||msgType === 'F' - ) { - $(tagId+' .subject').closest('.form-group').show(); - }else if(msgType === 'M'){ - $(tagId+' .subject').closest('.form-group').show(); - $(tagId+' .fileUploadGroup').show(); - } - - var $message = $(tagId + ' .message'); - $message.val(generateMessage(msgType)); - - }); - - - $('.toggle-info-btn').on('click', function() { - var $card = $(this).closest('.card'); // 클릭한 버튼의 가장 가까운 부모 .card 요소 찾기 - var $hiddenInfo = $card.find('.hidden-info'); // 해당 카드 내에서 .hidden-info 요소 찾기 - - $hiddenInfo.slideToggle(); // 애니메이션 효과를 추가하여 더 부드럽게 보이도록 함 - var icon = $(this).find('i'); - if ($hiddenInfo.is(':visible')) { - icon.removeClass('fa-info-circle').addClass('fa-times-circle'); - } else { - icon.removeClass('fa-times-circle').addClass('fa-info-circle'); - } - }); - - - - $('textarea').on('input', function() { - updateByteCount(this); - }); - -}); - -// function updateByteCount(textarea) { -// console.log('textarea : ', textarea); -// var text = $(textarea).val(); -// var byteLength = new TextEncoder().encode(text).length; -// $(textarea).closest('.form-group').find('.byte-count').text(byteLength + ' bytes'); -// } -function updateByteCount(textarea) { - var text = $(textarea).val(); - var byteLength = calculateByteLength(text); - $(textarea).closest('.form-group').find('.byte-count').text(byteLength + ' bytes'); -} - -function calculateByteLength(text) { - var byteLength = 0; - for (var i = 0; i < text.length; i++) { - var charCode = text.charCodeAt(i); - if (charCode <= 0x007F) { - byteLength += 1; // 1 byte for ASCII characters - } else if (charCode <= 0x07FF) { - byteLength += 2; // 2 bytes for characters from U+0080 to U+07FF - } else { - byteLength += 2; // 2 bytes for characters from U+0800 and above (including Hangul) - } - } - return byteLength; -} -function getParentsId($obj){ - - var $col = $obj.closest('.col-md-6'); // 클릭한 버튼의 가장 가까운 부모 .card 요소 찾기 - // 해당 카드 내에서 .hidden-info 요소 찾기 - return '#' + $col.attr('id'); -} - -function getNowDate(){ - - // 현재 날짜와 시간을 가져와서 포맷팅 - var now = new Date(); - var year = String(now.getFullYear()).substring(2); // 년도 마지막 두 자리 - var month = ('0' + (now.getMonth() + 1)).slice(-2); // 월 (0부터 시작하므로 +1 필요) - var day = ('0' + now.getDate()).slice(-2); // 일 - var hours = ('0' + now.getHours()).slice(-2); // 시 - var minutes = ('0' + now.getMinutes()).slice(-2); // 분 - - return year + month + day + '|' + hours + ':' + minutes; -} \ No newline at end of file diff --git a/src/main/resources/static/cmn/js/agent/timerForOneC.js b/src/main/resources/static/cmn/js/agent/timerForOneC.js deleted file mode 100644 index 603ea30..0000000 --- a/src/main/resources/static/cmn/js/agent/timerForOneC.js +++ /dev/null @@ -1,272 +0,0 @@ - -// 타이머 ID 저장을 위한 변수 -let oneInsertCntIntervalId; -let oneTransferCntIntervalId; -let oneReporingCntIntervalId; -// insert 타이머 -let oneIntervalId_insertSeconds; -// 이관 타이머 -let oneIntervalId_transferSeconds; -// 이관 타이머 -let oneIntervalId_reporingSeconds; - - - -function fn_oneInsertScriptStart(){ - // 건수를 현황확인으로 이동 - $('#divOneSmsCard .sendCntTxt').text('('+$('#divOneSms .sliderValue').val()+'건)'); - oneStartInsertTimer(); // insert 타임어택 시작 -} - - -// LOG 테이블에 -function fn_oneReportScriptStart(){ - oneStartReportTimer(); // report 타임어택 시작 -} - - - - -function oneStartInsertTimer() { - console.log(' :: startInsertTimer :: '); - let startTime = Date.now(); - oneStartInsertCntTimer(); - oneIntervalId_insertSeconds = setInterval(function() { - let currentTime = Date.now(); - let elapsedTime = (currentTime - startTime) / 1000; // 밀리초를 초 단위로 변환 - // 분과 초로 변환 - let minutes = Math.floor(elapsedTime / 60); // 분 계산 - let seconds = (elapsedTime % 60).toFixed(3); // 나머지 초 계산 - - document.querySelector('#divOneSmsCard .insertSeconds').innerText = minutes + ' 분 ' + seconds + ' 초'; - }, 1); -} - -function oneStartInsertCntTimer() { - console.log('oneStartInsertCntTimer ::'); - // 1초마다 fn_insertCntAndTime 함수를 호출 - oneInsertCntIntervalId = setInterval(fn_oneInsertCntAndTime, 1000); -} - -function oneStopInsertTimer() { - clearInterval(oneIntervalId_insertSeconds); - clearInterval(oneInsertCntIntervalId); - console.log("insert 타이머가 멈췄습니다."); -} - - - - -function fn_oneInsertCntAndTime(){ - - console.log('fn_oneInsertCntAndTime ::'); - // 폼 데이터를 수집 - var formData = new FormData($("#divOneSms .sendForm")[0]); - - var jsonObject = {}; - formData.forEach((value, key) => { - if (!(value instanceof File)) { - jsonObject[key] = value; - } - }); - console.log('url : /agent/one/findByInsertCnt'); - $.ajax({ - type: "POST", - url: "/agent/one/findByInsertCnt", - data: JSON.stringify(jsonObject), // JSON 문자열로 변환된 데이터를 전송 - dataType: 'json', - contentType: 'application/json', - // async: true, - success: function (data) { - console.log(' one findByInsertCnt data : ', data); - - if (data.status === 'OK') { - var cnt = data.data; - - $('#divOneSmsCard .insertCnt').text(cnt); - let text = $('#divOneSmsCard .sendCntTxt').text(); - let numberOnly = text.match(/\d+/)[0]; - console.log(' one numberOnly :', numberOnly); - console.log(' one cnt >= numberOnly :', cnt >= numberOnly); - if(cnt >= numberOnly){ - oneStopInsertTimer(); - // oneStartTransferTimer($('#oneUserId').val()); // 이관 카운트 - // fn_oneReportScriptStart(); - } - } - else { - alert("오류 알림 : :: "+data.msg); - } - }, - error: function (e) { - alert(" findByInsertCnt 조회에 실패하였습니다."); - oneStopInsertTimer(); - console.log("ERROR : " + JSON.stringify(e)); - }, - beforeSend : function(xmlHttpRequest) { - }, - complete : function(xhr, textStatus) { - //로딩창 hide - } - }); -} - - - -// 이관 타이머 start -function oneStartTransferTimer(userId) { - console.log(' :: one startTransferTimer :: '); - let startTime = Date.now(); - oneStartTransferCntTimer(userId) - oneIntervalId_transferSeconds = setInterval(function() { - let currentTime = Date.now(); - let elapsedTime = (currentTime - startTime) / 1000; // 밀리초를 초 단위로 변환 - document.querySelector('#divOneSmsCard .transferSeconds').innerText = elapsedTime.toFixed(3) + ' 초'; - }, 1); -} - -function oneStartTransferCntTimer(userId) { - // 1초마다 fn_tranferCntAndTime 함수를 호출 - oneTransferCntIntervalId = setInterval(function() { - fn_oneTranferCntAndTime(userId); - }, 1000); -} - - -function oneStopTransferTimer() { - clearInterval(oneIntervalId_transferSeconds); - clearInterval(oneTransferCntIntervalId); - console.log("이관 타이머가 멈췄습니다."); -} - - - -function fn_oneTranferCntAndTime(userId){ - - // 폼 데이터를 수집 - var formData = new FormData($("#divOneSms .sendForm")[0]); - - var jsonObject = {}; - formData.forEach((value, key) => { - jsonObject[key] = value; - }); - jsonObject['userId'] = userId; - console.log('jsonObject : ', jsonObject); - - $.ajax({ - type: "POST", - url: "/agent/server/findByTransferCnt", - data: JSON.stringify(jsonObject), // JSON 문자열로 변환된 데이터를 전송 - dataType: 'json', - contentType: 'application/json', - // async: true, - success: function (data) { - console.log('tranfer data : ', data); - - if (data.status == 'OK') { - var cnt = data.data; - - $('#divOneSmsCard .transferCnt').text(cnt); - let text = $('#divOneSmsCard .insertCnt').text(); - let numberOnly = text.match(/\d+/)[0]; - if(cnt >= Number(numberOnly)){ - oneStopTransferTimer(); - } - } - else { - alert("오류 알림 : :: "+data.msg); - } - }, - error: function (e) { - alert("이관 조회에 실패하였습니다."); - console.log("ERROR : " + JSON.stringify(e)); - }, - beforeSend : function(xmlHttpRequest) { - }, - complete : function(xhr, textStatus) { - //로딩창 hide - } - }); -} - -// 리포트 영역 -// 리포트 영역 -// 리포트 영역 -function oneStartReportTimer(userId) { - console.log(' :: startReportTimer :: '); - let startTime = Date.now(); - oneStartReporingCntTimer(); - oneIntervalId_reporingSeconds = setInterval(function() { - let currentTime = Date.now(); - let elapsedTime = (currentTime - startTime) / 1000; // 밀리초를 초 단위로 변환 - document.querySelector('#divOneSmsCard .reportSeconds').innerText = elapsedTime.toFixed(3) + ' 초'; - }, 1); -} - -function oneStartReporingCntTimer() { - // 1초마다 fn_twoReportCntAndTime 함수를 호출 - oneReporingCntIntervalId = setInterval(function() { - fn_oneReportCntAndTime(); - }, 1000); -} - -function oneStopReporingTimer() { - clearInterval(oneIntervalId_reporingSeconds); - clearInterval(oneReporingCntIntervalId); - console.log("report 타이머가 멈췄습니다."); -} - - -function fn_oneReportCntAndTime(userId){ - - // 폼 데이터를 수집 - var formData = new FormData($("#divOneSms .sendForm")[0]); - - var jsonObject = {}; - formData.forEach((value, key) => { - if (!(value instanceof File)) { - jsonObject[key] = value; - } - }); - jsonObject['userId'] = userId; - - $.ajax({ - type: "POST", - url: "/agent/one/findByLogMoveCntWhereMessage", - data: JSON.stringify(jsonObject), // JSON 문자열로 변환된 데이터를 전송 - dataType: 'json', - contentType: 'application/json', - // async: true, - success: function (data) { - console.log('findByLogMoveCntWhereMessage data : ', data); - - if (data.status == 'OK') { - var cnt = data.data; - $('#divOneSmsCard .reportStartCnt').text(cnt); - - var transferCnt = $('#divOneSmsCard .insertCnt').text(); - - - console.log('cnt : ', cnt); - console.log('reportStartCnt : ', transferCnt); - console.log(''); - if(cnt >= Number(transferCnt)){ - oneStopReporingTimer(); - } - } - else { - alert("오류 알림 : :: "+data.msg); - } - }, - error: function (e) { - alert("report 조회에 실패하였습니다."); - console.log("ERROR : " + JSON.stringify(e)); - }, - beforeSend : function(xmlHttpRequest) { - - }, - complete : function(xhr, textStatus) { - //로딩창 hide - } - }); -} diff --git a/src/main/resources/static/cmn/js/agent/timerForTwoC.js b/src/main/resources/static/cmn/js/agent/timerForTwoC.js deleted file mode 100644 index 2c591df..0000000 --- a/src/main/resources/static/cmn/js/agent/timerForTwoC.js +++ /dev/null @@ -1,273 +0,0 @@ - -// 타이머 ID 저장을 위한 변수 -let twoInsertCntIntervalId; -let twoTransferCntIntervalId; -let twoReporingCntIntervalId; -// insert 타이머 -let twoIntervalId_insertSeconds; -// 이관 타이머 -let twoIntervalId_transferSeconds; -// 리포트 타이머 -let twoIntervalId_reporingSeconds; - - - -function fn_twoInsertScriptStart(){ - // 건수를 현황확인으로 이동 - $('#divTwoSmsCard .sendCntTxt').text('('+$('#divTwoSms .sliderValue').val()+'건)'); - twoStartInsertTimer(); // insert 타임어택 시작 -} - - -function fn_twoReportScriptStart(){ - twoStartReportTimer(); // report 타임어택 시작 -} - - - - -function twoStartInsertTimer() { - console.log(' :: startInsertTimer :: '); - let startTime = Date.now(); - twoStartInsertCntTimer(); - twoIntervalId_insertSeconds = setInterval(function() { - let currentTime = Date.now(); - let elapsedTime = (currentTime - startTime) / 1000; // 밀리초를 초 단위로 변환 - // 분과 초로 변환 - let minutes = Math.floor(elapsedTime / 60); // 분 계산 - let seconds = (elapsedTime % 60).toFixed(3); // 나머지 초 계산 - - document.querySelector('#divTwoSmsCard .insertSeconds').innerText = minutes + ' 분 ' + seconds + ' 초'; - }, 1); -} - -function twoStartInsertCntTimer() { - // 1초마다 fn_insertCntAndTime 함수를 호출 - twoInsertCntIntervalId = setInterval(fn_twoInsertCntAndTime, 1000); -} - -function twoStopInsertTimer() { - clearInterval(twoIntervalId_insertSeconds); - clearInterval(twoInsertCntIntervalId); - console.log("insert 타이머가 멈췄습니다."); -} - - - - -function fn_twoInsertCntAndTime(){ - - // 폼 데이터를 수집 - var formData = new FormData($("#divTwoSms .sendForm")[0]); - - var jsonObject = {}; - formData.forEach((value, key) => { - if (!(value instanceof File)) { - jsonObject[key] = value; - } - }); - - console.log('fn_twoInsertCntAndTime : [{}]',jsonObject); - $.ajax({ - type: "POST", - url: "/agent/two/findByInsertCnt", - data: JSON.stringify(jsonObject), // JSON 문자열로 변환된 데이터를 전송 - dataType: 'json', - contentType: 'application/json', - // async: true, - success: function (data) { - // console.log('insert data : ', data); - - if (data.status == 'OK') { - var cnt = data.data; - - $('#divTwoSmsCard .insertCnt').text(cnt); - let text = $('#divTwoSmsCard .sendCntTxt').text(); - let numberOnly = text.match(/\d+/)[0]; - // console.log('numberOnly :', numberOnly); - // console.log('cnt >= numberOnly :', cnt >= numberOnly); - if(cnt >= numberOnly){ - twoStopInsertTimer(); - // twoStartTransferTimer($('#twoUserId').val()); // 이관 카운트 - // fn_twoReportScriptStart(); - } - } - else { - alert("오류 알림 : :: "+data.msg); - } - }, - error: function (e) { - alert("조회에 실패하였습니다."); - console.log("ERROR : " + JSON.stringify(e)); - }, - beforeSend : function(xmlHttpRequest) { - }, - complete : function(xhr, textStatus) { - //로딩창 hide - } - }); -} - - -// 이관 타이머 start -function twoStartTransferTimer(userId) { - console.log(' :: two startTransferTimer :: '); - let startTime = Date.now(); - twoStartTransferCntTimer(userId) - twoIntervalId_transferSeconds = setInterval(function() { - let currentTime = Date.now(); - let elapsedTime = (currentTime - startTime) / 1000; // 밀리초를 초 단위로 변환 - // console.log('elapsedTime : ', elapsedTime); - document.querySelector('#divTwoSmsCard .transferSeconds').innerText = elapsedTime.toFixed(3) + ' 초'; - }, 1); -} - -function twoStartTransferCntTimer(userId) { - // 1초마다 fn_tranferCntAndTime 함수를 호출 - twoTransferCntIntervalId = setInterval(function() { - fn_twoTranferCntAndTime(userId); - }, 1000); -} - - -function twoStopTransferTimer() { - clearInterval(twoIntervalId_transferSeconds); - clearInterval(twoTransferCntIntervalId); - console.log("이관 타이머가 멈췄습니다."); -} - - - -function fn_twoTranferCntAndTime(userId){ - - // 폼 데이터를 수집 - var formData = new FormData($("#divTwoSms .sendForm")[0]); - - // console.log('? :: ', formData); - var jsonObject = {}; - formData.forEach((value, key) => { - jsonObject[key] = value; - }); - jsonObject['userId'] = userId; - // console.log('jsonObject : ', jsonObject); - - $.ajax({ - type: "POST", - url: "/agent/server/findByTransferCnt", - data: JSON.stringify(jsonObject), // JSON 문자열로 변환된 데이터를 전송 - dataType: 'json', - contentType: 'application/json', - // async: true, - success: function (data) { - console.log('tranfer data : ', data); - - if (data.status == 'OK') { - var cnt = data.data; - - $('#divTwoSmsCard .transferCnt').text(cnt); - let text = $('#divTwoSmsCard .insertCnt').text(); - let numberOnly = text.match(/\d+/)[0]; - if(cnt >= Number(numberOnly)){ - twoStopTransferTimer(); - } - } - else { - alert("오류 알림 : :: "+data.msg); - } - }, - error: function (e) { - alert("이관 조회에 실패하였습니다."); - console.log("ERROR : " + JSON.stringify(e)); - }, - beforeSend : function(xmlHttpRequest) { - }, - complete : function(xhr, textStatus) { - //로딩창 hide - } - }); -} - -// 리포트 영역 -// 리포트 영역 -// 리포트 영역 -function twoStartReportTimer() { - // console.log(' :: startReportTimer :: '); - let startTime = Date.now(); - twoStartReporingCntTimer(); - twoIntervalId_reporingSeconds = setInterval(function() { - let currentTime = Date.now(); - let elapsedTime = (currentTime - startTime) / 1000; // 밀리초를 초 단위로 변환 - document.querySelector('#divTwoSmsCard .reportSeconds').innerText = elapsedTime.toFixed(3) + ' 초'; - }, 1); -} - -function twoStartReporingCntTimer() { - // 1초마다 fn_twoReportCntAndTime 함수를 호출 - twoReporingCntIntervalId = setInterval(function() { - fn_twoReportCntAndTime(); - }, 1000); -} - -function twoStopReporingTimer() { - clearInterval(twoIntervalId_reporingSeconds); - clearInterval(twoReporingCntIntervalId); - console.log("report 타이머가 멈췄습니다."); -} - - -function fn_twoReportCntAndTime(userId){ - - // 폼 데이터를 수집 - var formData = new FormData($("#divTwoSms .sendForm")[0]); - - var jsonObject = {}; - formData.forEach((value, key) => { - if (!(value instanceof File)) { - jsonObject[key] = value; - } - }); - jsonObject['userId'] = userId; - - - $.ajax({ - type: "POST", - url: "/agent/two/findByLogMoveCntWhereMessage", - data: JSON.stringify(jsonObject), // JSON 문자열로 변환된 데이터를 전송 - dataType: 'json', - contentType: 'application/json', - // async: true, - success: function (data) { - console.log('findByLogMoveCntWhereMessage data : ', data); - - if (data.status == 'OK') { - var cnt = data.data; - - console.log('cnt : ', cnt); - // 리포트 영역에 cnt 추가 - $('#divTwoSmsCard .reportStartCnt').text(cnt); - // server DB에 update한 건수와 cnt비교 - var transferCnt = $('#divTwoSmsCard .insertCnt').text(); - - console.log('cnt : ', cnt); - console.log('reportStartCnt : ', transferCnt); - console.log(''); - if(cnt >= Number(transferCnt)){ - twoStopReporingTimer(); - } - } - else { - alert("오류 알림 : :: "+data.msg); - } - }, - error: function (e) { - alert("report 조회에 실패하였습니다."); - console.log("ERROR : " + JSON.stringify(e)); - }, - beforeSend : function(xmlHttpRequest) { - - }, - complete : function(xhr, textStatus) { - //로딩창 hide - } - }); -} diff --git a/src/main/resources/static/cmn/js/bizTrip/event.js b/src/main/resources/static/cmn/js/bizTrip/event.js new file mode 100644 index 0000000..5ca8500 --- /dev/null +++ b/src/main/resources/static/cmn/js/bizTrip/event.js @@ -0,0 +1,247 @@ +/** + * 페이지 로드 완료 후 실행되는 초기화 함수 (비워둠) + * @function + */ +$(function () { + // 초기화 코드 필요 시 여기에 작성 +}); + +/** + * Enter 키 입력 시 사용자 검색 수행 + * @event keydown + * @param {KeyboardEvent} e + */ +document.getElementById("userSearchKeyword").addEventListener("keydown", function (e) { + if (e.key === "Enter") { + e.preventDefault(); + searchUser(); + } +}); +document.getElementById("approvalSearchKeyword").addEventListener("keydown", function (e) { + if (e.key === "Enter") { + e.preventDefault(); + searchApproval(); + } +}); + +/** + * 모달이 열릴 때 사용자 목록을 불러오고 포커스를 맞춤 + * @event shown.bs.modal + */ +$('#userSearchModal').on('shown.bs.modal', function () { + loadUserList(); + $('#userSearchKeyword').trigger('focus'); +}); +$('#approvalSearchModal').on('shown.bs.modal', function () { + loadApprovalList(); + $('#approvalSearchKeyword').trigger('focus'); +}); + +/** + * 날짜 입력 필드 클릭 시 브라우저 기본 날짜 선택기 표시 + * @event click + */ +document.querySelector('input[id="tripDate"]').addEventListener('click', function () { + this.showPicker && this.showPicker(); +}); + +/** + * 사용자 목록을 불러오는 Ajax 요청 함수 + * @function + * @param {string} [keyword=""] - 검색 키워드 + * @returns {void} + */ +function loadUserList(keyword = "") { + $.ajax({ + url: '/api/admin/user/search/name', + type: 'GET', + data: { userName: keyword }, + success: function (result) { + const tbody = document.getElementById("userSearchResult"); + tbody.innerHTML = ""; + + const data = result.data; + if (data && data.length > 0) { + data.forEach(user => { + const row = document.createElement("tr"); + row.innerHTML = ` + ${user.userName} + ${user.deptNm || "-"} + ${user.rankCd || "-"} + + + + `; + tbody.appendChild(row); + }); + } else { + tbody.innerHTML = `검색 결과가 없습니다.`; + } + }, + error: function () { + alert("사용자 목록을 불러오는 데 실패했습니다."); + } + }); +} + +function loadApprovalList(keyword = "") { + $.ajax({ + url: '/api/admin/approval/search/name', + type: 'GET', + data: { userName: keyword }, + success: function (result) { + const tbody = document.getElementById("approvalSearchResult"); + const data = result.data; + + tbody.innerHTML = ""; + if (data && data.length > 0) { + data.forEach(user => { + const row = document.createElement("tr"); + row.innerHTML = ` + ${user.userName} + ${user.deptNm || "-"} + ${user.rankCd || "-"} + + + + `; + tbody.appendChild(row); + }); + } else { + tbody.innerHTML = `검색 결과가 없습니다.`; + } + }, + error: function () { + alert("사용자 목록을 불러오는 데 실패했습니다."); + } + }); +} + +/** + * 사용자를 선택하여 출장 인원 또는 결재라인에 추가 + * @function + * @param {string} name - 사용자 이름 + * @param {string} dept - 부서명 + * @param {string} phone - 전화번호 + * @param {string} uniqId - 사용자 고유 ID + * @returns {void} + */ +function selectUser(name, dept, phone, uniqId) { + + const tbody = document.getElementById("tripMemberTbody"); + const newRow = document.createElement("tr"); + newRow.setAttribute("data-uniqid", uniqId); + newRow.innerHTML = ` + ${name} + ${dept} + ${phone} + + + 삭제 + + + `; + tbody.appendChild(newRow); + $('#userSearchModal').modal('hide'); +} + + +function selectApproval(name, dept, phone, uniqId) { + const stageId = $('#approvalSearchModal').data('stageId'); + + const rowMap = { + approval1: { index: 0, inputId: "approver1" }, + approval2: { index: 1, inputId: "approver2" }, + approval3: { index: 2, inputId: "approver3" } + }; + + const { index, inputId } = rowMap[stageId]; + const tbody = document.getElementById("approvalLineTbody"); + const targetRow = tbody.rows[index]; + if (!targetRow) return; + + targetRow.innerHTML = ` + ${targetRow.cells[0].textContent} + ${name} + ${dept} + ${phone} + + + 삭제 + + + `; + + // hidden input에 uniqId 저장 + document.getElementById(inputId).value = uniqId; + + $('#approvalSearchModal').modal('hide'); + $('#approvalSearchModal').data('stageId', null); + +} + +/** + * 검색창 버튼 클릭 시 사용자 검색 실행 + * @function + */ +function searchUser() { + const keyword = document.getElementById("userSearchKeyword").value.trim(); + loadUserList(keyword); +} +function searchApproval() { + const keyword = document.getElementById("userSearchKeyword").value.trim(); + loadApprovalList(keyword); +} + +/** + * 출장 인원 행 삭제 + * @function + * @param {HTMLElement} el - 삭제 버튼 요소 + */ +function removeMemberRow(el) { + if (confirm("정말 삭제하시겠습니까?")) { + const row = el.closest("tr"); + if (row) row.remove(); + } +} + + +/** + * 결재자 지정 모달 열기 + * @function + * @param {string} stageId - 결재 단계 ID (approval1, approval2, approval3) + */ +function openApprovalModal(stageId) { + $('#approvalSearchModal').data('stageId', stageId).modal('show'); +} + +/** + * 결재자 삭제 및 단계 초기화 + * @function + * @param {string} stageId - 결재 단계 ID + */ +function resetApproval(stageId) { + const rowIndex = { + approval1: 0, + approval2: 1, + approval3: 2 + }[stageId]; + + const tbody = document.getElementById("approvalLineTbody"); + const label = ['검토 1', '검토 2', '결제'][rowIndex]; + + tbody.rows[rowIndex].innerHTML = ` + ${label} + + + + `; +} diff --git a/src/main/resources/static/cmn/js/bizTrip/init.js b/src/main/resources/static/cmn/js/bizTrip/init.js new file mode 100644 index 0000000..665c22e --- /dev/null +++ b/src/main/resources/static/cmn/js/bizTrip/init.js @@ -0,0 +1,26 @@ + +$(function () { + + /** + * 시작 시간 선택기 초기화 + * @function + */ + $('#startTimePicker').datetimepicker({ + format: 'HH:mm', + stepping: 10 + }); + + /** + * 종료 시간 선택기 초기화 + * @function + */ + $('#endTimePicker').datetimepicker({ + format: 'HH:mm', + stepping: 10, + icons: { + time: 'far fa-clock', + up: 'fas fa-chevron-up', + down: 'fas fa-chevron-down' + } + }); +}); \ No newline at end of file diff --git a/src/main/resources/static/cmn/js/bizTrip/service.js b/src/main/resources/static/cmn/js/bizTrip/service.js new file mode 100644 index 0000000..6ec90f1 --- /dev/null +++ b/src/main/resources/static/cmn/js/bizTrip/service.js @@ -0,0 +1,52 @@ +/** + * 출장 등록 전송 함수 (값 생성 + 전송 담당) + */ +function collectAndSubmitTripData() { + if (!validateTripForm()) return; + + // 값 생성 + const tripInfo = { + tripTypeCd: $('#tripType').val(), + locationCd: $('#tripLocation').val(), + locationTxt: $('#locationTxt').val(), + purpose: $('#purpose').val(), + moveCd: $('#tripMove').val(), + tripDt: $('#tripDate').val(), + startTime: $('#startTimePicker input').val(), + endTime: $('#endTimePicker input').val(), + status: '10' + }; + + const tripMembers = []; + $('#tripMemberTbody tr').each(function () { + const uniqId = $(this).data('uniqid'); + if (!uniqId) return; + const role = $(this).find('td').eq(3).text().trim() === '기안자' ? '0' : '1'; + tripMembers.push({ uniqId, role }); + }); + + const approvalLines = []; + ['approver1', 'approver2', 'approver3'].forEach((id, idx) => { + const uniqId = $(`#${id}`).val(); + if (uniqId) { + approvalLines.push({ + approverId: uniqId, + orderNo: idx + 1, + approveStatus: '10' + }); + } + }); + + const payload = { tripInfo, tripMembers, approvalLines }; + console.log("payload:", payload); + + // Ajax 전송 + $.ajax({ + url: '/api/bizTrip/register', + method: 'POST', + contentType: 'application/json', + data: JSON.stringify(payload), + success: () => fn_successAlert("등록 성공", "출장 정보가 저장되었습니다."), + error: () => fn_failedAlert("등록 실패", "오류가 발생했습니다.") + }); +} \ No newline at end of file diff --git a/src/main/resources/static/cmn/js/bizTrip/validation.js b/src/main/resources/static/cmn/js/bizTrip/validation.js new file mode 100644 index 0000000..acffa11 --- /dev/null +++ b/src/main/resources/static/cmn/js/bizTrip/validation.js @@ -0,0 +1,65 @@ +/** + * 출장 등록 입력값 유효성 검사 함수 + * - 필수 항목 누락 여부 확인 + * - 출장일자가 오늘 이전인지 확인 + * - 시작 시간이 종료 시간보다 늦은지 확인 + * + * @returns {boolean} 유효하면 true, 유효하지 않으면 false + */ +function validateTripForm() { + // ===== [1] 입력값 가져오기 ===== + const tripType = $('#tripType'); + const location = $('#tripLocation'); + const locationTxt = $('#locationTxt'); + const purpose = $('#purpose'); + const move = $('#tripMove'); + const date = $('#tripDate'); + const start = $('#startTimePicker input'); + const end = $('#endTimePicker input'); + const approver = $('#approver3'); + + const tripTypeCd = tripType.val(); + const locationCd = location.val(); + const locationTxtVal = locationTxt.val(); + const purposeVal = purpose.val(); + const moveCd = move.val(); + const tripDt = date.val(); + const startTime = start.val(); + const endTime = end.val(); + const approver3 = approver.val(); + + // ===== [2] 필수값 체크 + focus ===== + if (!tripTypeCd) return fn_failedAlert("입력 오류", "출장 구분을 선택해주세요.", 1000), tripType.focus(), false; + if (!locationCd) return fn_failedAlert("입력 오류", "출장지를 선택해주세요.", 1000), location.focus(), false; + if (!locationTxtVal || locationTxtVal.trim() === "") return fn_failedAlert("입력 오류", "목적지를 입력해주세요.", 1000), locationTxt.focus(), false; + if (!purposeVal || purposeVal.trim() === "") return fn_failedAlert("입력 오류", "출장 목적을 입력해주세요.", 1000), purpose.focus(), false; + if (!moveCd) return fn_failedAlert("입력 오류", "이동 수단을 선택해주세요.", 1000), move.focus(), false; + if (!tripDt) return fn_failedAlert("입력 오류", "출장일자를 선택해주세요.", 1000), date.focus(), false; + if (!startTime || !endTime) return fn_failedAlert("입력 오류", "시작/종료 시간을 입력해주세요.", 1000), start.focus(), false; + if (!approver3) return fn_failedAlert("입력 오류", "최종 결재자를 지정해주세요.", 1000), approver.focus(), false; + + // ===== [3] 출장일자가 오늘보다 과거일 경우 ===== + const today = new Date(); + const inputDate = new Date(tripDt); + today.setHours(0, 0, 0, 0); + inputDate.setHours(0, 0, 0, 0); + if (inputDate < today) { + fn_failedAlert("입력 오류", "출장일자는 오늘보다 빠를 수 없습니다."); + date.focus(); + return false; + } + + // ===== [4] 시간 순서 확인 (종료 > 시작) ===== + const [sH, sM] = startTime.split(':').map(Number); + const [eH, eM] = endTime.split(':').map(Number); + const startDate = new Date(); startDate.setHours(sH, sM, 0, 0); + const endDate = new Date(); endDate.setHours(eH, eM, 0, 0); + if (endDate <= startDate) { + fn_failedAlert("입력 오류", "종료 시간은 시작 시간보다 이후여야 합니다."); + end.focus(); + return false; + } + + // ===== [5] 유효성 통과 ===== + return true; +} diff --git a/src/main/resources/templates/fragments/header.html b/src/main/resources/templates/fragments/header.html index cb2739d..7b70e42 100644 --- a/src/main/resources/templates/fragments/header.html +++ b/src/main/resources/templates/fragments/header.html @@ -37,7 +37,7 @@ - + @@ -121,6 +121,16 @@ body: msg }) } + function fn_failedAlert(title, msg, delay){ + $(document).Toasts('create', { + class: 'bg-danger', + title: title, + subtitle: '', + autohide : true, + delay: delay, + body: msg + }) + } diff --git a/src/main/resources/templates/fragments/mainsidebar.html b/src/main/resources/templates/fragments/mainsidebar.html index 8df4574..42d68fb 100644 --- a/src/main/resources/templates/fragments/mainsidebar.html +++ b/src/main/resources/templates/fragments/mainsidebar.html @@ -92,7 +92,7 @@