Index.cshtml 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. @page
  2. @model Admin.Pages.Config.Attendance.IndexModel
  3. @{
  4. ViewData["Title"] = "출석 관리";
  5. }
  6. <div class="container">
  7. <h3>@ViewData["Title"]</h3>
  8. <hr />
  9. <div asp-validation-summary="ModelOnly" class="text-danger"></div>
  10. <partial name="_StatusMessage" />
  11. <form name="f_admin_write" id="fAdminWrite" class="mt-2" method="post" autocomplete="off" accept-charset="UTF-8">
  12. <!-- 출석 기능 -->
  13. <details open>
  14. <summary class="fs-5 mb-3">출석 기능</summary>
  15. <div class="row mb-2">
  16. <label asp-for="Input.Attendance.IsEnabled" class="col-sm-2 col-form-label">출석 기능 활성화</label>
  17. <div class="col-sm-10 align-content-center">
  18. <div class="form-check-inline">
  19. <input asp-for="Input.Attendance.IsEnabled" class="form-check-input" />
  20. <label class="form-check-label" for="Input_Attendance_IsEnabled">사용</label>
  21. </div>
  22. <span asp-validation-for="Input.Attendance.IsEnabled" class="text-danger"></span>
  23. </div>
  24. </div>
  25. </details>
  26. <hr />
  27. <!-- 기본 보상 -->
  28. <details open>
  29. <summary class="fs-5 mb-3">기본 보상</summary>
  30. <div class="row mb-2">
  31. <label asp-for="Input.Attendance.BaseExp" class="col-sm-2 col-form-label">기본 경험치</label>
  32. <div class="col-sm-3">
  33. <input asp-for="Input.Attendance.BaseExp" type="number" class="form-control" min="0" max="100000" />
  34. <div class="form-text text-muted">출석 시 기본으로 지급되는 경험치</div>
  35. <span asp-validation-for="Input.Attendance.BaseExp" class="text-danger"></span>
  36. </div>
  37. </div>
  38. <div class="row mb-2">
  39. <label asp-for="Input.Attendance.BasePoint" class="col-sm-2 col-form-label">기본 코인</label>
  40. <div class="col-sm-3">
  41. <input asp-for="Input.Attendance.BasePoint" type="number" class="form-control" min="0" max="100000" />
  42. <div class="form-text text-muted">출석 시 기본으로 지급되는 코인</div>
  43. <span asp-validation-for="Input.Attendance.BasePoint" class="text-danger"></span>
  44. </div>
  45. </div>
  46. </details>
  47. <hr />
  48. <!-- 연속 출석 가중치 -->
  49. <details open>
  50. <summary class="fs-5 mb-3">연속 출석 가중치</summary>
  51. <div class="row mb-2">
  52. <label asp-for="Input.Attendance.UseStreakBonus" class="col-sm-2 col-form-label">가중치 사용</label>
  53. <div class="col-sm-10 align-content-center">
  54. <div class="form-check-inline">
  55. <input asp-for="Input.Attendance.UseStreakBonus" class="form-check-input" />
  56. <label class="form-check-label" for="Input_Attendance_UseStreakBonus">사용</label>
  57. </div>
  58. <div class="form-text text-muted">연속 출석 시 추가 보상을 지급합니다.</div>
  59. <span asp-validation-for="Input.Attendance.UseStreakBonus" class="text-danger"></span>
  60. </div>
  61. </div>
  62. <div class="row mb-2">
  63. <label asp-for="Input.Attendance.StreakBonusPerDay" class="col-sm-2 col-form-label">1일당 추가 경험치</label>
  64. <div class="col-sm-3">
  65. <input asp-for="Input.Attendance.StreakBonusPerDay" type="number" class="form-control" min="0" max="10000" />
  66. <div class="form-text text-muted">연속 출석 1일당 추가 지급 경험치</div>
  67. <span asp-validation-for="Input.Attendance.StreakBonusPerDay" class="text-danger"></span>
  68. </div>
  69. </div>
  70. <div class="row mb-2">
  71. <label asp-for="Input.Attendance.StreakBonusPointPerDay" class="col-sm-2 col-form-label">1일당 추가 코인</label>
  72. <div class="col-sm-3">
  73. <input asp-for="Input.Attendance.StreakBonusPointPerDay" type="number" class="form-control" min="0" max="10000" />
  74. <div class="form-text text-muted">연속 출석 1일당 추가 지급 코인</div>
  75. <span asp-validation-for="Input.Attendance.StreakBonusPointPerDay" class="text-danger"></span>
  76. </div>
  77. </div>
  78. <div class="row mb-2">
  79. <label asp-for="Input.Attendance.StreakBonusMaxDays" class="col-sm-2 col-form-label">가중치 최대 적용 일수</label>
  80. <div class="col-sm-3">
  81. <input asp-for="Input.Attendance.StreakBonusMaxDays" type="number" class="form-control" min="0" max="365" />
  82. <div class="form-text text-muted">가중치가 적용되는 최대 연속 출석 일수 (0 = 무제한)</div>
  83. <span asp-validation-for="Input.Attendance.StreakBonusMaxDays" class="text-danger"></span>
  84. </div>
  85. </div>
  86. </details>
  87. <hr />
  88. <!-- 순위 보상 -->
  89. <details open>
  90. <summary class="fs-5 mb-3">순위 보상</summary>
  91. <div class="row mb-2">
  92. <label asp-for="Input.Attendance.UseRankBonus" class="col-sm-2 col-form-label">순위 보상 사용</label>
  93. <div class="col-sm-10 align-content-center">
  94. <div class="form-check-inline">
  95. <input asp-for="Input.Attendance.UseRankBonus" class="form-check-input" />
  96. <label class="form-check-label" for="Input_Attendance_UseRankBonus">사용</label>
  97. </div>
  98. <div class="form-text text-muted">출석 순위(1~10등)에 따라 추가 보상을 지급합니다.</div>
  99. <span asp-validation-for="Input.Attendance.UseRankBonus" class="text-danger"></span>
  100. </div>
  101. </div>
  102. <div class="row mb-2">
  103. <div class="col-sm-2"></div>
  104. <div class="col-sm-10">
  105. <table class="table table-bordered table-sm" style="max-width: 500px;" id="rankBonusTable">
  106. <thead class="table-light">
  107. <tr>
  108. <th class="text-center" style="width: 80px;">순위</th>
  109. <th class="text-center">추가 경험치</th>
  110. <th class="text-center">추가 코인</th>
  111. </tr>
  112. </thead>
  113. <tbody>
  114. @for (var i = 1; i <= 10; i++)
  115. {
  116. <tr>
  117. <td class="text-center align-middle fw-bold">@(i)등</td>
  118. <td><input type="number" class="form-control form-control-sm rank-exp" data-rank="@i" min="0" max="100000" value="0" /></td>
  119. <td><input type="number" class="form-control form-control-sm rank-point" data-rank="@i" min="0" max="100000" value="0" /></td>
  120. </tr>
  121. }
  122. </tbody>
  123. </table>
  124. </div>
  125. </div>
  126. <input asp-for="Input.Attendance.RankBonusConfig" type="hidden" id="rankBonusConfigInput" />
  127. </details>
  128. <hr />
  129. <!-- 보상 계산 예시 -->
  130. <details>
  131. <summary class="fs-5 mb-3">보상 계산 예시</summary>
  132. <div class="alert alert-info mb-0">
  133. <strong>보상 계산 예시</strong><br />
  134. 기본 경험치 10, 1일당 추가 경험치 2, 최대 7일 설정 시:<br />
  135. 1일차: 10 EXP | 2일차: 12 EXP | 3일차: 14 EXP | ... | 7일차+: 22 EXP<br />
  136. <br />
  137. 순위 보상이 설정된 경우 위 보상에 추가로 지급됩니다.
  138. </div>
  139. </details>
  140. <hr />
  141. <div class="row">
  142. <div class="col text-center p-3">
  143. <button type="submit" class="btn btn-success">저장하기</button>
  144. </div>
  145. </div>
  146. <br />
  147. </form>
  148. </div>
  149. @section Scripts {
  150. <script>
  151. (function () {
  152. const configInput = document.getElementById('rankBonusConfigInput');
  153. const table = document.getElementById('rankBonusTable');
  154. // 초기 로드: JSON → 테이블
  155. try {
  156. const data = JSON.parse(configInput.value || '[]');
  157. data.forEach(item => {
  158. const expInput = table.querySelector(`.rank-exp[data-rank="${item.rank}"]`);
  159. const pointInput = table.querySelector(`.rank-point[data-rank="${item.rank}"]`);
  160. if (expInput) expInput.value = item.exp || 0;
  161. if (pointInput) pointInput.value = item.point || 0;
  162. });
  163. } catch (e) { }
  164. // 폼 제출 시: 테이블 → JSON
  165. document.getElementById('fAdminWrite').addEventListener('submit', function () {
  166. const entries = [];
  167. for (let i = 1; i <= 10; i++) {
  168. const exp = parseInt(table.querySelector(`.rank-exp[data-rank="${i}"]`).value) || 0;
  169. const point = parseInt(table.querySelector(`.rank-point[data-rank="${i}"]`).value) || 0;
  170. if (exp > 0 || point > 0) {
  171. entries.push({ rank: i, exp: exp, point: point });
  172. }
  173. }
  174. configInput.value = JSON.stringify(entries);
  175. });
  176. })();
  177. </script>
  178. }