- 파트너십 글 단건 조회/삭제 메서드 추가 및 본인 확인 로직 적용
- TestCleanupService 확장 - 테스트 글 삭제 및 첨부파일 정리 추가 - 레이아웃 개선 - footer 하단 고정(sticky) 처리
This commit is contained in:
+19
@@ -61,4 +61,23 @@ public class PartnershipApplicationController {
|
||||
return "redirect:/partnership";
|
||||
}
|
||||
|
||||
/**
|
||||
* 본인이 작성한 피드백/개선요청 1건 삭제.
|
||||
* 목록(최근 3건)의 삭제 버튼이 항목별 form 을 POST 한다 — 등록과 동일하게 폼 전송 + flash 메시지 방식.
|
||||
*/
|
||||
@PostMapping("/{id}/delete")
|
||||
public String deleteMyPartnershipApplication(@PathVariable String id, RedirectAttributes redirectAttributes) {
|
||||
if (!SecurityUtil.isAuthenticated()) {
|
||||
return "redirect:/login?reason=auth&redirect=/partnership";
|
||||
}
|
||||
|
||||
try {
|
||||
partnershipApplicationFacade.deleteMyApplication(id);
|
||||
redirectAttributes.addFlashAttribute("success", "피드백/개선요청이 삭제되었습니다.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
redirectAttributes.addFlashAttribute("error", e.getMessage());
|
||||
}
|
||||
return "redirect:/partnership";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+13
@@ -3,6 +3,7 @@ package com.eactive.apim.portal.apps.community.partnership.repository;
|
||||
import com.eactive.apim.portal.partnershipapplication.entity.PartnershipApplication;
|
||||
import com.eactive.eai.rms.data.EMSDataSource;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.stereotype.Repository;
|
||||
@@ -18,7 +19,19 @@ public interface PartnershipApplicationRepository extends JpaRepository<Partners
|
||||
*/
|
||||
List<PartnershipApplication> findTop3ByCreatedByOrderByCreatedDateDesc(String createdBy);
|
||||
|
||||
/**
|
||||
* 본인 글 삭제용 단건 조회. id 만으로 찾지 않고 createdBy 를 함께 걸어
|
||||
* 남의 글 id 를 넣어도 조회되지 않게 한다(소유자 검증을 쿼리 단계에서 강제).
|
||||
*/
|
||||
Optional<PartnershipApplication> findByIdAndCreatedBy(String id, String createdBy);
|
||||
|
||||
/** createdBy = PortalUser.id (평문 등가 조회 가능한 이유는 위와 동일). */
|
||||
@Transactional
|
||||
long deleteByCreatedBy(String createdBy);
|
||||
|
||||
/**
|
||||
* test-cleanup 전용 — 특정 작성자의 글 중 제목이 지정 접두사로 시작하는 것만 조회한다.
|
||||
* (bizSubject 는 암호화 컬럼이 아니라 LIKE 조회가 가능하다.)
|
||||
*/
|
||||
List<PartnershipApplication> findAllByCreatedByAndBizSubjectStartingWith(String createdBy, String bizSubjectPrefix);
|
||||
}
|
||||
|
||||
+6
@@ -13,4 +13,10 @@ public interface PartnershipApplicationFacade {
|
||||
* 현재 로그인 사용자가 작성한 최근 3건을 조회한다. 미인증이면 빈 목록.
|
||||
*/
|
||||
List<PartnershipApplicationSummaryDTO> getMyRecentApplications();
|
||||
|
||||
/**
|
||||
* 현재 로그인 사용자가 작성한 글 1건을 삭제한다(첨부파일 포함).
|
||||
* 본인 글이 아니거나 이미 삭제된 경우 {@link IllegalArgumentException}.
|
||||
*/
|
||||
void deleteMyApplication(String id);
|
||||
}
|
||||
|
||||
+20
@@ -20,6 +20,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@@ -74,4 +75,23 @@ public class PartnershipApplicationFacadeImpl implements PartnershipApplicationF
|
||||
List<PartnershipApplication> recent = partnershipApplicationService.findRecentByCreatedBy(user.getId());
|
||||
return partnershipApplicationMapper.toSummaryDtoList(recent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteMyApplication(String id) {
|
||||
PortalAuthenticatedUser user = SecurityUtil.getPortalAuthenticatedUser();
|
||||
if (user == null) {
|
||||
throw new IllegalArgumentException("로그인이 필요합니다.");
|
||||
}
|
||||
|
||||
// id 만으로 조회하지 않고 createdBy 를 함께 걸어 타인 글 삭제를 원천 차단한다.
|
||||
PartnershipApplication target = partnershipApplicationService
|
||||
.findOwnedByCreatedBy(id, user.getId())
|
||||
.orElseThrow(() -> new IllegalArgumentException("삭제할 수 있는 피드백/개선요청이 아닙니다."));
|
||||
|
||||
// 첨부파일도 함께 정리한다(관리자 삭제 PortalPartnershipManService.delete 와 동일 처리).
|
||||
if (StringUtils.isNotBlank(target.getFileId())) {
|
||||
fileService.deleteFile(target.getFileId());
|
||||
}
|
||||
partnershipApplicationService.deletePartnershipApplication(target);
|
||||
}
|
||||
}
|
||||
|
||||
+13
@@ -3,6 +3,7 @@ package com.eactive.apim.portal.apps.community.partnership.service;
|
||||
import com.eactive.apim.portal.apps.community.partnership.repository.PartnershipApplicationRepository;
|
||||
import com.eactive.apim.portal.partnershipapplication.entity.PartnershipApplication;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -30,4 +31,16 @@ public class PartnershipApplicationService {
|
||||
public List<PartnershipApplication> findRecentByCreatedBy(String createdBy) {
|
||||
return partnershipApplicationRepository.findTop3ByCreatedByOrderByCreatedDateDesc(createdBy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 작성자 본인 글 단건 조회. id 와 createdBy 를 함께 조건으로 걸어 타인 글은 조회되지 않는다.
|
||||
*/
|
||||
@Transactional(readOnly = true)
|
||||
public Optional<PartnershipApplication> findOwnedByCreatedBy(String id, String createdBy) {
|
||||
return partnershipApplicationRepository.findByIdAndCreatedBy(id, createdBy);
|
||||
}
|
||||
|
||||
public void deletePartnershipApplication(PartnershipApplication partnershipApplication) {
|
||||
partnershipApplicationRepository.delete(partnershipApplication);
|
||||
}
|
||||
}
|
||||
|
||||
+27
@@ -199,6 +199,33 @@ public class TestCleanupInternalController {
|
||||
return ResponseEntity.ok(body);
|
||||
}
|
||||
|
||||
/**
|
||||
* 4020 재실행 전, 해당 계정이 남긴 테스트 피드백/개선요청 글만 삭제한다.
|
||||
* 대상은 이메일(작성자)과 {@code 단위테스트*} 제목 접두사로 이중 한정한다.
|
||||
*/
|
||||
@PostMapping("/partnership")
|
||||
public ResponseEntity<Map<String, Object>> deletePartnershipApplications(
|
||||
@RequestParam String email, @RequestParam String subjectPrefix, HttpServletRequest request) {
|
||||
ResponseEntity<Map<String, Object>> guardFailure = checkGuards(request);
|
||||
if (guardFailure != null) {
|
||||
return guardFailure;
|
||||
}
|
||||
if (StringUtils.isBlank(email) || StringUtils.isBlank(subjectPrefix)) {
|
||||
return badRequest("email 과 subjectPrefix 는 필수입니다.");
|
||||
}
|
||||
|
||||
TestCleanupResult result = testCleanupService.deletePartnershipApplicationsByEmail(email, subjectPrefix);
|
||||
log.info("테스트 정리(partnership) 실행 - email: {}, subjectPrefix: {}, found: {}, from: {}",
|
||||
email, subjectPrefix, result.isFound(), request.getRemoteAddr());
|
||||
|
||||
Map<String, Object> body = baseBody();
|
||||
body.put("email", email);
|
||||
body.put("subjectPrefix", subjectPrefix);
|
||||
body.put("partnershipFound", result.isFound());
|
||||
body.put("deletedCounts", result.getDeletedCounts());
|
||||
return ResponseEntity.ok(body);
|
||||
}
|
||||
|
||||
/**
|
||||
* 휴대폰 번호로 남아 있는 초대 레코드를 삭제한다. 1020 재실행 전 초대중 중복을 정리하는 용도다.
|
||||
*/
|
||||
|
||||
@@ -16,6 +16,8 @@ import com.eactive.apim.portal.djb.webhook.repository.WebhookRequestRepository;
|
||||
import com.eactive.apim.portal.djb.webhook.repository.entity.WebhookRequest;
|
||||
import com.eactive.apim.portal.djb.webhook.service.WebhookService;
|
||||
import com.eactive.apim.portal.common.util.PhoneNumberUtil;
|
||||
import com.eactive.apim.portal.file.service.FileService;
|
||||
import com.eactive.apim.portal.partnershipapplication.entity.PartnershipApplication;
|
||||
import com.eactive.apim.portal.invitation.repository.UserInvitationRepository;
|
||||
import com.eactive.apim.portal.portalorg.entity.PortalOrg;
|
||||
import com.eactive.apim.portal.portaluser.entity.PortalUser;
|
||||
@@ -68,6 +70,7 @@ public class TestCleanupService {
|
||||
private final WebhookRequestEventRepository webhookRequestEventRepository;
|
||||
private final WebhookService webhookService;
|
||||
private final UserInvitationRepository userInvitationRepository;
|
||||
private final FileService fileService;
|
||||
private final TestCleanupNativeQueries nativeQueries;
|
||||
|
||||
/**
|
||||
@@ -232,6 +235,46 @@ public class TestCleanupService {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 4020 재실행 전, 특정 계정이 남긴 테스트 피드백/개선요청 글만 삭제한다(첨부파일 포함).
|
||||
* 운영 글 오삭제를 막기 위해 이메일(작성자)과 {@code 단위테스트} 로 시작하는 제목 접두사를 모두 요구한다.
|
||||
*/
|
||||
public TestCleanupResult deletePartnershipApplicationsByEmail(String email, String subjectPrefix) {
|
||||
assertNonProdProfile();
|
||||
if (subjectPrefix == null || !subjectPrefix.startsWith("단위테스트")) {
|
||||
throw new IllegalArgumentException("테스트 글 제목 접두사(단위테스트*)만 정리할 수 있습니다.");
|
||||
}
|
||||
|
||||
Optional<PortalUser> userOpt = portalUserRepository.findPortalUserByEmailAddr(email);
|
||||
if (!userOpt.isPresent()) {
|
||||
return TestCleanupResult.notFound();
|
||||
}
|
||||
PortalUser user = userOpt.get();
|
||||
|
||||
List<PartnershipApplication> targets =
|
||||
partnershipApplicationRepository.findAllByCreatedByAndBizSubjectStartingWith(user.getId(), subjectPrefix);
|
||||
if (targets.isEmpty()) {
|
||||
TestCleanupResult empty = TestCleanupResult.notFound();
|
||||
empty.put("PTL_PARTNERSHIP_APPLICATION", 0L);
|
||||
return empty;
|
||||
}
|
||||
|
||||
long files = 0L;
|
||||
for (PartnershipApplication target : targets) {
|
||||
if (target.getFileId() != null && !target.getFileId().trim().isEmpty()) {
|
||||
fileService.deleteFile(target.getFileId());
|
||||
files++;
|
||||
}
|
||||
}
|
||||
partnershipApplicationRepository.deleteAll(targets);
|
||||
|
||||
TestCleanupResult result = TestCleanupResult.found(user.getId());
|
||||
result.put("PTL_PARTNERSHIP_APPLICATION", (long) targets.size());
|
||||
result.put("PTL_FILE_INFO", files);
|
||||
log.info("테스트 정리 - 피드백/개선요청 삭제 완료: email={}, prefix={}, count={}", email, subjectPrefix, targets.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
private TestCleanupResult deleteUserCascadeInternal(PortalUser user) {
|
||||
TestCleanupResult result = TestCleanupResult.found(user.getId());
|
||||
result.put("PTL_INQUIRY_COMMENT", inquiryCommentRepository.deleteByInquiry_Inquirer_Id(user.getId()));
|
||||
|
||||
@@ -23,6 +23,13 @@ body {
|
||||
color: #1A1A2E;
|
||||
background-color: #FFFFFF;
|
||||
overflow-x: hidden;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
body > * {
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
@@ -1240,7 +1247,7 @@ hr {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.mobile-drawer .drawer-welcome .btn-drawer-login:hover {
|
||||
background: rgb(0%, 25.7647058824%, 63.5294117647%);
|
||||
background: rgb(0, 65.7, 162);
|
||||
}
|
||||
.mobile-drawer .drawer-welcome.authenticated {
|
||||
flex-direction: row;
|
||||
@@ -1870,6 +1877,7 @@ hr {
|
||||
background-color: rgb(15, 23, 42);
|
||||
color: rgb(100, 116, 139);
|
||||
padding: 60px 0px;
|
||||
margin-top: auto;
|
||||
}
|
||||
.global-footer .container {
|
||||
max-width: 1200px;
|
||||
@@ -2551,7 +2559,7 @@ hr {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.btn-success:hover {
|
||||
background: rgb(32.662665066%, 78.1608643457%, 41.762304922%);
|
||||
background: rgb(83.2897959184, 199.3102040816, 106.493877551);
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
.btn-danger {
|
||||
@@ -2559,7 +2567,7 @@ hr {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
.btn-danger:hover {
|
||||
background: rgb(100%, 27.7647058824%, 27.7647058824%);
|
||||
background: rgb(255, 70.8, 70.8);
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
.btn-ghost {
|
||||
@@ -2825,7 +2833,7 @@ hr {
|
||||
.action-btn-delete:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(75, 155, 255, 0.1);
|
||||
background: rgb(100%, 34.862745098%, 34.862745098%);
|
||||
background: rgb(255, 88.9, 88.9);
|
||||
}
|
||||
.action-btn-delete:active {
|
||||
transform: translateY(0);
|
||||
@@ -2943,7 +2951,7 @@ hr {
|
||||
background: #a4d6ea;
|
||||
}
|
||||
.btn-input-action.btn-change:hover {
|
||||
background: rgb(51.6323529412%, 78.2079831933%, 88.8382352941%);
|
||||
background: rgb(131.6625, 199.4303571429, 226.5375);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(75, 155, 255, 0.1);
|
||||
}
|
||||
@@ -3018,7 +3026,7 @@ hr {
|
||||
border: none;
|
||||
}
|
||||
.btn-action-primary:hover {
|
||||
background: rgb(12.4992826399%, 36.3615494978%, 80.6771879484%);
|
||||
background: rgb(31.8731707317, 92.7219512195, 205.7268292683);
|
||||
transform: translateY(-2px);
|
||||
color: #fff;
|
||||
}
|
||||
@@ -3068,7 +3076,7 @@ hr {
|
||||
}
|
||||
.status-badge.status-processing {
|
||||
background: rgba(255, 217, 61, 0.1);
|
||||
color: rgb(86.7450980392%, 69.7537901759%, 0%);
|
||||
color: rgb(221.2, 177.8721649485, 0);
|
||||
}
|
||||
.status-badge.status-failed {
|
||||
background: rgba(255, 107, 107, 0.1);
|
||||
@@ -3108,7 +3116,7 @@ hr {
|
||||
}
|
||||
.status-badge-header.status-processing {
|
||||
background: rgba(255, 217, 61, 0.1);
|
||||
color: rgb(86.7450980392%, 69.7537901759%, 0%);
|
||||
color: rgb(221.2, 177.8721649485, 0);
|
||||
}
|
||||
|
||||
.badge-sm {
|
||||
@@ -4296,7 +4304,7 @@ select.form-control {
|
||||
.file-upload-wrapper .file-remove-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(75, 155, 255, 0.1);
|
||||
background: rgb(100%, 34.862745098%, 34.862745098%);
|
||||
background: rgb(255, 88.9, 88.9);
|
||||
}
|
||||
.file-upload-wrapper .file-remove-btn:active {
|
||||
transform: translateY(0);
|
||||
@@ -4617,7 +4625,7 @@ select.form-control {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.form-actions--with-withdrawal .withdrawal-link:hover {
|
||||
background: rgb(82.4349376114%, 91.2174688057%, 95.2709447415%);
|
||||
background: rgb(210.2090909091, 232.6045454545, 242.9409090909);
|
||||
}
|
||||
.form-actions--with-withdrawal .withdrawal-link img {
|
||||
width: 22px;
|
||||
@@ -4772,7 +4780,7 @@ select.form-control {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.notice-content-box a:hover {
|
||||
color: rgb(0%, 25.7647058824%, 63.5294117647%);
|
||||
color: rgb(0, 65.7, 162);
|
||||
}
|
||||
|
||||
.form-row--content .form-label-wrapper {
|
||||
@@ -5708,7 +5716,7 @@ select.form-control {
|
||||
font-size: 16px;
|
||||
}
|
||||
.drawer-logout-btn:hover {
|
||||
background: rgb(100%, 27.7647058824%, 27.7647058824%);
|
||||
background: rgb(255, 70.8, 70.8);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 24px rgba(75, 155, 255, 0.15);
|
||||
}
|
||||
@@ -6421,7 +6429,7 @@ select.form-control {
|
||||
color: #64748b;
|
||||
}
|
||||
.list-table-btn--default:hover {
|
||||
background-color: rgb(91.512605042%, 91.512605042%, 90.6638655462%);
|
||||
background-color: rgb(233.3571428571, 233.3571428571, 231.1928571429);
|
||||
}
|
||||
.list-table-btn--primary {
|
||||
background-color: #ecf0fa;
|
||||
@@ -6429,7 +6437,7 @@ select.form-control {
|
||||
color: #2a69de;
|
||||
}
|
||||
.list-table-btn--primary:hover {
|
||||
background-color: rgb(85.0049019608%, 88.1617647059%, 96.0539215686%);
|
||||
background-color: rgb(216.7625, 224.8125, 244.9375);
|
||||
}
|
||||
.list-table-btn--secondary {
|
||||
background-color: #f5f5f4;
|
||||
@@ -6437,7 +6445,7 @@ select.form-control {
|
||||
color: #64748b;
|
||||
}
|
||||
.list-table-btn--secondary:hover {
|
||||
background-color: rgb(91.512605042%, 91.512605042%, 90.6638655462%);
|
||||
background-color: rgb(233.3571428571, 233.3571428571, 231.1928571429);
|
||||
}
|
||||
.list-table-btn--danger {
|
||||
background-color: #fbe7e9;
|
||||
@@ -6445,7 +6453,7 @@ select.form-control {
|
||||
color: #bb1026;
|
||||
}
|
||||
.list-table-btn--danger:hover {
|
||||
background-color: rgb(97.081232493%, 82.487394958%, 83.9467787115%);
|
||||
background-color: rgb(247.5571428571, 210.3428571429, 214.0642857143);
|
||||
}
|
||||
|
||||
.table-pagination {
|
||||
@@ -7095,7 +7103,7 @@ select.form-control {
|
||||
.alert.alert-error {
|
||||
background: rgba(255, 107, 107, 0.1);
|
||||
border: 1px solid rgba(255, 107, 107, 0.3);
|
||||
color: rgb(100%, 27.7647058824%, 27.7647058824%);
|
||||
color: rgb(255, 70.8, 70.8);
|
||||
align-items: center;
|
||||
}
|
||||
.alert.alert-error svg {
|
||||
@@ -7109,7 +7117,7 @@ select.form-control {
|
||||
.alert.alert-success {
|
||||
background: rgba(107, 207, 127, 0.1);
|
||||
border: 1px solid rgba(107, 207, 127, 0.3);
|
||||
color: rgb(24.12484994%, 74.3849539816%, 34.1768707483%);
|
||||
color: rgb(61.5183673469, 189.6816326531, 87.1510204082);
|
||||
}
|
||||
.alert.alert-info {
|
||||
background: rgba(0, 73, 180, 0.1);
|
||||
@@ -11480,10 +11488,10 @@ body.index-page-body {
|
||||
line-height: 20px;
|
||||
}
|
||||
.login-button:hover {
|
||||
background: rgb(10.0588235294%, 27.568627451%, 68.1764705882%);
|
||||
background: rgb(25.65, 70.3, 173.85);
|
||||
}
|
||||
.login-button:active {
|
||||
background: rgb(9.5294117647%, 26.1176470588%, 64.5882352941%);
|
||||
background: rgb(24.3, 66.6, 164.7);
|
||||
}
|
||||
.login-button:disabled {
|
||||
opacity: 0.6;
|
||||
@@ -11526,10 +11534,10 @@ body.index-page-body {
|
||||
border-bottom-right-radius: 8px;
|
||||
}
|
||||
.login-links-container .link-btn:hover {
|
||||
background: rgb(86.5137254902%, 89.3529411765%, 96.4509803922%);
|
||||
background: rgb(220.61, 227.85, 245.95);
|
||||
}
|
||||
.login-links-container .link-btn:active {
|
||||
background: rgb(80.4784313725%, 84.5882352941%, 94.862745098%);
|
||||
background: rgb(205.22, 215.7, 241.9);
|
||||
}
|
||||
|
||||
.login-alert {
|
||||
@@ -12038,12 +12046,12 @@ body.index-page-body {
|
||||
}
|
||||
.auth-request-button:hover,
|
||||
.auth-verify-button:hover {
|
||||
background: rgb(14.6320689023%, 60.3648891332%, 92.1600879604%);
|
||||
background: rgb(37.3117757009, 153.9304672897, 235.0082242991);
|
||||
transform: none !important;
|
||||
}
|
||||
.auth-request-button:active,
|
||||
.auth-verify-button:active {
|
||||
background: rgb(8.3967014843%, 57.3774601429%, 91.4307494961%);
|
||||
background: rgb(21.411588785, 146.3125233645, 233.148411215);
|
||||
}
|
||||
.auth-request-button:disabled,
|
||||
.auth-verify-button:disabled {
|
||||
@@ -12092,10 +12100,10 @@ body.index-page-body {
|
||||
background: #f0f2f5;
|
||||
}
|
||||
.account-recovery-card .form-actions .cancel-button:hover {
|
||||
background: rgb(88.4117647059%, 89.9568627451%, 92.2745098039%);
|
||||
background: rgb(225.45, 229.39, 235.3);
|
||||
}
|
||||
.account-recovery-card .form-actions .cancel-button:active {
|
||||
background: rgb(82.7058823529%, 85.0117647059%, 88.4705882353%);
|
||||
background: rgb(210.9, 216.78, 225.6);
|
||||
}
|
||||
.account-recovery-card .form-actions .submit-button {
|
||||
color: #FFFFFF;
|
||||
@@ -12105,7 +12113,7 @@ body.index-page-body {
|
||||
background: rgb(6, 54, 125);
|
||||
}
|
||||
.account-recovery-card .form-actions .submit-button:active {
|
||||
background: rgb(0%, 25.7647058824%, 63.5294117647%);
|
||||
background: rgb(0, 65.7, 162);
|
||||
}
|
||||
.account-recovery-card .form-actions .submit-button:disabled {
|
||||
opacity: 0.6;
|
||||
@@ -12341,7 +12349,7 @@ body.index-page-body {
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
.result-info-box .info-text .info-link:hover {
|
||||
color: rgb(0%, 25.7647058824%, 63.5294117647%);
|
||||
color: rgb(0, 65.7, 162);
|
||||
}
|
||||
@media (max-width: 576px) {
|
||||
.result-info-box .info-text {
|
||||
@@ -17541,7 +17549,7 @@ input[type=checkbox]:checked + .custom-checkbox {
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
.btn-copy-action:hover {
|
||||
background: rgb(51.6323529412%, 78.2079831933%, 88.8382352941%);
|
||||
background: rgb(131.6625, 199.4303571429, 226.5375);
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.btn-copy-action {
|
||||
@@ -17568,7 +17576,7 @@ input[type=checkbox]:checked + .custom-checkbox {
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
.btn-view-secret:hover {
|
||||
background: rgb(12.5057724024%, 59.377680044%, 91.9648158329%);
|
||||
background: rgb(31.8897196262, 151.4130841121, 234.5102803738);
|
||||
}
|
||||
.btn-view-secret svg {
|
||||
width: 20px;
|
||||
@@ -17753,7 +17761,7 @@ input[type=checkbox]:checked + .custom-checkbox {
|
||||
border-radius: 8px;
|
||||
}
|
||||
.btn-copy-action:hover {
|
||||
background: rgb(51.6323529412%, 78.2079831933%, 88.8382352941%);
|
||||
background: rgb(131.6625, 199.4303571429, 226.5375);
|
||||
}
|
||||
.btn-view-secret {
|
||||
width: 100% !important;
|
||||
@@ -17769,7 +17777,7 @@ input[type=checkbox]:checked + .custom-checkbox {
|
||||
height: 16px;
|
||||
}
|
||||
.btn-view-secret:hover {
|
||||
background: rgb(12.5057724024%, 59.377680044%, 91.9648158329%);
|
||||
background: rgb(31.8897196262, 151.4130841121, 234.5102803738);
|
||||
}
|
||||
#revealedSecretBox {
|
||||
width: 100%;
|
||||
@@ -18042,7 +18050,7 @@ input[type=checkbox]:checked + .custom-checkbox {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.detail-wrap .dt-btn-copy:hover {
|
||||
background: rgb(74.3529411765%, 90.2296918768%, 100%);
|
||||
background: rgb(189.6, 230.0857142857, 255);
|
||||
}
|
||||
.detail-wrap .dt-btn-copy svg {
|
||||
color: #2a69de;
|
||||
@@ -18223,7 +18231,7 @@ input[type=checkbox]:checked + .custom-checkbox {
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
.detail-wrap .dt-btn-gray:hover {
|
||||
background: rgb(66.9250773994%, 71.9364293086%, 75.9455108359%);
|
||||
background: rgb(170.6589473684, 183.4378947368, 193.6610526316);
|
||||
}
|
||||
.detail-wrap .dt-btn-red {
|
||||
width: 156px;
|
||||
@@ -18241,7 +18249,7 @@ input[type=checkbox]:checked + .custom-checkbox {
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
.detail-wrap .dt-btn-red:hover {
|
||||
background: rgb(100%, 27.4868759774%, 25.1921568627%);
|
||||
background: rgb(255, 70.0915337423, 64.24);
|
||||
}
|
||||
.detail-wrap .dt-btn-blue {
|
||||
width: 156px;
|
||||
@@ -19815,7 +19823,7 @@ input[type=checkbox]:checked + .custom-checkbox {
|
||||
}
|
||||
}
|
||||
.btn-inquiry-list:hover {
|
||||
background: rgb(84.6615515772%, 85.8414322251%, 88.2011935209%);
|
||||
background: rgb(215.8869565217, 218.8956521739, 224.9130434783);
|
||||
}
|
||||
.btn-inquiry-list:active {
|
||||
transform: scale(0.98);
|
||||
@@ -19848,7 +19856,7 @@ input[type=checkbox]:checked + .custom-checkbox {
|
||||
}
|
||||
}
|
||||
.btn-inquiry-edit:hover {
|
||||
background: rgb(0%, 27.1960784314%, 67.0588235294%);
|
||||
background: rgb(0, 69.35, 171);
|
||||
}
|
||||
.btn-inquiry-edit:active {
|
||||
transform: scale(0.98);
|
||||
@@ -19881,7 +19889,7 @@ input[type=checkbox]:checked + .custom-checkbox {
|
||||
}
|
||||
}
|
||||
.btn-inquiry-delete:hover {
|
||||
background: rgb(85.4839910648%, 16.2218912882%, 22.8577810871%);
|
||||
background: rgb(217.9841772152, 41.3658227848, 58.2873417722);
|
||||
}
|
||||
.btn-inquiry-delete:active {
|
||||
transform: scale(0.98);
|
||||
@@ -19953,7 +19961,7 @@ input[type=checkbox]:checked + .custom-checkbox {
|
||||
margin-left: 8px;
|
||||
}
|
||||
.file-upload-inline .btn-remove-file-inline:hover {
|
||||
background: rgb(82.1236038719%, 14.2293373045%, 20.7341772152%);
|
||||
background: rgb(209.4151898734, 36.2848101266, 52.8721518987);
|
||||
}
|
||||
.file-upload-inline .btn-remove-file-inline svg {
|
||||
width: 12px;
|
||||
@@ -19985,7 +19993,7 @@ input[type=checkbox]:checked + .custom-checkbox {
|
||||
}
|
||||
}
|
||||
.file-upload-inline .btn-file-attach:hover {
|
||||
background: rgb(14.6320689023%, 60.3648891332%, 92.1600879604%);
|
||||
background: rgb(37.3117757009, 153.9304672897, 235.0082242991);
|
||||
}
|
||||
.file-upload-inline .btn-file-attach svg {
|
||||
width: 22px;
|
||||
@@ -20045,7 +20053,7 @@ input[type=checkbox]:checked + .custom-checkbox {
|
||||
border: none;
|
||||
}
|
||||
.inquiry-form-container .form-actions .btn-secondary:hover {
|
||||
background: rgb(84.6615515772%, 85.8414322251%, 88.2011935209%);
|
||||
background: rgb(215.8869565217, 218.8956521739, 224.9130434783);
|
||||
}
|
||||
.inquiry-form-container .form-actions .btn-primary {
|
||||
background: #0049b4;
|
||||
@@ -20053,7 +20061,7 @@ input[type=checkbox]:checked + .custom-checkbox {
|
||||
border: none;
|
||||
}
|
||||
.inquiry-form-container .form-actions .btn-primary:hover {
|
||||
background: rgb(0%, 27.1960784314%, 67.0588235294%);
|
||||
background: rgb(0, 69.35, 171);
|
||||
}
|
||||
.inquiry-form-container .file-upload-inline .file-input-display {
|
||||
min-height: 50px;
|
||||
@@ -20842,7 +20850,7 @@ input[type=checkbox]:checked + .custom-checkbox {
|
||||
cursor: pointer;
|
||||
}
|
||||
.djb-board-write-container .form-actions .btn-submit:hover {
|
||||
background-color: rgb(13.193687231%, 38.3816355811%, 85.1592539455%);
|
||||
background-color: rgb(33.643902439, 97.8731707317, 217.156097561);
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.djb-board-write-container .form-actions .btn-submit {
|
||||
@@ -21378,7 +21386,7 @@ input[type=checkbox]:checked + .custom-checkbox {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.org-file-remove:hover {
|
||||
background: rgb(100%, 27.7647058824%, 27.7647058824%);
|
||||
background: rgb(255, 70.8, 70.8);
|
||||
}
|
||||
|
||||
.org-file-notice {
|
||||
@@ -21843,6 +21851,35 @@ input[type=checkbox]:checked + .custom-checkbox {
|
||||
color: #334155;
|
||||
word-break: break-word;
|
||||
}
|
||||
.recent-apps .recent-apps-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 16px;
|
||||
padding-top: 14px;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
}
|
||||
.recent-apps .recent-apps-delete {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 7px 16px;
|
||||
background: #ffffff;
|
||||
border: 1px solid #fca5a5;
|
||||
border-radius: 6px;
|
||||
color: #dc2626;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease, border-color 0.2s ease;
|
||||
}
|
||||
.recent-apps .recent-apps-delete:hover {
|
||||
background: #fef2f2;
|
||||
border-color: #f87171;
|
||||
}
|
||||
.recent-apps .recent-apps-delete:focus {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.15);
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 40px;
|
||||
@@ -22390,7 +22427,7 @@ input[type=checkbox]:checked + .custom-checkbox {
|
||||
}
|
||||
.status-indicator.status-active {
|
||||
background-color: rgba(107, 207, 127, 0.1);
|
||||
color: rgb(32.662665066%, 78.1608643457%, 41.762304922%);
|
||||
color: rgb(83.2897959184, 199.3102040816, 106.493877551);
|
||||
}
|
||||
.status-indicator.status-active .status-dot {
|
||||
background-color: #6BCF7F;
|
||||
|
||||
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -30,6 +30,19 @@ body {
|
||||
color: $text-dark;
|
||||
background-color: $white;
|
||||
overflow-x: hidden;
|
||||
|
||||
// 콘텐츠가 짧은 페이지에서도 footer 가 화면 하단에 붙도록(sticky footer).
|
||||
// footer 는 .global-footer 의 margin-top:auto 로 밀려난다.
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
// flex 컨테이너가 되면 자식이 축소(shrink)되거나 내부 콘텐츠 min-content 폭까지 늘어난다.
|
||||
// block 레이아웃과 동일한 폭 계산이 되도록 고정한다(모바일 가로 스크롤 방지).
|
||||
> * {
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// Lists
|
||||
|
||||
@@ -163,6 +163,8 @@
|
||||
background-color: rgb(15, 23, 42);
|
||||
color: rgb(100, 116, 139);
|
||||
padding: 60px 0px;
|
||||
// body(flex column) 기준으로 남은 공간을 위쪽 여백으로 흡수 → 짧은 페이지에서 화면 하단 고정
|
||||
margin-top: auto;
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
|
||||
@@ -102,6 +102,40 @@
|
||||
color: #334155;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
// 본인 글 삭제 버튼(아코디언 펼친 상태에서만 노출)
|
||||
.recent-apps-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 16px;
|
||||
padding-top: 14px;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.recent-apps-delete {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 7px 16px;
|
||||
background: #ffffff;
|
||||
border: 1px solid #fca5a5;
|
||||
border-radius: 6px;
|
||||
color: #dc2626;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease, border-color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
background: #fef2f2;
|
||||
border-color: #f87171;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.15);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.page-title {
|
||||
|
||||
@@ -57,6 +57,21 @@
|
||||
</button>
|
||||
<div class="recent-apps-body">
|
||||
<p class="recent-apps-detail" th:text="${item.bizDetail}">내용</p>
|
||||
<div class="recent-apps-actions">
|
||||
<form class="recent-apps-delete-form" method="post"
|
||||
th:action="@{/partnership/{id}/delete(id=${item.id})}">
|
||||
<button type="button" class="recent-apps-delete" th:attr="data-subject=${item.bizSubject}">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
||||
stroke-width="2" aria-hidden="true">
|
||||
<polyline points="3 6 5 6 21 6"></polyline>
|
||||
<path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"></path>
|
||||
<path d="M10 11v6"></path>
|
||||
<path d="M14 11v6"></path>
|
||||
</svg>
|
||||
삭제
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -265,6 +280,17 @@
|
||||
item.siblings('.recent-apps-item').removeClass('active').find('.recent-apps-body').slideUp(200);
|
||||
});
|
||||
|
||||
// 최근 글 삭제 — 확인 팝업 후 항목별 form 전송(POST /partnership/{id}/delete)
|
||||
$('.recent-apps-delete').on('click', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const form = $(this).closest('form.recent-apps-delete-form').get(0);
|
||||
const subject = $(this).data('subject') || '';
|
||||
customPopups.showConfirm('피드백/개선요청 [' + subject + '] 을(를) 삭제하시겠습니까?', function (ok) {
|
||||
if (ok) form.submit();
|
||||
});
|
||||
});
|
||||
|
||||
// Focus on subject field
|
||||
document.getElementById('bizSubject').focus();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user