-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
37bc97d
commit 2126555
Showing
12 changed files
with
403 additions
and
1 deletion.
There are no files selected for viewing
67 changes: 67 additions & 0 deletions
67
Bootstrap-Module/Admin/src/main/kotlin/com/clip/admin/StoreContactController.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package com.clip.admin | ||
|
||
import com.clip.admin.dto.CreateContactRequest | ||
import com.clip.admin.dto.GetStoreContactInfoResponse | ||
import com.clip.admin.dto.UpdateContactRequest | ||
import org.springframework.stereotype.Controller | ||
import org.springframework.web.bind.annotation.* | ||
import java.util.UUID | ||
|
||
@Controller | ||
@RequestMapping("/store/{id}/contact") | ||
class StoreContactController { | ||
|
||
@GetMapping | ||
@ResponseBody | ||
fun getStoreContact( | ||
@PathVariable id: String | ||
): List<GetStoreContactInfoResponse> { | ||
return listOf( | ||
GetStoreContactInfoResponse(UUID.randomUUID().toString(),"PHONE", "010-1234-5678"), | ||
GetStoreContactInfoResponse(UUID.randomUUID().toString(),"INSTAGRAM", "@store1"), | ||
) | ||
} | ||
|
||
@GetMapping("/type") | ||
@ResponseBody | ||
fun getStoreContactType(): List<String> { | ||
return listOf("PHONE", "INSTAGRAM") | ||
} | ||
|
||
@GetMapping("/{contactId}") | ||
@ResponseBody | ||
fun getStoreContactDetail( | ||
@PathVariable id: String, | ||
@PathVariable contactId: String | ||
): GetStoreContactInfoResponse { | ||
return GetStoreContactInfoResponse(UUID.randomUUID().toString(),"PHONE", "010-1234-5678") | ||
} | ||
|
||
@PostMapping | ||
@ResponseBody | ||
fun createStoreContact( | ||
@PathVariable id: String, | ||
@RequestBody request: CreateContactRequest | ||
) { | ||
println("create contact: $request") | ||
} | ||
|
||
@PutMapping("/{contactId}") | ||
@ResponseBody | ||
fun updateStoreContact( | ||
@PathVariable id: String, | ||
@PathVariable contactId: String, | ||
@RequestBody request: UpdateContactRequest | ||
) { | ||
println("update contact: $request") | ||
} | ||
|
||
@DeleteMapping("/{contactId}") | ||
@ResponseBody | ||
fun deleteStoreContact( | ||
@PathVariable id: String, | ||
@PathVariable contactId: String | ||
) { | ||
println("delete contact: $contactId") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 7 additions & 0 deletions
7
Bootstrap-Module/Admin/src/main/kotlin/com/clip/admin/dto/CreateContactRequest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.clip.admin.dto | ||
|
||
data class CreateContactRequest( | ||
val contactType: String, | ||
val value: String | ||
) { | ||
} |
8 changes: 8 additions & 0 deletions
8
Bootstrap-Module/Admin/src/main/kotlin/com/clip/admin/dto/GetStoreContactInfoResponse.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.clip.admin.dto | ||
|
||
data class GetStoreContactInfoResponse( | ||
val id: String, | ||
val contactType: String, | ||
val value: String | ||
) { | ||
} |
7 changes: 7 additions & 0 deletions
7
Bootstrap-Module/Admin/src/main/kotlin/com/clip/admin/dto/UpdateContactRequest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.clip.admin.dto | ||
|
||
data class UpdateContactRequest( | ||
val contactType: String, | ||
val value: String | ||
) { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 57 additions & 0 deletions
57
Bootstrap-Module/Admin/src/main/resources/static/js/store/contact/StoreContactApi.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
const StoreContactApi = { | ||
|
||
endpoints: { | ||
list: (storeId) => `/store/${storeId}/contact`, | ||
create: (storeId) => `/store/${storeId}/contact`, | ||
detail: (storeId, contactId) => `/store/${storeId}/contact/${contactId}`, | ||
update: (storeId, contactId) => `/store/${storeId}/contact/${contactId}`, | ||
delete: (storeId, contactId) => `/store/${storeId}/contact/${contactId}`, | ||
types: (storeId) => `/store/${storeId}/contact/type` | ||
}, | ||
|
||
async getContacts(storeId) { | ||
const response = await fetch(this.endpoints.list(storeId)); | ||
if (!response.ok) throw new Error('연락처 목록을 불러오는데 실패했습니다.'); | ||
return await response.json(); | ||
}, | ||
|
||
async getContact(storeId, contactId) { | ||
const response = await fetch(this.endpoints.detail(storeId, contactId)); | ||
if (!response.ok) throw new Error('연락처 정보를 불러오는데 실패했습니다.'); | ||
return await response.json(); | ||
}, | ||
|
||
async createContact(storeId, contactData) { | ||
const response = await fetch(this.endpoints.create(storeId), { | ||
method: 'POST', | ||
headers: {'Content-Type': 'application/json'}, | ||
body: JSON.stringify(contactData) | ||
}); | ||
if (!response.ok) throw new Error('연락처 추가에 실패했습니다.'); | ||
}, | ||
|
||
async updateContact(storeId, contactId, contactData) { | ||
const response = await fetch(this.endpoints.update(storeId, contactId), { | ||
method: 'PUT', | ||
headers: {'Content-Type': 'application/json'}, | ||
body: JSON.stringify(contactData) | ||
}); | ||
if (!response.ok) throw new Error('연락처 수정에 실패했습니다.'); | ||
}, | ||
|
||
async deleteContact(storeId, contactId) { | ||
const response = await fetch(this.endpoints.delete(storeId, contactId), { | ||
method: 'DELETE' | ||
}); | ||
if (!response.ok) throw new Error('연락처 삭제에 실패했습니다.'); | ||
return true; | ||
}, | ||
|
||
async getContactTypes(storeId) { | ||
const response = await fetch(this.endpoints.types(storeId)); | ||
if (!response.ok) throw new Error('연락처 유형을 불러오는데 실패했습니다.'); | ||
return await response.json(); | ||
} | ||
|
||
|
||
} |
184 changes: 184 additions & 0 deletions
184
Bootstrap-Module/Admin/src/main/resources/static/js/store/contact/StoreContactList.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
const StoreContact = { | ||
contactModal: null, | ||
currentContactId: null, | ||
currentStoreId: null, | ||
contactTypes: [], // 연락처 타입 저장 | ||
|
||
init(storeId) { | ||
this.contactModal = new bootstrap.Modal(document.getElementById('contactModal')); | ||
this.currentStoreId = storeId; | ||
this.loadContactTypes(storeId).then(() => { | ||
this.loadContacts(); | ||
this.initializeEvents(); | ||
}); | ||
}, | ||
|
||
initializeEvents() { | ||
|
||
document.getElementById('contactForm').addEventListener('submit', (e) => { | ||
e.preventDefault(); | ||
this.saveContact(); | ||
}); | ||
|
||
document.getElementById('contactModal').addEventListener('hidden.bs.modal', () => { | ||
document.getElementById('contactForm').reset(); | ||
this.currentContactId = null; | ||
}); | ||
}, | ||
|
||
async loadContacts() { | ||
try { | ||
const contacts = await StoreContactApi.getContacts(this.currentStoreId); | ||
this.renderContactTable(contacts); | ||
} catch (error) { | ||
console.error('Error:', error); | ||
this.showError('연락처 목록을 불러오는데 실패했습니다.'); | ||
} | ||
}, | ||
|
||
// 연락처 타입 로드 | ||
async loadContactTypes(storeId) { | ||
try { | ||
this.contactTypes = await StoreContactApi.getContactTypes(storeId); | ||
this.renderContactTypes(); | ||
} catch (error) { | ||
console.error('Error:', error); | ||
this.showError('연락처 타입을 불러오는데 실패했습니다.'); | ||
} | ||
}, | ||
|
||
renderContactTable(contacts) { | ||
const tableBody = document.getElementById('contactTableBody'); | ||
tableBody.innerHTML = ''; | ||
|
||
if (contacts.length === 0) { | ||
tableBody.innerHTML = ` | ||
<tr> | ||
<td colspan="3" class="text-center">등록된 연락처가 없습니다.</td> | ||
</tr> | ||
`; | ||
return; | ||
} | ||
|
||
contacts.forEach(contact => { | ||
const row = document.createElement('tr'); | ||
row.innerHTML = ` | ||
<td> | ||
<span class="badge bg-secondary">${this.formatContactType(contact.contactType)}</span> | ||
</td> | ||
<td>${contact.value}</td> | ||
<td> | ||
<button class="btn btn-sm btn-outline-primary me-1" | ||
onclick="StoreContact.openEditModal('${contact.id}')"> | ||
<i class="bi bi-pencil"></i> | ||
</button> | ||
<button class="btn btn-sm btn-outline-danger" | ||
onclick="StoreContact.deleteContact('${contact.id}')"> | ||
<i class="bi bi-trash"></i> | ||
</button> | ||
</td> | ||
`; | ||
tableBody.appendChild(row); | ||
}); | ||
}, | ||
|
||
|
||
// 연락처 타입 드롭다운 렌더링 | ||
renderContactTypes() { | ||
const select = document.getElementById('contactType'); | ||
select.innerHTML = '<option value="">선택하세요</option>'; | ||
|
||
this.contactTypes.forEach(type => { | ||
const option = document.createElement('option'); | ||
option.value = type; | ||
option.textContent = this.formatContactType(type); | ||
select.appendChild(option); | ||
}); | ||
}, | ||
|
||
// 연락처 타입 포맷팅 | ||
formatContactType(type) { | ||
const typeMap = { | ||
'PHONE': '전화번호', | ||
'INSTAGRAM': '인스타그램' | ||
}; | ||
return typeMap[type] || type; | ||
}, | ||
|
||
openAddModal() { | ||
this.currentContactId = null; | ||
document.getElementById('contactModalLabel').textContent = '메뉴 추가'; | ||
document.getElementById('contactForm').reset(); | ||
this.contactModal.show(); | ||
}, | ||
|
||
async openEditModal(contactId) { | ||
try { | ||
const contact = await StoreContactApi.getContact(this.currentStoreId, contactId); | ||
this.currentContactId = contactId; | ||
document.getElementById('contactModalLabel').textContent = '연락처 수정'; | ||
document.getElementById('contactType').value = contact.contactType; | ||
document.getElementById('contactValue').value = contact.value; | ||
this.contactModal.show(); | ||
} catch (error) { | ||
console.error('Error:', error); | ||
this.showError('연락처 정보를 불러오는데 실패했습니다.'); | ||
} | ||
}, | ||
|
||
|
||
async saveContact() { | ||
if (!this.validateContactForm()) return; | ||
|
||
const contactData = { | ||
contactType: document.getElementById('contactType').value, | ||
value: document.getElementById('contactValue').value | ||
} | ||
|
||
try { | ||
if (this.currentContactId) { | ||
await StoreContactApi.updateContact(this.currentStoreId, this.currentContactId, contactData); | ||
} else { | ||
await StoreContactApi.createContact(this.currentStoreId, contactData); | ||
} | ||
this.loadContacts(); | ||
this.showSuccess(this.currentContactId ? '연락처가 수정되었습니다.' : '연락처가 추가되었습니다.'); | ||
this.contactModal.hide(); | ||
} catch (error) { | ||
console.error('Error:', error); | ||
this.showError('연락처 저장에 실패했습니다.'); | ||
} | ||
}, | ||
|
||
async deleteContact(contactId) { | ||
if (!confirm('정말로 삭제하시겠습니까?')) return; | ||
|
||
try { | ||
await StoreContactApi.deleteContact(this.currentStoreId, contactId); | ||
this.loadContacts(); | ||
this.showSuccess('연락처가 삭제되었습니다.'); | ||
} catch (error) { | ||
console.error('Error:', error); | ||
this.showError('연락처 삭제에 실패했습니다.'); | ||
} | ||
}, | ||
|
||
|
||
validateContactForm() { | ||
const type = document.getElementById('contactType').value; | ||
const value = document.getElementById('contactValue').value; | ||
if (!type || !value) { | ||
this.showError('연락처 유형과 값을 모두 입력해주세요.'); | ||
return false; | ||
} | ||
return true; | ||
}, | ||
|
||
showError(message) { | ||
alert(message); | ||
}, | ||
|
||
showSuccess(message) { | ||
alert(message); | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
Bootstrap-Module/Admin/src/main/resources/templates/admin/store/contact/contactEdit.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
<!DOCTYPE html> | ||
<html xmlns:th="http://www.thymeleaf.org"> | ||
<th:block th:fragment="contactModal"> | ||
<div class="modal fade" id="contactModal" tabindex="-1" aria-labelledby="contactModalLabel" aria-hidden="true"> | ||
<div class="modal-dialog"> | ||
<div class="modal-content"> | ||
<div class="modal-header"> | ||
<h5 class="modal-title" id="contactModalLabel">연락처 추가/수정</h5> | ||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> | ||
</div> | ||
<div class="modal-body"> | ||
<form id="contactForm"> | ||
<input type="hidden" id="contactId"> | ||
<div class="mb-3"> | ||
<label for="contactType" class="form-label">종류</label> | ||
<select class="form-select" id="contactType" required> | ||
<option value="">선택하세요</option> | ||
<!-- 옵션들이 동적으로 추가됨 --> | ||
</select> | ||
</div> | ||
<div class="mb-3"> | ||
<label for="contactValue" class="form-label">정보(번호, 주소)</label> | ||
<input type="text" class="form-control" id="contactValue" required> | ||
</div> | ||
</form> | ||
</div> | ||
<div class="modal-footer"> | ||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">취소</button> | ||
<button type="button" class="btn btn-primary" onclick="StoreContact.saveContact()">저장</button> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</th:block> | ||
</html> |
Oops, something went wrong.