+ {{ record.title }} +
+ + {% if record.location %} ++ + {{ record.location.name }} +
+ {% endif %} +diff --git a/diary/static/diary/css/diary.css b/diary/static/diary/css/diary.css
index abbbe7b..6d2182c 100644
--- a/diary/static/diary/css/diary.css
+++ b/diary/static/diary/css/diary.css
@@ -1,2 +1,1000 @@
/* 담당 D 전용 */
+.diary-list-container {
+ width: min(1200px, calc(100% - 80px));
+ margin: 0 auto;
+ padding: 40px 0 80px;
+}
+
+.diary-tabs-wrapper {
+ display: flex;
+ justify-content: center;
+ width: 100%;
+ margin-bottom: 12px;
+}
+
+.diary-tabs {
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
+
+ padding: 4px;
+ background: #ffffff;
+ border: 1px solid #dedede;
+ border-radius: 999px;
+
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.08);
+}
+
+.diary-tab {
+ border: 0;
+ padding: 10px 18px;
+ background: transparent;
+ border-radius: 999px;
+
+ color: #333333;
+ font-size: 14px;
+ cursor: pointer;
+
+ text-decoration: none;
+}
+
+.diary-tab.active {
+ border-color: #FBEED1;
+ background: #fff4cb;
+ color: #222222;
+ font-weight: 700;
+}
+/*
+.diary-list-header {
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 24px;
+ margin-bottom: 24px;
+}
+
+.diary-list-summary {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.diary-list-title {
+ margin: 0;
+ font-size: 26px;
+}
+
+.diary-list-count {
+ padding: 5px 10px;
+ border-radius: 999px;
+ background: #fff1ba;
+ font-size: 13px;
+ font-weight: 700;
+}
+*/
+.diary-filter-form {
+ position: relative;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.diary-sort-select,
+.period-toggle-button {
+ min-height: 42px;
+ padding: 0 16px;
+ border: 1px solid #e6ded0;
+ border-radius: 12px;
+ background: #ffffff;
+ color: #333333;
+ font: inherit;
+ cursor: pointer;
+}
+
+.diary-sort-select:focus,
+.period-toggle-button:focus,
+.period-filter input:focus {
+ outline: 2px solid #e9c767;
+ outline-offset: 2px;
+}
+
+.period-toggle-button.active {
+ border-color: #e1bd55;
+ background: #fff7d9;
+ font-weight: 700;
+}
+
+.period-filter {
+ position: absolute;
+ z-index: 10;
+ top: calc(100% + 10px);
+ right: 0;
+
+ display: flex;
+ align-items: flex-end;
+ gap: 10px;
+
+ min-width: 500px;
+ padding: 18px;
+
+ border: 1px solid #e6ded0;
+ border-radius: 16px;
+ background: #ffffff;
+ box-shadow: 0 10px 30px rgb(0 0 0 / 12%);
+}
+
+.period-filter[hidden] {
+ display: none;
+}
+
+.period-input-group {
+ display: flex;
+ flex-direction: column;
+ gap: 7px;
+}
+
+.period-input-group label {
+ color: #666666;
+ font-size: 12px;
+}
+
+.period-input-group input {
+ min-height: 40px;
+ padding: 0 10px;
+ border: 1px solid #ded7cb;
+ border-radius: 9px;
+ font: inherit;
+}
+
+.period-separator {
+ padding-bottom: 10px;
+ color: #999999;
+}
+
+.period-apply-button,
+.period-reset-button {
+ display: inline-flex;
+ min-height: 40px;
+ align-items: center;
+ justify-content: center;
+ padding: 0 14px;
+ border-radius: 9px;
+ font: inherit;
+ text-decoration: none;
+ cursor: pointer;
+}
+
+.period-apply-button {
+ border: 1px solid #e0bd59;
+ background: #ffe99c;
+ color: #222222;
+}
+
+.period-reset-button {
+ border: 1px solid #ded7cb;
+ background: #ffffff;
+ color: #555555;
+}
+
+@media (max-width: 760px) {
+ .diary-list-header {
+ align-items: flex-start;
+ flex-direction: column;
+ }
+
+ .diary-filter-form {
+ width: 100%;
+ }
+}
+
+/* 점 세 개 메뉴 스타일 */
+
+.map-display-settings {
+ position: absolute;
+ z-index: 20;
+ top: 10px;
+ right: 10px;
+}
+
+.map-settings-button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ width: 42px;
+ height: 42px;
+
+ padding: 0;
+ border: 1px solid #dedede;
+ border-radius: 50%;
+ background: #ffffff;
+
+ color: #444444;
+ font-size: 24px;
+ line-height: 1;
+ cursor: pointer;
+
+ box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
+}
+
+.map-settings-button:hover {
+ background: #f7f7f7;
+}
+
+.map-settings-menu {
+ position: absolute;
+ top: 50px;
+ right: 0;
+
+ width: 230px;
+ padding: 10px;
+
+ border: 1px solid #dedede;
+ border-radius: 14px;
+ background: #ffffff;
+
+ box-shadow: 0 10px 28px rgba(0, 0, 0, 0.14);
+}
+
+.map-settings-menu[hidden] {
+ display: none;
+}
+
+.map-settings-title {
+ margin: 4px 8px 8px;
+
+ color: #777777;
+ font-size: 12px;
+ font-weight: 700;
+}
+
+.map-settings-menu button {
+ display: block;
+
+ width: 100%;
+ padding: 11px 12px;
+
+ border: 0;
+ border-radius: 9px;
+ background: transparent;
+
+ color: #333333;
+ font: inherit;
+ text-align: left;
+ cursor: pointer;
+}
+
+.map-settings-menu button:hover,
+.map-settings-menu button.active {
+ background: #fff4cb;
+ font-weight: 700;
+}
+
+/* =========================
+ 감정별 화면
+========================= */
+
+.emotion-page-header {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 30px;
+ margin-bottom: 30px;
+}
+
+.emotion-month-summary {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+
+ padding: 12px 18px;
+ border: 1px solid #eadfce;
+ border-radius: 14px;
+ background: #ffffff;
+
+ font-size: 13px;
+}
+
+.emotion-month-summary strong {
+ color: #d98d36;
+ font-size: 18px;
+}
+
+.emotion-bubble-area {
+ position: relative;
+
+ width: 100%;
+ min-height: 560px;
+
+ border-radius: 24px;
+ background: rgba(255, 251, 238, 0.5);
+}
+
+.emotion-bubble {
+ position: absolute;
+
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 4px;
+
+ border: 2px solid #e8c578;
+ background: rgba(255, 250, 231, 0.95);
+ color: #333333;
+ font: inherit;
+ cursor: pointer;
+
+ /* emotion-bubble의 각 꼭짓점의 둥근 정도 다르게 */
+ border-radius: 42% 58% 53% 47% / 45% 41% 59% 49%;
+
+ transition:
+ transform 0.2s ease,
+ box-shadow 0.2s ease;
+}
+
+.emotion-bubble--yellow {
+ background: #fff7e3;
+ border-color: #F9D472;
+}
+
+.emotion-bubble--blue {
+ background: #eff7ff;
+ border-color: #DAE6F3;
+}
+
+.emotion-bubble--pink {
+ background: #fff2f2;
+ border-color: #F8B3B4;
+}
+
+.emotion-bubble--green {
+ background: #f3fff1;
+ border-color: #a9ce72;
+}
+
+.emotion-bubble--default {
+ background: #dedede;
+ border-color: #c5c5c5;
+}
+
+.emotion-bubble:hover {
+ z-index: 5;
+
+ transform: scale(1.08);
+
+ box-shadow: 0 10px 24px rgba(0, 0, 0, 0.12);
+}
+
+.emotion-bubble-name {
+ font-size: 14px;
+ font-weight: 700;
+}
+
+.emotion-bubble-count {
+ color: #8e877c;
+ font-size: 11px;
+}
+
+/* 기록 개수에 따른 크기 */
+
+.emotion-bubble--large {
+ width: 130px;
+ height: 130px;
+}
+
+.emotion-bubble--medium {
+ width: 92px;
+ height: 92px;
+}
+
+.emotion-bubble--small {
+ width: 64px;
+ height: 64px;
+}
+
+/* 임시 위치 배치 */
+
+.emotion-bubble:nth-child(1) {
+ top: 30px;
+ left: 9%;
+}
+
+.emotion-bubble:nth-child(2) {
+ top: 70px;
+ left: 25%;
+}
+
+.emotion-bubble:nth-child(3) {
+ top: 160px;
+ left: 15%;
+}
+
+.emotion-bubble:nth-child(4) {
+ top: 110px;
+ left: 44%;
+}
+
+.emotion-bubble:nth-child(5) {
+ top: 260px;
+ left: 34%;
+}
+
+.emotion-bubble:nth-child(6) {
+ top: 80px;
+ left: 68%;
+}
+
+.emotion-bubble:nth-child(7) {
+ top: 210px;
+ left: 75%;
+}
+
+.emotion-bubble:nth-child(8) {
+ top: 340px;
+ left: 60%;
+}
+
+.emotion-bubble:nth-child(9) {
+ top: 390px;
+ left: 20%;
+}
+
+.emotion-bubble:nth-child(10) {
+ top: 370px;
+ left: 80%;
+}
+
+/* =========================
+ 감정 팝업
+========================= */
+
+body.modal-open {
+ overflow: hidden;
+}
+
+.emotion-modal {
+ position: fixed;
+ z-index: 1000;
+ inset: 0;
+}
+
+.emotion-modal[hidden] {
+ display: none;
+}
+
+.emotion-modal-backdrop {
+ position: absolute;
+ inset: 0;
+
+ background: rgba(35, 30, 25, 0.45);
+ backdrop-filter: blur(3px);
+}
+
+.emotion-modal-content {
+ position: relative;
+ z-index: 1;
+
+ width: min(1040px, calc(100% - 60px));
+ max-height: calc(100vh - 80px);
+ margin: 40px auto;
+ padding: 30px;
+
+ overflow-y: auto;
+
+ border: 1px solid #eadfce;
+ border-radius: 24px;
+ background: #fffdf7;
+
+ box-shadow: 0 24px 60px rgba(0, 0, 0, 0.2);
+}
+
+.emotion-modal-header {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+
+ margin-bottom: 24px;
+}
+
+.emotion-modal-label {
+ margin: 0 0 6px;
+
+ color: #8d857b;
+ font-size: 13px;
+}
+
+.emotion-modal-header h2 {
+ margin: 0;
+
+ font-size: 28px;
+}
+
+.emotion-modal-close {
+ width: 42px;
+ height: 42px;
+
+ border: 1px solid #e5ddd1;
+ border-radius: 50%;
+ background: #ffffff;
+
+ font-size: 26px;
+ cursor: pointer;
+}
+
+.emotion-modal-grid {
+ display: grid;
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ gap: 20px;
+}
+
+.emotion-modal-empty {
+ padding: 80px 20px;
+ text-align: center;
+}
+
+@media (max-width: 900px) {
+ .emotion-modal-grid {
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ }
+}
+
+@media (max-width: 650px) {
+ .emotion-page-header {
+ flex-direction: column;
+ }
+
+ .emotion-bubble-area {
+ min-height: 720px;
+ }
+
+ .emotion-modal-content {
+ width: calc(100% - 28px);
+ padding: 20px;
+ }
+
+ .emotion-modal-grid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+}
+
+/* =========================
+ 장소별 기록 팝업
+========================= */
+
+.location-modal {
+ position: fixed;
+ z-index: 1000;
+ inset: 0;
+}
+
+.location-modal[hidden] {
+ display: none;
+}
+
+.location-modal-backdrop {
+ position: absolute;
+ inset: 0;
+
+ background: rgba(35, 30, 25, 0.45);
+ backdrop-filter: blur(3px);
+}
+
+.location-modal-content {
+ position: relative;
+ z-index: 1;
+
+ width: min(1040px, calc(100% - 60px));
+ max-height: calc(100vh - 80px);
+ margin: 40px auto;
+ padding: 30px;
+
+ overflow-y: auto;
+
+ border: 1px solid #eadfce;
+ border-radius: 24px;
+ background: #fffdf7;
+
+ box-shadow: 0 24px 60px rgba(0, 0, 0, 0.2);
+}
+
+.location-modal-header {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+
+ margin-bottom: 24px;
+}
+
+.location-modal-label {
+ margin: 0 0 6px;
+
+ color: #8d857b;
+ font-size: 13px;
+}
+
+.location-modal-header h2 {
+ margin: 0;
+ font-size: 28px;
+}
+
+.location-modal-close {
+ width: 42px;
+ height: 42px;
+
+ border: 1px solid #e5ddd1;
+ border-radius: 50%;
+ background: #ffffff;
+
+ font-size: 26px;
+ cursor: pointer;
+}
+
+.location-modal-grid {
+ display: grid;
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ gap: 20px;
+}
+
+.location-modal-empty {
+ padding: 80px 20px;
+ text-align: center;
+}
+
+@media (max-width: 900px) {
+ .location-modal-grid {
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ }
+}
+
+@media (max-width: 650px) {
+ .location-modal-content {
+ width: calc(100% - 28px);
+ padding: 20px;
+ }
+
+ .location-modal-grid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+}
+
+.location-map-wrapper {
+ position: relative;
+ width: min(580px, 100%);
+ margin: 0 auto;
+}
+
+.seoul-map {
+ display: block;
+ width: 100%;
+ height: auto;
+}
+
+.seoul-map path {
+ fill: #fff;
+ stroke: #777;
+ stroke-width: 1;
+
+ transition:
+ fill 0.25s ease,
+ stroke 0.25s ease;
+}
+
+/* 방문한 장소 채우기 */
+.seoul-map path.has-record {
+ stroke: #789447;
+ stroke-width: 2;
+ cursor: pointer;
+}
+
+.seoul-map path.has-record:hover {
+ filter: brightness(0.94);
+ stroke-width: 3;
+}
+
+/* 지도 위 구 이름 */
+.district-labels {
+ pointer-events: none;
+}
+
+.district-labels text {
+ fill: #444;
+ font-size: 25px;
+ font-weight: 700;
+ text-anchor: middle;
+ dominant-baseline: middle;
+
+ paint-order: stroke;
+ stroke: #fff;
+ stroke-width: 5px;
+ stroke-linejoin: round;
+}
+
+.seoul-map path {
+ pointer-events: all;
+}
+
+.seoul-map path:focus {
+ outline: none;
+}
+
+.seoul-map path.has-record {
+ cursor: pointer;
+}
+
+.seoul-map path.has-record:hover {
+ filter: brightness(0.94);
+ stroke-width: 3;
+}
+
+/* =========================
+ 기록 카드 목록
+========================= */
+
+.diary-grid {
+ display: grid;
+ grid-template-columns: repeat(5, minmax(0, 1fr));
+ gap: 22px;
+ width: 100%;
+}
+
+.diary-card {
+ min-width: 0;
+ overflow: hidden;
+
+ border: 1px solid #ebe5da;
+ border-radius: 14px;
+ background-color: #ffffff;
+
+ box-shadow:
+ 0 3px 8px rgba(0, 0, 0, 0.08),
+ 0 1px 2px rgba(0, 0, 0, 0.04);
+
+ transition:
+ transform 0.2s ease,
+ box-shadow 0.2s ease;
+}
+
+.diary-card:hover {
+ transform: translateY(-5px);
+
+ box-shadow:
+ 0 10px 20px rgba(0, 0, 0, 0.12),
+ 0 3px 6px rgba(0, 0, 0, 0.06);
+}
+
+.diary-card-link {
+ display: block;
+ height: 100%;
+
+ color: inherit;
+ text-decoration: none;
+}
+
+
+/* =========================
+ 카드 이미지
+========================= */
+
+.diary-card-image-wrapper {
+ position: relative;
+
+ width: 100%;
+ aspect-ratio: 4 / 3;
+
+ overflow: hidden;
+ background-color: #f3efe8;
+}
+
+.diary-card-image {
+ display: block;
+
+ width: 100%;
+ height: 100%;
+
+ object-fit: cover;
+
+ transition: transform 0.3s ease;
+}
+
+.diary-card:hover .diary-card-image {
+ transform: scale(1.04);
+}
+
+.diary-card-image-placeholder {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ width: 100%;
+ height: 100%;
+
+ background-color: #f4f0e8;
+ color: #aaa397;
+
+ font-size: 13px;
+}
+
+
+/* =========================
+ 감정 표시
+========================= */
+
+.diary-card-emotion {
+ position: absolute;
+ right: 10px;
+ bottom: 10px;
+
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ min-width: 34px;
+ height: 34px;
+ padding: 0 9px;
+
+ border: 2px solid #ffffff;
+ border-radius: 999px;
+ background-color: #fff3f3;
+
+ color: #d65d69;
+ font-size: 12px;
+ font-weight: 700;
+
+ box-shadow: 0 2px 7px rgba(0, 0, 0, 0.12);
+}
+
+.diary-card-emotion img {
+ display: block;
+
+ width: 28px;
+ height: 28px;
+
+ object-fit: contain;
+}
+
+
+/* =========================
+ 카드 내용
+========================= */
+
+.diary-card-content {
+ display: flex;
+ flex-direction: column;
+
+ min-height: 130px;
+ padding: 15px 14px 16px;
+}
+
+.diary-card-date {
+ margin-bottom: 8px;
+
+ color: #7f7a72;
+ font-size: 12px;
+ line-height: 1.3;
+}
+
+.diary-card-title {
+ display: -webkit-box;
+ overflow: hidden;
+
+ min-height: 44px;
+ margin: 0;
+
+ color: #222222;
+ font-size: 16px;
+ font-weight: 700;
+ line-height: 1.4;
+
+ word-break: keep-all;
+
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 2;
+}
+
+.diary-card-location {
+ display: flex;
+ align-items: center;
+ gap: 5px;
+
+ overflow: hidden;
+
+ margin: auto 0 0;
+ padding-top: 13px;
+
+ color: #7c776f;
+ font-size: 12px;
+
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+
+/* =========================
+ 기록 없음
+========================= */
+
+.diary-empty {
+ grid-column: 1 / -1;
+
+ padding: 80px 20px;
+
+ text-align: center;
+}
+
+.diary-empty-title {
+ margin: 0 0 10px;
+
+ color: #333333;
+ font-size: 20px;
+ font-weight: 700;
+}
+
+.diary-empty-description {
+ margin: 0 0 24px;
+
+ color: #777777;
+ font-size: 14px;
+}
+
+.diary-empty-button {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+
+ min-height: 44px;
+ padding: 0 20px;
+
+ border: 1px solid #e3c566;
+ border-radius: 12px;
+ background-color: #fff1b8;
+
+ color: #2e2a20;
+ font-size: 14px;
+ font-weight: 700;
+ text-decoration: none;
+
+ transition:
+ transform 0.2s ease,
+ background-color 0.2s ease;
+}
+
+.diary-empty-button:hover {
+ transform: translateY(-2px);
+ background-color: #ffe894;
+}
+
+
+/* =========================
+ 반응형
+========================= */
+
+@media (max-width: 1200px) {
+ .diary-grid {
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+ }
+}
+
+@media (max-width: 900px) {
+ .diary-grid {
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ }
+}
+
+@media (max-width: 650px) {
+ .diary-grid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 14px;
+ }
+
+ .diary-card-content {
+ min-height: 118px;
+ padding: 12px;
+ }
+
+ .diary-card-title {
+ font-size: 14px;
+ }
+}
\ No newline at end of file
diff --git a/diary/static/diary/js/diary.js b/diary/static/diary/js/diary.js
index 5aaf506..7f39669 100644
--- a/diary/static/diary/js/diary.js
+++ b/diary/static/diary/js/diary.js
@@ -1,2 +1,446 @@
// 담당 D 전용
+document.addEventListener("DOMContentLoaded", () => {
+ const emotionColors = {
+ "화남": "#ef9a9a",
+ "분노": "#ef9a9a",
+
+ "슬픔": "#9fc5e8",
+ "불안": "#9fc5e8",
+ "걱정": "#9fc5e8",
+ "우울": "#9fc5e8",
+ "긴장": "#9fc5e8",
+ "피로": "#9fc5e8",
+ "답답": "#9fc5e8",
+ "지침": "#9fc5e8",
+
+ "기쁨": "#f8dc7d",
+ "행복": "#f8dc7d",
+ "감사": "#f8dc7d",
+ "감사함": "#f8dc7d",
+ "설렘": "#f8dc7d",
+ "평온": "#f8dc7d",
+ "안정": "#f8dc7d",
+
+ "뿌듯": "#efb2c7",
+ "뿌듯함": "#efb2c7",
+ "성취": "#efb2c7",
+ "기대": "#efb2c7",
+ "자신감": "#efb2c7",
+
+ "외로움": "#b9d99d",
+ "민망함": "#b9d99d"
+ };
+
+ const districtColors = [
+ "#f3b7b7",
+ "#f2cf87",
+ "#b8d8ec",
+ "#c6dca7",
+ "#d5bee8",
+ "#f1bdd0",
+ "#abd8cf",
+ "#e5c0a6",
+ "#c8c3e8",
+ "#efd6a6",
+ "#aed7ef",
+ "#b8dfbe",
+ "#e7b9ae",
+ "#d4c5a5",
+ "#c5d8e5"
+ ];
+
+ const mapSettingsButton =
+ document.querySelector("#map-settings-button");
+
+ const mapSettingsMenu =
+ document.querySelector("#map-settings-menu");
+
+ const mapModeButtons =
+ document.querySelectorAll("[data-map-mode]");
+
+ const districtDataElements =
+ document.querySelectorAll(
+ "#district-map-data [data-district]"
+ );
+
+
+ /* =========================
+ 장소 팝업 요소
+ ========================= */
+
+ const locationModal =
+ document.querySelector("#location-modal");
+
+ const locationModalName =
+ document.querySelector("#location-modal-name");
+
+ const locationModalRecords =
+ document.querySelectorAll(".location-modal-record");
+
+ const locationModalEmpty =
+ document.querySelector("#location-modal-empty");
+
+ const locationModalCloseButtons =
+ document.querySelectorAll(
+ "[data-location-modal-close]"
+ );
+
+
+ /* HTML 데이터를 JavaScript 객체로 변환 */
+
+ function getDistrictMapData() {
+ return Array.from(districtDataElements).map((element) => ({
+ district: element.dataset.district.trim(),
+ count: Number(element.dataset.count),
+ dominantEmotion:
+ element.dataset.dominantEmotion.trim(),
+ latestEmotion:
+ element.dataset.latestEmotion.trim()
+ }));
+ }
+
+
+ /* 기존 지도 색상 초기화 */
+
+ function clearDistrictStyles() {
+ document
+ .querySelectorAll(".seoul-map path")
+ .forEach((path) => {
+ path.style.fill = "";
+ path.classList.remove("has-record");
+ });
+ }
+
+
+ /* 선택한 지도 색상 방식 적용 */
+
+ function applyMapMode(mode) {
+ clearDistrictStyles();
+
+ const mapData = getDistrictMapData();
+
+ const maximumCount = Math.max(
+ ...mapData.map((district) => district.count),
+ 1
+ );
+
+ mapData.forEach((district, index) => {
+ const path =
+ document.getElementById(district.district);
+
+ if (!path) {
+ console.warn(
+ `SVG에서 구를 찾지 못했습니다: ${district.district}`
+ );
+ return;
+ }
+
+ path.classList.add("has-record");
+
+ if (mode === "uniform") {
+ path.style.fill = "#dce9b7";
+ }
+
+ if (mode === "dominant") {
+ path.style.fill =
+ emotionColors[district.dominantEmotion]
+ || "#d4d4d4";
+ }
+
+ if (mode === "latest") {
+ path.style.fill =
+ emotionColors[district.latestEmotion]
+ || "#d4d4d4";
+ }
+
+ if (mode === "intensity") {
+ const minimumOpacity = 0.05;
+ const ratio =
+ district.count / maximumCount;
+
+ const opacity =
+ minimumOpacity
+ + ratio * (1 - minimumOpacity);
+
+ path.style.fill =
+ `rgba(245, 196, 80, ${opacity})`;
+ }
+
+ if (mode === "rainbow") {
+ path.style.fill =
+ districtColors[
+ index % districtColors.length
+ ];
+ }
+ });
+
+ mapModeButtons.forEach((button) => {
+ button.classList.toggle(
+ "active",
+ button.dataset.mapMode === mode
+ );
+ });
+
+ localStorage.setItem("diaryMapMode", mode);
+ }
+
+
+ /* =========================
+ 장소 팝업
+ ========================= */
+
+ function openLocationModal(locationName) {
+ if (!locationModal || !locationModalName) {
+ return;
+ }
+
+ let visibleRecordCount = 0;
+
+ locationModalName.textContent = locationName;
+
+ locationModalRecords.forEach((record) => {
+ const recordLocation =
+ record.dataset.recordLocation.trim();
+
+ const isMatched =
+ recordLocation === locationName;
+
+ record.hidden = !isMatched;
+
+ if (isMatched) {
+ visibleRecordCount += 1;
+ }
+ });
+
+ if (locationModalEmpty) {
+ locationModalEmpty.hidden =
+ visibleRecordCount !== 0;
+ }
+
+ locationModal.hidden = false;
+ document.body.classList.add("modal-open");
+ }
+
+
+ function closeLocationModal() {
+ if (!locationModal) {
+ return;
+ }
+
+ locationModal.hidden = true;
+ document.body.classList.remove("modal-open");
+ }
+
+
+ /* 기록이 있는 구에 클릭 이벤트 연결 */
+
+ districtDataElements.forEach((element) => {
+ const districtName =
+ element.dataset.district.trim();
+
+ const districtPath =
+ document.getElementById(districtName);
+
+ if (!districtPath) {
+ console.warn(
+ `클릭 이벤트를 연결할 구를 찾지 못했습니다: ${districtName}`
+ );
+ return;
+ }
+
+ /*
+ * tabindex를 넣지 않습니다.
+ * SVG에 직사각형 포커스 테두리가 나타나는 것을 방지합니다.
+ */
+ districtPath.addEventListener("click", () => {
+ openLocationModal(districtName);
+ });
+ });
+
+
+ /* 장소 팝업 닫기 */
+
+ locationModalCloseButtons.forEach((button) => {
+ button.addEventListener(
+ "click",
+ closeLocationModal
+ );
+ });
+
+
+ /* =========================
+ 점 세 개 메뉴
+ ========================= */
+
+ if (mapSettingsButton && mapSettingsMenu) {
+ mapSettingsButton.addEventListener("click", (event) => {
+ event.stopPropagation();
+
+ const willOpen = mapSettingsMenu.hidden;
+
+ mapSettingsMenu.hidden = !willOpen;
+
+ mapSettingsButton.setAttribute(
+ "aria-expanded",
+ String(willOpen)
+ );
+ });
+
+ mapSettingsMenu.addEventListener("click", (event) => {
+ event.stopPropagation();
+ });
+
+ document.addEventListener("click", () => {
+ mapSettingsMenu.hidden = true;
+
+ mapSettingsButton.setAttribute(
+ "aria-expanded",
+ "false"
+ );
+ });
+ }
+
+
+ /* 지도 색상 방식 선택 */
+
+ mapModeButtons.forEach((button) => {
+ button.addEventListener("click", () => {
+ const selectedMode =
+ button.dataset.mapMode;
+
+ applyMapMode(selectedMode);
+
+ if (mapSettingsMenu) {
+ mapSettingsMenu.hidden = true;
+ }
+
+ if (mapSettingsButton) {
+ mapSettingsButton.setAttribute(
+ "aria-expanded",
+ "false"
+ );
+ }
+ });
+ });
+
+
+ /* 페이지 진입 시 저장된 색상 모드 적용 */
+
+ if (districtDataElements.length > 0) {
+ const savedMapMode =
+ localStorage.getItem("diaryMapMode")
+ || "dominant";
+
+ applyMapMode(savedMapMode);
+ }
+
+ const sortSelect = document.querySelector("#diary-sort");
+ const filterForm = document.querySelector("#diary-filter-form");
+
+ const periodToggleButton = document.querySelector(
+ "#period-toggle-button"
+ );
+ const periodFilter = document.querySelector("#period-filter");
+
+ if (sortSelect && filterForm) {
+ sortSelect.addEventListener("change", () => {
+ filterForm.submit();
+ });
+ }
+
+ if (periodToggleButton && periodFilter) {
+ periodToggleButton.addEventListener("click", () => {
+ const isOpen = !periodFilter.hidden;
+
+ periodFilter.hidden = isOpen;
+ periodToggleButton.classList.toggle("active", !isOpen);
+ periodToggleButton.setAttribute(
+ "aria-expanded",
+ String(!isOpen)
+ );
+ });
+ }
+ const emotionButtons =
+ document.querySelectorAll(".emotion-bubble");
+
+ const modal =
+ document.querySelector("#emotion-modal");
+
+ const modalName =
+ document.querySelector("#emotion-modal-name");
+
+ const modalRecords =
+ document.querySelectorAll(".emotion-modal-record");
+
+ const modalEmpty =
+ document.querySelector("#emotion-modal-empty");
+
+ const closeButtons =
+ document.querySelectorAll("[data-modal-close]");
+
+ function openEmotionModal(emotionId, emotionName) {
+ let visibleRecordCount = 0;
+
+ modalName.textContent = emotionName;
+
+ modalRecords.forEach((record) => {
+ const isMatched =
+ record.dataset.recordEmotion === emotionId;
+
+ record.hidden = !isMatched;
+
+ if (isMatched) {
+ visibleRecordCount += 1;
+ }
+ });
+
+ modalEmpty.hidden = visibleRecordCount !== 0;
+ modal.hidden = false;
+
+ document.body.classList.add("modal-open");
+ }
+
+ function closeEmotionModal() {
+ modal.hidden = true;
+ document.body.classList.remove("modal-open");
+ }
+
+ emotionButtons.forEach((button) => {
+ button.addEventListener("click", () => {
+ openEmotionModal(
+ button.dataset.emotionId,
+ button.dataset.emotionName
+ );
+ });
+ });
+
+ closeButtons.forEach((button) => {
+ button.addEventListener("click", closeEmotionModal);
+ });
+
+ document.addEventListener("keydown", (event) => {
+ if (event.key !== "Escape") {
+ return;
+ }
+
+ if (modal && !modal.hidden) {
+ closeEmotionModal();
+ }
+
+ if (locationModal && !locationModal.hidden) {
+ closeLocationModal();
+ }
+
+ if (mapSettingsMenu && !mapSettingsMenu.hidden) {
+ mapSettingsMenu.hidden = true;
+
+ if (mapSettingsButton) {
+ mapSettingsButton.setAttribute(
+ "aria-expanded",
+ "false"
+ );
+ }
+ }
+ });
+});
\ No newline at end of file
diff --git a/diary/templates/diary/list.html b/diary/templates/diary/list.html
index 991f6b9..1d229f0 100644
--- a/diary/templates/diary/list.html
+++ b/diary/templates/diary/list.html
@@ -1,3 +1,935 @@
+
+
{% extends "base.html" %}
-{% block content %}{% endblock %}
+{% load static %}
+
+{% block title %}다이어리 목록 | HeartMark{% endblock %}
+
+{% block extra_css %}
+
+{% endblock %}
+
+{% block content %}
+
+
+ {{ record.location.name }}
+
+ 감정의 크기는 이번 달 기록 횟수를 나타내요.
+ 표시할 감정이 없어요.전체 기록
+
+ {{ records|length }}개
+
+
+ {% else %}
+
+ {% else %}
+
+ {{ record.emotion.name }}
+
+ {% endif %}
+
+ {{ record.title }}
+
+
+ {% if record.location %}
+ 감정별 기록
+