docs(design): 웹메일 디자인 원본과 브랜드 로고 추가

사용자가 확정한 디자인 캔버스(버전 11)의 원본이다. 화면 구현의 기준으로 쓴다.

- 로그인·메일함(분할·목록 보기)·읽기·쓰기·상태 화면의 캔버스 원본과 자산 대응표
- 디자인 원칙(뱃지 없음·높은 밀도·콘텐츠 열 규칙·밀도 예외)과 구현하며 정한 보완
- 흰 로고 원본 (어두운 브랜드 바탕 전용)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 01:32:09 +09:00
parent 5ed9fb922a
commit a5afbc040e
9 changed files with 1746 additions and 0 deletions
+283
View File
@@ -0,0 +1,283 @@
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>메일 쓰기</title>
<script src="./support.js"></script>
</head>
<body>
<x-dc>
<helmet>
<style>
@font-face{font-family:'Pretendard';src:url('/_blob/16067b9aa5137abf808e3dbef30c42e0') format('woff2-variations'),url('/_blob/16067b9aa5137abf808e3dbef30c42e0') format('woff2');font-weight:45 920;font-style:normal;font-display:swap}
body{margin:0}
a{color:#0E7C86}a:hover{color:#0A5F67}
:focus-visible{outline:2px solid #0E7C86;outline-offset:2px}
</style>
</helmet>
<div style="min-height: 100vh; display: flex; background: #FFFFFF; color: #171C26; font-family: Pretendard, -apple-system, 'Apple SD Gothic Neo', 'Malgun Gothic', system-ui, sans-serif; font-size: 13px; line-height: 1.5">
<nav aria-label="사내 시스템" style="flex: 0 0 72px; box-sizing: border-box; display: flex; flex-direction: column; align-items: center; gap: 14px; padding: 10px 0 12px; background: #0B3640">
<a href="Main.dc.html" style="display: inline-flex; align-items: center; justify-content: center; width: 60px; min-height: 44px; border-radius: 10px; text-decoration: none">
<img src="/_blob/2b9ef4349333debc969432c859eaf1bf" alt="ARA SYSTEM" width="48" height="17" style="display: block; width: 48px; height: auto">
</a>
<ul style="list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px">
<li>
<a href="Main.dc.html" aria-current="page" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; background: #155A64; color: #FFFFFF; font-size: 11px; font-weight: 600; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path><polyline points="22,6 12,13 2,6"></polyline></svg>
<span>메일</span>
</a>
</li>
<li>
<a href="#" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; color: #A3C2C6; font-size: 11px; font-weight: 500; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg>
<span>캘린더</span>
</a>
</li>
<li>
<a href="#" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; color: #A3C2C6; font-size: 11px; font-weight: 500; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
<span>파일</span>
</a>
</li>
<li>
<a href="#" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; color: #A3C2C6; font-size: 11px; font-weight: 500; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="7" width="20" height="14" rx="2" ry="2"></rect><path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"></path></svg>
<span>ERP</span>
</a>
</li>
<li>
<a href="#" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; color: #A3C2C6; font-size: 11px; font-weight: 500; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg>
<span>주소록</span>
</a>
</li>
</ul>
<a href="#" style="margin-top: auto; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; color: #A3C2C6; font-size: 11px; font-weight: 500; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
<span>설정</span>
</a>
</nav>
<div style="flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column">
<header style="display: flex; align-items: center; flex-wrap: wrap; gap: 8px 16px; box-sizing: border-box; min-height: 56px; padding: 6px 12px 6px 20px; border-bottom: 1px solid #E6E8EC">
<span style="font-size: 16px; font-weight: 700; letter-spacing: -0.01em">메일</span>
<div style="flex: 1 1 280px; max-width: 520px; display: flex; align-items: center; gap: 8px; box-sizing: border-box; height: 40px; padding: 0 12px; border: 1px solid #8A94A3; border-radius: 8px; background: #FFFFFF">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#667085" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="search" aria-label="통합 검색" placeholder="통합 검색" style="flex: 1 1 auto; min-width: 0; border: 0; background: transparent; font: inherit; color: #171C26">
</div>
<div style="margin-left: auto; display: flex; align-items: center; gap: 10px">
<span aria-hidden="true" style="display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 50%; background: #E3F2F3; color: #0A5F67; font-size: 13px; font-weight: 600">김</span>
<div style="display: flex; flex-direction: column; line-height: 1.3">
<span style="font-weight: 600">김아라</span>
<span style="font-size: 12px; color: #667085">ara.kim@arasystem.it.kr</span>
</div>
<a href="Login.dc.html" style="display: inline-flex; align-items: center; gap: 6px; min-height: 44px; padding: 0 10px; border-radius: 8px; color: #4A5363; font-weight: 500; text-decoration: none">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line></svg>
<span>로그아웃</span>
</a>
</div>
</header>
<div style="flex: 1 1 auto; display: flex; flex-wrap: wrap; align-items: stretch">
<nav aria-label="메일함" style="flex: 1 1 208px; box-sizing: border-box; display: flex; flex-direction: column; gap: 14px; padding: 14px 10px; background: #F8F9FB; border-right: 1px solid #E6E8EC">
<span aria-current="page" style="display: flex; align-items: center; justify-content: center; gap: 8px; min-height: 44px; border-radius: 8px; background: #0A5F67; color: #FFFFFF; font-size: 13.5px; font-weight: 600">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20h9"></path><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path></svg>
<span>메일 쓰는 중</span>
</span>
<ul style="list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1px">
<li><a href="Main.dc.html" style="display: flex; align-items: center; gap: 10px; min-height: 40px; box-sizing: border-box; padding: 0 10px; border-radius: 7px; color: #344054; font-weight: 500; text-decoration: none">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="22 12 16 12 14 15 10 15 8 12 2 12"></polyline><path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"></path></svg>
<span style="flex: 1 1 auto">받은편지함</span>
<span style="font-size: 12px; font-weight: 600; color: #171C26; font-variant-numeric: tabular-nums">3</span>
</a></li>
<li><a href="Main.dc.html" style="display: flex; align-items: center; gap: 10px; min-height: 40px; box-sizing: border-box; padding: 0 10px; border-radius: 7px; color: #344054; font-weight: 500; text-decoration: none">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
<span>보낸편지함</span>
</a></li>
<li><a href="Main.dc.html" style="display: flex; align-items: center; gap: 10px; min-height: 40px; box-sizing: border-box; padding: 0 10px; border-radius: 7px; color: #344054; font-weight: 500; text-decoration: none">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line></svg>
<span>임시보관함</span>
</a></li>
<li><a href="Main.dc.html" style="display: flex; align-items: center; gap: 10px; min-height: 40px; box-sizing: border-box; padding: 0 10px; border-radius: 7px; color: #344054; font-weight: 500; text-decoration: none">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polygon points="7.86 2 16.14 2 22 7.86 22 16.14 16.14 22 7.86 22 2 16.14 2 7.86 7.86 2"></polygon><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>
<span>스팸메일함</span>
</a></li>
<li><a href="Main.dc.html" style="display: flex; align-items: center; gap: 10px; min-height: 40px; box-sizing: border-box; padding: 0 10px; border-radius: 7px; color: #344054; font-weight: 500; text-decoration: none">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>
<span>휴지통</span>
</a></li>
</ul>
<div style="margin-top: auto; display: flex; flex-direction: column; gap: 6px; padding: 12px 10px 2px; border-top: 1px solid #E6E8EC">
<div style="display: flex; justify-content: space-between; gap: 8px; font-size: 12px; color: #667085">
<span>메일함 사용량</span>
<span style="font-variant-numeric: tabular-nums">0.9 / 5 GB</span>
</div>
<div style="height: 4px; border-radius: 2px; background: #E4E7EC">
<div style="width: 18%; height: 4px; border-radius: 2px; background: #0E7C86"></div>
</div>
</div>
</nav>
<main style="flex: 999 1 560px; min-width: 0; box-sizing: border-box; display: flex; flex-direction: column">
<form aria-labelledby="compose-title" style="flex: 1 1 auto; display: flex; flex-direction: column; max-width: 800px">
<div style="display: flex; align-items: center; gap: 8px; min-height: 52px; box-sizing: border-box; padding: 4px 18px 4px 32px; border-bottom: 1px solid #E6E8EC">
<h1 id="compose-title" style="flex: 1 1 auto; margin: 0; font-size: 15px; font-weight: 700; letter-spacing: -0.01em">{{ title }}</h1>
<a href="Main.dc.html" aria-label="쓰기 닫기" style="width: 44px; height: 44px; display: inline-flex; align-items: center; justify-content: center; border-radius: 8px; color: #4A5363">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
</a>
</div>
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; min-height: 44px; box-sizing: border-box; padding: 4px 22px 4px 32px; border-bottom: 1px solid #EEF0F3">
<label for="compose-to" style="flex: 0 0 64px; font-size: 12.5px; font-weight: 600; color: #4A5363">받는 사람</label>
<div style="flex: 1 1 320px; min-width: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px">
<sc-for list="{{ chips }}" as="chip" hint-placeholder-count="1">
<span style="display: inline-flex; align-items: center; gap: 2px; min-height: 28px; box-sizing: border-box; padding: 0 2px 0 8px; border-radius: 4px; background: {{ chip.bg }}; color: {{ chip.fg }}; font-size: 13px">
<span style="text-decoration: {{ chip.deco }}">{{ chip.label }}</span>
<button type="button" onClick="{{ chip.remove }}" aria-label="{{ chip.removeLabel }}" style="width: 26px; height: 26px; display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: 4px; background: transparent; color: inherit; cursor: pointer">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
</button>
</span>
</sc-for>
<input id="compose-to" type="text" value="{{ toInput }}" onChange="{{ onToInput }}" onKeyDown="{{ onToKey }}" placeholder="이름 또는 메일 주소 입력 후 Enter" style="flex: 1 1 200px; min-width: 120px; min-height: 34px; border: 0; font: inherit; color: #171C26; background: transparent">
</div>
<button type="button" onClick="{{ toggleCc }}" aria-expanded="{{ ccExpanded }}" style="min-height: 44px; padding: 0 10px; border: 0; border-radius: 8px; background: transparent; color: #0E7C86; font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer">참조 · 숨은참조</button>
</div>
<sc-if value="{{ hasInvalid }}" hint-placeholder-val="{{ false }}">
<p role="alert" style="display: flex; align-items: center; gap: 8px; margin: 0; padding: 8px 32px; background: #FEF3F2; color: #B42318; font-size: 12.5px; border-bottom: 1px solid #FBD7D3">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>
<span>받는 사람 주소를 다시 확인해 주세요.</span>
</p>
</sc-if>
<sc-if value="{{ showCc }}" hint-placeholder-val="{{ false }}">
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; min-height: 44px; box-sizing: border-box; padding: 4px 22px 4px 32px; border-bottom: 1px solid #EEF0F3">
<label for="compose-cc" style="flex: 0 0 64px; font-size: 12.5px; font-weight: 600; color: #4A5363">참조</label>
<input id="compose-cc" type="text" placeholder="이름 또는 메일 주소" style="flex: 1 1 320px; min-width: 0; min-height: 34px; border: 0; font: inherit; color: #171C26; background: transparent">
</div>
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; min-height: 44px; box-sizing: border-box; padding: 4px 22px 4px 32px; border-bottom: 1px solid #EEF0F3">
<label for="compose-bcc" style="flex: 0 0 64px; font-size: 12.5px; font-weight: 600; color: #4A5363">숨은참조</label>
<input id="compose-bcc" type="text" placeholder="이름 또는 메일 주소" style="flex: 1 1 320px; min-width: 0; min-height: 34px; border: 0; font: inherit; color: #171C26; background: transparent">
</div>
</sc-if>
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 6px 12px; min-height: 44px; box-sizing: border-box; padding: 4px 22px 4px 32px; border-bottom: 1px solid #EEF0F3">
<label for="compose-subject" style="flex: 0 0 64px; font-size: 12.5px; font-weight: 600; color: #4A5363">제목</label>
<input id="compose-subject" type="text" value="{{ subject }}" onChange="{{ onSubject }}" placeholder="제목을 입력하세요" style="flex: 1 1 320px; min-width: 0; min-height: 34px; border: 0; font: inherit; font-size: 14px; font-weight: 600; color: #171C26; background: transparent">
</div>
<textarea aria-label="본문" value="{{ body }}" onChange="{{ onBody }}" rows="13" placeholder="내용을 입력하세요" style="flex: 1 1 auto; min-height: 300px; width: 100%; max-width: 744px; box-sizing: border-box; padding: 16px 32px; border: 0; resize: vertical; font: inherit; font-size: 14px; line-height: 1.75; color: #1F2733; background: #FFFFFF"></textarea>
<div style="display: flex; flex-direction: column; gap: 8px; padding: 10px 32px 14px; border-top: 1px solid #EEF0F3">
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 4px 10px">
<button type="button" style="display: inline-flex; align-items: center; gap: 6px; min-height: 44px; margin-left: -6px; padding: 0 10px 0 6px; border: 0; border-radius: 8px; background: transparent; color: #344054; font: inherit; font-weight: 500; cursor: pointer">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"></path></svg>
<span>파일 첨부</span>
</button>
<span style="font-size: 12px; color: #667085">파일당 최대 20MB · 문서, 이미지, 압축 파일</span>
</div>
<sc-if value="{{ hasFiles }}" hint-placeholder-val="{{ true }}">
<ul style="list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr)); gap: 6px">
<sc-for list="{{ files }}" as="file" hint-placeholder-count="1">
<li style="display: flex; align-items: center; gap: 10px; padding: 2px 2px 2px 10px; border: 1px solid #E6E8EC; border-radius: 8px">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#4A5363" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path><polyline points="13 2 13 9 20 9"></polyline></svg>
<span style="flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500">{{ file.name }}</span>
<span style="flex: 0 0 auto; font-size: 12px; color: #667085">{{ file.size }}</span>
<button type="button" onClick="{{ file.remove }}" aria-label="{{ file.removeLabel }}" style="flex: 0 0 auto; width: 40px; height: 40px; display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: 6px; background: transparent; color: #4A5363; cursor: pointer">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
</button>
</li>
</sc-for>
</ul>
</sc-if>
</div>
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 10px 32px; border-top: 1px solid #E6E8EC">
<a href="Main.dc.html" style="display: inline-flex; align-items: center; gap: 8px; min-height: 44px; padding: 0 20px; border-radius: 8px; background: #0E7C86; color: #FFFFFF; font-size: 13.5px; font-weight: 600; text-decoration: none">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
<span>보내기</span>
</a>
<a href="Main.dc.html" style="display: inline-flex; align-items: center; min-height: 44px; padding: 0 14px; border-radius: 8px; color: #344054; font-weight: 500; text-decoration: none">취소</a>
<span style="margin-left: auto; font-size: 12px; color: #667085">보내는 사람 김아라 &lt;ara.kim@arasystem.it.kr&gt;</span>
</div>
</form>
</main>
</div>
</div>
</div>
</x-dc>
<script type="text/x-dc" data-dc-script data-props='{"mode":{"editor":"enum","options":["reply","new"],"default":"reply"},"$preview":{"width":1440,"height":900}}'>
class Component extends DCLogic {
state = { mode: null };
initial(mode) {
if (mode === 'new') {
return { mode: 'new', chips: [], toInput: '', showCc: false, subject: '', body: '', files: [] };
}
return {
mode: 'reply',
chips: [{ label: '이도윤 <doyun.lee@arasystem.it.kr>', invalid: false }],
toInput: '',
showCc: false,
subject: 'Re: 메일 서버 이전 일정 공유',
body: '도윤님, 확인했습니다.\n체크리스트 기준으로 금요일 저녁 전환을 진행해 주세요.\n\n김아라\n\n──────────\n2026. 10. 26. (월) 오전 10:42, 이도윤 <doyun.lee@arasystem.it.kr> 작성:\n> 김아라님, 안녕하세요.\n> 말씀하신 대로 이번 주 금요일(10/30) 저녁 9시에 MX 레코드를 새 메일 서버로 전환하겠습니다.',
files: [{ name: '메일서버_전환_체크리스트_v2.xlsx', size: '52 KB' }],
};
}
renderVals() {
const mode = this.props.mode ?? 'reply';
// 모드(답장/새 메일)가 바뀌면 그 모드의 초기값에서 다시 시작한다
const data = this.state.mode === mode ? this.state : this.initial(mode);
const update = (patch) => this.setState({ ...data, ...patch });
const EMAIL = /^[^@\s<>]+@[^@\s<>]+\.[^@\s<>]+$/;
const chips = data.chips.map((c, i) => ({
label: c.label,
bg: c.invalid ? '#FEF3F2' : '#F2F4F7',
fg: c.invalid ? '#B42318' : '#171C26',
deco: c.invalid ? 'underline wavy #B42318' : 'none',
removeLabel: c.label + ' 삭제',
remove: () => update({ chips: data.chips.filter((_, j) => j !== i) }),
}));
const files = data.files.map((f, i) => ({
name: f.name,
size: f.size,
removeLabel: f.name + ' 첨부 취소',
remove: () => update({ files: data.files.filter((_, j) => j !== i) }),
}));
return {
title: mode === 'new' ? '새 메일' : '답장',
chips: chips,
hasInvalid: data.chips.some((c) => c.invalid),
toInput: data.toInput,
showCc: data.showCc,
ccExpanded: data.showCc ? 'true' : 'false',
subject: data.subject,
body: data.body,
files: files,
hasFiles: files.length > 0,
onToInput: (e) => update({ toInput: e.target.value }),
onToKey: (e) => {
if (e.key !== 'Enter' && e.key !== ',') return;
e.preventDefault();
const value = data.toInput.trim();
if (!value) return;
const address = value.includes('<') ? value.slice(value.indexOf('<') + 1, value.indexOf('>')) : value;
update({ chips: [...data.chips, { label: value, invalid: !EMAIL.test(address) }], toInput: '' });
},
toggleCc: () => update({ showCc: !data.showCc }),
onSubject: (e) => update({ subject: e.target.value }),
onBody: (e) => update({ body: e.target.value }),
};
}
}
</script>
</body>
</html>
+72
View File
@@ -0,0 +1,72 @@
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>로그인</title>
<script src="./support.js"></script>
</head>
<body>
<x-dc>
<helmet>
<style>
@font-face{font-family:'Pretendard';src:url('/_blob/16067b9aa5137abf808e3dbef30c42e0') format('woff2-variations'),url('/_blob/16067b9aa5137abf808e3dbef30c42e0') format('woff2');font-weight:45 920;font-style:normal;font-display:swap}
body{margin:0}
a{color:#0E7C86}a:hover{color:#0A5F67}
:focus-visible{outline:2px solid #0E7C86;outline-offset:2px}
</style>
</helmet>
<div style="min-height: 100vh; display: flex; flex-wrap: wrap; background: #FFFFFF; color: #171C26; font-family: Pretendard, -apple-system, 'Apple SD Gothic Neo', 'Malgun Gothic', system-ui, sans-serif; font-size: 13px; line-height: 1.5">
<section aria-label="ARA SYSTEM 소개" style="flex: 1 1 400px; box-sizing: border-box; display: flex; flex-direction: column; justify-content: space-between; gap: 56px; padding: 44px 48px 48px; background: #0B3640; color: #E6EFF0">
<img src="/_blob/3a2f4115a8f90207faa7d3a51cd9aec2" alt="ARA SYSTEM" width="240" height="115" style="display: block; width: 240px; height: auto">
<div style="display: flex; flex-direction: column; gap: 14px; max-width: 420px">
<p style="margin: 0; font-size: 30px; line-height: 1.32; font-weight: 700; letter-spacing: -0.025em; color: #FFFFFF">사내 업무를<br>한 계정으로</p>
<p style="margin: 0; font-size: 14px; line-height: 1.65; color: #A3C2C6">메일부터 시작해 캘린더, 파일 저장소, ERP까지 같은 계정으로 이어집니다.</p>
</div>
</section>
<main style="flex: 999 1 480px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; padding: 48px 24px">
<div style="width: 100%; max-width: 360px; display: flex; flex-direction: column; gap: 22px">
<div style="display: flex; flex-direction: column; gap: 6px">
<h1 style="margin: 0; font-size: 22px; line-height: 1.35; font-weight: 700; letter-spacing: -0.02em">로그인</h1>
<p style="margin: 0; color: #4A5363">회사 메일 계정(@arasystem.it.kr)으로 로그인하세요.</p>
</div>
<sc-if value="{{ showError }}" hint-placeholder-val="{{ false }}">
<p role="alert" style="display: flex; align-items: flex-start; gap: 8px; margin: 0; padding: 10px 12px; border-radius: 8px; background: #FEF3F2; color: #B42318; font-size: 12.5px">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="flex: 0 0 auto; margin-top: 1px"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>
<span>이메일 또는 비밀번호가 올바르지 않습니다.</span>
</p>
</sc-if>
<form style="display: flex; flex-direction: column; gap: 14px">
<div style="display: flex; flex-direction: column; gap: 6px">
<label for="login-email" style="font-size: 12.5px; font-weight: 600; color: #344054">메일 주소</label>
<input id="login-email" type="email" autocomplete="username" placeholder="name@arasystem.it.kr" style="min-height: 44px; box-sizing: border-box; padding: 0 12px; border: 1px solid {{ inputBorder }}; border-radius: 8px; font: inherit; font-size: 14px; color: #171C26; background: #FFFFFF">
</div>
<div style="display: flex; flex-direction: column; gap: 6px">
<label for="login-password" style="font-size: 12.5px; font-weight: 600; color: #344054">비밀번호</label>
<input id="login-password" type="password" autocomplete="current-password" placeholder="비밀번호 입력" style="min-height: 44px; box-sizing: border-box; padding: 0 12px; border: 1px solid {{ inputBorder }}; border-radius: 8px; font: inherit; font-size: 14px; color: #171C26; background: #FFFFFF">
</div>
<a href="Main.dc.html" style="display: flex; align-items: center; justify-content: center; min-height: 44px; margin-top: 6px; border-radius: 8px; background: #0E7C86; color: #FFFFFF; font-size: 14px; font-weight: 600; text-decoration: none">로그인</a>
</form>
<p style="margin: 0; font-size: 12.5px; color: #667085">비밀번호를 잊으셨다면 시스템 관리자에게 문의해 주세요.</p>
<p style="margin: 0; padding-top: 16px; border-top: 1px solid #EEF0F3; font-size: 12px; line-height: 1.6; color: #667085">Outlook이나 휴대폰 기본 메일 앱에서도 같은 계정으로 메일을 쓸 수 있습니다. (IMAP · SMTP 설정)</p>
</div>
</main>
</div>
</x-dc>
<script type="text/x-dc" data-dc-script data-props='{"showError":{"editor":"boolean","default":false},"$preview":{"width":1440,"height":900}}'>
class Component extends DCLogic {
renderVals() {
const showError = this.props.showError ?? false;
return {
showError: showError,
inputBorder: showError ? '#B42318' : '#8A94A3',
};
}
}
</script>
</body>
</html>
+475
View File
@@ -0,0 +1,475 @@
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>메일함</title>
<script src="./support.js"></script>
</head>
<body>
<x-dc>
<helmet>
<style>
@font-face{font-family:'Pretendard';src:url('/_blob/16067b9aa5137abf808e3dbef30c42e0') format('woff2-variations'),url('/_blob/16067b9aa5137abf808e3dbef30c42e0') format('woff2');font-weight:45 920;font-style:normal;font-display:swap}
body{margin:0}
a{color:#0E7C86}a:hover{color:#0A5F67}
:focus-visible{outline:2px solid #0E7C86;outline-offset:2px}
</style>
</helmet>
<div style="min-height: 100vh; display: flex; background: #FFFFFF; color: #171C26; font-family: Pretendard, -apple-system, 'Apple SD Gothic Neo', 'Malgun Gothic', system-ui, sans-serif; font-size: 13px; line-height: 1.5">
<nav aria-label="사내 시스템" style="flex: 0 0 72px; box-sizing: border-box; display: flex; flex-direction: column; align-items: center; gap: 14px; padding: 10px 0 12px; background: #0B3640">
<a href="Main.dc.html" style="display: inline-flex; align-items: center; justify-content: center; width: 60px; min-height: 44px; border-radius: 10px; text-decoration: none">
<img src="/_blob/2b9ef4349333debc969432c859eaf1bf" alt="ARA SYSTEM" width="48" height="17" style="display: block; width: 48px; height: auto">
</a>
<ul style="list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px">
<li>
<a href="Main.dc.html" aria-current="page" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; background: #155A64; color: #FFFFFF; font-size: 11px; font-weight: 600; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path><polyline points="22,6 12,13 2,6"></polyline></svg>
<span>메일</span>
</a>
</li>
<li>
<a href="#" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; color: #A3C2C6; font-size: 11px; font-weight: 500; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg>
<span>캘린더</span>
</a>
</li>
<li>
<a href="#" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; color: #A3C2C6; font-size: 11px; font-weight: 500; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
<span>파일</span>
</a>
</li>
<li>
<a href="#" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; color: #A3C2C6; font-size: 11px; font-weight: 500; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="7" width="20" height="14" rx="2" ry="2"></rect><path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"></path></svg>
<span>ERP</span>
</a>
</li>
<li>
<a href="#" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; color: #A3C2C6; font-size: 11px; font-weight: 500; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg>
<span>주소록</span>
</a>
</li>
</ul>
<a href="#" style="margin-top: auto; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; color: #A3C2C6; font-size: 11px; font-weight: 500; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
<span>설정</span>
</a>
</nav>
<div style="flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column">
<header style="display: flex; align-items: center; flex-wrap: wrap; gap: 8px 16px; box-sizing: border-box; min-height: 56px; padding: 6px 12px 6px 20px; border-bottom: 1px solid #E6E8EC">
<h1 style="margin: 0; font-size: 16px; font-weight: 700; letter-spacing: -0.01em">메일</h1>
<div style="flex: 1 1 280px; max-width: 520px; display: flex; align-items: center; gap: 8px; box-sizing: border-box; height: 40px; padding: 0 12px; border: 1px solid #8A94A3; border-radius: 8px; background: #FFFFFF">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#667085" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="search" aria-label="통합 검색" placeholder="통합 검색" style="flex: 1 1 auto; min-width: 0; border: 0; background: transparent; font: inherit; color: #171C26">
</div>
<div style="margin-left: auto; display: flex; align-items: center; gap: 10px">
<span aria-hidden="true" style="display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 50%; background: #E3F2F3; color: #0A5F67; font-size: 13px; font-weight: 600">김</span>
<div style="display: flex; flex-direction: column; line-height: 1.3">
<span style="font-weight: 600">김아라</span>
<span style="font-size: 12px; color: #667085">ara.kim@arasystem.it.kr</span>
</div>
<a href="Login.dc.html" style="display: inline-flex; align-items: center; gap: 6px; min-height: 44px; padding: 0 10px; border-radius: 8px; color: #4A5363; font-weight: 500; text-decoration: none">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line></svg>
<span>로그아웃</span>
</a>
</div>
</header>
<div style="flex: 1 1 auto; display: flex; flex-wrap: wrap; align-items: stretch">
<nav aria-label="메일함" style="flex: 1 1 208px; box-sizing: border-box; display: flex; flex-direction: column; gap: 14px; padding: 14px 10px; background: #F8F9FB; border-right: 1px solid #E6E8EC">
<a href="Compose.dc.html" style="display: flex; align-items: center; justify-content: center; gap: 8px; min-height: 44px; border-radius: 8px; background: #0E7C86; color: #FFFFFF; font-size: 13.5px; font-weight: 600; text-decoration: none">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20h9"></path><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path></svg>
<span>메일 쓰기</span>
</a>
<ul style="list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1px">
<sc-for list="{{ boxes }}" as="b" hint-placeholder-count="5">
<li>
<button type="button" onClick="{{ b.pick }}" aria-current="{{ b.current }}" style="width: 100%; display: flex; align-items: center; gap: 10px; min-height: 40px; box-sizing: border-box; padding: 0 10px; border: 0; border-radius: 7px; background: {{ b.bg }}; color: {{ b.fg }}; font: inherit; font-weight: {{ b.fw }}; text-align: left; cursor: pointer">
<sc-if value="{{ b.isInbox }}" hint-placeholder-val="{{ false }}"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="22 12 16 12 14 15 10 15 8 12 2 12"></polyline><path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"></path></svg></sc-if>
<sc-if value="{{ b.isSent }}" hint-placeholder-val="{{ false }}"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg></sc-if>
<sc-if value="{{ b.isDrafts }}" hint-placeholder-val="{{ false }}"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line></svg></sc-if>
<sc-if value="{{ b.isJunk }}" hint-placeholder-val="{{ false }}"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polygon points="7.86 2 16.14 2 22 7.86 22 16.14 16.14 22 7.86 22 2 16.14 2 7.86 7.86 2"></polygon><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg></sc-if>
<sc-if value="{{ b.isTrash }}" hint-placeholder-val="{{ false }}"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg></sc-if>
<span style="flex: 1 1 auto">{{ b.name }}</span>
<sc-if value="{{ b.hasUnread }}" hint-placeholder-val="{{ false }}"><span style="font-size: 12px; font-weight: 600; color: {{ b.countColor }}; font-variant-numeric: tabular-nums">{{ b.unread }}</span></sc-if>
</button>
</li>
</sc-for>
</ul>
<div style="margin-top: auto; display: flex; flex-direction: column; gap: 6px; padding: 12px 10px 2px; border-top: 1px solid #E6E8EC">
<div style="display: flex; justify-content: space-between; gap: 8px; font-size: 12px; color: #667085">
<span>메일함 사용량</span>
<span style="font-variant-numeric: tabular-nums">0.9 / 5 GB</span>
</div>
<div style="height: 4px; border-radius: 2px; background: #E4E7EC">
<div style="width: 18%; height: 4px; border-radius: 2px; background: #0E7C86"></div>
</div>
</div>
</nav>
<main style="flex: 999 1 560px; min-width: 0; display: flex; flex-wrap: wrap; align-items: stretch">
<section aria-labelledby="list-title" style="flex: 1 1 384px; min-width: 0; box-sizing: border-box; display: flex; flex-direction: column; border-right: 1px solid #E6E8EC">
<div style="display: flex; align-items: center; gap: 8px; min-height: 52px; box-sizing: border-box; padding: 4px 4px 4px 16px; border-bottom: 1px solid #E6E8EC">
<h2 id="list-title" style="margin: 0; font-size: 15px; font-weight: 700; letter-spacing: -0.01em">{{ boxName }}</h2>
<span style="font-size: 12px; color: #667085; font-variant-numeric: tabular-nums">{{ countText }}</span>
<div style="margin-left: auto; display: inline-flex; gap: 2px; padding: 2px; border: 1px solid #D0D5DD; border-radius: 8px; background: #FFFFFF">
<button type="button" aria-pressed="true" aria-label="분할 보기" style="display: inline-flex; align-items: center; gap: 4px; min-height: 30px; padding: 0 8px; border: 0; border-radius: 6px; background: #E3F2F3; color: #0A5F67; font: inherit; font-size: 12px; font-weight: 600; cursor: pointer">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="2" ry="2"></rect><line x1="10" y1="4" x2="10" y2="20"></line></svg>
<span>분할</span>
</button>
<a href="MainList.dc.html" aria-label="목록 보기" style="display: inline-flex; align-items: center; gap: 4px; min-height: 30px; padding: 0 8px; border-radius: 6px; color: #344054; font-size: 12px; font-weight: 500; text-decoration: none">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="9" y1="6" x2="20" y2="6"></line><line x1="9" y1="12" x2="20" y2="12"></line><line x1="9" y1="18" x2="20" y2="18"></line><line x1="4" y1="6" x2="4.01" y2="6"></line><line x1="4" y1="12" x2="4.01" y2="12"></line><line x1="4" y1="18" x2="4.01" y2="18"></line></svg>
<span>목록</span>
</a>
</div>
<button type="button" aria-label="새로고침" style="width: 40px; height: 44px; display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: 8px; background: transparent; color: #4A5363; cursor: pointer">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></svg>
</button>
</div>
<sc-if value="{{ hasItems }}" hint-placeholder-val="{{ true }}">
<ul style="list-style: none; margin: 0; padding: 0">
<sc-for list="{{ items }}" as="item" hint-placeholder-count="7">
<li style="display: flex; align-items: stretch; border-bottom: 1px solid #EEF0F3; background: {{ item.bg }}">
<button type="button" onClick="{{ item.pick }}" style="flex: 1 1 auto; min-width: 0; min-height: 50px; box-sizing: border-box; display: flex; flex-direction: column; justify-content: center; gap: 1px; padding: 6px 2px 6px 16px; border: 0; background: transparent; font: inherit; line-height: 1.4; color: inherit; text-align: left; cursor: pointer">
<span style="display: flex; align-items: center; gap: 6px">
<span style="flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: {{ item.fw }}; color: {{ item.whoColor }}">{{ item.who }}</span>
<span style="flex: 0 0 auto; font-size: 12px; font-weight: {{ item.timeFw }}; color: {{ item.timeColor }}; font-variant-numeric: tabular-nums">{{ item.time }}</span>
</span>
<span style="display: flex; align-items: center; gap: 6px; min-width: 0">
<span style="min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: {{ item.subFw }}; color: #171C26">{{ item.subject }}</span>
<sc-if value="{{ item.hasAtt }}" hint-placeholder-val="{{ false }}"><svg role="img" aria-label="첨부 있음" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#667085" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="flex: 0 0 auto"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"></path></svg></sc-if>
</span>
</button>
<button type="button" onClick="{{ item.toggleStar }}" aria-label="{{ item.starLabel }}" aria-pressed="{{ item.starredStr }}" style="flex: 0 0 auto; width: 44px; display: flex; align-items: center; justify-content: center; border: 0; background: transparent; color: {{ item.starColor }}; cursor: pointer">
<svg width="15" height="15" viewBox="0 0 24 24" fill="{{ item.starFill }}" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon></svg>
</button>
</li>
</sc-for>
</ul>
<div style="padding: 8px 12px 16px">
<button type="button" style="width: 100%; min-height: 40px; border: 0; border-radius: 8px; background: transparent; color: #0E7C86; font: inherit; font-weight: 600; cursor: pointer">메일 더 보기</button>
</div>
</sc-if>
<sc-if value="{{ noItems }}" hint-placeholder-val="{{ false }}">
<div style="display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 80px 24px; text-align: center">
<svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#98A2B3" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="22 12 16 12 14 15 10 15 8 12 2 12"></polyline><path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"></path></svg>
<p style="margin: 6px 0 0; font-size: 14px; font-weight: 600">{{ emptyText }}</p>
<p style="margin: 0; font-size: 12.5px; color: #667085">{{ emptyHint }}</p>
</div>
</sc-if>
</section>
<div style="flex: 999 1 480px; min-width: 0; box-sizing: border-box; display: flex; flex-direction: column">
<sc-if value="{{ hasSel }}" hint-placeholder-val="{{ true }}">
<article aria-labelledby="mail-subject" style="display: flex; flex-direction: column">
<div style="border-bottom: 1px solid #E6E8EC"><div style="display: flex; flex-wrap: wrap; align-items: center; gap: 2px; min-height: 52px; max-width: 800px; box-sizing: border-box; padding: 4px 32px 4px 22px">
<a href="Compose.dc.html" style="display: inline-flex; align-items: center; gap: 6px; min-height: 44px; box-sizing: border-box; padding: 0 10px; border-radius: 8px; color: #344054; font-weight: 500; text-decoration: none">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="9 14 4 9 9 4"></polyline><path d="M20 20v-7a4 4 0 0 0-4-4H4"></path></svg>
<span>답장</span>
</a>
<a href="Compose.dc.html" style="display: inline-flex; align-items: center; gap: 6px; min-height: 44px; box-sizing: border-box; padding: 0 10px; border-radius: 8px; color: #344054; font-weight: 500; text-decoration: none">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="7 17 2 12 7 7"></polyline><polyline points="12 17 7 12 12 7"></polyline><path d="M22 18v-2a4 4 0 0 0-4-4H7"></path></svg>
<span>전체 답장</span>
</a>
<a href="Compose.dc.html" style="display: inline-flex; align-items: center; gap: 6px; min-height: 44px; box-sizing: border-box; padding: 0 10px; border-radius: 8px; color: #344054; font-weight: 500; text-decoration: none">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="15 14 20 9 15 4"></polyline><path d="M4 20v-7a4 4 0 0 1 4-4h12"></path></svg>
<span>전달</span>
</a>
<span aria-hidden="true" style="width: 1px; height: 20px; margin: 0 6px; background: #E6E8EC"></span>
<button type="button" onClick="{{ deleteCur }}" style="display: inline-flex; align-items: center; gap: 6px; min-height: 44px; box-sizing: border-box; padding: 0 10px; border: 0; border-radius: 8px; background: transparent; color: #B42318; font: inherit; font-weight: 500; cursor: pointer">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>
<span>{{ deleteLabel }}</span>
</button>
</div>
</div><div style="display: flex; flex-direction: column; gap: 18px; max-width: 800px; box-sizing: border-box; padding: 20px 32px 36px">
<div style="display: flex; align-items: flex-start; gap: 8px">
<h1 id="mail-subject" style="flex: 1 1 auto; margin: 4px 0 0; font-size: 20px; line-height: 1.4; font-weight: 650; letter-spacing: -0.015em">{{ m.subject }}</h1>
<button type="button" onClick="{{ toggleStarCur }}" aria-label="{{ m.starLabel }}" aria-pressed="{{ m.starredStr }}" style="flex: 0 0 auto; width: 44px; height: 44px; display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: 8px; background: transparent; color: {{ m.starColor }}; cursor: pointer">
<svg width="18" height="18" viewBox="0 0 24 24" fill="{{ m.starFill }}" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon></svg>
</button>
</div>
<div style="display: flex; align-items: flex-start; gap: 12px">
<span aria-hidden="true" style="flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: 50%; background: #E3F2F3; color: #0A5F67; font-size: 14px; font-weight: 600">{{ m.initial }}</span>
<div style="flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px">
<div style="display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 8px">
<span style="font-size: 13.5px; font-weight: 600">{{ m.fromName }}</span>
<span style="font-size: 12.5px; color: #667085">{{ m.fromEmail }}</span>
<span style="margin-left: auto; font-size: 12.5px; color: #667085; white-space: nowrap; font-variant-numeric: tabular-nums">{{ m.date }}</span>
</div>
<span style="font-size: 12.5px; color: #4A5363">받는 사람 {{ m.to }}</span>
<sc-if value="{{ m.hasCc }}" hint-placeholder-val="{{ false }}"><span style="font-size: 12.5px; color: #4A5363">참조 {{ m.cc }}</span></sc-if>
</div>
</div>
<sc-if value="{{ m.remote }}" hint-placeholder-val="{{ false }}">
<p style="display: flex; align-items: flex-start; gap: 8px; margin: 0; padding: 10px 12px; border-radius: 8px; background: #FFF7E6; color: #7A5200; font-size: 12.5px">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="flex: 0 0 auto; margin-top: 2px"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path></svg>
<span>외부 이미지를 차단했습니다. 개인정보 보호를 위해 보낸 사람 서버의 이미지는 불러오지 않습니다.</span>
</p>
</sc-if>
<div style="white-space: pre-line; font-size: 14px; line-height: 1.75; color: #1F2733; max-width: 680px">{{ m.body }}</div>
<sc-if value="{{ m.hasAtt }}" hint-placeholder-val="{{ true }}">
<section aria-labelledby="att-title" style="display: flex; flex-direction: column; gap: 8px; padding-top: 16px; border-top: 1px solid #EEF0F3">
<h2 id="att-title" style="margin: 0; font-size: 12.5px; font-weight: 600; color: #4A5363">첨부 파일 {{ m.attCount }}개</h2>
<ul style="list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(260px, 100%), 1fr)); gap: 8px">
<sc-for list="{{ m.atts }}" as="att" hint-placeholder-count="1">
<li style="display: flex; align-items: center; gap: 10px; padding: 4px 2px 4px 8px; border: 1px solid #E6E8EC; border-radius: 8px">
<span aria-hidden="true" style="flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 6px; background: #F2F4F7; color: #4A5363">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path><polyline points="13 2 13 9 20 9"></polyline></svg>
</span>
<div style="flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; line-height: 1.35">
<span style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500">{{ att.name }}</span>
<span style="font-size: 12px; color: #667085">{{ att.size }}</span>
</div>
<button type="button" aria-label="{{ att.label }}" style="flex: 0 0 auto; width: 44px; height: 44px; display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: 8px; background: transparent; color: #344054; cursor: pointer">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>
</button>
</li>
</sc-for>
</ul>
</section>
</sc-if>
</div>
</article>
</sc-if>
<sc-if value="{{ noSel }}" hint-placeholder-val="{{ false }}">
<div style="flex: 1 1 auto; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; min-height: 520px; padding: 24px; text-align: center">
<svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#98A2B3" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path><polyline points="22,6 12,13 2,6"></polyline></svg>
<p style="margin: 6px 0 0; font-size: 14px; font-weight: 600">메일을 선택하세요</p>
<p style="margin: 0; font-size: 12.5px; color: #667085">목록에서 메일을 고르면 내용이 여기에 표시됩니다.</p>
</div>
</sc-if>
</div>
</main>
</div>
</div>
</div>
</x-dc>
<script type="text/x-dc" data-dc-script data-props='{"$preview":{"width":1440,"height":900}}'>
class Component extends DCLogic {
state = { box: 'inbox', sel: 'm1', read: {}, star: { m3: true }, moved: {}, gone: {} };
messages() {
return [
{
id: 'm1', box: 'inbox', who: '이도윤', fromName: '이도윤', fromEmail: 'doyun.lee@arasystem.it.kr',
to: '김아라 <ara.kim@arasystem.it.kr>', cc: '인프라팀 <infra@arasystem.it.kr>',
subject: 'Re: 메일 서버 이전 일정 공유',
time: '오전 10:42', date: '2026. 10. 26. (월) 오전 10:42', unread: true, remote: false,
body: '김아라님, 안녕하세요.\n\n말씀하신 대로 이번 주 금요일(10/30) 저녁 9시에 MX 레코드를 새 메일 서버로 전환하겠습니다.\n전환 하루 전에 DNS TTL을 300초로 낮춰 두고, 기존 서버는 2주 동안 함께 운영합니다.\n\n전환 체크리스트를 첨부했으니 확인 부탁드립니다.\n\n이도윤 드림',
atts: [{ name: '메일서버_전환_체크리스트.xlsx', size: '48 KB' }],
},
{
id: 'm2', box: 'inbox', who: '인사팀', fromName: '인사팀', fromEmail: 'hr@arasystem.it.kr',
to: '전체 구성원 <all@arasystem.it.kr>', cc: '',
subject: '[인사팀] 11월 연차 사용 계획 제출 안내',
time: '오전 9:15', date: '2026. 10. 26. (월) 오전 9:15', unread: true, remote: false,
body: '구성원 여러분, 안녕하세요. 인사팀입니다.\n\n11월 연차 사용 계획을 10월 30일(금)까지 사내 시스템으로 제출해 주세요.\n팀별 일정이 겹치지 않도록 팀장님과 미리 협의해 주시기 바랍니다.\n\n감사합니다.',
atts: [],
},
{
id: 'm3', box: 'inbox', who: '박준호', fromName: '박준호', fromEmail: 'junho.park@arasystem.it.kr',
to: '인프라팀 <infra@arasystem.it.kr>', cc: '',
subject: '주간 인프라 점검 결과 (10/20~10/24)',
time: '어제', date: '2026. 10. 25. (일) 오후 6:03', unread: false, remote: false,
body: '인프라팀 여러분,\n\n이번 주 점검 결과를 공유드립니다.\n백업 작업이 한 차례 지연된 것 외에는 특이사항이 없습니다. 상세 내용은 첨부 보고서를 확인해 주세요.\n\n박준호',
atts: [{ name: '주간점검_1024.pdf', size: '312 KB' }],
},
{
id: 'm4', box: 'inbox', who: '(주)한빛솔루션', fromName: '(주)한빛솔루션 영업팀', fromEmail: 'sales@hanbit.example',
to: '김아라 <ara.kim@arasystem.it.kr>', cc: '',
subject: '서버 증설 견적서 송부드립니다',
time: '어제', date: '2026. 10. 25. (일) 오후 2:30', unread: true, remote: true,
body: '안녕하세요, (주)한빛솔루션 영업팀입니다.\n\n요청하신 서버 증설 견적서를 첨부하여 보내드립니다.\n검토 후 궁금하신 점은 회신 주시면 안내드리겠습니다.\n\n감사합니다.',
atts: [{ name: '견적서_서버증설.pdf', size: '184 KB' }],
},
{
id: 'm5', box: 'inbox', who: '보안팀', fromName: '보안팀', fromEmail: 'security@arasystem.it.kr',
to: '전체 구성원 <all@arasystem.it.kr>', cc: '',
subject: '[공지] 하반기 정보보안 교육 이수 요청',
time: '10월 24일', date: '2026. 10. 24. (토) 오전 11:00', unread: false, remote: false,
body: '구성원 여러분, 보안팀입니다.\n\n하반기 정보보안 교육을 11월 13일(금)까지 이수해 주세요.\n교육은 사내 교육 시스템에서 들으실 수 있습니다.',
atts: [],
},
{
id: 'm6', box: 'inbox', who: '최서연', fromName: '최서연', fromEmail: 'seoyeon.choi@arasystem.it.kr',
to: '전체 구성원 <all@arasystem.it.kr>', cc: '',
subject: '10월 경비 정산 마감 안내',
time: '10월 23일', date: '2026. 10. 23. (금) 오후 4:20', unread: false, remote: false,
body: '안녕하세요, 재무팀 최서연입니다.\n\n10월분 경비는 11월 2일(월)까지 증빙과 함께 올려 주세요.\n마감 이후 접수분은 다음 달로 넘어갑니다.',
atts: [],
},
{
id: 'm7', box: 'inbox', who: '이도윤', fromName: '이도윤', fromEmail: 'doyun.lee@arasystem.it.kr',
to: '김아라 <ara.kim@arasystem.it.kr>', cc: '',
subject: '메일 서버 이전 일정 공유',
time: '10월 22일', date: '2026. 10. 22. (목) 오후 3:12', unread: false, remote: false,
body: '김아라님,\n\n새 메일 서버 전환 일정 초안을 공유드립니다.\n금요일 저녁 전환을 기준으로 잡았고, 의견 주시면 반영하겠습니다.\n\n이도윤 드림',
atts: [],
},
{
id: 's1', box: 'sent', who: '받는 사람: 박준호', fromName: '김아라', fromEmail: 'ara.kim@arasystem.it.kr',
to: '박준호 <junho.park@arasystem.it.kr>', cc: '',
subject: 'Re: 주간 인프라 점검 결과 (10/20~10/24)',
time: '어제', date: '2026. 10. 25. (일) 오후 7:10', unread: false, remote: false,
body: '준호님, 공유 감사합니다.\n\n지연된 백업 작업은 원인 확인 후 알려 주세요.\n\n김아라',
atts: [],
},
{
id: 's2', box: 'sent', who: '받는 사람: 이도윤', fromName: '김아라', fromEmail: 'ara.kim@arasystem.it.kr',
to: '이도윤 <doyun.lee@arasystem.it.kr>', cc: '',
subject: 'Re: 메일 서버 이전 일정 공유',
time: '10월 23일', date: '2026. 10. 23. (금) 오전 9:40', unread: false, remote: false,
body: '도윤님,\n\n금요일 저녁 전환으로 진행해 주세요. TTL은 하루 전에 미리 낮춰 두면 좋겠습니다.\n\n김아라',
atts: [],
},
{
id: 't1', box: 'trash', who: '호스팅 안내', fromName: '호스팅 안내', fromEmail: 'promo@hosting.example',
to: '김아라 <ara.kim@arasystem.it.kr>', cc: '',
subject: '(광고) 서버 호스팅 연말 할인 안내',
time: '10월 21일', date: '2026. 10. 21. (수) 오전 8:00', unread: false, remote: true,
body: '연말을 맞아 서버 호스팅 상품을 할인된 가격에 제공합니다.',
atts: [],
},
];
}
renderVals() {
const st = this.state;
const BOXES = [
{ id: 'inbox', name: '받은편지함', emptyHint: '새 메일이 오면 여기에 표시됩니다.' },
{ id: 'sent', name: '보낸편지함', emptyHint: '보낸 메일이 여기에 표시됩니다.' },
{ id: 'drafts', name: '임시보관함', emptyHint: '임시 저장한 메일이 여기에 표시됩니다.' },
{ id: 'junk', name: '스팸메일함', emptyHint: '스팸으로 분류된 메일이 여기에 표시됩니다.' },
{ id: 'trash', name: '휴지통', emptyHint: '삭제한 메일이 여기에 표시됩니다.' },
];
const all = this.messages()
.filter((x) => !st.gone[x.id])
.map((x) => ({ ...x, box: st.moved[x.id] || x.box }));
const isUnread = (x) => x.unread && !st.read[x.id];
const starOf = (id) => !!st.star[id];
const boxes = BOXES.map((b) => {
const active = b.id === st.box;
const unread = all.filter((x) => x.box === b.id && isUnread(x)).length;
return {
name: b.name,
isInbox: b.id === 'inbox',
isSent: b.id === 'sent',
isDrafts: b.id === 'drafts',
isJunk: b.id === 'junk',
isTrash: b.id === 'trash',
current: active ? 'page' : 'false',
bg: active ? '#E3F2F3' : 'transparent',
fg: active ? '#0A5F67' : '#344054',
fw: active ? '600' : '500',
countColor: active ? '#0A5F67' : '#171C26',
unread: unread,
hasUnread: unread > 0,
pick: () => {
const first = all.find((x) => x.box === b.id);
this.setState({ box: b.id, sel: first ? first.id : null });
},
};
});
const list = all.filter((x) => x.box === st.box);
const items = list.map((x) => {
const selected = x.id === st.sel;
const unread = isUnread(x);
const starred = starOf(x.id);
return {
who: x.who,
subject: x.subject,
time: x.time,
hasAtt: x.atts.length > 0,
bg: selected ? '#E3F2F3' : '#FFFFFF',
fw: unread ? '650' : '450',
whoColor: unread ? '#171C26' : '#344054',
subFw: unread ? '600' : '400',
timeFw: unread ? '600' : '400',
timeColor: unread ? '#171C26' : '#667085',
starLabel: starred ? '중요 표시 해제' : '중요 표시',
starredStr: starred ? 'true' : 'false',
starColor: starred ? '#B7791F' : '#98A2B3',
starFill: starred ? '#B7791F' : 'none',
pick: () => this.setState({ sel: x.id, read: { ...st.read, [x.id]: true } }),
toggleStar: () => this.setState({ star: { ...st.star, [x.id]: !starred } }),
};
});
const box = BOXES.find((b) => b.id === st.box) || BOXES[0];
const unreadInBox = list.filter(isUnread).length;
const cur = list.find((x) => x.id === st.sel) || null;
const curStarred = cur ? starOf(cur.id) : false;
const empty = { subject: '', fromName: '', fromEmail: '', to: '', cc: '', date: '', body: '', atts: [], remote: false };
const base = cur || empty;
const m = {
subject: base.subject,
fromName: base.fromName,
fromEmail: base.fromEmail,
to: base.to,
cc: base.cc,
hasCc: !!base.cc,
date: base.date,
body: base.body,
remote: base.remote,
initial: base.fromName ? base.fromName.replace('(주)', '').slice(0, 1) : '',
atts: base.atts.map((a) => ({ name: a.name, size: a.size, label: a.name + ' 다운로드' })),
attCount: base.atts.length,
hasAtt: base.atts.length > 0,
starLabel: curStarred ? '중요 표시 해제' : '중요 표시',
starredStr: curStarred ? 'true' : 'false',
starColor: curStarred ? '#B7791F' : '#98A2B3',
starFill: curStarred ? '#B7791F' : 'none',
};
return {
boxes: boxes,
items: items,
boxName: box.name,
countText: unreadInBox > 0 ? `전체 ${list.length} · 안 읽음 ${unreadInBox}` : `전체 ${list.length}`,
hasItems: items.length > 0,
noItems: items.length === 0,
emptyText: box.name + '이 비어 있습니다',
emptyHint: box.emptyHint,
hasSel: !!cur,
noSel: !cur,
m: m,
deleteLabel: st.box === 'trash' ? '영구 삭제' : '삭제',
toggleStarCur: () => {
if (!cur) return;
this.setState({ star: { ...st.star, [cur.id]: !curStarred } });
},
deleteCur: () => {
if (!cur) return;
const idx = list.findIndex((x) => x.id === cur.id);
const rest = list.filter((x) => x.id !== cur.id);
const next = rest[idx] || rest[idx - 1] || null;
if (st.box === 'trash') {
this.setState({ gone: { ...st.gone, [cur.id]: true }, sel: next ? next.id : null });
} else {
this.setState({ moved: { ...st.moved, [cur.id]: 'trash' }, sel: next ? next.id : null });
}
},
};
}
}
</script>
</body>
</html>
+300
View File
@@ -0,0 +1,300 @@
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>메일함 · 목록 보기</title>
<script src="./support.js"></script>
</head>
<body>
<x-dc>
<helmet>
<style>
@font-face{font-family:'Pretendard';src:url('/_blob/16067b9aa5137abf808e3dbef30c42e0') format('woff2-variations'),url('/_blob/16067b9aa5137abf808e3dbef30c42e0') format('woff2');font-weight:45 920;font-style:normal;font-display:swap}
body{margin:0}
a{color:#0E7C86}a:hover{color:#0A5F67}
:focus-visible{outline:2px solid #0E7C86;outline-offset:2px}
.ml-grid{display:grid;grid-template-columns:32px 168px minmax(0,1fr) 84px 64px;column-gap:12px;align-items:center}
.ml-inner{min-width:400px}
.ml-row{position:relative}
.ml-row:hover{background:#F3F8F8}
.ml-row:focus-within{background:#E3F2F3}
.ml-link::after{content:"";position:absolute;inset:0}
.ml-star{position:relative;z-index:1}
.sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
@media (max-width:1099px){.ml-grid{grid-template-columns:32px 168px minmax(0,1fr) 84px}.ml-size{display:none}}
@media (max-width:599px){.ml-grid{grid-template-columns:32px 112px minmax(0,1fr) 72px}}
</style>
</helmet>
<div style="min-height: 100vh; display: flex; background: #FFFFFF; color: #171C26; font-family: Pretendard, -apple-system, 'Apple SD Gothic Neo', 'Malgun Gothic', system-ui, sans-serif; font-size: 13px; line-height: 1.5">
<nav aria-label="사내 시스템" style="flex: 0 0 72px; box-sizing: border-box; display: flex; flex-direction: column; align-items: center; gap: 14px; padding: 10px 0 12px; background: #0B3640">
<a href="Main.dc.html" style="display: inline-flex; align-items: center; justify-content: center; width: 60px; min-height: 44px; border-radius: 10px; text-decoration: none">
<img src="/_blob/2b9ef4349333debc969432c859eaf1bf" alt="ARA SYSTEM" width="48" height="17" style="display: block; width: 48px; height: auto">
</a>
<ul style="list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px">
<li>
<a href="MainList.dc.html" aria-current="page" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; background: #155A64; color: #FFFFFF; font-size: 11px; font-weight: 600; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path><polyline points="22,6 12,13 2,6"></polyline></svg>
<span>메일</span>
</a>
</li>
<li>
<a href="#" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; color: #A3C2C6; font-size: 11px; font-weight: 500; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg>
<span>캘린더</span>
</a>
</li>
<li>
<a href="#" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; color: #A3C2C6; font-size: 11px; font-weight: 500; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
<span>파일</span>
</a>
</li>
<li>
<a href="#" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; color: #A3C2C6; font-size: 11px; font-weight: 500; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="7" width="20" height="14" rx="2" ry="2"></rect><path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"></path></svg>
<span>ERP</span>
</a>
</li>
<li>
<a href="#" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; color: #A3C2C6; font-size: 11px; font-weight: 500; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg>
<span>주소록</span>
</a>
</li>
</ul>
<a href="#" style="margin-top: auto; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; color: #A3C2C6; font-size: 11px; font-weight: 500; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
<span>설정</span>
</a>
</nav>
<div style="flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column">
<header style="display: flex; align-items: center; flex-wrap: wrap; gap: 8px 16px; box-sizing: border-box; min-height: 56px; padding: 6px 12px 6px 20px; border-bottom: 1px solid #E6E8EC">
<h1 style="margin: 0; font-size: 16px; font-weight: 700; letter-spacing: -0.01em">메일</h1>
<div style="flex: 1 1 280px; max-width: 520px; display: flex; align-items: center; gap: 8px; box-sizing: border-box; height: 40px; padding: 0 12px; border: 1px solid #8A94A3; border-radius: 8px; background: #FFFFFF">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#667085" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="search" aria-label="통합 검색" placeholder="통합 검색" style="flex: 1 1 auto; min-width: 0; border: 0; background: transparent; font: inherit; color: #171C26">
</div>
<div style="margin-left: auto; display: flex; align-items: center; gap: 10px">
<span aria-hidden="true" style="display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 50%; background: #E3F2F3; color: #0A5F67; font-size: 13px; font-weight: 600">김</span>
<div style="display: flex; flex-direction: column; line-height: 1.3">
<span style="font-weight: 600">김아라</span>
<span style="font-size: 12px; color: #667085">ara.kim@arasystem.it.kr</span>
</div>
<a href="Login.dc.html" style="display: inline-flex; align-items: center; gap: 6px; min-height: 44px; padding: 0 10px; border-radius: 8px; color: #4A5363; font-weight: 500; text-decoration: none">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line></svg>
<span>로그아웃</span>
</a>
</div>
</header>
<div style="flex: 1 1 auto; display: flex; flex-wrap: wrap; align-items: stretch">
<nav aria-label="메일함" style="flex: 1 1 208px; box-sizing: border-box; display: flex; flex-direction: column; gap: 14px; padding: 14px 10px; background: #F8F9FB; border-right: 1px solid #E6E8EC">
<a href="Compose.dc.html" style="display: flex; align-items: center; justify-content: center; gap: 8px; min-height: 44px; border-radius: 8px; background: #0E7C86; color: #FFFFFF; font-size: 13.5px; font-weight: 600; text-decoration: none">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20h9"></path><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path></svg>
<span>메일 쓰기</span>
</a>
<ul style="list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1px">
<sc-for list="{{ boxes }}" as="b" hint-placeholder-count="5">
<li>
<button type="button" onClick="{{ b.pick }}" aria-current="{{ b.current }}" style="width: 100%; display: flex; align-items: center; gap: 10px; min-height: 40px; box-sizing: border-box; padding: 0 10px; border: 0; border-radius: 7px; background: {{ b.bg }}; color: {{ b.fg }}; font: inherit; font-weight: {{ b.fw }}; text-align: left; cursor: pointer">
<sc-if value="{{ b.isInbox }}" hint-placeholder-val="{{ false }}"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="22 12 16 12 14 15 10 15 8 12 2 12"></polyline><path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"></path></svg></sc-if>
<sc-if value="{{ b.isSent }}" hint-placeholder-val="{{ false }}"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg></sc-if>
<sc-if value="{{ b.isDrafts }}" hint-placeholder-val="{{ false }}"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line></svg></sc-if>
<sc-if value="{{ b.isJunk }}" hint-placeholder-val="{{ false }}"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polygon points="7.86 2 16.14 2 22 7.86 22 16.14 16.14 22 7.86 22 2 16.14 2 7.86 7.86 2"></polygon><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg></sc-if>
<sc-if value="{{ b.isTrash }}" hint-placeholder-val="{{ false }}"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg></sc-if>
<span style="flex: 1 1 auto">{{ b.name }}</span>
<sc-if value="{{ b.hasUnread }}" hint-placeholder-val="{{ false }}"><span style="font-size: 12px; font-weight: 600; color: {{ b.countColor }}; font-variant-numeric: tabular-nums">{{ b.unread }}</span></sc-if>
</button>
</li>
</sc-for>
</ul>
<div style="margin-top: auto; display: flex; flex-direction: column; gap: 6px; padding: 12px 10px 2px; border-top: 1px solid #E6E8EC">
<div style="display: flex; justify-content: space-between; gap: 8px; font-size: 12px; color: #667085">
<span>메일함 사용량</span>
<span style="font-variant-numeric: tabular-nums">0.9 / 5 GB</span>
</div>
<div style="height: 4px; border-radius: 2px; background: #E4E7EC">
<div style="width: 18%; height: 4px; border-radius: 2px; background: #0E7C86"></div>
</div>
</div>
</nav>
<main style="flex: 999 1 560px; min-width: 0; display: flex; flex-direction: column">
<section aria-labelledby="list-title" style="display: flex; flex-direction: column">
<div style="display: flex; align-items: center; gap: 8px; min-height: 52px; box-sizing: border-box; padding: 4px 8px 4px 20px; border-bottom: 1px solid #E6E8EC">
<h2 id="list-title" style="margin: 0; font-size: 15px; font-weight: 700; letter-spacing: -0.01em">{{ boxName }}</h2>
<span style="font-size: 12px; color: #667085; font-variant-numeric: tabular-nums">{{ countText }}</span>
<div style="margin-left: auto; display: inline-flex; gap: 2px; padding: 2px; border: 1px solid #D0D5DD; border-radius: 8px; background: #FFFFFF">
<a href="Main.dc.html" aria-label="분할 보기" style="display: inline-flex; align-items: center; gap: 4px; min-height: 30px; padding: 0 8px; border-radius: 6px; color: #344054; font-size: 12px; font-weight: 500; text-decoration: none">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="2" ry="2"></rect><line x1="10" y1="4" x2="10" y2="20"></line></svg>
<span>분할</span>
</a>
<button type="button" aria-pressed="true" aria-label="목록 보기" style="display: inline-flex; align-items: center; gap: 4px; min-height: 30px; padding: 0 8px; border: 0; border-radius: 6px; background: #E3F2F3; color: #0A5F67; font: inherit; font-size: 12px; font-weight: 600; cursor: pointer">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="9" y1="6" x2="20" y2="6"></line><line x1="9" y1="12" x2="20" y2="12"></line><line x1="9" y1="18" x2="20" y2="18"></line><line x1="4" y1="6" x2="4.01" y2="6"></line><line x1="4" y1="12" x2="4.01" y2="12"></line><line x1="4" y1="18" x2="4.01" y2="18"></line></svg>
<span>목록</span>
</button>
</div>
<button type="button" aria-label="새로고침" style="width: 40px; height: 44px; display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: 8px; background: transparent; color: #4A5363; cursor: pointer">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></svg>
</button>
</div>
<sc-if value="{{ hasItems }}" hint-placeholder-val="{{ true }}">
<div style="overflow-x: auto">
<div class="ml-inner">
<div aria-hidden="true" class="ml-grid" style="height: 32px; box-sizing: border-box; padding: 0 8px 0 20px; background: #FBFBFC; border-bottom: 1px solid #E6E8EC; font-size: 12px; font-weight: 600; color: #667085">
<span style="display: flex; align-items: center">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#667085" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon></svg>
</span>
<span>{{ whoHeader }}</span>
<span>제목</span>
<span style="text-align: right">날짜</span>
<span class="ml-size" style="text-align: right">크기</span>
</div>
<ul aria-label="{{ listLabel }}" style="list-style: none; margin: 0; padding: 0">
<sc-for list="{{ items }}" as="item" hint-placeholder-count="15">
<li class="ml-row ml-grid" style="height: 38px; box-sizing: border-box; padding: 0 8px 0 20px; border-bottom: 1px solid #EEF0F3">
<button class="ml-star" type="button" onClick="{{ item.toggleStar }}" aria-label="{{ item.starLabel }}" aria-pressed="{{ item.starredStr }}" style="width: 32px; height: 37px; display: flex; align-items: center; justify-content: flex-start; padding: 0; border: 0; border-radius: 4px; background: transparent; color: {{ item.starColor }}; cursor: pointer">
<svg width="14" height="14" viewBox="0 0 24 24" fill="{{ item.starFill }}" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon></svg>
</button>
<span style="min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: {{ item.fw }}; color: {{ item.whoColor }}">
<sc-if value="{{ item.unread }}" hint-placeholder-val="{{ false }}"><span class="sr">안 읽음, </span></sc-if>
<span class="sr">{{ whoHeader }} </span>{{ item.who }}
</span>
<span style="min-width: 0; display: flex; align-items: center; gap: 6px">
<a class="ml-link" href="MainListRead.dc.html" style="min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: {{ item.subFw }}; color: #171C26; text-decoration: none">{{ item.subject }}</a>
<sc-if value="{{ item.hasAtt }}" hint-placeholder-val="{{ false }}"><svg role="img" aria-label="첨부 있음" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#667085" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="flex: 0 0 auto"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"></path></svg></sc-if>
</span>
<span style="text-align: right; white-space: nowrap; font-size: 12px; font-weight: {{ item.timeFw }}; color: {{ item.timeColor }}; font-variant-numeric: tabular-nums"><span class="sr">날짜 </span>{{ item.date }}</span>
<span class="ml-size" style="text-align: right; white-space: nowrap; font-size: 12px; color: #667085; font-variant-numeric: tabular-nums"><span class="sr">크기 </span>{{ item.size }}</span>
</li>
</sc-for>
</ul>
</div>
</div>
<div style="display: flex; justify-content: center; padding: 10px 20px 20px">
<button type="button" style="min-height: 40px; padding: 0 24px; border: 0; border-radius: 8px; background: transparent; color: #0E7C86; font: inherit; font-weight: 600; cursor: pointer">메일 더 보기</button>
</div>
</sc-if>
<sc-if value="{{ noItems }}" hint-placeholder-val="{{ false }}">
<div style="display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 96px 24px; text-align: center">
<svg width="36" height="36" viewBox="0 0 24 24" fill="none" stroke="#98A2B3" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="22 12 16 12 14 15 10 15 8 12 2 12"></polyline><path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"></path></svg>
<p style="margin: 6px 0 0; font-size: 14px; font-weight: 600">{{ emptyText }}</p>
<p style="margin: 0; font-size: 12.5px; color: #667085">{{ emptyHint }}</p>
</div>
</sc-if>
</section>
</main>
</div>
</div>
</div>
</x-dc>
<script type="text/x-dc" data-dc-script data-props='{"$preview":{"width":1440,"height":900}}'>
class Component extends DCLogic {
state = { box: 'inbox', star: { m3: true } };
messages() {
return [
{ id: 'm1', box: 'inbox', who: '이도윤', subject: 'Re: 메일 서버 이전 일정 공유', date: '오전 10:42', size: '62 KB', unread: true, att: true },
{ id: 'm2', box: 'inbox', who: '인사팀', subject: '[인사팀] 11월 연차 사용 계획 제출 안내', date: '오전 9:15', size: '8 KB', unread: true, att: false },
{ id: 'm3', box: 'inbox', who: '박준호', subject: '주간 인프라 점검 결과 (10/20~10/24)', date: '10.25', size: '318 KB', unread: false, att: true },
{ id: 'm4', box: 'inbox', who: '(주)한빛솔루션', subject: '서버 증설 견적서 송부드립니다', date: '10.25', size: '190 KB', unread: true, att: true },
{ id: 'm5', box: 'inbox', who: '보안팀', subject: '[공지] 하반기 정보보안 교육 이수 요청', date: '10.24', size: '9 KB', unread: false, att: false },
{ id: 'm6', box: 'inbox', who: '최서연', subject: '10월 경비 정산 마감 안내', date: '10.23', size: '7 KB', unread: false, att: false },
{ id: 'm8', box: 'inbox', who: '정하늘', subject: '신규 입사자 메일 계정 생성 요청 (11/2 입사)', date: '10.23', size: '11 KB', unread: false, att: false },
{ id: 'm9', box: 'inbox', who: '총무팀', subject: '[총무팀] 사무실 공조 설비 점검 일정 안내', date: '10.23', size: '6 KB', unread: false, att: false },
{ id: 'm7', box: 'inbox', who: '이도윤', subject: '메일 서버 이전 일정 공유', date: '10.22', size: '15 KB', unread: false, att: false },
{ id: 'm10', box: 'inbox', who: '박준호', subject: 'Re: 백업 스토리지 증설 검토', date: '10.22', size: '96 KB', unread: false, att: true },
{ id: 'm11', box: 'inbox', who: '김태윤', subject: '회의실 예약 시스템 저장 오류 문의', date: '10.21', size: '9 KB', unread: false, att: false },
{ id: 'm13', box: 'inbox', who: '(주)한빛솔루션', subject: '유지보수 계약 갱신 안내', date: '10.20', size: '22 KB', unread: false, att: false },
{ id: 'm12', box: 'inbox', who: '인사팀', subject: '[인사팀] 10월 사내 교육 일정 공유', date: '10.20', size: '140 KB', unread: false, att: true },
{ id: 'm14', box: 'inbox', who: '보안팀', subject: '[보안] VPN 접속 프로그램 업데이트 안내', date: '10.19', size: '12 KB', unread: false, att: false },
{ id: 'm15', box: 'inbox', who: '이서준', subject: '사내 위키 메일 서버 문서 정리 요청', date: '10.16', size: '10 KB', unread: false, att: false },
{ id: 's1', box: 'sent', who: '박준호', subject: 'Re: 주간 인프라 점검 결과 (10/20~10/24)', date: '10.25', size: '6 KB', unread: false, att: false },
{ id: 's3', box: 'sent', who: '정하늘', subject: 'Re: 신규 입사자 메일 계정 생성 요청 (11/2 입사)', date: '10.23', size: '5 KB', unread: false, att: false },
{ id: 's2', box: 'sent', who: '이도윤', subject: 'Re: 메일 서버 이전 일정 공유', date: '10.23', size: '7 KB', unread: false, att: false },
{ id: 't1', box: 'trash', who: '호스팅 안내', subject: '(광고) 서버 호스팅 연말 할인 안내', date: '10.21', size: '24 KB', unread: false, att: false },
];
}
renderVals() {
const st = this.state;
const BOXES = [
{ id: 'inbox', name: '받은편지함', emptyHint: '새 메일이 오면 여기에 표시됩니다.' },
{ id: 'sent', name: '보낸편지함', emptyHint: '보낸 메일이 여기에 표시됩니다.' },
{ id: 'drafts', name: '임시보관함', emptyHint: '임시 저장한 메일이 여기에 표시됩니다.' },
{ id: 'junk', name: '스팸메일함', emptyHint: '스팸으로 분류된 메일이 여기에 표시됩니다.' },
{ id: 'trash', name: '휴지통', emptyHint: '삭제한 메일이 여기에 표시됩니다.' },
];
const all = this.messages();
const starOf = (id) => !!st.star[id];
const boxes = BOXES.map((b) => {
const active = b.id === st.box;
const unread = all.filter((x) => x.box === b.id && x.unread).length;
return {
name: b.name,
isInbox: b.id === 'inbox',
isSent: b.id === 'sent',
isDrafts: b.id === 'drafts',
isJunk: b.id === 'junk',
isTrash: b.id === 'trash',
current: active ? 'page' : 'false',
bg: active ? '#E3F2F3' : 'transparent',
fg: active ? '#0A5F67' : '#344054',
fw: active ? '600' : '500',
countColor: active ? '#0A5F67' : '#171C26',
unread: unread,
hasUnread: unread > 0,
pick: () => this.setState({ box: b.id }),
};
});
const list = all.filter((x) => x.box === st.box);
const items = list.map((x) => {
const starred = starOf(x.id);
return {
who: x.who,
subject: x.subject,
date: x.date,
size: x.size,
unread: x.unread,
hasAtt: x.att,
fw: x.unread ? '650' : '450',
whoColor: x.unread ? '#171C26' : '#344054',
subFw: x.unread ? '650' : '400',
timeFw: x.unread ? '600' : '400',
timeColor: x.unread ? '#171C26' : '#667085',
starLabel: starred ? '중요 표시 해제' : '중요 표시',
starredStr: starred ? 'true' : 'false',
starColor: starred ? '#B7791F' : '#98A2B3',
starFill: starred ? '#B7791F' : 'none',
toggleStar: () => this.setState({ star: { ...st.star, [x.id]: !starred } }),
};
});
const box = BOXES.find((b) => b.id === st.box) || BOXES[0];
const unreadInBox = list.filter((x) => x.unread).length;
return {
boxes: boxes,
items: items,
boxName: box.name,
listLabel: box.name + ' 메일 목록',
whoHeader: st.box === 'sent' ? '받는 사람' : '보낸 사람',
countText: unreadInBox > 0 ? `전체 ${list.length} · 안 읽음 ${unreadInBox}` : `전체 ${list.length}`,
hasItems: items.length > 0,
noItems: items.length === 0,
emptyText: box.name + '이 비어 있습니다',
emptyHint: box.emptyHint,
};
}
}
</script>
</body>
</html>
+373
View File
@@ -0,0 +1,373 @@
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>목록 보기 · 메일 읽기</title>
<script src="./support.js"></script>
</head>
<body>
<x-dc>
<helmet>
<style>
@font-face{font-family:'Pretendard';src:url('/_blob/16067b9aa5137abf808e3dbef30c42e0') format('woff2-variations'),url('/_blob/16067b9aa5137abf808e3dbef30c42e0') format('woff2');font-weight:45 920;font-style:normal;font-display:swap}
body{margin:0}
a{color:#0E7C86}a:hover{color:#0A5F67}
:focus-visible{outline:2px solid #0E7C86;outline-offset:2px}
</style>
</helmet>
<div style="min-height: 100vh; display: flex; background: #FFFFFF; color: #171C26; font-family: Pretendard, -apple-system, 'Apple SD Gothic Neo', 'Malgun Gothic', system-ui, sans-serif; font-size: 13px; line-height: 1.5">
<nav aria-label="사내 시스템" style="flex: 0 0 72px; box-sizing: border-box; display: flex; flex-direction: column; align-items: center; gap: 14px; padding: 10px 0 12px; background: #0B3640">
<a href="Main.dc.html" style="display: inline-flex; align-items: center; justify-content: center; width: 60px; min-height: 44px; border-radius: 10px; text-decoration: none">
<img src="/_blob/2b9ef4349333debc969432c859eaf1bf" alt="ARA SYSTEM" width="48" height="17" style="display: block; width: 48px; height: auto">
</a>
<ul style="list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px">
<li>
<a href="MainList.dc.html" aria-current="page" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; background: #155A64; color: #FFFFFF; font-size: 11px; font-weight: 600; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path><polyline points="22,6 12,13 2,6"></polyline></svg>
<span>메일</span>
</a>
</li>
<li>
<a href="#" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; color: #A3C2C6; font-size: 11px; font-weight: 500; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg>
<span>캘린더</span>
</a>
</li>
<li>
<a href="#" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; color: #A3C2C6; font-size: 11px; font-weight: 500; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"></path></svg>
<span>파일</span>
</a>
</li>
<li>
<a href="#" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; color: #A3C2C6; font-size: 11px; font-weight: 500; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="7" width="20" height="14" rx="2" ry="2"></rect><path d="M16 21V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16"></path></svg>
<span>ERP</span>
</a>
</li>
<li>
<a href="#" style="display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; color: #A3C2C6; font-size: 11px; font-weight: 500; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg>
<span>주소록</span>
</a>
</li>
</ul>
<a href="#" style="margin-top: auto; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px; width: 60px; min-height: 56px; border-radius: 10px; color: #A3C2C6; font-size: 11px; font-weight: 500; text-decoration: none">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>
<span>설정</span>
</a>
</nav>
<div style="flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column">
<header style="display: flex; align-items: center; flex-wrap: wrap; gap: 8px 16px; box-sizing: border-box; min-height: 56px; padding: 6px 12px 6px 20px; border-bottom: 1px solid #E6E8EC">
<span style="font-size: 16px; font-weight: 700; letter-spacing: -0.01em">메일</span>
<div style="flex: 1 1 280px; max-width: 520px; display: flex; align-items: center; gap: 8px; box-sizing: border-box; height: 40px; padding: 0 12px; border: 1px solid #8A94A3; border-radius: 8px; background: #FFFFFF">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#667085" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
<input type="search" aria-label="통합 검색" placeholder="통합 검색" style="flex: 1 1 auto; min-width: 0; border: 0; background: transparent; font: inherit; color: #171C26">
</div>
<div style="margin-left: auto; display: flex; align-items: center; gap: 10px">
<span aria-hidden="true" style="display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 50%; background: #E3F2F3; color: #0A5F67; font-size: 13px; font-weight: 600">김</span>
<div style="display: flex; flex-direction: column; line-height: 1.3">
<span style="font-weight: 600">김아라</span>
<span style="font-size: 12px; color: #667085">ara.kim@arasystem.it.kr</span>
</div>
<a href="Login.dc.html" style="display: inline-flex; align-items: center; gap: 6px; min-height: 44px; padding: 0 10px; border-radius: 8px; color: #4A5363; font-weight: 500; text-decoration: none">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line></svg>
<span>로그아웃</span>
</a>
</div>
</header>
<div style="flex: 1 1 auto; display: flex; flex-wrap: wrap; align-items: stretch">
<nav aria-label="메일함" style="flex: 1 1 208px; box-sizing: border-box; display: flex; flex-direction: column; gap: 14px; padding: 14px 10px; background: #F8F9FB; border-right: 1px solid #E6E8EC">
<a href="Compose.dc.html" style="display: flex; align-items: center; justify-content: center; gap: 8px; min-height: 44px; border-radius: 8px; background: #0E7C86; color: #FFFFFF; font-size: 13.5px; font-weight: 600; text-decoration: none">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20h9"></path><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path></svg>
<span>메일 쓰기</span>
</a>
<ul style="list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1px">
<li><a href="MainList.dc.html" aria-current="page" style="display: flex; align-items: center; gap: 10px; min-height: 40px; box-sizing: border-box; padding: 0 10px; border-radius: 7px; background: #E3F2F3; color: #0A5F67; font-weight: 600; text-decoration: none">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="22 12 16 12 14 15 10 15 8 12 2 12"></polyline><path d="M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"></path></svg>
<span style="flex: 1 1 auto">받은편지함</span>
<span style="font-size: 12px; font-weight: 600; color: #0A5F67; font-variant-numeric: tabular-nums">2</span>
</a></li>
<li><a href="MainList.dc.html" style="display: flex; align-items: center; gap: 10px; min-height: 40px; box-sizing: border-box; padding: 0 10px; border-radius: 7px; color: #344054; font-weight: 500; text-decoration: none">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
<span>보낸편지함</span>
</a></li>
<li><a href="MainList.dc.html" style="display: flex; align-items: center; gap: 10px; min-height: 40px; box-sizing: border-box; padding: 0 10px; border-radius: 7px; color: #344054; font-weight: 500; text-decoration: none">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line></svg>
<span>임시보관함</span>
</a></li>
<li><a href="MainList.dc.html" style="display: flex; align-items: center; gap: 10px; min-height: 40px; box-sizing: border-box; padding: 0 10px; border-radius: 7px; color: #344054; font-weight: 500; text-decoration: none">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polygon points="7.86 2 16.14 2 22 7.86 22 16.14 16.14 22 7.86 22 2 16.14 2 7.86 7.86 2"></polygon><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>
<span>스팸메일함</span>
</a></li>
<li><a href="MainList.dc.html" style="display: flex; align-items: center; gap: 10px; min-height: 40px; box-sizing: border-box; padding: 0 10px; border-radius: 7px; color: #344054; font-weight: 500; text-decoration: none">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>
<span>휴지통</span>
</a></li>
</ul>
<div style="margin-top: auto; display: flex; flex-direction: column; gap: 6px; padding: 12px 10px 2px; border-top: 1px solid #E6E8EC">
<div style="display: flex; justify-content: space-between; gap: 8px; font-size: 12px; color: #667085">
<span>메일함 사용량</span>
<span style="font-variant-numeric: tabular-nums">0.9 / 5 GB</span>
</div>
<div style="height: 4px; border-radius: 2px; background: #E4E7EC">
<div style="width: 18%; height: 4px; border-radius: 2px; background: #0E7C86"></div>
</div>
</div>
</nav>
<main style="flex: 999 1 560px; min-width: 0; display: flex; flex-direction: column">
<div style="border-bottom: 1px solid #E6E8EC"><div style="display: flex; flex-wrap: wrap; align-items: center; gap: 2px; min-height: 52px; max-width: 800px; box-sizing: border-box; padding: 4px 32px 4px 22px">
<a href="MainList.dc.html" style="display: inline-flex; align-items: center; gap: 6px; min-height: 44px; box-sizing: border-box; padding: 0 10px; border-radius: 8px; color: #0A5F67; font-weight: 600; text-decoration: none">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="19" y1="12" x2="5" y2="12"></line><polyline points="12 19 5 12 12 5"></polyline></svg>
<span>목록으로</span>
</a>
<span aria-hidden="true" style="width: 1px; height: 20px; margin: 0 6px; background: #E6E8EC"></span>
<button type="button" onClick="{{ prev }}" disabled="{{ isFirst }}" aria-label="이전 메일" style="width: 40px; height: 44px; display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: 8px; background: transparent; color: {{ prevColor }}; cursor: pointer">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="15 18 9 12 15 6"></polyline></svg>
</button>
<span style="min-width: 48px; text-align: center; font-size: 12px; color: #667085; font-variant-numeric: tabular-nums">{{ position }}</span>
<button type="button" onClick="{{ next }}" disabled="{{ isLast }}" aria-label="다음 메일" style="width: 40px; height: 44px; display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: 8px; background: transparent; color: {{ nextColor }}; cursor: pointer">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="9 18 15 12 9 6"></polyline></svg>
</button>
<span aria-hidden="true" style="width: 1px; height: 20px; margin: 0 6px; background: #E6E8EC"></span>
<a href="Compose.dc.html" style="display: inline-flex; align-items: center; gap: 6px; min-height: 44px; box-sizing: border-box; padding: 0 10px; border-radius: 8px; color: #344054; font-weight: 500; text-decoration: none">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="9 14 4 9 9 4"></polyline><path d="M20 20v-7a4 4 0 0 0-4-4H4"></path></svg>
<span>답장</span>
</a>
<a href="Compose.dc.html" style="display: inline-flex; align-items: center; gap: 6px; min-height: 44px; box-sizing: border-box; padding: 0 10px; border-radius: 8px; color: #344054; font-weight: 500; text-decoration: none">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="7 17 2 12 7 7"></polyline><polyline points="12 17 7 12 12 7"></polyline><path d="M22 18v-2a4 4 0 0 0-4-4H7"></path></svg>
<span>전체 답장</span>
</a>
<a href="Compose.dc.html" style="display: inline-flex; align-items: center; gap: 6px; min-height: 44px; box-sizing: border-box; padding: 0 10px; border-radius: 8px; color: #344054; font-weight: 500; text-decoration: none">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="15 14 20 9 15 4"></polyline><path d="M4 20v-7a4 4 0 0 1 4-4h12"></path></svg>
<span>전달</span>
</a>
<a href="MainList.dc.html" style="display: inline-flex; align-items: center; gap: 6px; min-height: 44px; box-sizing: border-box; padding: 0 10px; border-radius: 8px; color: #B42318; font-weight: 500; text-decoration: none">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>
<span>삭제</span>
</a>
</div>
</div><article aria-labelledby="mail-subject" style="display: flex; flex-direction: column; gap: 18px; max-width: 800px; box-sizing: border-box; padding: 20px 32px 36px">
<div style="display: flex; align-items: flex-start; gap: 8px">
<h1 id="mail-subject" style="flex: 1 1 auto; margin: 4px 0 0; font-size: 20px; line-height: 1.4; font-weight: 650; letter-spacing: -0.015em">{{ m.subject }}</h1>
<button type="button" onClick="{{ toggleStar }}" aria-label="{{ m.starLabel }}" aria-pressed="{{ m.starredStr }}" style="flex: 0 0 auto; width: 44px; height: 44px; display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: 8px; background: transparent; color: {{ m.starColor }}; cursor: pointer">
<svg width="18" height="18" viewBox="0 0 24 24" fill="{{ m.starFill }}" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"></polygon></svg>
</button>
</div>
<div style="display: flex; align-items: flex-start; gap: 12px">
<span aria-hidden="true" style="flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; border-radius: 50%; background: #E3F2F3; color: #0A5F67; font-size: 14px; font-weight: 600">{{ m.initial }}</span>
<div style="flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 1px">
<div style="display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 8px">
<span style="font-size: 13.5px; font-weight: 600">{{ m.fromName }}</span>
<span style="font-size: 12.5px; color: #667085">{{ m.fromEmail }}</span>
<span style="margin-left: auto; font-size: 12.5px; color: #667085; white-space: nowrap; font-variant-numeric: tabular-nums">{{ m.date }}</span>
</div>
<span style="font-size: 12.5px; color: #4A5363">받는 사람 {{ m.to }}</span>
<sc-if value="{{ m.hasCc }}" hint-placeholder-val="{{ false }}"><span style="font-size: 12.5px; color: #4A5363">참조 {{ m.cc }}</span></sc-if>
</div>
</div>
<sc-if value="{{ m.remote }}" hint-placeholder-val="{{ false }}">
<p style="display: flex; align-items: flex-start; gap: 8px; margin: 0; padding: 10px 12px; border-radius: 8px; background: #FFF7E6; color: #7A5200; font-size: 12.5px">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="flex: 0 0 auto; margin-top: 2px"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path></svg>
<span>외부 이미지를 차단했습니다. 개인정보 보호를 위해 보낸 사람 서버의 이미지는 불러오지 않습니다.</span>
</p>
</sc-if>
<div style="white-space: pre-line; font-size: 14px; line-height: 1.75; color: #1F2733; max-width: 680px">{{ m.body }}</div>
<sc-if value="{{ m.hasAtt }}" hint-placeholder-val="{{ true }}">
<section aria-labelledby="att-title" style="display: flex; flex-direction: column; gap: 8px; padding-top: 16px; border-top: 1px solid #EEF0F3">
<h2 id="att-title" style="margin: 0; font-size: 12.5px; font-weight: 600; color: #4A5363">첨부 파일 {{ m.attCount }}개</h2>
<ul style="list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(260px, 100%), 1fr)); gap: 8px">
<sc-for list="{{ m.atts }}" as="att" hint-placeholder-count="1">
<li style="display: flex; align-items: center; gap: 10px; padding: 4px 2px 4px 8px; border: 1px solid #E6E8EC; border-radius: 8px">
<span aria-hidden="true" style="flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 6px; background: #F2F4F7; color: #4A5363">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path><polyline points="13 2 13 9 20 9"></polyline></svg>
</span>
<div style="flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; line-height: 1.35">
<span style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500">{{ att.name }}</span>
<span style="font-size: 12px; color: #667085">{{ att.size }}</span>
</div>
<button type="button" aria-label="{{ att.label }}" style="flex: 0 0 auto; width: 44px; height: 44px; display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: 8px; background: transparent; color: #344054; cursor: pointer">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>
</button>
</li>
</sc-for>
</ul>
</section>
</sc-if>
</article>
</main>
</div>
</div>
</div>
</x-dc>
<script type="text/x-dc" data-dc-script data-props='{"$preview":{"width":1440,"height":900}}'>
class Component extends DCLogic {
state = { idx: 0, star: { m3: true } };
messages() {
return [
{
id: 'm1', fromName: '이도윤', fromEmail: 'doyun.lee@arasystem.it.kr',
to: '김아라 <ara.kim@arasystem.it.kr>', cc: '인프라팀 <infra@arasystem.it.kr>',
subject: 'Re: 메일 서버 이전 일정 공유', date: '2026. 10. 26. (월) 오전 10:42', remote: false,
body: '김아라님, 안녕하세요.\n\n말씀하신 대로 이번 주 금요일(10/30) 저녁 9시에 MX 레코드를 새 메일 서버로 전환하겠습니다.\n전환 하루 전에 DNS TTL을 300초로 낮춰 두고, 기존 서버는 2주 동안 함께 운영합니다.\n\n전환 체크리스트를 첨부했으니 확인 부탁드립니다.\n\n이도윤 드림',
atts: [{ name: '메일서버_전환_체크리스트.xlsx', size: '48 KB' }],
},
{
id: 'm2', fromName: '인사팀', fromEmail: 'hr@arasystem.it.kr',
to: '전체 구성원 <all@arasystem.it.kr>', cc: '',
subject: '[인사팀] 11월 연차 사용 계획 제출 안내', date: '2026. 10. 26. (월) 오전 9:15', remote: false,
body: '구성원 여러분, 안녕하세요. 인사팀입니다.\n\n11월 연차 사용 계획을 10월 30일(금)까지 사내 시스템으로 제출해 주세요.\n팀별 일정이 겹치지 않도록 팀장님과 미리 협의해 주시기 바랍니다.\n\n감사합니다.',
atts: [],
},
{
id: 'm3', fromName: '박준호', fromEmail: 'junho.park@arasystem.it.kr',
to: '인프라팀 <infra@arasystem.it.kr>', cc: '',
subject: '주간 인프라 점검 결과 (10/20~10/24)', date: '2026. 10. 25. (일) 오후 6:03', remote: false,
body: '인프라팀 여러분,\n\n이번 주 점검 결과를 공유드립니다.\n백업 작업이 한 차례 지연된 것 외에는 특이사항이 없습니다. 상세 내용은 첨부 보고서를 확인해 주세요.\n\n박준호',
atts: [{ name: '주간점검_1024.pdf', size: '312 KB' }],
},
{
id: 'm4', fromName: '(주)한빛솔루션 영업팀', fromEmail: 'sales@hanbit.example',
to: '김아라 <ara.kim@arasystem.it.kr>', cc: '',
subject: '서버 증설 견적서 송부드립니다', date: '2026. 10. 25. (일) 오후 2:30', remote: true,
body: '안녕하세요, (주)한빛솔루션 영업팀입니다.\n\n요청하신 서버 증설 견적서를 첨부하여 보내드립니다.\n검토 후 궁금하신 점은 회신 주시면 안내드리겠습니다.\n\n감사합니다.',
atts: [{ name: '견적서_서버증설.pdf', size: '184 KB' }],
},
{
id: 'm5', fromName: '보안팀', fromEmail: 'security@arasystem.it.kr',
to: '전체 구성원 <all@arasystem.it.kr>', cc: '',
subject: '[공지] 하반기 정보보안 교육 이수 요청', date: '2026. 10. 24. (토) 오전 11:00', remote: false,
body: '구성원 여러분, 보안팀입니다.\n\n하반기 정보보안 교육을 11월 13일(금)까지 이수해 주세요.\n교육은 사내 교육 시스템에서 들으실 수 있습니다.',
atts: [],
},
{
id: 'm6', fromName: '최서연', fromEmail: 'seoyeon.choi@arasystem.it.kr',
to: '전체 구성원 <all@arasystem.it.kr>', cc: '',
subject: '10월 경비 정산 마감 안내', date: '2026. 10. 23. (금) 오후 4:20', remote: false,
body: '안녕하세요, 재무팀 최서연입니다.\n\n10월분 경비는 11월 2일(월)까지 증빙과 함께 올려 주세요.\n마감 이후 접수분은 다음 달로 넘어갑니다.',
atts: [],
},
{
id: 'm8', fromName: '정하늘', fromEmail: 'haneul.jung@arasystem.it.kr',
to: '인프라팀 <infra@arasystem.it.kr>', cc: '',
subject: '신규 입사자 메일 계정 생성 요청 (11/2 입사)', date: '2026. 10. 23. (금) 오후 1:40', remote: false,
body: '인프라팀 담당자님, 인사팀 정하늘입니다.\n\n11월 2일(월) 입사 예정인 신규 입사자 2명의 메일 계정 생성을 요청드립니다.\n\n- 한지우 (개발팀)\n- 오민석 (영업팀)\n\n입사 전날까지 계정 정보를 전달해 주시면 감사하겠습니다.',
atts: [],
},
{
id: 'm9', fromName: '총무팀', fromEmail: 'ga@arasystem.it.kr',
to: '전체 구성원 <all@arasystem.it.kr>', cc: '',
subject: '[총무팀] 사무실 공조 설비 점검 일정 안내', date: '2026. 10. 23. (금) 오전 10:05', remote: false,
body: '구성원 여러분, 총무팀입니다.\n\n10월 28일(수) 오후 2시부터 4시까지 사무실 공조 설비 점검이 있습니다.\n점검 중에는 일부 구역의 냉난방이 잠시 멈출 수 있습니다.',
atts: [],
},
{
id: 'm7', fromName: '이도윤', fromEmail: 'doyun.lee@arasystem.it.kr',
to: '김아라 <ara.kim@arasystem.it.kr>', cc: '',
subject: '메일 서버 이전 일정 공유', date: '2026. 10. 22. (목) 오후 3:12', remote: false,
body: '김아라님,\n\n새 메일 서버 전환 일정 초안을 공유드립니다.\n금요일 저녁 전환을 기준으로 잡았고, 의견 주시면 반영하겠습니다.\n\n이도윤 드림',
atts: [],
},
{
id: 'm10', fromName: '박준호', fromEmail: 'junho.park@arasystem.it.kr',
to: '김아라 <ara.kim@arasystem.it.kr>', cc: '',
subject: 'Re: 백업 스토리지 증설 검토', date: '2026. 10. 22. (목) 오전 11:30', remote: false,
body: '김아라님,\n\n요청하신 백업 스토리지 증설 검토 자료를 첨부합니다.\n현재 사용 추세를 기준으로 두 가지 안을 정리했습니다.\n\n박준호',
atts: [{ name: '백업스토리지_증설검토.pdf', size: '88 KB' }],
},
{
id: 'm11', fromName: '김태윤', fromEmail: 'taeyun.kim@arasystem.it.kr',
to: '인프라팀 <infra@arasystem.it.kr>', cc: '',
subject: '회의실 예약 시스템 저장 오류 문의', date: '2026. 10. 21. (수) 오후 2:15', remote: false,
body: '안녕하세요, 영업팀 김태윤입니다.\n\n오늘 오전부터 회의실 예약 화면에서 저장 버튼을 눌러도 예약이 저장되지 않습니다.\n확인 부탁드립니다.',
atts: [],
},
{
id: 'm13', fromName: '(주)한빛솔루션', fromEmail: 'support@hanbit.example',
to: '김아라 <ara.kim@arasystem.it.kr>', cc: '',
subject: '유지보수 계약 갱신 안내', date: '2026. 10. 20. (화) 오후 4:45', remote: true,
body: '안녕하세요, (주)한빛솔루션입니다.\n\n현재 유지보수 계약이 12월 31일에 끝날 예정이라 갱신 절차를 안내드립니다.\n세부 조건은 담당 영업 대표가 따로 연락드리겠습니다.',
atts: [],
},
{
id: 'm12', fromName: '인사팀', fromEmail: 'hr@arasystem.it.kr',
to: '전체 구성원 <all@arasystem.it.kr>', cc: '',
subject: '[인사팀] 10월 사내 교육 일정 공유', date: '2026. 10. 20. (화) 오전 9:00', remote: false,
body: '구성원 여러분, 인사팀입니다.\n\n10월 사내 교육 일정을 첨부와 같이 공유드립니다.\n신청은 교육 시작 3일 전까지 해 주세요.',
atts: [{ name: '10월_사내교육_일정.pdf', size: '132 KB' }],
},
{
id: 'm14', fromName: '보안팀', fromEmail: 'security@arasystem.it.kr',
to: '전체 구성원 <all@arasystem.it.kr>', cc: '',
subject: '[보안] VPN 접속 프로그램 업데이트 안내', date: '2026. 10. 19. (월) 오전 10:00', remote: false,
body: '구성원 여러분, 보안팀입니다.\n\n이번 주부터 VPN 접속 프로그램 새 버전을 배포합니다.\n이전 버전은 11월 첫째 주부터 접속이 제한되니 미리 업데이트해 주세요.',
atts: [],
},
{
id: 'm15', fromName: '이서준', fromEmail: 'seojun.lee@arasystem.it.kr',
to: '김아라 <ara.kim@arasystem.it.kr>', cc: '',
subject: '사내 위키 메일 서버 문서 정리 요청', date: '2026. 10. 16. (금) 오후 5:20', remote: false,
body: '김아라님,\n\n사내 위키의 메일 서버 설정 문서가 예전 서버 기준으로 남아 있습니다.\n이번 이전 작업에 맞춰 정리를 부탁드립니다.\n\n이서준 드림',
atts: [],
},
];
}
renderVals() {
const st = this.state;
const list = this.messages();
const n = list.length;
const i = Math.min(Math.max(st.idx, 0), n - 1);
const cur = list[i];
const starred = !!st.star[cur.id];
return {
m: {
subject: cur.subject,
fromName: cur.fromName,
fromEmail: cur.fromEmail,
to: cur.to,
cc: cur.cc,
hasCc: !!cur.cc,
date: cur.date,
body: cur.body,
remote: cur.remote,
initial: cur.fromName.replace('(주)', '').slice(0, 1),
atts: cur.atts.map((a) => ({ name: a.name, size: a.size, label: a.name + ' 다운로드' })),
attCount: cur.atts.length,
hasAtt: cur.atts.length > 0,
starLabel: starred ? '중요 표시 해제' : '중요 표시',
starredStr: starred ? 'true' : 'false',
starColor: starred ? '#B7791F' : '#98A2B3',
starFill: starred ? '#B7791F' : 'none',
},
position: `${i + 1} / ${n}`,
isFirst: i === 0,
isLast: i === n - 1,
prevColor: i === 0 ? '#98A2B3' : '#344054',
nextColor: i === n - 1 ? '#98A2B3' : '#344054',
prev: () => {
if (i > 0) this.setState({ idx: i - 1 });
},
next: () => {
if (i < n - 1) this.setState({ idx: i + 1 });
},
toggleStar: () => this.setState({ star: { ...st.star, [cur.id]: !starred } }),
};
}
}
</script>
</body>
</html>
+43
View File
@@ -0,0 +1,43 @@
# 웹메일 디자인 원본 (1단계)
확정된 디자인 캔버스의 원본이다. 화면 구현의 기준으로 쓴다.
- 캔버스: https://claude.ai/artifact/WJvVsKvXepxn3p75CGJAr2 (확정본: 2026-10-26, 버전 11)
- 화면: `Login`(로그인) · `Main`(분할 보기: 메일함·목록·본문) · `MainList`(목록 보기: 게시판형 표) · `MainListRead`(목록 보기에서 연 메일)
· `Compose`(메일 쓰기) · `States`(오류·빈 상태·피드백 모음)
- 파일 형식은 캔버스 전용(`<x-dc>` 마크업 + `DCLogic` 클래스)이다. 그대로 쓰지 말고 **구조·간격·색·문구의 기준**으로만 본다.
## 캔버스 안의 자산 주소
캔버스는 자산을 `/_blob/<id>` 로 참조한다. 저장소에서는 아래 파일에 대응한다.
| 캔버스 주소 | 저장소 파일 | 용도 |
| --- | --- | --- |
| `/_blob/16067b9aa5137abf808e3dbef30c42e0` | Pretendard Variable (npm `pretendard` 패키지로 자체 호스팅) | 전체 글꼴 |
| `/_blob/2b9ef4349333debc969432c859eaf1bf` | `frontend/src/assets/brand/logo-ara-white.png` | 왼쪽 앱 레일 로고(가로 48px) |
| `/_blob/3a2f4115a8f90207faa7d3a51cd9aec2` | `frontend/src/assets/brand/logo-arasystem-white.png` | 로그인 브랜드 패널 로고(가로 240px) |
로고 원본은 `docs/brand/logo_arasystem_white.png` 다. 흰 로고라 어두운 브랜드 바탕(`#0B3640`) 위에만 둔다.
## 디자인 원칙 (사용자 확정)
- 안 읽음 점·개수 알약·코드 태그 같은 **뱃지를 쓰지 않는다** — 굵은 글자와 평범한 숫자로 구분한다.
- 왼쪽 **전역 앱 레일**(메일·캘린더·파일·ERP·주소록·설정)에 모듈을 계속 붙인다. 지금은 메일만 동작한다.
- 정보 밀도가 높은 데스크톱 화면 — 기본 13px. 목록에 본문 미리보기를 넣지 않는다.
- 분할 보기 행: 2줄(보낸 사람·시간 / 제목+첨부 클립), 약 50px
- 목록 보기 행: 1줄 표, 38px. 칸 = 별 32 · 보낸 사람 168 · 제목(나머지) · 날짜 84 · 크기 64 (px, 칸 사이 12).
폭 1100px 미만에서 크기 칸 숨김, 600px 미만에서 보낸 사람·날짜 축소, 더 좁으면 가로 스크롤.
캔버스는 파서 제약 때문에 grid 로 그렸지만 **구현은 실제 `<table>`** 로 한다.
- 별 버튼·"분할 | 목록" 전환은 밀도를 위해 32~36px 로 둔다(클릭 영역 44px 기준의 의도된 예외 — 사용자 확정).
- 보기 방식: 목록 머리의 "분할 | 목록" 전환. 선택은 사용자별로 기억한다.
- **콘텐츠 열(읽기·쓰기 공통)**: 최대 폭 800px, 좌우 여백 32px, 위 20px·아래 36px, 본문 글줄 최대 680px, 영역 안 왼쪽 정렬.
분할 보기 읽기·목록 보기 읽기·쓰기 세 곳이 같은 값을 쓰고, 툴바 첫 아이콘도 같은 32px 선에서 시작한다.
- 브랜드 컬러 "바다 청록": 바탕 `#0B3640`, 강조 `#0E7C86`. 파생 색은 각 화면 원본의 인라인 스타일을 따른다.
## 구현하며 정한 보완 (2026-10-26 — 캔버스에는 아직 없다)
- 쓰기 화면 첨부 안내문에 합계·개수 한도를 함께 적는다: "파일당 최대 20MB · 합계 25MB · 최대 20개 · 문서, 이미지, 압축 파일" (용량은 서버 값, 개수는 계약 상수).
- 답장·전달 초안이 한도(제목 255자·본문 100,000자)를 넘으면 인용을 줄이고 끝에 "(원문이 길어 이하 생략)"을 둔다.
- 찾지 못한 첨부(보관 시간이 지난 업로드·지워진 원본)는 실패로 표시하고, 전달 원본에는 "빼기"만 둔다(다시 올릴 수 없다).
- 없는 메일함·메일(주소창에 남은 옛 id 등)에는 "다시 시도" 대신 "받은편지함으로"·"목록으로"를 둔다.
"다시 시도"는 메일 서버 장애처럼 다시 해 보면 나아질 수 있는 오류에만 쓴다.
+180
View File
@@ -0,0 +1,180 @@
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<title>오류 · 빈 상태 · 피드백</title>
<script src="./support.js"></script>
</head>
<body>
<x-dc>
<helmet>
<style>
@font-face{font-family:'Pretendard';src:url('/_blob/16067b9aa5137abf808e3dbef30c42e0') format('woff2-variations'),url('/_blob/16067b9aa5137abf808e3dbef30c42e0') format('woff2');font-weight:45 920;font-style:normal;font-display:swap}
body{margin:0}
a{color:#0E7C86}a:hover{color:#0A5F67}
:focus-visible{outline:2px solid #0E7C86;outline-offset:2px}
</style>
</helmet>
<div style="width: 1440px; height: 1040px; box-sizing: border-box; padding: 48px; display: flex; flex-direction: column; gap: 28px; background: #F6F7F9; color: #171C26; font-family: Pretendard, -apple-system, 'Apple SD Gothic Neo', 'Malgun Gothic', system-ui, sans-serif; font-size: 13px; line-height: 1.5">
<div style="display: flex; flex-direction: column; gap: 6px; max-width: 760px">
<h1 style="margin: 0; font-size: 24px; font-weight: 700; letter-spacing: -0.02em">오류 · 빈 상태 · 피드백</h1>
<p style="margin: 0; font-size: 13.5px; color: #4A5363">문구는 에러 코드 사전에서 가져오고, 화면은 코드로 분기합니다. 문제가 생기면 문의 코드(traceId)를 함께 보여 줍니다.</p>
</div>
<div style="display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 20px">
<section style="display: flex; flex-direction: column; gap: 14px; padding: 20px; background: #FFFFFF; border: 1px solid #E6E8EC; border-radius: 12px">
<div style="display: flex; flex-direction: column; gap: 2px">
<h2 style="margin: 0; font-size: 14px; font-weight: 700">오류 알림</h2>
<p style="margin: 0; font-size: 12px; color: #667085">에러 코드 · MAIL_001</p>
</div>
<p style="margin: 0; font-size: 12.5px; color: #4A5363">브라우저 alert 대신 화면 오른쪽 아래에 띄웁니다. 같은 코드는 3초 안에 한 번만 나옵니다.</p>
<div style="display: flex; align-items: flex-start; gap: 10px; padding: 12px 6px 12px 14px; border: 1px solid #E6E8EC; border-radius: 10px; background: #FFFFFF; box-shadow: 0 10px 28px rgba(16, 24, 28, 0.12)">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#B42318" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="flex: 0 0 auto; margin-top: 1px"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>
<div style="flex: 1 1 auto; display: flex; flex-direction: column; gap: 2px">
<span style="font-weight: 500">메일 서버와 연결할 수 없습니다. 잠시 후 다시 시도해 주세요.</span>
<span style="font-size: 12px; color: #667085; font-variant-numeric: tabular-nums">문의 코드 3f1c0a2e</span>
</div>
<button type="button" aria-label="알림 닫기" style="flex: 0 0 auto; width: 36px; height: 36px; display: inline-flex; align-items: center; justify-content: center; border: 0; border-radius: 6px; background: transparent; color: #4A5363; cursor: pointer">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
</button>
</div>
</section>
<section style="display: flex; flex-direction: column; gap: 14px; padding: 20px; background: #FFFFFF; border: 1px solid #E6E8EC; border-radius: 12px">
<div style="display: flex; flex-direction: column; gap: 2px">
<h2 style="margin: 0; font-size: 14px; font-weight: 700">세션 만료</h2>
<p style="margin: 0; font-size: 12px; color: #667085">에러 코드 · AUTH_002</p>
</div>
<p style="margin: 0; font-size: 12.5px; color: #4A5363">로그인 상태에서 401을 받으면 세션을 정리하고 이 안내를 띄웁니다.</p>
<dialog open aria-labelledby="expired-title" style="position: static; margin: 0; padding: 18px 18px 14px; width: auto; border: 1px solid #E6E8EC; border-radius: 12px; background: #FFFFFF; color: #171C26; box-shadow: 0 14px 36px rgba(16, 24, 28, 0.14); display: flex; flex-direction: column; gap: 8px">
<h3 id="expired-title" style="margin: 0; font-size: 15px; font-weight: 700">다시 로그인해 주세요</h3>
<p style="margin: 0; color: #4A5363">안전을 위해 로그아웃 되었습니다. 다시 로그인해 주세요.</p>
<a href="Login.dc.html" style="align-self: flex-end; display: inline-flex; align-items: center; min-height: 44px; margin-top: 4px; padding: 0 16px; border-radius: 8px; background: #0E7C86; color: #FFFFFF; font-weight: 600; text-decoration: none">로그인 화면으로</a>
</dialog>
</section>
<section style="display: flex; flex-direction: column; gap: 14px; padding: 20px; background: #FFFFFF; border: 1px solid #E6E8EC; border-radius: 12px">
<div style="display: flex; flex-direction: column; gap: 2px">
<h2 style="margin: 0; font-size: 14px; font-weight: 700">메일 서버 장애 (목록 영역)</h2>
<p style="margin: 0; font-size: 12px; color: #667085">에러 코드 · MAIL_001</p>
</div>
<p style="margin: 0; font-size: 12.5px; color: #4A5363">목록을 못 불러오면 빈 화면 대신 다시 시도할 수 있게 합니다.</p>
<div style="display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 20px 16px; border: 1px solid #EEF0F3; border-radius: 10px; background: #FBFBFC; text-align: center">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#B42318" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>
<p style="margin: 2px 0 0; font-weight: 600">메일 서버와 연결할 수 없습니다. 잠시 후 다시 시도해 주세요.</p>
<button type="button" style="min-height: 44px; padding: 0 14px; border: 0; border-radius: 8px; background: transparent; color: #0E7C86; font: inherit; font-weight: 600; cursor: pointer">다시 시도</button>
<span style="font-size: 12px; color: #667085; font-variant-numeric: tabular-nums">문의 코드 3f1c0a2e</span>
</div>
</section>
<section style="display: flex; flex-direction: column; gap: 14px; padding: 20px; background: #FFFFFF; border: 1px solid #E6E8EC; border-radius: 12px">
<div style="display: flex; flex-direction: column; gap: 2px">
<h2 style="margin: 0; font-size: 14px; font-weight: 700">받는 사람 오류</h2>
<p style="margin: 0; font-size: 12px; color: #667085">에러 코드 · MAIL_002</p>
</div>
<p style="margin: 0; font-size: 12.5px; color: #4A5363">형식이 틀린 주소는 입력 즉시 표시하고, 서버가 거부해도 같은 문구로 알립니다.</p>
<div style="display: flex; flex-direction: column; border: 1px solid #E6E8EC; border-radius: 10px">
<div style="display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; min-height: 44px; box-sizing: border-box; padding: 6px 12px">
<span style="flex: 0 0 auto; margin-right: 6px; font-size: 12.5px; font-weight: 600; color: #4A5363">받는 사람</span>
<span style="display: inline-flex; align-items: center; min-height: 28px; padding: 0 8px; border-radius: 4px; background: #F2F4F7; color: #171C26; font-size: 13px">박준호 &lt;junho.park@arasystem.it.kr&gt;</span>
<span style="display: inline-flex; align-items: center; min-height: 28px; padding: 0 8px; border-radius: 4px; background: #FEF3F2; color: #B42318; font-size: 13px; text-decoration: underline wavy #B42318">kim@arasystem</span>
</div>
<p role="alert" style="display: flex; align-items: center; gap: 8px; margin: 0; padding: 8px 12px; border-top: 1px solid #FBD7D3; border-radius: 0 0 10px 10px; background: #FEF3F2; color: #B42318; font-size: 12.5px">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>
<span>받는 사람 주소를 다시 확인해 주세요.</span>
</p>
</div>
</section>
<section style="display: flex; flex-direction: column; gap: 14px; padding: 20px; background: #FFFFFF; border: 1px solid #E6E8EC; border-radius: 12px">
<div style="display: flex; flex-direction: column; gap: 2px">
<h2 style="margin: 0; font-size: 14px; font-weight: 700">첨부 업로드</h2>
<p style="margin: 0; font-size: 12px; color: #667085">에러 코드 · FILE_001 · FILE_002</p>
</div>
<p style="margin: 0; font-size: 12.5px; color: #4A5363">파일마다 진행률을 보여 주고, 실패한 파일만 다시 고르게 합니다.</p>
<ul style="list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; border: 1px solid #E6E8EC; border-radius: 10px">
<li style="display: flex; flex-direction: column; gap: 6px; padding: 10px 12px; border-bottom: 1px solid #EEF0F3">
<div style="display: flex; align-items: center; gap: 8px">
<span style="flex: 1 1 auto; font-weight: 500">회의록_1027.docx</span>
<span style="font-size: 12px; color: #667085; font-variant-numeric: tabular-nums">올리는 중 62%</span>
</div>
<div style="height: 3px; border-radius: 2px; background: #E4E7EC">
<div style="width: 62%; height: 3px; border-radius: 2px; background: #0E7C86"></div>
</div>
</li>
<li style="display: flex; align-items: center; gap: 8px; padding: 10px 12px; border-bottom: 1px solid #EEF0F3">
<span style="flex: 1 1 auto; font-weight: 500">견적_비교표.xlsx</span>
<span style="font-size: 12px; color: #667085">52 KB · 완료</span>
</li>
<li style="display: flex; flex-direction: column; gap: 2px; padding: 10px 12px; border-radius: 0 0 10px 10px; background: #FEF3F2">
<div style="display: flex; align-items: center; gap: 8px">
<span style="flex: 1 1 auto; font-weight: 500; color: #B42318">현장사진_원본.zip</span>
<span style="font-size: 12px; color: #B42318">38 MB</span>
</div>
<span style="font-size: 12.5px; color: #B42318">파일 크기가 허용 범위를 초과했습니다.</span>
</li>
</ul>
</section>
<section style="display: flex; flex-direction: column; gap: 14px; padding: 20px; background: #FFFFFF; border: 1px solid #E6E8EC; border-radius: 12px">
<div style="display: flex; flex-direction: column; gap: 2px">
<h2 style="margin: 0; font-size: 14px; font-weight: 700">외부 이미지 차단</h2>
<p style="margin: 0; font-size: 12px; color: #667085">보안</p>
</div>
<p style="margin: 0; font-size: 12.5px; color: #4A5363">추적 픽셀을 막기 위해 외부 이미지는 불러오지 않습니다. 본문은 스크립트가 꺼진 격리 영역에 그립니다.</p>
<p style="display: flex; align-items: flex-start; gap: 8px; margin: 0; padding: 10px 12px; border-radius: 8px; background: #FFF7E6; color: #7A5200; font-size: 12.5px">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="flex: 0 0 auto; margin-top: 2px"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path></svg>
<span>외부 이미지를 차단했습니다. 개인정보 보호를 위해 보낸 사람 서버의 이미지는 불러오지 않습니다.</span>
</p>
</section>
<section style="display: flex; flex-direction: column; gap: 14px; padding: 20px; background: #FFFFFF; border: 1px solid #E6E8EC; border-radius: 12px">
<div style="display: flex; flex-direction: column; gap: 2px">
<h2 style="margin: 0; font-size: 14px; font-weight: 700">빈 메일함</h2>
<p style="margin: 0; font-size: 12px; color: #667085">메일함마다 이름과 안내를 바꿉니다</p>
</div>
<div style="display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 26px 16px; border: 1px solid #EEF0F3; border-radius: 10px; background: #FBFBFC; text-align: center">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#98A2B3" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline></svg>
<p style="margin: 4px 0 0; font-weight: 600">임시보관함이 비어 있습니다</p>
<p style="margin: 0; font-size: 12.5px; color: #667085">임시 저장한 메일이 여기에 표시됩니다.</p>
</div>
</section>
<section style="display: flex; flex-direction: column; gap: 14px; padding: 20px; background: #FFFFFF; border: 1px solid #E6E8EC; border-radius: 12px">
<div style="display: flex; flex-direction: column; gap: 2px">
<h2 style="margin: 0; font-size: 14px; font-weight: 700">불러오는 중</h2>
<p style="margin: 0; font-size: 12px; color: #667085">목록 자리를 미리 잡아 화면이 튀지 않게 합니다</p>
</div>
<ul aria-label="메일 목록 불러오는 중" style="list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; border: 1px solid #EEF0F3; border-radius: 10px">
<li style="display: flex; flex-direction: column; gap: 6px; padding: 12px; border-bottom: 1px solid #EEF0F3"><span style="width: 36%; height: 10px; border-radius: 5px; background: #E4E7EC"></span><span style="width: 82%; height: 10px; border-radius: 5px; background: #EEF0F3"></span></li>
<li style="display: flex; flex-direction: column; gap: 6px; padding: 12px; border-bottom: 1px solid #EEF0F3"><span style="width: 28%; height: 10px; border-radius: 5px; background: #E4E7EC"></span><span style="width: 70%; height: 10px; border-radius: 5px; background: #EEF0F3"></span></li>
<li style="display: flex; flex-direction: column; gap: 6px; padding: 12px"><span style="width: 44%; height: 10px; border-radius: 5px; background: #E4E7EC"></span><span style="width: 76%; height: 10px; border-radius: 5px; background: #EEF0F3"></span></li>
</ul>
</section>
<section style="display: flex; flex-direction: column; gap: 14px; padding: 20px; background: #FFFFFF; border: 1px solid #E6E8EC; border-radius: 12px">
<div style="display: flex; flex-direction: column; gap: 2px">
<h2 style="margin: 0; font-size: 14px; font-weight: 700">보내기 완료</h2>
<p style="margin: 0; font-size: 12px; color: #667085">보던 메일함으로 돌아가며 짧게 알립니다</p>
</div>
<div style="display: flex; align-items: center; gap: 10px; padding: 8px 6px 8px 14px; border-radius: 10px; background: #0B3640; color: #FFFFFF; box-shadow: 0 10px 28px rgba(16, 24, 28, 0.22)">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#9FE0B5" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12"></polyline></svg>
<span style="flex: 1 1 auto; font-weight: 500">메일을 보냈습니다.</span>
<a href="Main.dc.html" style="display: inline-flex; align-items: center; min-height: 44px; padding: 0 10px; border-radius: 6px; color: #9ED6DB; font-weight: 600; text-decoration: none">보낸편지함 보기</a>
</div>
</section>
</div>
</div>
</x-dc>
<script type="text/x-dc" data-dc-script data-props='{"$preview":{"width":1440,"height":1040}}'>
class Component extends DCLogic {
renderVals() {
return {};
}
}
</script>
</body>
</html>
+20
View File
@@ -0,0 +1,20 @@
{
"v": 3,
"createdOnFiles": { "v": 1, "at": "2026-10-26T04:25:00Z" },
"title": "arasystem 웹메일 디자인",
"launch": { "view": "canvas" },
"pages": [],
"boards": {
"Main.dc.html": { "x": 1520, "y": 0, "w": 1440, "h": 900, "title": "메일함 · 읽기", "expand": "fill", "is_interactive": true },
"Login.dc.html": { "x": 0, "y": 0, "w": 1440, "h": 900, "title": "로그인", "expand": "fill", "is_interactive": true },
"Compose.dc.html": { "x": 3040, "y": 0, "w": 1440, "h": 900, "title": "메일 쓰기", "expand": "fill", "is_interactive": true },
"States.dc.html": { "x": 0, "y": 1020, "w": 1440, "h": 1040, "title": "오류 · 빈 상태 · 피드백" },
"MainList.dc.html": { "x": 1520, "y": 1020, "w": 1440, "h": 900, "title": "메일함 · 목록 보기", "expand": "fill", "is_interactive": true },
"MainListRead.dc.html": { "x": 3040, "y": 1020, "w": 1440, "h": 900, "title": "목록 보기 · 메일 읽기", "expand": "fill", "is_interactive": true }
},
"order": ["Main.dc.html", "Login.dc.html", "Compose.dc.html", "States.dc.html", "MainList.dc.html", "MainListRead.dc.html"],
"notes": {
"flow": { "x": 0, "y": -300, "text": "arasystem 웹메일 — 1단계 화면 (로그인 → 메일함 → 쓰기)", "kind": "title1", "maxW": 4480 }
},
"designSystems": []
}