1011 lines
75 KiB
HTML
1011 lines
75 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ko">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>메인 배너 이미지 2종 디자인 — Relay</title>
|
||
<link rel="preconnect" href="https://cdn.jsdelivr.net">
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.css">
|
||
<style>
|
||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||
:root {
|
||
--bg: #f4f5f7; --panel: #ffffff; --border: #e6e8ec; --border-strong: #d7dae0;
|
||
--text: #16181d; --text-2: #5b626e; --text-3: #9298a3;
|
||
--accent: #4f46e5; --accent-hover: #4338ca; --accent-weak: #eef0fe; --accent-border: #c7ccfb;
|
||
--green: #1f9d57; --green-weak: #e8f6ee; --blue: #1d4ed8; --blue-weak: #e8efff; --blue-border: #c9dbff;
|
||
--red: #d6455d; --red-weak: #fdeef0; --red-border: #f1c0c8; --amber: #b7791f; --amber-weak: #fdf4e3; --amber-border: #f3e2bd;
|
||
--radius: 6px; --radius-sm: 4px; --shadow-sm: 0 1px 2px rgba(20,24,33,.05);
|
||
}
|
||
html, body { background: var(--bg); color: var(--text); font-family: "Pretendard", -apple-system, system-ui, sans-serif; font-size: 14px; line-height: 1.5; -webkit-font-smoothing: antialiased; }
|
||
body { min-height: 100vh; }
|
||
|
||
/* topbar */
|
||
.topbar { height: 52px; background: var(--panel); border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 28px; padding: 0 20px; position: sticky; top: 0; z-index: 50; }
|
||
.brand { display: flex; align-items: center; gap: 9px; font-weight: 700; font-size: 15px; letter-spacing: -.2px; }
|
||
.brand .logo { width: 26px; height: 26px; border-radius: 7px; background: linear-gradient(135deg, #5b54f0, #4338ca); display: grid; place-items: center; color: #fff; font-size: 15px; font-weight: 800; box-shadow: 0 2px 6px rgba(79,70,229,.35); }
|
||
.topnav { display: flex; align-items: center; gap: 2px; }
|
||
.topnav a { color: var(--text-2); text-decoration: none; font-size: 13.5px; font-weight: 500; padding: 7px 12px; border-radius: var(--radius-sm); line-height: 1; white-space: nowrap; }
|
||
.topnav a:hover { background: #f1f2f4; color: var(--text); }
|
||
.topnav a.active { color: var(--accent); background: var(--accent-weak); font-weight: 600; }
|
||
.topbar-right { margin-left: auto; display: flex; align-items: center; gap: 8px; }
|
||
.icon-btn { width: 32px; height: 32px; border-radius: var(--radius-sm); border: none; background: transparent; color: var(--text-2); display: grid; place-items: center; cursor: pointer; }
|
||
.icon-btn:hover { background: #f1f2f4; color: var(--text); }
|
||
.icon-btn.agent { color: var(--accent); }
|
||
.icon-btn.agent:hover { background: var(--accent-weak); color: var(--accent-hover); }
|
||
.me { width: 28px; height: 28px; border-radius: 50%; display: grid; place-items: center; font-size: 11.5px; font-weight: 700; color: #fff; background: #0ea5a3; margin-left: 4px; }
|
||
|
||
/* page */
|
||
.page { max-width: 1080px; margin: 0 auto; padding: 0 24px 60px; }
|
||
.crumb { display: flex; align-items: center; gap: 7px; color: var(--text-3); font-size: 12.5px; padding: 16px 0 0; white-space: nowrap; }
|
||
.crumb .lnk { color: var(--text-2); font-weight: 600; text-decoration: none; }
|
||
.crumb .lnk:hover { color: var(--accent); }
|
||
.crumb .sep { opacity: .5; }
|
||
.crumb b { color: var(--text-2); font-weight: 600; }
|
||
|
||
.pagehead { display: flex; align-items: flex-start; gap: 14px; padding: 12px 0 18px; }
|
||
.ph-main { flex: 1; min-width: 0; }
|
||
.ph-titlerow { display: flex; align-items: center; gap: 10px; }
|
||
.st-badge { font-size: 11.5px; font-weight: 600; padding: 3px 10px; border-radius: 20px; white-space: nowrap; }
|
||
.st-badge.prog { color: var(--blue); background: var(--blue-weak); border: 1px solid var(--blue-border); }
|
||
.st-badge.review { color: var(--amber); background: var(--amber-weak); border: 1px solid var(--amber-border); }
|
||
.st-badge.done { color: var(--green); background: var(--green-weak); border: 1px solid #cde8d8; }
|
||
.st-badge.changes { color: var(--red); background: var(--red-weak); border: 1px solid var(--red-border); }
|
||
.ph-tid { font-size: 14px; font-weight: 600; color: var(--text-3); }
|
||
h1.ph-title { font-size: 23px; font-weight: 700; letter-spacing: -.5px; margin-top: 8px; line-height: 1.3; }
|
||
.pagehead .actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; padding-top: 2px; }
|
||
.btn { height: 34px; padding: 0 14px; border-radius: var(--radius); font-size: 13.5px; font-weight: 600; border: 1px solid var(--border-strong); background: var(--panel); color: var(--text-2); cursor: pointer; display: inline-flex; align-items: center; gap: 6px; line-height: 1; text-decoration: none; white-space: nowrap; }
|
||
.btn:hover { background: #f7f8fa; color: var(--text); }
|
||
.btn svg { width: 15px; height: 15px; }
|
||
.btn.icon { width: 34px; padding: 0; justify-content: center; }
|
||
|
||
/* more menu */
|
||
.more-wrap { position: relative; }
|
||
.menu { display: none; position: absolute; top: calc(100% + 6px); right: 0; z-index: 40; width: 168px; background: #fff; border: 1px solid var(--border); border-radius: 9px; box-shadow: 0 12px 32px rgba(20,24,33,.16); padding: 5px; }
|
||
.menu.open { display: block; }
|
||
.menu-item { display: flex; align-items: center; gap: 10px; width: 100%; border: none; background: transparent; font-family: inherit; font-size: 13px; font-weight: 500; color: var(--text); padding: 8px 9px; border-radius: var(--radius-sm); cursor: pointer; text-align: left; white-space: nowrap; }
|
||
.menu-item:hover { background: #f5f6f8; }
|
||
.menu-item svg { width: 16px; height: 16px; color: var(--text-3); flex-shrink: 0; }
|
||
.menu-item.danger { color: var(--red); }
|
||
.menu-item.danger svg { color: var(--red); }
|
||
.menu-item.danger:hover { background: var(--red-weak); }
|
||
.menu-sep { height: 1px; background: var(--border); margin: 5px 0; }
|
||
|
||
/* modal */
|
||
.modal-backdrop { position: fixed; inset: 0; background: rgba(20,24,33,.5); display: none; align-items: center; justify-content: center; z-index: 100; padding: 24px; }
|
||
.modal-backdrop.open { display: flex; }
|
||
.modal { width: 100%; max-width: 440px; background: #fff; border-radius: 14px; box-shadow: 0 24px 64px rgba(20,24,33,.32); overflow: hidden; }
|
||
.modal-body { padding: 26px 24px 8px; text-align: center; }
|
||
.modal-ico { width: 46px; height: 46px; border-radius: 50%; background: var(--red-weak); color: var(--red); display: grid; place-items: center; margin: 0 auto 14px; }
|
||
.modal-ico svg { width: 22px; height: 22px; }
|
||
.modal-title { font-size: 17px; font-weight: 700; letter-spacing: -.3px; }
|
||
.modal-desc { font-size: 13px; color: var(--text-2); line-height: 1.6; margin-top: 8px; }
|
||
.modal-desc b { color: var(--text); font-weight: 600; }
|
||
.modal-foot { display: flex; gap: 9px; padding: 18px 24px 20px; }
|
||
.mbtn { flex: 1; height: 40px; border-radius: var(--radius); font-size: 13.5px; font-weight: 600; border: 1px solid var(--border-strong); background: #fff; color: var(--text-2); cursor: pointer; }
|
||
.mbtn:hover { background: #f1f2f4; color: var(--text); }
|
||
.mbtn.danger { background: var(--red); border-color: var(--red); color: #fff; box-shadow: 0 1px 2px rgba(214,69,93,.4); }
|
||
.mbtn.danger:hover { background: #c23a51; }
|
||
.mbtn.approve { background: var(--green); border-color: var(--green); color: #fff; box-shadow: 0 1px 2px rgba(31,157,87,.4); }
|
||
.mbtn.approve:hover { background: #188a4b; }
|
||
.modal-textarea { width: 100%; min-height: 92px; border: 1px solid var(--border-strong); border-radius: var(--radius); padding: 10px 12px; font-family: inherit; font-size: 13.5px; line-height: 1.6; color: var(--text); resize: vertical; }
|
||
.modal-textarea:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-weak); }
|
||
.modal-textarea::placeholder { color: var(--text-3); }
|
||
|
||
/* approval sidebar card */
|
||
.ap-card { padding: 16px 18px 18px; border-width: 1px; border-style: solid; }
|
||
.ap-card.review { border-color: var(--amber-border); background: var(--amber-weak); }
|
||
.ap-card.request { border-color: var(--accent-border); background: var(--accent-weak); }
|
||
.ap-card-head { display: flex; align-items: center; gap: 9px; }
|
||
.ap-card-ico { width: 30px; height: 30px; border-radius: 8px; background: #fff; display: grid; place-items: center; flex-shrink: 0; border: 1px solid; }
|
||
.ap-card.review .ap-card-ico { color: var(--amber); border-color: var(--amber-border); }
|
||
.ap-card.request .ap-card-ico { color: var(--accent); border-color: var(--accent-border); }
|
||
.ap-card-ico svg { width: 17px; height: 17px; }
|
||
.ap-card-title { font-size: 14px; font-weight: 700; white-space: nowrap; }
|
||
.ap-card.review .ap-card-title { color: #8a5a12; }
|
||
.ap-card.request .ap-card-title { color: var(--accent-hover); }
|
||
.ap-card-meta { font-size: 12px; color: var(--text-2); margin-top: 10px; line-height: 1.5; }
|
||
.ap-card-meta b { color: var(--text); font-weight: 600; }
|
||
.ap-card-desc { font-size: 12.5px; color: var(--text-2); margin-top: 10px; line-height: 1.55; }
|
||
.ap-card-note { font-size: 12.5px; color: var(--text); background: #fff; border: 1px solid var(--amber-border); border-radius: 8px; padding: 9px 11px; margin-top: 10px; line-height: 1.5; }
|
||
.ap-card-actions { display: flex; flex-direction: column; gap: 8px; margin-top: 13px; }
|
||
.ap-btn { height: 38px; padding: 0 14px; border-radius: var(--radius); font-size: 13.5px; font-weight: 600; cursor: pointer; border: 1px solid var(--border-strong); background: #fff; color: var(--text-2); display: inline-flex; align-items: center; justify-content: center; gap: 6px; }
|
||
.ap-btn svg { width: 15px; height: 15px; }
|
||
.ap-btn.reject:hover { background: #fdeef0; border-color: var(--red-border); color: var(--red); }
|
||
.ap-btn.approve { background: var(--green); border-color: var(--green); color: #fff; box-shadow: 0 1px 2px rgba(31,157,87,.4); }
|
||
.ap-btn.approve:hover { background: #188a4b; }
|
||
.ap-btn.request-send { background: var(--accent); border-color: var(--accent); color: #fff; box-shadow: 0 1px 2px rgba(79,70,229,.4); }
|
||
.ap-btn.request-send:hover { background: var(--accent-hover); }
|
||
|
||
/* thread tabs: 댓글 / 활동 */
|
||
.thread-tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--border); margin-bottom: 18px; }
|
||
.thread-tab { position: relative; border: none; background: transparent; font-family: inherit; font-size: 13.5px; font-weight: 600; color: var(--text-3); padding: 8px 12px 11px; cursor: pointer; display: inline-flex; align-items: center; gap: 7px; line-height: 1; white-space: nowrap; }
|
||
.thread-tab:hover { color: var(--text-2); }
|
||
.thread-tab.active { color: var(--accent); }
|
||
.thread-tab.active::after { content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px; background: var(--accent); border-radius: 2px; }
|
||
.tt-count { font-size: 11.5px; font-weight: 600; color: var(--text-3); background: #f1f2f4; border-radius: 20px; padding: 1px 7px; line-height: 1.55; }
|
||
.thread-tab.active .tt-count { color: var(--accent); background: var(--accent-weak); }
|
||
.thread-panel[hidden] { display: none; }
|
||
|
||
/* activity timeline */
|
||
.activity { display: flex; flex-direction: column; padding-top: 2px; }
|
||
.act { display: grid; grid-template-columns: 28px 1fr; gap: 13px; position: relative; }
|
||
.act::before { content: ''; position: absolute; left: 13px; top: 30px; bottom: -2px; width: 2px; background: var(--border); }
|
||
.act:last-child::before { display: none; }
|
||
.act-ico { width: 28px; height: 28px; border-radius: 50%; display: grid; place-items: center; border: 1px solid var(--border); background: #fff; color: var(--text-2); z-index: 1; }
|
||
.act-ico svg { width: 14px; height: 14px; }
|
||
.act-ico.green { background: var(--green-weak); border-color: #cde8d8; color: var(--green); }
|
||
.act-ico.amber { background: var(--amber-weak); border-color: var(--amber-border); color: var(--amber); }
|
||
.act-ico.blue { background: var(--blue-weak); border-color: var(--blue-border); color: var(--blue); }
|
||
.act-ico.accent { background: var(--accent-weak); border-color: var(--accent-border); color: var(--accent); }
|
||
.act-body { padding-bottom: 20px; min-width: 0; }
|
||
.act:last-child .act-body { padding-bottom: 0; }
|
||
.act-text { font-size: 13.5px; color: var(--text); line-height: 1.5; }
|
||
.act-text b { font-weight: 600; }
|
||
.act-file { display: inline-flex; align-items: center; gap: 7px; margin-top: 7px; padding: 5px 9px; border: 1px solid var(--border); border-radius: var(--radius); font-size: 12px; font-weight: 600; color: var(--text-2); }
|
||
.act-file .fi { width: 20px; height: 20px; border-radius: 4px; background: #e25950; color: #fff; display: grid; place-items: center; font-size: 7px; font-weight: 800; }
|
||
.act-chips { display: inline-flex; align-items: center; gap: 7px; margin-top: 8px; }
|
||
.act-chip { font-size: 11.5px; font-weight: 600; padding: 2px 9px; border-radius: 20px; white-space: nowrap; }
|
||
.act-chip.from { color: var(--text-2); background: #f1f2f4; border: 1px solid var(--border); }
|
||
.act-chip.to { color: var(--amber); background: var(--amber-weak); border: 1px solid var(--amber-border); }
|
||
.act-chips svg { width: 14px; height: 14px; color: var(--text-3); }
|
||
.act-time { font-size: 11.5px; color: var(--text-3); margin-top: 6px; }
|
||
|
||
/* layout */
|
||
.layout { display: grid; grid-template-columns: 1fr 320px; gap: 20px; align-items: start; }
|
||
.card { background: var(--panel); border: 1px solid var(--border); border-radius: 10px; box-shadow: var(--shadow-sm); }
|
||
.main-card { padding: 22px 26px 26px; }
|
||
|
||
.sect + .sect { margin-top: 26px; padding-top: 22px; border-top: 1px solid var(--border); }
|
||
.sect-label { display: flex; align-items: center; gap: 8px; font-size: 12.5px; font-weight: 600; color: var(--text-2); margin-bottom: 12px; white-space: nowrap; }
|
||
.sect-label .pr { margin-left: auto; display: flex; align-items: center; gap: 9px; }
|
||
.progress-bar { width: 92px; height: 6px; border-radius: 4px; background: #eceef1; overflow: hidden; }
|
||
.progress-bar > i { display: block; height: 100%; background: var(--green); border-radius: 4px; }
|
||
.progress-txt { font-size: 12px; font-weight: 600; color: var(--text-2); white-space: nowrap; }
|
||
|
||
.content { font-size: 14px; color: var(--text); line-height: 1.7; }
|
||
.content p { margin-bottom: 11px; }
|
||
.content p:last-child { margin-bottom: 0; }
|
||
.content .mention { color: var(--accent); font-weight: 600; background: var(--accent-weak); padding: 0 3px; border-radius: 3px; }
|
||
|
||
/* checklist */
|
||
.checklist { display: flex; flex-direction: column; }
|
||
.citem { display: flex; align-items: center; gap: 11px; padding: 8px 0; border-radius: var(--radius-sm); }
|
||
.cbox { width: 18px; height: 18px; border-radius: 5px; border: 1.5px solid var(--border-strong); flex-shrink: 0; display: grid; place-items: center; background: #fff; }
|
||
.cbox.done { background: var(--green); border-color: var(--green); }
|
||
.cbox svg { width: 12px; height: 12px; color: #fff; opacity: 0; }
|
||
.cbox.done svg { opacity: 1; }
|
||
.citem .ctext { font-size: 13.5px; color: var(--text); white-space: nowrap; }
|
||
.citem.done .ctext { color: var(--text-3); text-decoration: line-through; }
|
||
|
||
/* nested subtasks (read-only) */
|
||
.task-group + .task-group { margin-top: 11px; padding-top: 11px; border-top: 1px solid var(--border); }
|
||
.citem.parent .ctext { font-weight: 600; }
|
||
.sub-count { margin-left: auto; font-size: 11px; font-weight: 600; color: var(--text-2); background: #f1f2f4; border: 1px solid var(--border); border-radius: 20px; padding: 1px 8px; line-height: 1.55; white-space: nowrap; }
|
||
.citem.parent.done .sub-count { color: var(--green); background: var(--green-weak); border-color: #cde8d8; }
|
||
.subtasks { margin-left: 29px; padding-left: 14px; border-left: 1.5px solid var(--border); display: flex; flex-direction: column; }
|
||
.subtask { display: flex; align-items: center; gap: 10px; padding: 6px 0; border-radius: var(--radius-sm); }
|
||
.cbox.sm { width: 16px; height: 16px; border-radius: 5px; }
|
||
.cbox.sm svg { width: 11px; height: 11px; }
|
||
.subtask .ctext { font-size: 13px; color: var(--text-2); white-space: nowrap; }
|
||
.subtask.done .ctext { color: var(--text-3); text-decoration: line-through; }
|
||
|
||
/* comments */
|
||
.comment { display: flex; gap: 11px; padding: 14px 0; border-top: 1px solid var(--border); }
|
||
.comment:first-of-type { border-top: none; }
|
||
.c-av { width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; font-size: 12px; font-weight: 700; color: #fff; flex-shrink: 0; }
|
||
.c-body { flex: 1; min-width: 0; }
|
||
.c-head { display: flex; align-items: center; gap: 8px; }
|
||
.c-name { font-size: 13.5px; font-weight: 600; white-space: nowrap; }
|
||
.c-role { font-size: 11px; font-weight: 600; color: var(--accent); background: var(--accent-weak); border-radius: 20px; padding: 1px 7px; white-space: nowrap; }
|
||
.c-time { font-size: 12px; color: var(--text-3); white-space: nowrap; }
|
||
.c-text { font-size: 13.5px; color: var(--text); line-height: 1.6; margin-top: 4px; }
|
||
.c-file { display: inline-flex; align-items: center; gap: 7px; margin-top: 8px; padding: 6px 10px; border: 1px solid var(--border); border-radius: var(--radius); font-size: 12.5px; font-weight: 600; color: var(--text-2); }
|
||
.c-file .fi { width: 22px; height: 22px; border-radius: 5px; background: #e25950; color: #fff; display: grid; place-items: center; font-size: 8px; font-weight: 800; }
|
||
|
||
/* 답글 (대댓글) */
|
||
.c-actions { margin-top: 7px; }
|
||
.c-reply-btn { font-size: 12px; font-weight: 600; color: var(--text-3); background: none; border: none; cursor: pointer; padding: 2px 0; display: inline-flex; align-items: center; gap: 5px; font-family: inherit; white-space: nowrap; }
|
||
.c-reply-btn:hover { color: var(--accent); }
|
||
.c-reply-btn svg { width: 13px; height: 13px; }
|
||
.c-replies { margin-top: 13px; display: flex; flex-direction: column; gap: 14px; }
|
||
.c-reply { display: flex; gap: 9px; }
|
||
.c-reply .c-av { width: 24px; height: 24px; font-size: 10.5px; }
|
||
.reply-form { display: none; gap: 9px; margin-top: 12px; }
|
||
.reply-form.open { display: flex; }
|
||
.reply-form .c-av { width: 24px; height: 24px; font-size: 10.5px; }
|
||
.reply-form .field { flex: 1; }
|
||
.reply-form textarea { width: 100%; min-height: 44px; border: 1px solid var(--border-strong); border-radius: var(--radius); padding: 8px 10px; font-family: inherit; font-size: 13px; line-height: 1.55; resize: vertical; color: var(--text); }
|
||
.reply-form textarea:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-weak); }
|
||
.reply-form textarea::placeholder { color: var(--text-3); }
|
||
.reply-form .crow { display: flex; justify-content: flex-end; gap: 8px; margin-top: 8px; }
|
||
.btn.sm { height: 30px; padding: 0 12px; font-size: 12.5px; }
|
||
.btn.ghost { border-color: transparent; background: transparent; }
|
||
.btn.ghost:hover { background: #f1f2f4; }
|
||
.composer { display: flex; gap: 11px; margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--border); }
|
||
.composer .field { flex: 1; }
|
||
.composer textarea { width: 100%; min-height: 64px; border: 1px solid var(--border-strong); border-radius: var(--radius); padding: 10px 12px; font-family: inherit; font-size: 13.5px; resize: vertical; color: var(--text); }
|
||
.composer textarea:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-weak); }
|
||
.composer textarea::placeholder { color: var(--text-3); }
|
||
.composer .crow { display: flex; justify-content: flex-end; margin-top: 9px; }
|
||
.btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; box-shadow: 0 1px 2px rgba(79,70,229,.4); }
|
||
.btn.primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
|
||
|
||
/* sidebar */
|
||
.side { display: flex; flex-direction: column; gap: 16px; }
|
||
.side-card { padding: 16px 18px 18px; }
|
||
.side-field + .side-field { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--border); }
|
||
.side-label { font-size: 12px; font-weight: 600; color: var(--text-3); margin-bottom: 9px; white-space: nowrap; }
|
||
.status-badge { display: inline-flex; align-items: center; gap: 8px; height: 30px; padding: 0 13px; border: 1px solid var(--blue-border); background: var(--blue-weak); border-radius: 20px; font-size: 13px; font-weight: 600; color: var(--blue); white-space: nowrap; }
|
||
.status-badge .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); }
|
||
.status-badge.review { color: var(--amber); background: var(--amber-weak); border-color: var(--amber-border); }
|
||
.status-badge.review .dot { background: var(--amber); }
|
||
.assignees { display: flex; flex-direction: column; gap: 9px; }
|
||
.ass { display: flex; align-items: center; gap: 9px; }
|
||
.avatar { width: 26px; height: 26px; border-radius: 50%; display: grid; place-items: center; font-size: 11px; font-weight: 700; color: #fff; flex-shrink: 0; }
|
||
.av-a { background: #e0518d; } .av-b { background: #3d8bf2; } .av-c { background: #16a37b; } .av-d { background: #b3631f; } .av-f { background: #d0982a; }
|
||
.ass .nm { font-size: 13px; font-weight: 600; white-space: nowrap; }
|
||
.ass .rl { font-size: 11.5px; color: var(--text-3); white-space: nowrap; }
|
||
.dl { display: flex; align-items: center; gap: 9px; font-size: 13.5px; font-weight: 600; color: var(--text); white-space: nowrap; }
|
||
.dl svg { width: 16px; height: 16px; color: var(--text-3); }
|
||
.dl .dday { font-size: 11.5px; font-weight: 700; color: var(--amber); background: #fdf4e3; border: 1px solid #f3e2bd; border-radius: 20px; padding: 1px 7px; margin-left: auto; white-space: nowrap; }
|
||
.files { display: flex; flex-direction: column; gap: 7px; }
|
||
.file { display: flex; align-items: center; gap: 10px; padding: 8px 9px; border: 1px solid var(--border); border-radius: var(--radius); }
|
||
.file-ico { width: 28px; height: 28px; border-radius: 6px; display: grid; place-items: center; flex-shrink: 0; font-size: 9px; font-weight: 800; color: #fff; }
|
||
.fi-img { background: #2aa775; } .fi-zip { background: #8a64d6; }
|
||
.file-info { min-width: 0; flex: 1; }
|
||
.file-name { font-size: 12.5px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block; }
|
||
.file-size { font-size: 11.5px; color: var(--text-3); display: block; }
|
||
.issuer { display: flex; align-items: center; gap: 10px; }
|
||
.issuer .nm { font-size: 13px; font-weight: 600; white-space: nowrap; }
|
||
.issuer .rl { font-size: 11.5px; color: var(--text-3); white-space: nowrap; }
|
||
.meta-line { font-size: 12px; color: var(--text-3); margin-top: 4px; white-space: nowrap; }
|
||
|
||
/* ===== AI 터미널 에이전트 패널 ===== */
|
||
.btn.agent-open { background: var(--accent); border-color: var(--accent); color: #fff; box-shadow: 0 1px 2px rgba(79,70,229,.4); }
|
||
.btn.agent-open:hover { background: var(--accent-hover); border-color: var(--accent-hover); color: #fff; }
|
||
.btn.agent-open svg { width: 15px; height: 15px; }
|
||
|
||
.agent-panel { position: fixed; top: 0; right: 0; bottom: 0; width: 440px; max-width: 92vw; z-index: 120; background: #0f1117; color: #e6e8ec; display: flex; flex-direction: column; border-left: 1px solid #262a33; box-shadow: -16px 0 48px rgba(0,0,0,.30); transform: translateX(100%); transition: transform .26s cubic-bezier(.4,0,.2,1); font-family: "Pretendard", system-ui, sans-serif; }
|
||
.agent-panel.open { transform: none; }
|
||
|
||
.ag-head { display: flex; align-items: center; gap: 11px; padding: 13px 14px; border-bottom: 1px solid #262a33; flex-shrink: 0; }
|
||
.ag-logo { width: 32px; height: 32px; border-radius: 8px; background: linear-gradient(135deg,#5b54f0,#4338ca); display: grid; place-items: center; color: #fff; flex-shrink: 0; }
|
||
.ag-logo svg { width: 17px; height: 17px; }
|
||
.ag-h-main { flex: 1; min-width: 0; }
|
||
.ag-name { font-size: 13.5px; font-weight: 700; display: flex; align-items: center; gap: 8px; white-space: nowrap; }
|
||
.ag-status { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; font-weight: 600; color: #3ecf8e; }
|
||
.ag-status .dot { width: 6px; height: 6px; border-radius: 50%; background: #3ecf8e; box-shadow: 0 0 0 3px rgba(62,207,142,.18); }
|
||
.ag-ctx { font-size: 11px; color: #7b8190; margin-top: 3px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||
.ag-hbtn { width: 30px; height: 30px; border: none; background: transparent; color: #9298a3; border-radius: 6px; display: grid; place-items: center; cursor: pointer; flex-shrink: 0; }
|
||
.ag-hbtn:hover { background: #1b1e26; color: #e6e8ec; }
|
||
.ag-hbtn svg { width: 16px; height: 16px; }
|
||
|
||
.ag-body { flex: 1; overflow-y: auto; overflow-x: hidden; padding: 16px 14px 24px; display: flex; flex-direction: column; gap: 14px; }
|
||
.ag-body > * { flex-shrink: 0; }
|
||
.ag-sys { font-size: 11px; color: #6b7280; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; text-align: center; padding: 2px 0; }
|
||
.ag-sys b { color: #9298a3; font-weight: 600; }
|
||
|
||
.ag-msg { display: flex; gap: 10px; width: 100%; align-items: flex-start; }
|
||
.ag-msg .ava { width: 26px; height: 26px; flex: 0 0 26px; border-radius: 7px; display: grid; place-items: center; font-size: 11px; font-weight: 700; line-height: 1; overflow: hidden; }
|
||
.ag-msg.bot .ava { background: linear-gradient(135deg,#5b54f0,#4338ca); color: #fff; }
|
||
.ag-msg.bot .ava svg { width: 15px; height: 15px; }
|
||
.ag-msg .bubble { font-size: 13px; line-height: 1.62; max-width: 300px; min-width: 0; word-break: break-word; overflow-wrap: anywhere; }
|
||
.ag-msg.bot .bubble { background: #171a21; border: 1px solid #23262f; border-radius: 10px; padding: 9px 12px; color: #d4d8e0; }
|
||
.ag-msg.user { justify-content: flex-end; }
|
||
.ag-msg.user .ava { order: 2; background: #0ea5a3; color: #fff; border-radius: 50%; }
|
||
.ag-msg.user .bubble { order: 1; background: #4f46e5; color: #fff; border-radius: 10px; padding: 9px 12px; }
|
||
|
||
.ag-tool { border: 1px solid #23262f; border-radius: 9px; overflow: hidden; background: #0a0c11; margin-left: 36px; }
|
||
.ag-tool-head { display: flex; align-items: center; gap: 7px; padding: 6px 10px; background: #13161d; border-bottom: 1px solid #23262f; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px; color: #7b8190; }
|
||
.ag-tool-head .tdot { width: 8px; height: 8px; border-radius: 50%; background: #3ecf8e; flex-shrink: 0; }
|
||
.ag-tool-head .cmd { color: #c7ccd6; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||
.ag-tool-body { padding: 9px 11px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11.5px; line-height: 1.75; color: #aab0bd; white-space: pre-wrap; }
|
||
.ag-tool-body .ok { color: #3ecf8e; }
|
||
.ag-tool-body .key { color: #7aa2f7; }
|
||
.ag-tool-body .warn { color: #e0af68; }
|
||
|
||
.ag-list { margin: 7px 0 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 7px; }
|
||
.ag-list li { display: flex; gap: 9px; font-size: 13px; line-height: 1.5; }
|
||
.ag-list .num { color: #7aa2f7; font-family: ui-monospace, monospace; font-weight: 700; flex-shrink: 0; }
|
||
|
||
/* 자동 생성 지시서 카드 */
|
||
.ag-draft { margin-left: 36px; border: 1px solid #2b2f3a; border-radius: 11px; overflow: hidden; background: #12151c; }
|
||
.ag-draft.created { opacity: .7; }
|
||
.ag-draft-top { padding: 11px 13px 12px; border-bottom: 1px solid #23262f; }
|
||
.ag-draft-badge { font-size: 10.5px; font-weight: 700; color: #7aa2f7; font-family: ui-monospace, monospace; letter-spacing: .3px; }
|
||
.ag-draft-title { font-size: 14px; font-weight: 700; color: #f0f1f4; margin-top: 7px; line-height: 1.4; }
|
||
.ag-draft-sec { padding: 11px 13px; border-bottom: 1px solid #23262f; }
|
||
.ag-draft-label { font-size: 10px; font-weight: 700; color: #6b7280; font-family: ui-monospace, monospace; letter-spacing: .5px; text-transform: uppercase; margin-bottom: 8px; }
|
||
.ag-draft-desc { font-size: 12.5px; line-height: 1.62; color: #c3c8d2; }
|
||
.ag-todos { display: flex; flex-direction: column; gap: 1px; }
|
||
.ag-todo { display: flex; align-items: flex-start; gap: 9px; padding: 7px 6px; border-radius: 7px; cursor: pointer; }
|
||
.ag-todo:hover { background: #171a21; }
|
||
.ag-todo .box { width: 17px; height: 17px; border-radius: 5px; border: 1.5px solid #3a3f4b; flex-shrink: 0; display: grid; place-items: center; margin-top: 1px; background: #0f1117; }
|
||
.ag-todo.sel .box { background: #4f46e5; border-color: #4f46e5; }
|
||
.ag-todo .box svg { width: 11px; height: 11px; color: #fff; opacity: 0; }
|
||
.ag-todo.sel .box svg { opacity: 1; }
|
||
.ag-todo .tt { font-size: 12.5px; line-height: 1.5; color: #969cab; }
|
||
.ag-todo.sel .tt { color: #e6e8ec; }
|
||
.ag-draft-count { font-size: 11px; color: #7b8190; padding: 10px 13px 0; font-family: ui-monospace, monospace; }
|
||
.ag-draft-count b { color: #c7ccd6; }
|
||
.ag-draft-foot { display: flex; gap: 8px; padding: 11px 13px 12px; }
|
||
.ag-draft-btn { height: 34px; border-radius: 8px; font-size: 12.5px; font-weight: 700; cursor: pointer; border: 1px solid #2b2f3a; background: #1b1e26; color: #aab0bd; display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 0 12px; white-space: nowrap; }
|
||
.ag-draft-btn:hover { color: #fff; border-color: #3a3f4b; }
|
||
.ag-draft-btn.primary { flex: 1; background: #4f46e5; border-color: #4f46e5; color: #fff; }
|
||
.ag-draft-btn.primary:hover { background: #4338ca; }
|
||
.ag-draft-btn.primary:disabled { background: #2b2f3a; border-color: #2b2f3a; color: #6b7280; cursor: not-allowed; }
|
||
.ag-draft-btn svg { width: 14px; height: 14px; display: block; flex-shrink: 0; position: relative; top: 1.5px; }
|
||
.ag-draft-done { display: flex; align-items: center; gap: 7px; padding: 11px 13px 12px; font-size: 12px; font-weight: 600; color: #3ecf8e; font-family: ui-monospace, monospace; }
|
||
.ag-draft-done svg { width: 14px; height: 14px; }
|
||
|
||
/* 구현 검토 리포트 카드 */
|
||
.ag-review { margin-left: 36px; border: 1px solid #2b2f3a; border-radius: 11px; overflow: hidden; background: #12151c; }
|
||
.ag-review-top { padding: 11px 13px 12px; border-bottom: 1px solid #23262f; }
|
||
.ag-review-badge { font-size: 10.5px; font-weight: 700; color: #7aa2f7; font-family: ui-monospace, monospace; letter-spacing: .3px; }
|
||
.ag-review-title { font-size: 13.5px; font-weight: 700; color: #f0f1f4; margin-top: 7px; line-height: 1.4; }
|
||
.ag-rv { display: flex; gap: 10px; padding: 10px 13px; border-bottom: 1px solid #1c1f27; align-items: flex-start; }
|
||
.ag-rv:last-of-type { border-bottom: none; }
|
||
.rv-ico { width: 20px; height: 20px; border-radius: 50%; flex-shrink: 0; display: grid; place-items: center; margin-top: 1px; }
|
||
.rv-ico svg { width: 12px; height: 12px; }
|
||
.ag-rv.ok .rv-ico { background: rgba(62,207,142,.15); color: #3ecf8e; }
|
||
.ag-rv.warn .rv-ico { background: rgba(224,175,104,.15); color: #e0af68; }
|
||
.ag-rv.miss .rv-ico { background: rgba(247,118,142,.15); color: #f7768e; }
|
||
.rv-main { flex: 1; min-width: 0; }
|
||
.rv-text { font-size: 12.5px; font-weight: 600; color: #d4d8e0; line-height: 1.4; }
|
||
.rv-note { font-size: 11px; color: #7b8190; margin-top: 3px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; line-height: 1.5; }
|
||
.rv-tag { font-size: 10px; font-weight: 700; padding: 2px 7px; border-radius: 20px; flex-shrink: 0; white-space: nowrap; font-family: ui-monospace, monospace; margin-top: 1px; }
|
||
.ag-rv.ok .rv-tag { color: #3ecf8e; background: rgba(62,207,142,.12); }
|
||
.ag-rv.warn .rv-tag { color: #e0af68; background: rgba(224,175,104,.12); }
|
||
.ag-rv.miss .rv-tag { color: #f7768e; background: rgba(247,118,142,.12); }
|
||
.ag-review-sum { display: flex; align-items: center; gap: 7px; padding: 10px 13px; background: #14171f; border-top: 1px solid #23262f; font-size: 11.5px; color: #aab0bd; font-family: ui-monospace, monospace; }
|
||
.ag-review-sum b.g { color: #3ecf8e; } .ag-review-sum b.a { color: #e0af68; } .ag-review-sum b.r { color: #f7768e; }
|
||
.ag-review-foot { display: flex; gap: 8px; padding: 11px 13px 12px; }
|
||
|
||
.ag-foot { border-top: 1px solid #262a33; padding: 11px 14px 12px; flex-shrink: 0; }
|
||
.ag-quick { display: flex; gap: 6px; margin-bottom: 9px; flex-wrap: wrap; }
|
||
.ag-chip { font-size: 11.5px; font-weight: 600; color: #aab0bd; background: #171a21; border: 1px solid #262a33; border-radius: 20px; padding: 4px 10px; cursor: pointer; font-family: ui-monospace, monospace; }
|
||
.ag-chip:hover { border-color: #4f46e5; color: #fff; }
|
||
.ag-inputrow { display: flex; align-items: center; gap: 8px; background: #171a21; border: 1px solid #2b2f3a; border-radius: 10px; padding: 7px 7px 7px 12px; }
|
||
.ag-inputrow:focus-within { border-color: #4f46e5; }
|
||
.ag-input { flex: 1; background: transparent; border: none; outline: none; resize: none; color: #e6e8ec; font-family: inherit; font-size: 13px; line-height: 1.5; max-height: 100px; padding: 4px 0; }
|
||
.ag-input::placeholder { color: #5b626e; }
|
||
.ag-send { width: 32px; height: 32px; border: none; border-radius: 8px; background: #4f46e5; color: #fff; display: grid; place-items: center; cursor: pointer; flex-shrink: 0; }
|
||
.ag-send:hover { background: #4338ca; }
|
||
.ag-send svg { width: 16px; height: 16px; }
|
||
.ag-hint { font-size: 10.5px; color: #5b626e; margin-top: 8px; text-align: center; font-family: ui-monospace, monospace; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header class="topbar">
|
||
<div class="brand"><div class="logo">R</div>Relay</div>
|
||
<nav class="topnav">
|
||
<a href="내 업무.html">내 업무</a>
|
||
<a href="저장소 목록.html" class="active">저장소</a>
|
||
</nav>
|
||
<div class="topbar-right">
|
||
<button class="icon-btn agent" id="agentOpen" type="button" title="Relay 에이전트"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg></button>
|
||
<button class="icon-btn" title="검색"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="m21 21-4-4"/></svg></button>
|
||
<button class="icon-btn" title="알림"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></svg></button>
|
||
<div class="me">정</div>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="page" data-screen-label="업무 상세">
|
||
<div class="crumb">
|
||
<a href="저장소 목록.html" class="lnk">저장소</a>
|
||
<span class="sep">/</span>
|
||
<a href="저장소 상세.html" class="lnk">3분기 신제품 런칭 캠페인</a>
|
||
<span class="sep">/</span>
|
||
<a href="저장소 상세.html" class="lnk">업무</a>
|
||
<span class="sep">/</span>
|
||
<b>#9</b>
|
||
</div>
|
||
|
||
<div class="pagehead">
|
||
<div class="ph-main">
|
||
<div class="ph-titlerow">
|
||
<span class="st-badge review">승인 대기</span>
|
||
<span class="ph-tid">#9</span>
|
||
</div>
|
||
<h1 class="ph-title">메인 배너 이미지 2종 (가로/세로) 디자인</h1>
|
||
</div>
|
||
<div class="actions">
|
||
<a class="btn" href="업무 작성.html"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z"/></svg>수정</a>
|
||
<div class="more-wrap">
|
||
<button class="btn icon" id="moreBtn" type="button" title="더보기"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="5" r="1.5"/><circle cx="12" cy="12" r="1.5"/><circle cx="12" cy="19" r="1.5"/></svg></button>
|
||
<div class="menu" id="moreMenu">
|
||
<button class="menu-item" type="button">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
|
||
업무 복제
|
||
</button>
|
||
<button class="menu-item" type="button">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7 0l3-3a5 5 0 0 0-7-7l-1 1"/><path d="M14 11a5 5 0 0 0-7 0l-3 3a5 5 0 0 0 7 7l1-1"/></svg>
|
||
링크 복사
|
||
</button>
|
||
<div class="menu-sep"></div>
|
||
<button class="menu-item danger" id="deleteOpen" type="button">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6M14 11v6"/></svg>
|
||
업무 삭제
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="layout">
|
||
<!-- main -->
|
||
<section class="card main-card">
|
||
<div class="sect">
|
||
<div class="sect-label">업무 내용</div>
|
||
<div class="content">
|
||
<p>9월 신제품 런칭 캠페인에 사용할 메인 배너를 가로형/세로형 2종으로 제작합니다. 톤앤매너는 <span class="mention">@브랜드_가이드라인.pdf</span> 기준을 따라주세요.</p>
|
||
<p>퍼포먼스 광고와 자사몰 상단에 동시 노출되므로, 두 비율 모두 핵심 메시지("민감 피부 저자극")가 명확히 보이도록 구성해주세요. 1차 시안 확정 후 모바일 적응형까지 진행합니다.</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="sect">
|
||
<div class="sect-label">해야 할 일
|
||
<span class="pr"><span class="progress-bar"><i style="width:33%"></i></span><span class="progress-txt">1 / 3 완료</span></span>
|
||
</div>
|
||
<div class="checklist">
|
||
<div class="citem done">
|
||
<span class="cbox done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg></span>
|
||
<span class="ctext">가로형 메인 배너 1920×1080 디자인</span>
|
||
</div>
|
||
<div class="citem">
|
||
<span class="cbox"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg></span>
|
||
<span class="ctext">세로형 메인 배너 1080×1920 디자인</span>
|
||
</div>
|
||
<div class="citem">
|
||
<span class="cbox"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg></span>
|
||
<span class="ctext">모바일 적응형 600×600 제작</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="sect">
|
||
<div class="thread-tabs" id="threadTabs">
|
||
<button class="thread-tab active" data-tab="comments" type="button">댓글 <span class="tt-count">3</span></button>
|
||
<button class="thread-tab" data-tab="activity" type="button">활동 <span class="tt-count">6</span></button>
|
||
</div>
|
||
|
||
<div class="thread-panel" data-panel="comments">
|
||
|
||
<div class="comment">
|
||
<span class="c-av av-f">정</span>
|
||
<div class="c-body">
|
||
<div class="c-head"><span class="c-name">정태경</span><span class="c-role">지시자</span><span class="c-time">2일 전</span></div>
|
||
<div class="c-text">가로형부터 확정하고 세로형 진행해주세요. 카피 위치는 좌측 정렬 기준입니다.</div>
|
||
<div class="c-replies">
|
||
<div class="c-reply">
|
||
<span class="c-av av-b">박</span>
|
||
<div class="c-body">
|
||
<div class="c-head"><span class="c-name">박지훈</span><span class="c-time">2일 전</span></div>
|
||
<div class="c-text">네, 가로형 정렬 기준으로 먼저 잡겠습니다.</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="c-actions"><button class="c-reply-btn" type="button"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 17 4 12 9 7"/><path d="M20 18v-2a4 4 0 0 0-4-4H4"/></svg>답글</button></div>
|
||
<div class="reply-form">
|
||
<span class="c-av" style="background:#0ea5a3">정</span>
|
||
<div class="field">
|
||
<textarea placeholder="답글을 입력하세요…"></textarea>
|
||
<div class="crow"><button class="btn ghost sm reply-cancel" type="button">취소</button><button class="btn primary sm reply-submit" type="button">답글 등록</button></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="comment">
|
||
<span class="c-av av-b">박</span>
|
||
<div class="c-body">
|
||
<div class="c-head"><span class="c-name">박지훈</span><span class="c-time">1일 전</span></div>
|
||
<div class="c-text">가로형 1차 시안 업로드했습니다. 검토 부탁드려요.</div>
|
||
<div class="c-file"><span class="fi">IMG</span>main_banner_h_v1.png</div>
|
||
<div class="c-replies"></div>
|
||
<div class="c-actions"><button class="c-reply-btn" type="button"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 17 4 12 9 7"/><path d="M20 18v-2a4 4 0 0 0-4-4H4"/></svg>답글</button></div>
|
||
<div class="reply-form">
|
||
<span class="c-av" style="background:#0ea5a3">정</span>
|
||
<div class="field">
|
||
<textarea placeholder="답글을 입력하세요…"></textarea>
|
||
<div class="crow"><button class="btn ghost sm reply-cancel" type="button">취소</button><button class="btn primary sm reply-submit" type="button">답글 등록</button></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="comment">
|
||
<span class="c-av av-d">최</span>
|
||
<div class="c-body">
|
||
<div class="c-head"><span class="c-name">최유진</span><span class="c-time">3시간 전</span></div>
|
||
<div class="c-text">세로형은 내일 오전까지 공유하겠습니다.</div>
|
||
<div class="c-replies"></div>
|
||
<div class="c-actions"><button class="c-reply-btn" type="button"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 17 4 12 9 7"/><path d="M20 18v-2a4 4 0 0 0-4-4H4"/></svg>답글</button></div>
|
||
<div class="reply-form">
|
||
<span class="c-av" style="background:#0ea5a3">정</span>
|
||
<div class="field">
|
||
<textarea placeholder="답글을 입력하세요…"></textarea>
|
||
<div class="crow"><button class="btn ghost sm reply-cancel" type="button">취소</button><button class="btn primary sm reply-submit" type="button">답글 등록</button></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="composer">
|
||
<span class="c-av" style="background:#0ea5a3">정</span>
|
||
<div class="field">
|
||
<textarea placeholder="댓글을 입력하세요…"></textarea>
|
||
<div class="crow"><button class="btn primary" type="button">댓글 등록</button></div>
|
||
</div>
|
||
</div>
|
||
</div><!-- /comments panel -->
|
||
|
||
<div class="thread-panel" data-panel="activity" hidden>
|
||
<div class="activity">
|
||
<div class="act">
|
||
<span class="act-ico amber"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M9 12l2 2 4-4"/></svg></span>
|
||
<div class="act-body">
|
||
<div class="act-text"><b>박지훈</b>님이 승인을 요청했습니다</div>
|
||
<div class="act-chips"><span class="act-chip from">진행 중</span><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg><span class="act-chip to">승인 대기</span></div>
|
||
<div class="act-time">2시간 전 · 오후 3:24</div>
|
||
</div>
|
||
</div>
|
||
<div class="act">
|
||
<span class="act-ico green"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg></span>
|
||
<div class="act-body">
|
||
<div class="act-text"><b>박지훈</b>님이 <b>가로형 메인 배너 1920×1080 디자인</b> 항목을 완료했습니다</div>
|
||
<div class="act-time">1일 전 · 오후 6:10</div>
|
||
</div>
|
||
</div>
|
||
<div class="act">
|
||
<span class="act-ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><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"/></svg></span>
|
||
<div class="act-body">
|
||
<div class="act-text"><b>박지훈</b>님이 파일을 첨부했습니다</div>
|
||
<div class="act-file"><span class="fi">IMG</span>main_banner_h_v1.png</div>
|
||
<div class="act-time">1일 전 · 오후 6:08</div>
|
||
</div>
|
||
</div>
|
||
<div class="act">
|
||
<span class="act-ico blue"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg></span>
|
||
<div class="act-body">
|
||
<div class="act-text"><b>정태경</b>님이 마감기한을 <b>6월 18일 (목)</b>로 설정했습니다</div>
|
||
<div class="act-time">6월 8일 · 오전 10:32</div>
|
||
</div>
|
||
</div>
|
||
<div class="act">
|
||
<span class="act-ico blue"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75"/></svg></span>
|
||
<div class="act-body">
|
||
<div class="act-text"><b>정태경</b>님이 <b>박지훈</b>, <b>최유진</b>님을 담당자로 지정했습니다</div>
|
||
<div class="act-time">6월 8일 · 오전 10:31</div>
|
||
</div>
|
||
</div>
|
||
<div class="act">
|
||
<span class="act-ico accent"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"/><line x1="4" y1="22" x2="4" y2="15"/></svg></span>
|
||
<div class="act-body">
|
||
<div class="act-text"><b>정태경</b>님이 업무를 생성했습니다</div>
|
||
<div class="act-time">6월 8일 · 오전 10:30</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- sidebar -->
|
||
<aside class="side">
|
||
<!-- 승인 대기: 지시자(정태경) 시점 결정 카드 -->
|
||
<div class="card ap-card review">
|
||
<div class="ap-card-head">
|
||
<span class="ap-card-ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 8v4l3 2"/><circle cx="12" cy="12" r="9"/></svg></span>
|
||
<span class="ap-card-title">승인 요청 도착</span>
|
||
</div>
|
||
<div class="ap-card-meta"><b>박지훈</b>님이 작업 완료 후 승인을 요청했습니다 · 2시간 전</div>
|
||
<div class="ap-card-note">가로형/세로형 메인 배너 1차 제작을 완료했습니다. 모바일 적응형은 컨펌 후 진행 예정이며, 우선 두 비율 검토 부탁드립니다.</div>
|
||
<div class="ap-card-actions">
|
||
<button class="ap-btn approve" id="approveOpen" type="button"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>승인하기</button>
|
||
<button class="ap-btn reject" id="changesOpen" type="button"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 7v6h6"/><path d="M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13"/></svg>수정 요청</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card side-card">
|
||
<div class="side-field">
|
||
<div class="side-label">상태</div>
|
||
<span class="status-badge review"><span class="dot"></span>승인 대기</span>
|
||
</div>
|
||
<div class="side-field">
|
||
<div class="side-label">담당자 · 2명</div>
|
||
<div class="assignees">
|
||
<div class="ass"><span class="avatar av-b">박</span><span><div class="nm">박지훈</div><div class="rl">퍼포먼스 마케팅</div></span></div>
|
||
<div class="ass"><span class="avatar av-d">최</span><span><div class="nm">최유진</div><div class="rl">UX 디자인</div></span></div>
|
||
</div>
|
||
</div>
|
||
<div class="side-field">
|
||
<div class="side-label">마감기한</div>
|
||
<div class="dl">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>
|
||
6월 18일 (목)
|
||
<span class="dday">D-3</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card side-card">
|
||
<div class="side-field">
|
||
<div class="side-label">첨부파일 · 2개</div>
|
||
<div class="files">
|
||
<div class="file"><span class="file-ico fi-img">IMG</span><span class="file-info"><span class="file-name">레퍼런스_무드보드.png</span><span class="file-size">3.2 MB</span></span></div>
|
||
<div class="file"><span class="file-ico fi-zip">ZIP</span><span class="file-info"><span class="file-name">배너_소스_파일.zip</span><span class="file-size">18.4 MB</span></span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card side-card">
|
||
<div class="side-field">
|
||
<div class="side-label">지시자</div>
|
||
<div class="issuer"><span class="avatar av-f">정</span><span><div class="nm">정태경</div><div class="rl">마케팅 그룹 리드</div></span></div>
|
||
<div class="meta-line">6월 8일 지시 · #9</div>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 삭제 확인 모달 -->
|
||
<div class="modal-backdrop" id="deleteModal">
|
||
<div class="modal" role="dialog" aria-modal="true">
|
||
<div class="modal-body">
|
||
<div class="modal-ico"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/><path d="M10 11v6M14 11v6"/></svg></div>
|
||
<h2 class="modal-title">업무를 삭제할까요?</h2>
|
||
<p class="modal-desc"><b>#9 메인 배너 이미지 2종 (가로/세로) 디자인</b> 업무와 체크리스트·댓글·첨부파일이 영구적으로 삭제됩니다. 이 작업은 되돌릴 수 없습니다.</p>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<button class="mbtn" id="deleteCancel" type="button">취소</button>
|
||
<button class="mbtn danger" type="button">삭제</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 승인 확인 모달 -->
|
||
<div class="modal-backdrop" id="approveModal">
|
||
<div class="modal" role="dialog" aria-modal="true">
|
||
<div class="modal-body">
|
||
<div class="modal-ico" style="background:var(--green-weak);color:var(--green)"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><path d="M22 4 12 14.01l-3-3"/></svg></div>
|
||
<h2 class="modal-title">이 업무를 승인할까요?</h2>
|
||
<p class="modal-desc"><b>박지훈</b>님의 검토 요청을 승인합니다. 업무 상태가 <b>완료</b>로 변경되고 담당자에게 알림이 전송됩니다.</p>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<button class="mbtn" data-close="approveModal" type="button">취소</button>
|
||
<button class="mbtn approve" type="button">승인</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 수정 요청(반려) 모달 -->
|
||
<div class="modal-backdrop" id="changesModal">
|
||
<div class="modal" role="dialog" aria-modal="true">
|
||
<div class="modal-body" style="text-align:left;padding:22px 24px 8px">
|
||
<h2 class="modal-title">수정 요청</h2>
|
||
<p class="modal-desc" style="margin:8px 0 14px">보완이 필요한 내용을 적어주세요. 담당자에게 전달되며 업무가 다시 <b>진행 중</b>으로 돌아갑니다.</p>
|
||
<textarea class="modal-textarea" placeholder="예) 세로형 카피 가독성이 낮으니 자간과 대비를 조정해주세요."></textarea>
|
||
</div>
|
||
<div class="modal-foot">
|
||
<button class="mbtn" data-close="changesModal" type="button">취소</button>
|
||
<button class="mbtn danger" type="button">수정 요청 보내기</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
/* 댓글 / 활동 탭 전환 */
|
||
(function () {
|
||
var tabs = document.getElementById('threadTabs');
|
||
if (!tabs) return;
|
||
var btns = tabs.querySelectorAll('.thread-tab');
|
||
var panels = document.querySelectorAll('.thread-panel');
|
||
btns.forEach(function (b) {
|
||
b.addEventListener('click', function () {
|
||
btns.forEach(function (x) { x.classList.remove('active'); });
|
||
b.classList.add('active');
|
||
var name = b.getAttribute('data-tab');
|
||
panels.forEach(function (p) { p.hidden = p.getAttribute('data-panel') !== name; });
|
||
});
|
||
});
|
||
})();
|
||
</script>
|
||
|
||
<script>
|
||
/* 댓글 답글 */
|
||
(function () {
|
||
document.querySelectorAll('.comment > .c-body').forEach(function (cb) {
|
||
var btn = cb.querySelector(':scope > .c-actions > .c-reply-btn');
|
||
var form = cb.querySelector(':scope > .reply-form');
|
||
var replies = cb.querySelector(':scope > .c-replies');
|
||
if (!btn || !form) return;
|
||
var ta = form.querySelector('textarea');
|
||
btn.addEventListener('click', function () {
|
||
form.classList.toggle('open');
|
||
if (form.classList.contains('open')) ta.focus();
|
||
});
|
||
form.querySelector('.reply-cancel').addEventListener('click', function () {
|
||
form.classList.remove('open'); ta.value = '';
|
||
});
|
||
form.querySelector('.reply-submit').addEventListener('click', function () {
|
||
var text = ta.value.trim(); if (!text) return;
|
||
var el = document.createElement('div');
|
||
el.className = 'c-reply';
|
||
el.innerHTML = '<span class="c-av" style="background:#0ea5a3">정</span>'
|
||
+ '<div class="c-body"><div class="c-head"><span class="c-name">정태경</span><span class="c-role">지시자</span><span class="c-time">방금</span></div>'
|
||
+ '<div class="c-text"></div></div>';
|
||
el.querySelector('.c-text').textContent = text;
|
||
replies.appendChild(el);
|
||
ta.value = ''; form.classList.remove('open');
|
||
});
|
||
});
|
||
})();
|
||
</script>
|
||
|
||
<script>
|
||
moreBtn.addEventListener('click', function (e) { e.stopPropagation(); moreMenu.classList.toggle('open'); });
|
||
document.addEventListener('click', function () { moreMenu.classList.remove('open'); });
|
||
moreMenu.addEventListener('click', function (e) { e.stopPropagation(); });
|
||
|
||
/* 삭제 모달 */
|
||
var dModal = document.getElementById('deleteModal');
|
||
document.getElementById('deleteOpen').addEventListener('click', function () { moreMenu.classList.remove('open'); dModal.classList.add('open'); });
|
||
document.getElementById('deleteCancel').addEventListener('click', function () { dModal.classList.remove('open'); });
|
||
dModal.addEventListener('click', function (e) { if (e.target === dModal) dModal.classList.remove('open'); });
|
||
document.addEventListener('keydown', function (e) { if (e.key === 'Escape') dModal.classList.remove('open'); });
|
||
|
||
/* 승인 / 수정 요청 모달 */
|
||
function openModal(id) { document.getElementById(id).classList.add('open'); }
|
||
function closeModal(el) { el.classList.remove('open'); }
|
||
document.getElementById('approveOpen').addEventListener('click', function () { openModal('approveModal'); });
|
||
document.getElementById('changesOpen').addEventListener('click', function () { openModal('changesModal'); });
|
||
document.querySelectorAll('[data-close]').forEach(function (b) {
|
||
b.addEventListener('click', function () { closeModal(document.getElementById(b.getAttribute('data-close'))); });
|
||
});
|
||
['approveModal', 'changesModal'].forEach(function (id) {
|
||
var m = document.getElementById(id);
|
||
m.addEventListener('click', function (e) { if (e.target === m) closeModal(m); });
|
||
});
|
||
document.addEventListener('keydown', function (e) {
|
||
if (e.key === 'Escape') { closeModal(document.getElementById('approveModal')); closeModal(document.getElementById('changesModal')); }
|
||
});
|
||
</script>
|
||
|
||
<!-- ===== AI 터미널 에이전트 패널 ===== -->
|
||
<aside class="agent-panel" id="agentPanel">
|
||
<div class="ag-head">
|
||
<div class="ag-logo"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg></div>
|
||
<div class="ag-h-main">
|
||
<div class="ag-name">Relay 에이전트 <span class="ag-status"><span class="dot"></span>온라인</span></div>
|
||
<div class="ag-ctx">repo: q3-launch-campaign · 지시서 작성</div>
|
||
</div>
|
||
<button class="ag-hbtn" id="agentReset" type="button" title="새 세션"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-3-6.7"/><path d="M21 4v5h-5"/></svg></button>
|
||
<button class="ag-hbtn" id="agentClose" type="button" title="닫기"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
|
||
</div>
|
||
|
||
<div class="ag-body" id="agBody">
|
||
<div class="ag-sys">— <b>업무 지시서 작성 모드</b> —</div>
|
||
|
||
<div class="ag-msg bot">
|
||
<span class="ava"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg></span>
|
||
<span class="bubble">지시할 업무를 자연어로 설명하거나 <b>/새지시</b> 명령으로 시작하세요. 제가 <b>업무 내용</b>과 <b>해야 할 일</b>을 자동으로 구성해 드립니다.</span>
|
||
</div>
|
||
|
||
<div class="ag-msg user">
|
||
<span class="ava">정</span>
|
||
<span class="bubble">/새지시 9월 신제품 출시용 인스타그램 릴스 3편 제작</span>
|
||
</div>
|
||
|
||
<div class="ag-tool">
|
||
<div class="ag-tool-head"><span class="tdot"></span><span class="cmd">$ relay order draft --analyze</span></div>
|
||
<div class="ag-tool-body">분석 완료 <span class="ok">✓</span>
|
||
<span class="key">유형</span> 콘텐츠 제작 · 영상
|
||
<span class="key">플랫폼</span> Instagram Reels
|
||
<span class="key">수량</span> 3편
|
||
<span class="key">저장소</span> q3-launch-campaign</div>
|
||
</div>
|
||
<div class="ag-msg bot">
|
||
<span class="ava"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg></span>
|
||
<span class="bubble">이렇게 이해했어요. 이 방향이 맞나요? 맞으면 <b>진행</b>이라고 알려주시고, 담당자나 마감을 함께 지정해 주셔도 좋아요.</span>
|
||
</div>
|
||
|
||
<div class="ag-msg user">
|
||
<span class="ava">정</span>
|
||
<span class="bubble">맞아. 담당자는 정민호, 마감은 6월 30일로 진행해줘</span>
|
||
</div>
|
||
<div class="ag-msg bot">
|
||
<span class="ava"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg></span>
|
||
<span class="bubble">업무 내용과 해야 할 일을 자동으로 구성했어요. 포함할 항목을 선택한 뒤 지시서를 만드세요.</span>
|
||
</div>
|
||
|
||
<div class="ag-draft">
|
||
<div class="ag-draft-top">
|
||
<div class="ag-draft-badge">⌁ 자동 생성된 지시서 초안</div>
|
||
<div class="ag-draft-title">9월 신제품 출시 인스타그램 릴스 3편 제작</div>
|
||
</div>
|
||
<div class="ag-draft-sec">
|
||
<div class="ag-draft-label">업무 내용</div>
|
||
<div class="ag-draft-desc">9월 신제품 출시를 알리기 위한 인스타그램 릴스 3편을 제작합니다. 숏폼 포맷으로 핵심 메시지를 빠르게 전달하고, 브랜드 가이드라인의 톤앤매너를 유지합니다. 1차 기획안 확정 후 촬영·편집을 진행합니다.</div>
|
||
</div>
|
||
<div class="ag-draft-sec">
|
||
<div class="ag-draft-label">해야 할 일 · 선택</div>
|
||
<div class="ag-todos">
|
||
<div class="ag-todo sel"><span class="box"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg></span><span class="tt">릴스 3편 기획안 및 스토리보드 작성</span></div>
|
||
<div class="ag-todo sel"><span class="box"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg></span><span class="tt">촬영 콘티 확정 및 촬영 소스 준비</span></div>
|
||
<div class="ag-todo sel"><span class="box"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg></span><span class="tt">영상 편집 및 자막 작업 (3편)</span></div>
|
||
<div class="ag-todo sel"><span class="box"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg></span><span class="tt">브랜드 가이드라인 톤앤매너 검수</span></div>
|
||
<div class="ag-todo sel"><span class="box"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg></span><span class="tt">업로드 일정 확정 및 예약 발행</span></div>
|
||
</div>
|
||
</div>
|
||
<div class="ag-draft-count">선택 <b class="sel-n">5</b> / 5 · 담당 정민호 · 마감 6월 30일</div>
|
||
<div class="ag-draft-foot">
|
||
<button class="ag-draft-btn regen" type="button">다시 생성</button>
|
||
<button class="ag-draft-btn primary create" type="button">지시서 만들기</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="ag-foot">
|
||
<div class="ag-quick">
|
||
<button class="ag-chip" type="button">/새지시</button>
|
||
<button class="ag-chip" type="button">/검토요청</button>
|
||
</div>
|
||
<div class="ag-inputrow">
|
||
<textarea class="ag-input" id="agInput" rows="1" placeholder="지시할 업무를 설명하거나 /새지시 …"></textarea>
|
||
<button class="ag-send" id="agSend" type="button" title="전송"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="m22 2-7 20-4-9-9-4Z"/><path d="M22 2 11 13"/></svg></button>
|
||
</div>
|
||
<div class="ag-hint">/새지시 [업무 설명] 입력 · Enter 전송</div>
|
||
</div>
|
||
</aside>
|
||
|
||
<script>
|
||
(function () {
|
||
var panel = document.getElementById('agentPanel');
|
||
var body = document.getElementById('agBody');
|
||
var input = document.getElementById('agInput');
|
||
document.getElementById('agentOpen').addEventListener('click', function () { panel.classList.add('open'); input.focus(); });
|
||
document.getElementById('agentClose').addEventListener('click', function () { panel.classList.remove('open'); });
|
||
document.addEventListener('keydown', function (e) { if (e.key === 'Escape') panel.classList.remove('open'); });
|
||
|
||
function esc(s) { return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); }
|
||
function scrollDown() { body.scrollTop = body.scrollHeight; }
|
||
var BOT = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg>';
|
||
var CHK = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>';
|
||
var NL = String.fromCharCode(10);
|
||
function addUser(text) {
|
||
var el = document.createElement('div');
|
||
el.className = 'ag-msg user';
|
||
el.innerHTML = '<span class="ava">정</span><span class="bubble">' + esc(text) + '</span>';
|
||
body.appendChild(el); scrollDown();
|
||
}
|
||
function addBot(text) {
|
||
var el = document.createElement('div');
|
||
el.className = 'ag-msg bot';
|
||
el.innerHTML = '<span class="ava">' + BOT + '</span><span class="bubble">' + text + '</span>';
|
||
body.appendChild(el); scrollDown();
|
||
}
|
||
function addTool(cmd, lines) {
|
||
var el = document.createElement('div');
|
||
el.className = 'ag-tool';
|
||
el.innerHTML = '<div class="ag-tool-head"><span class="tdot"></span><span class="cmd">' + esc(cmd) + '</span></div><div class="ag-tool-body">' + lines + '</div>';
|
||
body.appendChild(el); scrollDown();
|
||
}
|
||
function buildDraft(title) {
|
||
var list = [title + ' 기획안 및 범위 정리', '초안 제작 및 1차 작업', '중간 점검 및 피드백 반영', '브랜드 가이드라인 검수', '최종 산출물 공유 및 일정 확정'];
|
||
var items = list.map(function (t) { return '<div class="ag-todo sel"><span class="box">' + CHK + '</span><span class="tt">' + esc(t) + '</span></div>'; }).join('');
|
||
var el = document.createElement('div');
|
||
el.className = 'ag-draft';
|
||
el.innerHTML =
|
||
'<div class="ag-draft-top"><div class="ag-draft-badge">⌁ 자동 생성된 지시서 초안</div><div class="ag-draft-title">' + esc(title) + '</div></div>' +
|
||
'<div class="ag-draft-sec"><div class="ag-draft-label">업무 내용</div><div class="ag-draft-desc">“' + esc(title) + '” 업무를 진행합니다. 핵심 목표를 정의하고 단계별로 산출물을 완성하며, 브랜드 가이드라인과 일정에 맞춰 마무리합니다.</div></div>' +
|
||
'<div class="ag-draft-sec"><div class="ag-draft-label">해야 할 일 · 선택</div><div class="ag-todos">' + items + '</div></div>' +
|
||
'<div class="ag-draft-count">선택 <b class="sel-n">5</b> / 5</div>' +
|
||
'<div class="ag-draft-foot"><button class="ag-draft-btn regen" type="button">다시 생성</button><button class="ag-draft-btn primary create" type="button">지시서 만들기</button></div>';
|
||
body.appendChild(el); scrollDown();
|
||
}
|
||
function recount(card) {
|
||
if (!card) return;
|
||
var n = card.querySelectorAll('.ag-todo.sel').length;
|
||
var a = card.querySelector('.sel-n'); if (a) a.textContent = n;
|
||
var b2 = card.querySelector('.cnt'); if (b2) b2.textContent = n;
|
||
var btn = card.querySelector('.create'); if (btn) btn.disabled = (n === 0);
|
||
}
|
||
var orderSeq = 17;
|
||
function createOrder(card) {
|
||
if (!card || card.classList.contains('created')) return;
|
||
var n = card.querySelectorAll('.ag-todo.sel').length; if (n === 0) return;
|
||
card.classList.add('created');
|
||
var foot = card.querySelector('.ag-draft-foot'); if (foot) foot.outerHTML = '<div class="ag-draft-done">' + CHK + ' 지시서로 생성됨</div>';
|
||
var id = orderSeq++;
|
||
addTool('$ relay order create', '<span class="ok">✓ 지시서 생성됨</span> #' + id + NL + '<span class="key">저장소</span> q3-launch-campaign' + NL + '<span class="key">담당</span> 정민호' + NL + '<span class="key">항목</span> ' + n + '개' + NL + '<span class="key">알림</span> 전송 ✓');
|
||
addBot('지시서 <b>#' + id + '</b>이(가) 생성되어 담당자에게 전달됐어요. 업무 화면에서 확인할 수 있어요.');
|
||
}
|
||
function runReview() {
|
||
setTimeout(function () {
|
||
addTool('$ relay review --task #9 --scan', 'checklist 항목 3개 로드 ✓' + NL + 'repo 스캔 중… q3-launch-campaign@main' + NL + 'matched <span class="ok">banners/main_h_1920x1080.png</span>' + NL + 'matched <span class="warn">banners/main_v_1080x1920.png</span>' + NL + 'missing <span class="key">mobile/adaptive_600.*</span>');
|
||
addBot('해야 할 일 3개를 실제 산출물과 대조해 검토했어요. 결과는 아래와 같아요.');
|
||
buildReview();
|
||
}, 520);
|
||
}
|
||
function buildReview() {
|
||
var rows = [
|
||
{ st: 'ok', t: '가로형 메인 배너 1920×1080 디자인', n: 'banners/main_h_1920x1080.png · 규격·세이프영역 확인', tag: '구현 확인' },
|
||
{ st: 'warn', t: '세로형 메인 배너 1080×1920 디자인', n: '파일은 있으나 카피 세이프영역 미반영 · 보완 권장', tag: '부분 구현' },
|
||
{ st: 'miss', t: '모바일 적응형 600×600 제작', n: 'mobile/adaptive_600 산출물을 찾지 못했어요', tag: '누락' }
|
||
];
|
||
var ICO = {
|
||
ok: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>',
|
||
warn: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M12 9v4M12 17h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z"/></svg>',
|
||
miss: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18M6 6l12 12"/></svg>'
|
||
};
|
||
var items = rows.map(function (r) {
|
||
return '<div class="ag-rv ' + r.st + '"><span class="rv-ico">' + ICO[r.st] + '</span><span class="rv-main"><span class="rv-text">' + r.t + '</span><span class="rv-note">' + r.n + '</span></span><span class="rv-tag">' + r.tag + '</span></div>';
|
||
}).join('');
|
||
var el = document.createElement('div');
|
||
el.className = 'ag-review';
|
||
el.innerHTML =
|
||
'<div class="ag-review-top"><div class="ag-review-badge">⌁ 구현 검토 리포트</div><div class="ag-review-title">메인 배너 이미지 2종 디자인 · #9</div></div>' +
|
||
items +
|
||
'<div class="ag-review-sum">항목 3개 · <b class="g">확인 1</b> · <b class="a">보완 1</b> · <b class="r">누락 1</b></div>' +
|
||
'<div class="ag-review-foot"><button class="ag-draft-btn rv-comment" type="button">보완 항목 댓글로 추가</button><button class="ag-draft-btn primary rv-request" type="button">승인 요청 보내기</button></div>';
|
||
body.appendChild(el); scrollDown();
|
||
}
|
||
function send() {
|
||
var text = input.value.trim(); if (!text) return;
|
||
addUser(text); input.value = ''; input.style.height = 'auto';
|
||
var m = text.match(/^\/(새지시|지시)\s+(.+)/);
|
||
if (/^\/(검토요청|검토|review)/.test(text)) {
|
||
runReview();
|
||
} else if (m) {
|
||
var req = m[2].trim();
|
||
setTimeout(function () {
|
||
addTool('$ relay order draft --analyze', '분석 완료 <span class="ok">✓</span>' + NL + '<span class="key">요청</span> ' + esc(req) + NL + '<span class="key">저장소</span> q3-launch-campaign');
|
||
addBot('요청을 바탕으로 지시서 초안을 구성했어요. 포함할 항목을 선택한 뒤 지시서를 만드세요.');
|
||
buildDraft(req);
|
||
}, 480);
|
||
} else {
|
||
setTimeout(function () {
|
||
addBot('<b>/새지시 [업무 설명]</b> 형식으로 입력하시면 업무 내용과 해야 할 일을 자동으로 구성해 드려요. 예: <span style="color:#7aa2f7">/새지시 랜딩페이지 리뉴얼</span>');
|
||
}, 430);
|
||
}
|
||
}
|
||
body.addEventListener('click', function (e) {
|
||
var todo = e.target.closest('.ag-todo');
|
||
if (todo && !todo.closest('.created')) { todo.classList.toggle('sel'); recount(todo.closest('.ag-draft')); return; }
|
||
var create = e.target.closest('.create');
|
||
if (create) { createOrder(create.closest('.ag-draft')); return; }
|
||
var regen = e.target.closest('.regen');
|
||
if (regen) { addBot('초안을 다시 구성할게요. 항목을 조정하거나 다른 설명을 입력해 주세요.'); return; }
|
||
var rvc = e.target.closest('.rv-comment');
|
||
if (rvc) { addBot('보완이 필요한 <b>2개 항목</b>(세로형 세이프영역, 모바일 적응형)을 업무 댓글로 정리해 남겼어요.'); return; }
|
||
var rvr = e.target.closest('.rv-request');
|
||
if (rvr) {
|
||
var card = rvr.closest('.ag-review');
|
||
rvr.disabled = true; rvr.textContent = '요청 전송됨';
|
||
addTool('$ relay review --submit #9', '<span class="warn">⚠ 누락 1 · 보완 1</span> 포함하여 요청' + NL + '<span class="ok">✓ 승인 요청 전송됨</span> → 정태경(지시자)');
|
||
addBot('검토 결과를 포함해 승인을 요청했어요. 지시자가 리포트를 함께 확인합니다.');
|
||
return;
|
||
}
|
||
});
|
||
document.getElementById('agSend').addEventListener('click', send);
|
||
input.addEventListener('keydown', function (e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); send(); } });
|
||
input.addEventListener('input', function () { this.style.height = 'auto'; this.style.height = Math.min(this.scrollHeight, 100) + 'px'; });
|
||
document.querySelectorAll('.ag-chip').forEach(function (c) {
|
||
c.addEventListener('click', function () {
|
||
var t = c.textContent.trim();
|
||
if (t === '/새지시') { input.value = '/새지시 '; input.focus(); }
|
||
else if (t === '/검토요청') { runReview(); }
|
||
else { addBot('<b>/새지시 [업무 설명]</b>은 지시서 초안을, <b>/검토요청</b>은 해야 할 일의 구현 여부를 점검해 드려요.'); }
|
||
});
|
||
});
|
||
document.getElementById('agentReset').addEventListener('click', function () {
|
||
body.querySelectorAll('.ag-msg, .ag-tool, .ag-draft, .ag-review').forEach(function (n, i) { if (i > 0) n.remove(); });
|
||
addBot('새 세션을 시작했어요. 지시할 업무를 설명하거나 <b>/새지시</b> 명령으로 시작하세요.');
|
||
});
|
||
document.querySelectorAll('.ag-draft').forEach(recount);
|
||
})();
|
||
</script>
|
||
|
||
</body>
|
||
</html> |