feat(project): 완료 토글, 설명 이미지 크기조절/삭제, 연결폼 가독성, 모달 고정크기 800x1100
- 업무 편집 모달에 "완료로 표시" 버튼 추가(완료 시 리스트뷰는 기존 취소선 스타일 그대로 적용됨). 완료 상태에 따라 버튼 색/문구 토글. - 설명란 이미지 클릭 시 선택되어 우하단 드래그로 크기 조절, × 버튼으로 삭제 가능. 크기는 style이 아닌 width 속성으로 저장해 서버 sanitize 후에도 유지. - 연결된 프로젝트 select 를 세로 배치+작은 폰트로 바꿔 글자 잘림 해결. - 업무 편집 모달을 800x1100 고정 크기로 바꾸고 좌(메타/설명)·우(하위업무· 첨부·연결·댓글) 칼럼이 각자 내부 스크롤하도록 재정비. fix(shell): 사이드바 접힘 상태를 localStorage에서 첫 페인트 전에 반영해 페이지 이동 시 "펼쳐졌다 다시 접히는" 깜빡임 제거. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}
|
||||
<link rel="stylesheet" href="/static/project.css?v=20260916d" />
|
||||
<link rel="stylesheet" href="/static/project.css?v=20260916e" />
|
||||
<link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" />
|
||||
{% endblock %}
|
||||
|
||||
@@ -45,5 +45,5 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<script src="/static/project.js?v=20260916f" defer></script>
|
||||
<script src="/static/project.js?v=20260916g" defer></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}
|
||||
<link rel="stylesheet" href="/static/project.css?v=20260916d" />
|
||||
<link rel="stylesheet" href="/static/project.css?v=20260916e" />
|
||||
<link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" />
|
||||
{% endblock %}
|
||||
|
||||
@@ -292,7 +292,9 @@
|
||||
<label class="pj-upload-btn">이미지 추가<input type="file" id="pj-desc-img-input" accept="image/*" hidden /></label>
|
||||
<span class="pj-muted-sm" id="pj-desc-hint"></span>
|
||||
</div>
|
||||
<div class="pj-desc-editor" id="pj-t-desc" contenteditable="true" data-placeholder="설명을 입력하세요… (이미지는 붙여넣기 또는 위 버튼으로 추가)"></div>
|
||||
<div class="pj-desc-wrap">
|
||||
<div class="pj-desc-editor" id="pj-t-desc" contenteditable="true" data-placeholder="설명을 입력하세요… (이미지는 붙여넣기 또는 위 버튼으로 추가)"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pj-modal-col-side">
|
||||
@@ -343,6 +345,7 @@
|
||||
</div>
|
||||
|
||||
<div class="pj-modal-actions">
|
||||
<button type="button" class="pj-btn pj-btn-complete" id="pj-toggle-complete" hidden>✓ 완료로 표시</button>
|
||||
<button type="button" class="pj-btn pj-btn-danger" id="pj-delete-task">삭제</button>
|
||||
<span style="flex:1"></span>
|
||||
<a class="pj-btn" id="pj-t-open-project" href="#" target="_self">프로젝트에서 열기</a>
|
||||
@@ -373,5 +376,5 @@
|
||||
<script>
|
||||
window.PJ_COLORS = {{ ["#4573d2","#37a3a3","#62a420","#e8a33d","#e8384f","#aa62e3","#f06a6a","#5a6772"] | tojson }};
|
||||
</script>
|
||||
<script src="/static/project.js?v=20260916f" defer></script>
|
||||
<script src="/static/project.js?v=20260916g" defer></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}
|
||||
<link rel="stylesheet" href="/static/project.css?v=20260916d" />
|
||||
<link rel="stylesheet" href="/static/project.css?v=20260916e" />
|
||||
<!-- 구글 머티리얼 심볼(담당자 아이콘 등) — self-host -->
|
||||
<link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" />
|
||||
<!-- 타임라인 vis-timeline — self-host -->
|
||||
@@ -220,7 +220,9 @@
|
||||
<label class="pj-upload-btn">이미지 추가<input type="file" id="pj-desc-img-input" accept="image/*" hidden /></label>
|
||||
<span class="pj-muted-sm" id="pj-desc-hint"></span>
|
||||
</div>
|
||||
<div class="pj-desc-editor" id="pj-t-desc" contenteditable="true" data-placeholder="설명을 입력하세요… (이미지는 붙여넣기 또는 위 버튼으로 추가)"></div>
|
||||
<div class="pj-desc-wrap">
|
||||
<div class="pj-desc-editor" id="pj-t-desc" contenteditable="true" data-placeholder="설명을 입력하세요… (이미지는 붙여넣기 또는 위 버튼으로 추가)"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pj-modal-col-side">
|
||||
@@ -277,6 +279,7 @@
|
||||
</div>
|
||||
|
||||
<div class="pj-modal-actions">
|
||||
<button type="button" class="pj-btn pj-btn-complete" id="pj-toggle-complete" hidden>✓ 완료로 표시</button>
|
||||
<button type="button" class="pj-btn pj-btn-danger" id="pj-delete-task" hidden>삭제</button>
|
||||
<span style="flex:1"></span>
|
||||
<button type="button" class="pj-btn" data-close>취소</button>
|
||||
@@ -326,5 +329,5 @@
|
||||
</script>
|
||||
|
||||
<script src="/static/vendor/vis-timeline/vis-timeline-graph2d.min.js"></script>
|
||||
<script src="/static/project.js?v=20260916f" defer></script>
|
||||
<script src="/static/project.js?v=20260916g" defer></script>
|
||||
{% endblock %}
|
||||
|
||||
+46
-13
@@ -310,16 +310,23 @@
|
||||
.pj-modal { position: fixed; inset: 0; background: rgba(20,22,26,.46); display: flex; align-items: center; justify-content: center; z-index: 1000; }
|
||||
.pj-modal[hidden] { display: none; }
|
||||
.pj-modal-card { background: #fff; border-radius: 16px; padding: 22px; width: min(480px, 92vw); box-shadow: 0 20px 60px rgba(0,0,0,.25); max-height: 90vh; overflow-y: auto; }
|
||||
/* 업무 편집 팝업만 넓게 — 메타/설명(좌) + 하위업무·첨부·연결·댓글(우) 2단.
|
||||
우측 칼럼만 자체 스크롤 시켜 팝업 전체 스크롤은 웬만하면 안 생기게 한다. */
|
||||
.pj-task-modal-card { width: min(780px, 96vw); }
|
||||
.pj-modal-body { display: grid; grid-template-columns: 1.15fr .85fr; gap: 0 26px; align-items: start; }
|
||||
.pj-modal-col-main, .pj-modal-col-side { min-width: 0; }
|
||||
.pj-modal-col-side { max-height: 66vh; overflow-y: auto; padding-right: 4px; }
|
||||
/* 업무 편집 팝업만 고정 크기(800x1100) — 메타/설명(좌) + 하위업무·첨부·연결·
|
||||
댓글(우) 2단. 카드 자체는 스크롤 없이 고정하고, 좌우 칼럼이 각자 내부
|
||||
스크롤한다(우측은 늘 자체 스크롤, 좌측은 내용이 넘칠 때만). */
|
||||
.pj-task-modal-card {
|
||||
width: 800px; max-width: 96vw; height: 1100px; max-height: 94vh;
|
||||
display: flex; flex-direction: column; overflow: hidden;
|
||||
}
|
||||
.pj-task-modal-card > .pj-t-title-input { flex: none; }
|
||||
.pj-task-modal-card .pj-modal-body { flex: 1 1 auto; min-height: 0; }
|
||||
.pj-task-modal-card .pj-modal-actions { flex: none; }
|
||||
.pj-modal-body { display: grid; grid-template-columns: 1.15fr .85fr; gap: 0 26px; align-items: stretch; height: 100%; }
|
||||
.pj-modal-col-main, .pj-modal-col-side { min-width: 0; min-height: 0; overflow-y: auto; padding-right: 4px; }
|
||||
.pj-modal-col-side .pj-task-extra:first-child { margin-top: 0; padding-top: 0; border-top: none; }
|
||||
@media (max-width: 720px) {
|
||||
.pj-modal-body { grid-template-columns: 1fr; }
|
||||
.pj-modal-col-side { max-height: none; overflow-y: visible; padding-right: 0; }
|
||||
.pj-task-modal-card { width: min(480px, 94vw); height: auto; max-height: 90vh; overflow-y: auto; display: block; }
|
||||
.pj-modal-body { display: block; height: auto; }
|
||||
.pj-modal-col-main, .pj-modal-col-side { overflow: visible; height: auto; padding-right: 0; }
|
||||
.pj-modal-col-side .pj-task-extra:first-child { border-top: 1px solid #eef0f2; margin-top: 14px; padding-top: 12px; }
|
||||
}
|
||||
.pj-modal-card h3 { margin: 0 0 16px; font-size: 17px; }
|
||||
@@ -359,8 +366,10 @@
|
||||
.pj-form-row input[type="time"] { display: block; width: 100%; margin-top: 5px; padding: 8px 10px; border: 1px solid #d8dce2; border-radius: 8px; font-size: 13.5px; font-family: inherit; box-sizing: border-box; }
|
||||
.pj-form-row input[type="time"]:disabled { background: #f2f2f2; color: #aaa; cursor: not-allowed; }
|
||||
#pj-time-row:has(input:disabled) label { color: #aaa; }
|
||||
.pj-modal-actions { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
|
||||
.pj-modal-actions .pj-btn:last-child { }
|
||||
.pj-modal-actions { display: flex; align-items: center; gap: 8px; margin-top: 8px; flex-wrap: wrap; }
|
||||
.pj-btn-complete { background: #e9f7ef; color: #1e8e5a; border: 1px solid #bfe8d2; font-weight: 700; }
|
||||
.pj-btn-complete:hover { background: #d9f2e4; }
|
||||
.pj-btn-complete.is-done { background: #f2f2f4; color: #6b6f76; border-color: var(--pj-border); }
|
||||
|
||||
.pj-color-palette, #pj-f-colors { display: flex; gap: 8px; margin-top: 6px; }
|
||||
.pj-color-swatch { width: 24px; height: 24px; border-radius: 50%; border: 2px solid transparent; cursor: pointer; }
|
||||
@@ -476,9 +485,27 @@
|
||||
line-height: 1.5; margin-bottom: 12px; background: #fff;
|
||||
}
|
||||
.pj-desc-editor:empty::before { content: attr(data-placeholder); color: #b7bdc5; }
|
||||
.pj-desc-editor img { max-width: 100%; border-radius: 6px; margin: 4px 0; display: block; }
|
||||
.pj-desc-editor img { max-width: 100%; border-radius: 6px; margin: 4px 0; display: block; cursor: pointer; }
|
||||
.pj-desc-editor img.pj-img-selected { outline: 2px solid var(--pj-accent); outline-offset: 1px; }
|
||||
.pj-desc-editor[contenteditable="false"] { background: #f6f7f8; color: #9aa1a9; }
|
||||
|
||||
/* 설명란 이미지 선택 시 오버레이(삭제 × / 우하단 크기조절 핸들) — wrap 은
|
||||
position:relative, tools 는 선택된 img 의 offsetLeft/Top/Width/Height 로
|
||||
자리를 맞춘다(box 자체는 static 이라 offsetParent 가 wrap 이 된다). */
|
||||
.pj-desc-wrap { position: relative; }
|
||||
.pj-img-tools { position: absolute; pointer-events: none; }
|
||||
.pj-img-del {
|
||||
position: absolute; top: -9px; right: -9px; pointer-events: auto;
|
||||
width: 20px; height: 20px; border-radius: 50%; border: none; background: #1e1f21; color: #fff;
|
||||
font-size: 13px; line-height: 1; cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 0;
|
||||
}
|
||||
.pj-img-del:hover { background: #c22b10; }
|
||||
.pj-img-handle {
|
||||
position: absolute; bottom: -6px; right: -6px; pointer-events: auto;
|
||||
width: 13px; height: 13px; border-radius: 3px; background: var(--pj-accent); cursor: nwse-resize;
|
||||
border: 2px solid #fff; box-shadow: 0 0 0 1px rgba(0,0,0,.25);
|
||||
}
|
||||
|
||||
/* ── 하위 업무(인라인) ── */
|
||||
.pj-subtask-list { list-style: none; margin: 0 0 8px; padding: 0; }
|
||||
.pj-subtask-row { display: flex; align-items: center; gap: 6px; padding: 4px 2px; }
|
||||
@@ -518,8 +545,14 @@
|
||||
}
|
||||
.pj-link-chip .pj-link-del { border: none; background: transparent; cursor: pointer; color: #9aa1a9; font-size: 13px; line-height: 1; padding: 2px; }
|
||||
.pj-link-chip .pj-link-del:hover { color: #c22b10; }
|
||||
.pj-link-form { display: flex; gap: 6px; align-items: center; }
|
||||
.pj-link-form select { flex: 1; padding: 7px 8px; border: 1px solid #d8dce2; border-radius: 8px; font-size: 13px; font-family: inherit; }
|
||||
/* 세로로 쌓기 — 옆 칼럼이 좁아 가로 배치면 select 글자가 잘린다. */
|
||||
.pj-link-form { display: flex; flex-direction: column; gap: 6px; align-items: stretch; }
|
||||
.pj-link-form select {
|
||||
width: 100% !important; flex: none; margin: 0 !important;
|
||||
padding: 6px 8px !important; border: 1px solid #d8dce2 !important; border-radius: 8px;
|
||||
font-size: 11.5px !important; font-family: inherit; box-sizing: border-box;
|
||||
}
|
||||
.pj-link-form .pj-btn { align-self: flex-end; }
|
||||
|
||||
/* ── 알림센터 ── */
|
||||
.pj-inbox { list-style: none; margin: 0; padding: 0; }
|
||||
|
||||
+99
-1
@@ -348,6 +348,8 @@
|
||||
el("pj-t-dtime").disabled = !useTime;
|
||||
el("pj-t-open-project").href = "/project/p/" + task.project_id + "?task=" + task.id;
|
||||
el("pj-delete-task").hidden = false;
|
||||
el("pj-toggle-complete").hidden = false;
|
||||
setHomeCompleteBtn(!!task.completed_at);
|
||||
el("pj-t-open-project").hidden = false;
|
||||
el("pj-attach-block").hidden = false;
|
||||
el("pj-comment-block").hidden = false;
|
||||
@@ -377,6 +379,7 @@
|
||||
el("pj-t-stime").value = ""; el("pj-t-stime").disabled = true;
|
||||
el("pj-t-dtime").value = ""; el("pj-t-dtime").disabled = true;
|
||||
el("pj-delete-task").hidden = true;
|
||||
el("pj-toggle-complete").hidden = true;
|
||||
el("pj-t-open-project").hidden = true;
|
||||
el("pj-attach-block").hidden = true;
|
||||
el("pj-comment-block").hidden = true;
|
||||
@@ -385,6 +388,21 @@
|
||||
openModal(homeTaskModal);
|
||||
await Promise.all([fillAssignee(projectId, ""), fillStage(projectId, null)]);
|
||||
}
|
||||
function setHomeCompleteBtn(done) {
|
||||
const btn = el("pj-toggle-complete");
|
||||
btn.textContent = done ? "↩ 완료 취소" : "✓ 완료로 표시";
|
||||
btn.classList.toggle("is-done", done);
|
||||
btn.dataset.completed = done ? "1" : "";
|
||||
}
|
||||
el("pj-toggle-complete").addEventListener("click", async function () {
|
||||
const id = el("pj-t-id").value;
|
||||
if (!id) return;
|
||||
const done = el("pj-toggle-complete").dataset.completed === "1";
|
||||
try {
|
||||
await api("PUT", "/project/api/tasks/" + id, { completed: !done });
|
||||
location.reload();
|
||||
} catch (e) { alert("처리 실패: " + e.message); }
|
||||
});
|
||||
document.querySelectorAll(".pj-card-addtask").forEach(function (btn) {
|
||||
btn.addEventListener("click", function (e) {
|
||||
e.preventDefault(); e.stopPropagation();
|
||||
@@ -1584,6 +1602,8 @@
|
||||
el("pj-t-stime").disabled = !useTime;
|
||||
el("pj-t-dtime").disabled = !useTime;
|
||||
el("pj-delete-task").hidden = !task;
|
||||
el("pj-toggle-complete").hidden = !task;
|
||||
if (task) setCompleteBtn(!!task.completed_at);
|
||||
// 댓글·첨부·하위업무·연결은 기존 업무 편집 시에만
|
||||
currentTaskId = task ? task.id : null;
|
||||
el("pj-attach-block").hidden = !task;
|
||||
@@ -1594,6 +1614,24 @@
|
||||
openModal(taskModal);
|
||||
}
|
||||
|
||||
function setCompleteBtn(done) {
|
||||
const btn = el("pj-toggle-complete");
|
||||
btn.textContent = done ? "↩ 완료 취소" : "✓ 완료로 표시";
|
||||
btn.classList.toggle("is-done", done);
|
||||
btn.dataset.completed = done ? "1" : "";
|
||||
}
|
||||
el("pj-toggle-complete").addEventListener("click", async function () {
|
||||
const id = el("pj-t-id").value;
|
||||
if (!id) return;
|
||||
const done = el("pj-toggle-complete").dataset.completed === "1";
|
||||
try {
|
||||
const res = await api("PUT", "/project/api/tasks/" + id, { completed: !done });
|
||||
mergeTask(res.task);
|
||||
setCompleteBtn(!!res.task.completed_at);
|
||||
refreshActiveView();
|
||||
} catch (e) { alert("처리 실패: " + e.message); }
|
||||
});
|
||||
|
||||
// ── 첨부파일 ──
|
||||
function fmtSize(n) {
|
||||
if (n < 1024) return n + " B";
|
||||
@@ -1906,10 +1944,70 @@
|
||||
function initDescEditor() {
|
||||
const box = el("pj-t-desc");
|
||||
if (!box) return null;
|
||||
const wrap = box.parentElement;
|
||||
const imgInput = el("pj-desc-img-input");
|
||||
const hint = el("pj-desc-hint");
|
||||
let taskId = null;
|
||||
|
||||
// ── 설명란 이미지 선택 → 크기 조절 핸들 + 삭제 버튼 오버레이 ──
|
||||
// 크기는 style이 아니라 width 속성으로 저장한다(서버 sanitize가 style은
|
||||
// 버리지만 img width/height 속성은 허용 목록에 있어 저장 후에도 유지된다).
|
||||
const imgTools = document.createElement("div");
|
||||
imgTools.className = "pj-img-tools";
|
||||
imgTools.hidden = true;
|
||||
imgTools.innerHTML =
|
||||
'<button type="button" class="pj-img-del" title="이미지 삭제">×</button>' +
|
||||
'<span class="pj-img-handle" title="드래그로 크기 조절"></span>';
|
||||
wrap.appendChild(imgTools);
|
||||
const imgDelBtn = imgTools.querySelector(".pj-img-del");
|
||||
const imgHandle = imgTools.querySelector(".pj-img-handle");
|
||||
let selectedImg = null;
|
||||
|
||||
function positionImgTools() {
|
||||
if (!selectedImg || !selectedImg.isConnected) { selectedImg = null; imgTools.hidden = true; return; }
|
||||
imgTools.style.left = selectedImg.offsetLeft + "px";
|
||||
imgTools.style.top = selectedImg.offsetTop + "px";
|
||||
imgTools.style.width = selectedImg.offsetWidth + "px";
|
||||
imgTools.style.height = selectedImg.offsetHeight + "px";
|
||||
imgTools.hidden = false;
|
||||
}
|
||||
function selectImg(img) {
|
||||
if (selectedImg) selectedImg.classList.remove("pj-img-selected");
|
||||
selectedImg = img || null;
|
||||
if (selectedImg) { selectedImg.classList.add("pj-img-selected"); positionImgTools(); }
|
||||
else { imgTools.hidden = true; }
|
||||
}
|
||||
box.addEventListener("click", function (e) {
|
||||
if (e.target.tagName === "IMG") selectImg(e.target);
|
||||
else selectImg(null);
|
||||
});
|
||||
document.addEventListener("click", function (e) {
|
||||
if (selectedImg && !wrap.contains(e.target)) selectImg(null);
|
||||
});
|
||||
imgDelBtn.addEventListener("click", function () {
|
||||
if (selectedImg) selectedImg.remove();
|
||||
selectImg(null);
|
||||
});
|
||||
let dragging = false, dragStartX = 0, dragStartW = 0;
|
||||
imgHandle.addEventListener("mousedown", function (e) {
|
||||
if (!selectedImg) return;
|
||||
e.preventDefault();
|
||||
dragging = true;
|
||||
dragStartX = e.clientX;
|
||||
dragStartW = selectedImg.offsetWidth;
|
||||
});
|
||||
document.addEventListener("mousemove", function (e) {
|
||||
if (!dragging || !selectedImg) return;
|
||||
let newW = Math.round(dragStartW + (e.clientX - dragStartX));
|
||||
newW = Math.max(40, Math.min(newW, wrap.clientWidth || 9999));
|
||||
selectedImg.setAttribute("width", String(newW));
|
||||
selectedImg.removeAttribute("height");
|
||||
selectedImg.style.width = "";
|
||||
selectedImg.style.height = "";
|
||||
positionImgTools();
|
||||
});
|
||||
document.addEventListener("mouseup", function () { dragging = false; });
|
||||
|
||||
function setTaskId(id) {
|
||||
taskId = id;
|
||||
const enabled = !!id;
|
||||
@@ -1973,7 +2071,7 @@
|
||||
|
||||
return {
|
||||
setTaskId: setTaskId,
|
||||
setHtml: function (html) { box.innerHTML = html || ""; },
|
||||
setHtml: function (html) { selectImg(null); box.innerHTML = html || ""; },
|
||||
getHtml: function () { return box.innerHTML; },
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,6 +16,15 @@
|
||||
|
||||
<!-- ── 좌측 사이드바 ── -->
|
||||
<aside class="erp-sidebar" data-collapsed="false">
|
||||
<script>
|
||||
// 접힘 상태를 렌더 직후(첫 페인트 전) 바로 반영 — 아래쪽 DOMContentLoaded
|
||||
// 스크립트에서 뒤늦게 바꾸면 "펼쳐졌다가 접히는" 깜빡임이 보인다.
|
||||
try {
|
||||
if (localStorage.getItem("erp_sidebar_collapsed") === "true") {
|
||||
document.currentScript.closest(".erp-sidebar").setAttribute("data-collapsed", "true");
|
||||
}
|
||||
} catch (_) {}
|
||||
</script>
|
||||
|
||||
<div class="erp-sidebar-head">
|
||||
<a href="/" class="erp-sidebar-brand">
|
||||
@@ -144,15 +153,12 @@
|
||||
const collapseBtn = document.getElementById("erp-sidebar-toggle");
|
||||
const mobileBtn = document.getElementById("erp-sidebar-mobile-toggle");
|
||||
if (collapseBtn) {
|
||||
// 초기 접힘 상태는 위쪽 인라인 스크립트(첫 페인트 전)에서 이미 반영했다 — 여기선 클릭 시 토글/저장만.
|
||||
collapseBtn.addEventListener("click", () => {
|
||||
const next = sidebar.dataset.collapsed === "true" ? "false" : "true";
|
||||
sidebar.dataset.collapsed = next;
|
||||
try { localStorage.setItem("erp_sidebar_collapsed", next); } catch (_) {}
|
||||
});
|
||||
try {
|
||||
const saved = localStorage.getItem("erp_sidebar_collapsed");
|
||||
if (saved === "true") sidebar.dataset.collapsed = "true";
|
||||
} catch (_) {}
|
||||
}
|
||||
if (mobileBtn) {
|
||||
mobileBtn.addEventListener("click", () => {
|
||||
|
||||
Reference in New Issue
Block a user