feat: [rd] 디자인 인사이트 카드 미리보기 모달 + 와이어프레임 20종
- 카드 클릭 시 미리보기 모달 (좌: 와이어프레임, 우: 정보 패널) - 패턴 카드 20종 CSS 와이어프레임 자동 생성 - KPI 대시보드, 데이터 테이블, 칸반, Command Palette, 사이드바, 모달 폼, 설정, 타임라인, 트리 분할뷰, 온보딩 스테퍼, 토스트, Empty State, 검색 자동완성, 탭 레이아웃, 카드 그리드, 가격표, 캘린더, 채팅, 파일 업로드, 브레드크럼 - 미리보기에서 편집 모달로 전환 가능
This commit is contained in:
@@ -648,6 +648,77 @@
|
||||
.di-help-step .step-title { font-size: 13.5px; font-weight: 600; color: var(--di-text); margin-bottom: 2px; }
|
||||
.di-help-step .step-desc { font-size: 12.5px; color: var(--di-text-secondary); line-height: 1.6; }
|
||||
|
||||
/* Preview Modal */
|
||||
.di-preview { width: 820px; max-width: 95vw; }
|
||||
.di-preview .di-modal-body { padding: 0; }
|
||||
.di-preview-layout { display: flex; gap: 0; }
|
||||
.di-preview-left { flex: 1; min-width: 0; border-right: 1px solid var(--di-border); }
|
||||
.di-preview-right { width: 280px; flex-shrink: 0; padding: 20px; overflow-y: auto; max-height: 70vh; }
|
||||
.di-preview-wireframe {
|
||||
background: #f8fafc;
|
||||
min-height: 320px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.di-preview-wireframe img {
|
||||
max-width: 100%;
|
||||
max-height: 400px;
|
||||
object-fit: contain;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.di-preview-info-item { margin-bottom: 16px; }
|
||||
.di-preview-info-item label {
|
||||
font-size: 10.5px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .5px;
|
||||
color: var(--di-text-secondary);
|
||||
margin-bottom: 4px;
|
||||
display: block;
|
||||
}
|
||||
.di-preview-info-item .val { font-size: 13px; color: var(--di-text); line-height: 1.6; }
|
||||
.di-preview-comp {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 6px;
|
||||
padding: 5px 0;
|
||||
font-size: 12.5px;
|
||||
color: var(--di-text);
|
||||
border-bottom: 1px solid #f1f5f9;
|
||||
}
|
||||
.di-preview-comp:last-child { border-bottom: none; }
|
||||
.di-preview-comp .chk { color: #10b981; font-size: 13px; flex-shrink: 0; margin-top: 1px; }
|
||||
.di-preview-comp .opt { color: #94a3b8; font-size: 13px; flex-shrink: 0; margin-top: 1px; }
|
||||
|
||||
/* Wireframe Styles */
|
||||
.wf-wrap {
|
||||
width: 100%;
|
||||
max-width: 480px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e2e8f0;
|
||||
background: #fff;
|
||||
font-family: 'Pretendard', sans-serif;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,.06);
|
||||
}
|
||||
.wf-bar { height: 10px; border-radius: 3px; background: #e2e8f0; }
|
||||
.wf-bar.sm { width: 40px; }
|
||||
.wf-bar.md { width: 80px; }
|
||||
.wf-bar.lg { width: 120px; }
|
||||
.wf-bar.xl { width: 180px; }
|
||||
.wf-bar.blue { background: #bfdbfe; }
|
||||
.wf-bar.green { background: #bbf7d0; }
|
||||
.wf-bar.amber { background: #fde68a; }
|
||||
.wf-bar.red { background: #fecaca; }
|
||||
.wf-bar.purple { background: #ddd6fe; }
|
||||
.wf-bar.dark { background: #cbd5e1; }
|
||||
.wf-circle { border-radius: 50%; background: #e2e8f0; flex-shrink: 0; }
|
||||
.wf-box { border-radius: 6px; background: #f1f5f9; border: 1px solid #e2e8f0; }
|
||||
.wf-text { font-size: 8px; color: #94a3b8; font-weight: 600; letter-spacing: .3px; white-space: nowrap; }
|
||||
|
||||
/* Status Bar */
|
||||
.di-statusbar {
|
||||
display: flex;
|
||||
@@ -892,7 +963,7 @@
|
||||
<template x-if="viewMode === 'board' && filteredCards.length > 0">
|
||||
<div class="di-board">
|
||||
<template x-for="card in filteredCards" :key="card.id">
|
||||
<div class="di-card" @click="openEditCardModal(card)">
|
||||
<div class="di-card" @click="openPreviewModal(card)">
|
||||
<!-- Pin -->
|
||||
<div class="card-pin" :class="card.pinned && 'pinned'"
|
||||
@click.stop="togglePin(card)" x-text="card.pinned ? '📌' : '📌'"></div>
|
||||
@@ -958,7 +1029,7 @@
|
||||
<template x-if="viewMode === 'gallery' && filteredCards.length > 0">
|
||||
<div class="di-gallery">
|
||||
<template x-for="card in filteredCards" :key="card.id">
|
||||
<div class="di-card" @click="openEditCardModal(card)">
|
||||
<div class="di-card" @click="openPreviewModal(card)">
|
||||
<template x-if="card.image">
|
||||
<img :src="card.image" class="card-img">
|
||||
</template>
|
||||
@@ -984,7 +1055,7 @@
|
||||
<template x-if="viewMode === 'list' && filteredCards.length > 0">
|
||||
<div class="di-list">
|
||||
<template x-for="card in filteredCards" :key="card.id">
|
||||
<div class="di-list-item" @click="openEditCardModal(card)">
|
||||
<div class="di-list-item" @click="openPreviewModal(card)">
|
||||
<template x-if="card.image">
|
||||
<img :src="card.image" class="li-thumb">
|
||||
</template>
|
||||
@@ -1281,6 +1352,186 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- ===== Preview Modal ===== -->
|
||||
<template x-if="showPreviewModal && previewCard">
|
||||
<div class="di-modal-overlay" @click.self="showPreviewModal = false">
|
||||
<div class="di-modal di-preview">
|
||||
<div class="di-modal-header">
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<span class="card-type" :class="previewCard.type === 'reference' ? 'ref' : previewCard.type"
|
||||
x-text="getTypeLabel(previewCard.type)" style="font-size: 11px;"></span>
|
||||
<h3 x-text="previewCard.title || '(제목 없음)'" style="font-size: 15px;"></h3>
|
||||
</div>
|
||||
<div style="display: flex; gap: 6px; align-items: center;">
|
||||
<button class="di-btn sm" @click="showPreviewModal = false; openEditCardModal(previewCard)">
|
||||
<i class="ri-edit-line"></i> 편집
|
||||
</button>
|
||||
<button class="di-btn ghost" @click="showPreviewModal = false">
|
||||
<i class="ri-close-line" style="font-size: 18px;"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="di-preview-layout">
|
||||
<!-- Left: Wireframe / Image -->
|
||||
<div class="di-preview-left">
|
||||
<!-- User Image -->
|
||||
<template x-if="previewCard.image">
|
||||
<div class="di-preview-wireframe">
|
||||
<img :src="previewCard.image">
|
||||
</div>
|
||||
</template>
|
||||
<!-- Before/After Images -->
|
||||
<template x-if="previewCard.type === 'comparison' && !previewCard.image">
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; min-height: 320px;">
|
||||
<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 16px; border-right: 1px solid var(--di-border); background: #fef2f2;">
|
||||
<span style="font-size: 10px; font-weight: 700; color: #dc2626; margin-bottom: 8px;">BEFORE</span>
|
||||
<template x-if="previewCard.beforeImage"><img :src="previewCard.beforeImage" style="max-width: 100%; max-height: 260px; border-radius: 6px;"></template>
|
||||
<template x-if="!previewCard.beforeImage"><span style="color: #94a3b8;">이미지 없음</span></template>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 16px; background: #ecfdf5;">
|
||||
<span style="font-size: 10px; font-weight: 700; color: #059669; margin-bottom: 8px;">AFTER</span>
|
||||
<template x-if="previewCard.afterImage"><img :src="previewCard.afterImage" style="max-width: 100%; max-height: 260px; border-radius: 6px;"></template>
|
||||
<template x-if="!previewCard.afterImage"><span style="color: #94a3b8;">이미지 없음</span></template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<!-- Auto Wireframe (no user image) -->
|
||||
<template x-if="!previewCard.image && previewCard.type !== 'comparison'">
|
||||
<div class="di-preview-wireframe" x-html="getWireframe(previewCard)"></div>
|
||||
</template>
|
||||
|
||||
<!-- Guidelines (below wireframe) -->
|
||||
<template x-if="previewCard.guidelines || previewCard.memo || previewCard.suggestion || previewCard.effect">
|
||||
<div style="padding: 16px 20px; border-top: 1px solid var(--di-border); background: #fff;">
|
||||
<template x-if="previewCard.memo">
|
||||
<div style="margin-bottom: 12px;">
|
||||
<div style="font-size: 11px; font-weight: 700; color: var(--di-text-secondary); margin-bottom: 4px;">인사이트 메모</div>
|
||||
<div style="font-size: 13px; color: var(--di-text); line-height: 1.7;" x-text="previewCard.memo"></div>
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="previewCard.guidelines">
|
||||
<div style="margin-bottom: 12px;">
|
||||
<div style="font-size: 11px; font-weight: 700; color: var(--di-text-secondary); margin-bottom: 4px;">사용 가이드라인</div>
|
||||
<div style="font-size: 13px; color: var(--di-text); line-height: 1.7; background: #f0fdf4; padding: 10px 14px; border-radius: 8px; border-left: 3px solid #10b981;" x-text="previewCard.guidelines"></div>
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="previewCard.suggestion">
|
||||
<div style="margin-bottom: 12px;">
|
||||
<div style="font-size: 11px; font-weight: 700; color: var(--di-text-secondary); margin-bottom: 4px;">개선 제안</div>
|
||||
<div style="font-size: 13px; color: var(--di-text); line-height: 1.7; background: #fffbeb; padding: 10px 14px; border-radius: 8px; border-left: 3px solid #f59e0b;" x-text="previewCard.suggestion"></div>
|
||||
</div>
|
||||
</template>
|
||||
<template x-if="previewCard.effect">
|
||||
<div>
|
||||
<div style="font-size: 11px; font-weight: 700; color: var(--di-text-secondary); margin-bottom: 4px;">개선 효과</div>
|
||||
<div style="font-size: 13px; color: var(--di-text); line-height: 1.7; background: #eff6ff; padding: 10px 14px; border-radius: 8px; border-left: 3px solid #3b82f6;" x-text="previewCard.effect"></div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- Right: Info Panel -->
|
||||
<div class="di-preview-right">
|
||||
<!-- Rating -->
|
||||
<div class="di-preview-info-item">
|
||||
<label>평점</label>
|
||||
<div class="card-rating" style="font-size: 18px; letter-spacing: 2px;" x-text="getRatingStars(previewCard.rating || 0)"></div>
|
||||
</div>
|
||||
|
||||
<!-- Category -->
|
||||
<div class="di-preview-info-item">
|
||||
<label>카테고리</label>
|
||||
<div class="val" x-text="getCategoryLabel(previewCard.category)"></div>
|
||||
</div>
|
||||
|
||||
<!-- Tags -->
|
||||
<template x-if="(previewCard.tags || []).length > 0">
|
||||
<div class="di-preview-info-item">
|
||||
<label>태그</label>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 4px;">
|
||||
<template x-for="tag in previewCard.tags" :key="tag">
|
||||
<span class="card-tag" x-text="tag" style="font-size: 11.5px; padding: 3px 8px;"></span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Source -->
|
||||
<template x-if="previewCard.source">
|
||||
<div class="di-preview-info-item">
|
||||
<label>출처</label>
|
||||
<div class="val" x-text="previewCard.source"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Used In (Pattern) -->
|
||||
<template x-if="(previewCard.usedIn || []).length > 0">
|
||||
<div class="di-preview-info-item">
|
||||
<label>사용처</label>
|
||||
<div style="display: flex; flex-direction: column; gap: 2px;">
|
||||
<template x-for="u in previewCard.usedIn" :key="u">
|
||||
<div style="font-size: 12.5px; color: var(--di-text); padding: 2px 0;">• <span x-text="u"></span></div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Components (Pattern) -->
|
||||
<template x-if="(previewCard.components || []).length > 0 && previewCard.components[0]?.name">
|
||||
<div class="di-preview-info-item">
|
||||
<label>구성 요소</label>
|
||||
<div>
|
||||
<template x-for="comp in previewCard.components" :key="comp.name">
|
||||
<div class="di-preview-comp">
|
||||
<span :class="comp.required ? 'chk' : 'opt'" x-text="comp.required ? '✓' : '○'"></span>
|
||||
<span x-text="comp.name"></span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- CRAP Principles (Analysis) -->
|
||||
<template x-if="previewCard.type === 'analysis' && previewCard.principles">
|
||||
<div class="di-preview-info-item">
|
||||
<label>디자인 원칙</label>
|
||||
<div style="display: flex; flex-direction: column; gap: 3px;">
|
||||
<template x-for="p in designPrinciples" :key="p.key">
|
||||
<template x-if="previewCard.principles[p.key]">
|
||||
<div style="font-size: 12px; display: flex; gap: 6px; align-items: center;">
|
||||
<span x-text="({pass:'✅',warn:'⚠️',fail:'❌'})[previewCard.principles[p.key]] || '—'"></span>
|
||||
<span x-text="p.label"></span>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Changes (Comparison) -->
|
||||
<template x-if="(previewCard.changes || []).length > 0">
|
||||
<div class="di-preview-info-item">
|
||||
<label>변경 포인트</label>
|
||||
<div style="display: flex; flex-direction: column; gap: 3px;">
|
||||
<template x-for="(c, i) in previewCard.changes" :key="i">
|
||||
<div style="font-size: 12.5px; color: var(--di-text); line-height: 1.5;" x-text="c"></div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Date -->
|
||||
<div class="di-preview-info-item" style="margin-top: auto; padding-top: 12px; border-top: 1px solid #f1f5f9;">
|
||||
<label>생성일</label>
|
||||
<div class="val" x-text="formatDate(previewCard.createdAt)"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- ===== Help Modal ===== -->
|
||||
<template x-if="showHelpModal">
|
||||
<div class="di-modal-overlay" @click.self="showHelpModal = false">
|
||||
@@ -1769,6 +2020,8 @@ function designInsight() {
|
||||
showExportMenu: false,
|
||||
showHelpModal: false,
|
||||
helpTab: 'overview',
|
||||
showPreviewModal: false,
|
||||
previewCard: null,
|
||||
editingCard: {},
|
||||
newProjectTitle: '',
|
||||
toastMsg: '',
|
||||
@@ -1931,6 +2184,475 @@ function designInsight() {
|
||||
this.showCardModal = true;
|
||||
},
|
||||
|
||||
openPreviewModal(card) {
|
||||
this.previewCard = card;
|
||||
this.showPreviewModal = true;
|
||||
},
|
||||
|
||||
getCategoryLabel(code) {
|
||||
const c = this.categories.find(cat => cat.code === code);
|
||||
return c ? c.icon + ' ' + c.label : code;
|
||||
},
|
||||
|
||||
getWireframe(card) {
|
||||
const t = (card.title || '').toLowerCase();
|
||||
const tags = (card.tags || []).join(' ').toLowerCase();
|
||||
const key = t + ' ' + tags;
|
||||
|
||||
// KPI 대시보드
|
||||
if (key.includes('kpi') || key.includes('대시보드') && key.includes('통계')) return `
|
||||
<div class="wf-wrap" style="padding: 0;">
|
||||
<div style="padding: 8px 12px; border-bottom: 1px solid #e2e8f0; display: flex; align-items: center; gap: 8px;">
|
||||
<div class="wf-circle" style="width: 16px; height: 16px; background: #bfdbfe;"></div>
|
||||
<div class="wf-bar md dark"></div><div style="flex:1;"></div>
|
||||
<div class="wf-bar sm" style="background: #bfdbfe; border-radius: 4px; height: 14px;"></div>
|
||||
</div>
|
||||
<div style="padding: 12px; display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 8px;">
|
||||
<div class="wf-box" style="padding: 10px; text-align: center;"><div class="wf-text">매출</div><div style="font-size: 14px; font-weight: 800; color: #1e293b; margin: 4px 0;">₩24.5M</div><div style="font-size: 8px; color: #10b981;">▲ 12.5%</div></div>
|
||||
<div class="wf-box" style="padding: 10px; text-align: center;"><div class="wf-text">수주</div><div style="font-size: 14px; font-weight: 800; color: #1e293b; margin: 4px 0;">148건</div><div style="font-size: 8px; color: #10b981;">▲ 8.2%</div></div>
|
||||
<div class="wf-box" style="padding: 10px; text-align: center;"><div class="wf-text">미수금</div><div style="font-size: 14px; font-weight: 800; color: #1e293b; margin: 4px 0;">₩3.2M</div><div style="font-size: 8px; color: #ef4444;">▲ 2.1%</div></div>
|
||||
<div class="wf-box" style="padding: 10px; text-align: center;"><div class="wf-text">거래처</div><div style="font-size: 14px; font-weight: 800; color: #1e293b; margin: 4px 0;">52개</div><div style="font-size: 8px; color: #10b981;">▲ 3건</div></div>
|
||||
</div>
|
||||
<div style="padding: 0 12px 12px; display: grid; grid-template-columns: 2fr 1fr; gap: 8px;">
|
||||
<div class="wf-box" style="padding: 12px; height: 100px;">
|
||||
<div class="wf-text" style="margin-bottom: 8px;">월별 매출 추이</div>
|
||||
<svg width="100%" height="50" viewBox="0 0 200 50"><polyline points="10,40 40,30 70,35 100,20 130,25 160,10 190,15" fill="none" stroke="#3b82f6" stroke-width="2"/><polyline points="10,40 40,30 70,35 100,20 130,25 160,10 190,15" fill="url(#grad)" stroke="none"/><defs><linearGradient id="grad" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#3b82f6" stop-opacity="0.2"/><stop offset="100%" stop-color="#3b82f6" stop-opacity="0"/></linearGradient></defs></svg>
|
||||
</div>
|
||||
<div class="wf-box" style="padding: 12px; height: 100px;">
|
||||
<div class="wf-text" style="margin-bottom: 8px;">최근 활동</div>
|
||||
<div style="display:flex;flex-direction:column;gap:6px;">
|
||||
<div style="display:flex;gap:6px;align-items:center;"><div class="wf-circle" style="width:8px;height:8px;background:#10b981;"></div><div class="wf-bar lg"></div></div>
|
||||
<div style="display:flex;gap:6px;align-items:center;"><div class="wf-circle" style="width:8px;height:8px;background:#3b82f6;"></div><div class="wf-bar xl"></div></div>
|
||||
<div style="display:flex;gap:6px;align-items:center;"><div class="wf-circle" style="width:8px;height:8px;background:#f59e0b;"></div><div class="wf-bar md"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
// 데이터 테이블
|
||||
if (key.includes('테이블') || key.includes('검색/필터') || key.includes('페이지네이션')) return `
|
||||
<div class="wf-wrap" style="padding: 0;">
|
||||
<div style="padding: 10px 12px; border-bottom: 1px solid #e2e8f0; display: flex; gap: 8px; align-items: center;">
|
||||
<div style="flex:1; height: 28px; background: #f1f5f9; border-radius: 6px; display: flex; align-items: center; padding: 0 10px;"><span style="color: #94a3b8; font-size: 9px;">🔍 검색어를 입력하세요...</span></div>
|
||||
<div style="display: flex; gap: 4px;">
|
||||
<div style="padding: 4px 10px; background: #eff6ff; border-radius: 4px; font-size: 8px; color: #3b82f6;">상태 ▾</div>
|
||||
<div style="padding: 4px 10px; background: #f1f5f9; border-radius: 4px; font-size: 8px; color: #64748b;">날짜 ▾</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 0 12px;">
|
||||
<div style="display: grid; grid-template-columns: 30px 2fr 1fr 1fr 80px; gap: 8px; padding: 8px 0; border-bottom: 1px solid #e2e8f0; font-size: 8px; font-weight: 700; color: #94a3b8;">
|
||||
<div>☐</div><div>이름 ↕</div><div>상태</div><div>날짜</div><div>액션</div>
|
||||
</div>
|
||||
${[1,2,3,4,5].map(i => `<div style="display: grid; grid-template-columns: 30px 2fr 1fr 1fr 80px; gap: 8px; padding: 7px 0; border-bottom: 1px solid #f1f5f9; font-size: 8px; color: #475569; align-items: center;">
|
||||
<div>☐</div><div class="wf-bar" style="width:${60+i*15}px;height:8px;"></div>
|
||||
<div><span style="padding:2px 6px;border-radius:3px;background:${['#ecfdf5','#fef3c7','#eff6ff'][i%3]};font-size:7px;">${['완료','진행중','대기'][i%3]}</span></div>
|
||||
<div style="color:#94a3b8;">2026-03-0${i}</div>
|
||||
<div style="display:flex;gap:4px;"><span style="color:#3b82f6;font-size:7px;">보기</span><span style="color:#94a3b8;font-size:7px;">삭제</span></div>
|
||||
</div>`).join('')}
|
||||
</div>
|
||||
<div style="padding: 8px 12px; border-top: 1px solid #e2e8f0; display: flex; justify-content: space-between; align-items: center;">
|
||||
<span style="font-size: 8px; color: #94a3b8;">1-5 / 128건</span>
|
||||
<div style="display: flex; gap: 3px;">
|
||||
${['◀','1','2','3','...','26','▶'].map(p => `<span style="padding:2px 6px;border-radius:3px;background:${p==='1'?'#3b82f6':'#f1f5f9'};color:${p==='1'?'#fff':'#64748b'};font-size:8px;">${p}</span>`).join('')}
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
// 칸반 보드
|
||||
if (key.includes('칸반') || key.includes('kanban')) return `
|
||||
<div class="wf-wrap" style="padding: 0;">
|
||||
<div style="padding: 8px 12px; border-bottom: 1px solid #e2e8f0; display: flex; align-items: center; gap: 8px;">
|
||||
<div class="wf-bar md dark"></div><div style="flex:1;"></div>
|
||||
<div style="font-size: 8px; color: #94a3b8;">필터 ▾</div>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 0; min-height: 200px;">
|
||||
${['할 일|3|#94a3b8','진행 중|2|#3b82f6','검토|1|#f59e0b','완료|4|#10b981'].map(col => {
|
||||
const [name,cnt,color] = col.split('|');
|
||||
return `<div style="border-right: 1px solid #f1f5f9; padding: 8px 6px;">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;">
|
||||
<span style="font-size:8px;font-weight:700;color:${color};">${name}</span>
|
||||
<span style="font-size:7px;background:#f1f5f9;padding:1px 5px;border-radius:8px;color:#94a3b8;">${cnt}</span>
|
||||
</div>
|
||||
${Array.from({length:parseInt(cnt)},(_,i)=>`<div style="background:#fff;border:1px solid #e2e8f0;border-radius:6px;padding:6px 8px;margin-bottom:4px;box-shadow:0 1px 2px rgba(0,0,0,.04);">
|
||||
<div class="wf-bar" style="width:${50+i*20}px;height:7px;margin-bottom:4px;"></div>
|
||||
<div style="display:flex;gap:3px;"><span style="font-size:6px;padding:1px 4px;background:#f1f5f9;border-radius:2px;">태그</span></div>
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:4px;">
|
||||
<div class="wf-circle" style="width:12px;height:12px;background:${color};opacity:.3;"></div>
|
||||
<span style="font-size:6px;color:#94a3b8;">3/8</span>
|
||||
</div>
|
||||
</div>`).join('')}
|
||||
<div style="text-align:center;padding:4px;font-size:8px;color:#94a3b8;border:1px dashed #e2e8f0;border-radius:4px;cursor:pointer;">+ 추가</div>
|
||||
</div>`;
|
||||
}).join('')}
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
// Command Palette
|
||||
if (key.includes('command') || key.includes('cmd+k') || key.includes('커맨드')) return `
|
||||
<div style="width: 100%; max-width: 400px; background: rgba(0,0,0,.5); border-radius: 12px; padding: 40px 30px; display: flex; justify-content: center;">
|
||||
<div style="width: 100%; background: #fff; border-radius: 10px; box-shadow: 0 20px 60px rgba(0,0,0,.3); overflow: hidden;">
|
||||
<div style="padding: 12px; border-bottom: 1px solid #e2e8f0;">
|
||||
<div style="background: #f8fafc; border-radius: 6px; padding: 8px 12px; display: flex; align-items: center; gap: 8px;">
|
||||
<span style="color: #94a3b8; font-size: 12px;">🔍</span>
|
||||
<span style="font-size: 10px; color: #94a3b8;">명령어 또는 페이지 검색...</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 6px;">
|
||||
<div style="font-size: 8px; color: #94a3b8; padding: 4px 10px; font-weight: 600;">최근</div>
|
||||
${['수주 관리|📋','거래처 목록|🏢','품목 기준관리|📦'].map(item => {
|
||||
const [n,ic] = item.split('|');
|
||||
return `<div style="display:flex;align-items:center;gap:8px;padding:6px 10px;border-radius:6px;cursor:pointer;"><span>${ic}</span><span style="font-size:10px;flex:1;">${n}</span><span style="font-size:8px;color:#94a3b8;">↵</span></div>`;
|
||||
}).join('')}
|
||||
<div style="font-size: 8px; color: #94a3b8; padding: 4px 10px; font-weight: 600; margin-top: 4px;">액션</div>
|
||||
${['새 수주 등록|➕','설정 열기|⚙️','도움말|❓'].map(item => {
|
||||
const [n,ic] = item.split('|');
|
||||
return `<div style="display:flex;align-items:center;gap:8px;padding:6px 10px;border-radius:6px;cursor:pointer;"><span>${ic}</span><span style="font-size:10px;flex:1;">${n}</span></div>`;
|
||||
}).join('')}
|
||||
</div>
|
||||
<div style="padding: 6px 12px; border-top: 1px solid #f1f5f9; display: flex; gap: 12px;">
|
||||
<span style="font-size: 7px; color: #94a3b8;">↑↓ 이동</span>
|
||||
<span style="font-size: 7px; color: #94a3b8;">↵ 선택</span>
|
||||
<span style="font-size: 7px; color: #94a3b8;">ESC 닫기</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
// 사이드바
|
||||
if (key.includes('사이드바') && key.includes('네비게이션')) return `
|
||||
<div class="wf-wrap" style="display: flex; padding: 0; min-height: 240px;">
|
||||
<div style="width: 160px; background: #1e293b; padding: 12px 8px; display: flex; flex-direction: column; gap: 2px; flex-shrink: 0;">
|
||||
<div style="display: flex; align-items: center; gap: 6px; padding: 6px 8px; margin-bottom: 8px;"><div class="wf-circle" style="width: 20px; height: 20px; background: #3b82f6;"></div><span style="font-size: 10px; color: #fff; font-weight: 700;">SAM</span></div>
|
||||
<div style="font-size: 7px; color: #64748b; padding: 4px 8px; font-weight: 600;">메인</div>
|
||||
${['📊 대시보드|true','📋 수주관리|false','🏢 거래처|false','📦 품목관리|false'].map(m => {
|
||||
const [n,a] = m.split('|');
|
||||
return `<div style="display:flex;align-items:center;gap:6px;padding:5px 8px;border-radius:4px;font-size:9px;color:${a==='true'?'#fff':'#94a3b8'};background:${a==='true'?'rgba(59,130,246,.2)':'transparent'};${a==='true'?'border-left:2px solid #3b82f6;':''}">${n}</div>`;
|
||||
}).join('')}
|
||||
<div style="font-size: 7px; color: #64748b; padding: 4px 8px; font-weight: 600; margin-top: 6px;">관리</div>
|
||||
${['⚙️ 설정','👥 사용자','🔔 알림'].map(n => `<div style="display:flex;align-items:center;gap:6px;padding:5px 8px;font-size:9px;color:#94a3b8;">${n}</div>`).join('')}
|
||||
<div style="flex:1;"></div>
|
||||
<div style="display:flex;align-items:center;gap:6px;padding:6px 8px;border-top:1px solid #334155;margin-top:8px;"><div class="wf-circle" style="width:18px;height:18px;background:#64748b;"></div><span style="font-size:8px;color:#94a3b8;">홍길동</span></div>
|
||||
</div>
|
||||
<div style="flex: 1; padding: 16px; background: #f8fafc;">
|
||||
<div class="wf-bar xl dark" style="height: 12px; margin-bottom: 12px;"></div>
|
||||
<div class="wf-bar" style="width: 90%; height: 8px; margin-bottom: 6px;"></div>
|
||||
<div class="wf-bar" style="width: 70%; height: 8px; margin-bottom: 16px;"></div>
|
||||
<div class="wf-box" style="height: 120px; padding: 12px;"><div class="wf-text">콘텐츠 영역</div></div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
// 모달 폼
|
||||
if (key.includes('모달') && (key.includes('폼') || key.includes('생성'))) return `
|
||||
<div style="width: 100%; max-width: 420px; background: rgba(0,0,0,.4); border-radius: 12px; padding: 30px; display: flex; justify-content: center;">
|
||||
<div style="width: 100%; background: #fff; border-radius: 10px; box-shadow: 0 20px 60px rgba(0,0,0,.2); overflow: hidden;">
|
||||
<div style="padding: 14px 16px; border-bottom: 1px solid #e2e8f0; display: flex; justify-content: space-between; align-items: center;">
|
||||
<span style="font-size: 12px; font-weight: 700;">새 항목 등록</span><span style="color: #94a3b8; cursor: pointer;">✕</span>
|
||||
</div>
|
||||
<div style="padding: 16px; display: flex; flex-direction: column; gap: 12px;">
|
||||
${['이름 *','카테고리','설명'].map(f => `<div>
|
||||
<div style="font-size: 9px; font-weight: 600; color: #475569; margin-bottom: 4px;">${f}</div>
|
||||
<div style="height: ${f==='설명'?'48px':'28px'}; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 6px;"></div>
|
||||
</div>`).join('')}
|
||||
</div>
|
||||
<div style="padding: 12px 16px; border-top: 1px solid #e2e8f0; display: flex; justify-content: flex-end; gap: 6px;">
|
||||
<div style="padding: 5px 14px; border-radius: 6px; font-size: 10px; color: #64748b; border: 1px solid #e2e8f0;">취소</div>
|
||||
<div style="padding: 5px 14px; border-radius: 6px; font-size: 10px; color: #fff; background: #3b82f6;">저장</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
// 설정
|
||||
if (key.includes('설정') && key.includes('섹션')) return `
|
||||
<div class="wf-wrap" style="display: flex; padding: 0; min-height: 220px;">
|
||||
<div style="width: 130px; border-right: 1px solid #e2e8f0; padding: 12px 6px; display: flex; flex-direction: column; gap: 1px;">
|
||||
${['👤 프로필|true','🔔 알림','🔐 보안','🎨 테마','⚡ 연동','🗑️ 위험'].map(m => {
|
||||
const [n,a] = m.split('|');
|
||||
return `<div style="padding:5px 8px;border-radius:4px;font-size:9px;color:${a?'#3b82f6':'#64748b'};background:${a?'#eff6ff':'transparent'};font-weight:${a?'600':'400'};">${n}</div>`;
|
||||
}).join('')}
|
||||
</div>
|
||||
<div style="flex: 1; padding: 16px; display: flex; flex-direction: column; gap: 12px; overflow: hidden;">
|
||||
<div style="font-size: 12px; font-weight: 700; color: #1e293b;">프로필 설정</div>
|
||||
${['프로필 사진','이름','이메일'].map(f => `<div class="wf-box" style="padding: 10px 12px; display: flex; justify-content: space-between; align-items: center;">
|
||||
<div><div style="font-size:9px;font-weight:600;color:#475569;">${f}</div><div class="wf-bar md" style="margin-top:3px;"></div></div>
|
||||
<div style="font-size:8px;color:#3b82f6;">변경</div>
|
||||
</div>`).join('')}
|
||||
<div style="padding: 5px 12px; background: #3b82f6; color: #fff; border-radius: 6px; font-size: 9px; align-self: flex-start;">저장</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
// 타임라인
|
||||
if (key.includes('타임라인') || key.includes('활동') && key.includes('피드')) return `
|
||||
<div class="wf-wrap" style="padding: 16px 16px 16px 32px;">
|
||||
<div style="font-size: 10px; font-weight: 700; color: #1e293b; margin-bottom: 12px;">활동 기록</div>
|
||||
<div style="position: relative; padding-left: 20px;">
|
||||
<div style="position: absolute; left: 5px; top: 0; bottom: 0; width: 2px; background: #e2e8f0;"></div>
|
||||
${[
|
||||
{color:'#10b981',text:'수주 #1024 등록 완료',time:'10분 전',user:'김영업'},
|
||||
{color:'#3b82f6',text:'거래처 "ABC상사" 정보 수정',time:'1시간 전',user:'이관리'},
|
||||
{color:'#f59e0b',text:'견적서 #502 승인 대기',time:'3시간 전',user:'박대리'},
|
||||
{color:'#8b5cf6',text:'품목 "BL-200" 단가 변경',time:'어제',user:'최기준'},
|
||||
].map(e => `<div style="position:relative;margin-bottom:16px;">
|
||||
<div style="position:absolute;left:-18px;top:2px;width:12px;height:12px;border-radius:50%;background:${e.color};border:2px solid #fff;"></div>
|
||||
<div class="wf-box" style="padding:8px 12px;">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:3px;">
|
||||
<span style="font-size:10px;font-weight:600;color:#1e293b;">${e.text}</span>
|
||||
<span style="font-size:8px;color:#94a3b8;">${e.time}</span>
|
||||
</div>
|
||||
<span style="font-size:8px;color:#64748b;">${e.user}</span>
|
||||
</div>
|
||||
</div>`).join('')}
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
// 트리 + 상세
|
||||
if (key.includes('트리') && key.includes('분할')) return `
|
||||
<div class="wf-wrap" style="display: flex; padding: 0; min-height: 220px;">
|
||||
<div style="width: 150px; border-right: 1px solid #e2e8f0; padding: 10px 6px; font-size: 9px; overflow: hidden;">
|
||||
<div style="display:flex;align-items:center;gap:4px;padding:3px 4px;font-weight:600;color:#1e293b;">📁 전체 메뉴</div>
|
||||
<div style="padding-left:12px;">
|
||||
<div style="display:flex;align-items:center;gap:4px;padding:3px 4px;color:#64748b;">▾ 📂 영업관리</div>
|
||||
<div style="padding-left:14px;">
|
||||
<div style="padding:3px 4px;color:#3b82f6;background:#eff6ff;border-radius:3px;font-weight:600;">📄 수주관리</div>
|
||||
<div style="padding:3px 4px;color:#64748b;">📄 거래처</div>
|
||||
<div style="padding:3px 4px;color:#64748b;">📄 견적서</div>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:4px;padding:3px 4px;color:#64748b;">▸ 📂 생산관리</div>
|
||||
<div style="display:flex;align-items:center;gap:4px;padding:3px 4px;color:#64748b;">▸ 📂 품질관리</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="flex: 1; padding: 14px;">
|
||||
<div style="font-size:11px;font-weight:700;color:#1e293b;margin-bottom:10px;">📄 수주관리</div>
|
||||
<div style="display:flex;flex-direction:column;gap:8px;">
|
||||
${['메뉴명|수주관리','URL|/sales/orders','아이콘|shopping-cart','정렬|3'].map(f => {
|
||||
const [k,v] = f.split('|');
|
||||
return `<div style="display:flex;gap:8px;align-items:center;"><span style="font-size:8px;color:#94a3b8;width:40px;">${k}</span><div style="flex:1;height:24px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:4px;padding:0 8px;font-size:9px;line-height:24px;color:#475569;">${v}</div></div>`;
|
||||
}).join('')}
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
// 캘린더
|
||||
if (key.includes('캘린더') || key.includes('일정')) return `
|
||||
<div class="wf-wrap" style="padding: 0;">
|
||||
<div style="padding: 8px 12px; border-bottom: 1px solid #e2e8f0; display: flex; align-items: center; gap: 8px;">
|
||||
<span style="font-size:10px;color:#94a3b8;">◀</span>
|
||||
<span style="font-size:11px;font-weight:700;color:#1e293b;">2026년 3월</span>
|
||||
<span style="font-size:10px;color:#94a3b8;">▶</span>
|
||||
<div style="flex:1;"></div>
|
||||
<div style="font-size:8px;padding:3px 8px;background:#3b82f6;color:#fff;border-radius:4px;">오늘</div>
|
||||
<div style="display:flex;gap:2px;"><span style="font-size:8px;padding:2px 6px;background:#f1f5f9;border-radius:3px;">월</span><span style="font-size:8px;padding:2px 6px;background:#eff6ff;color:#3b82f6;border-radius:3px;">주</span></div>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: repeat(7, 1fr); font-size: 8px; text-align: center;">
|
||||
${['일','월','화','수','목','금','토'].map(d => `<div style="padding:4px;color:#94a3b8;font-weight:600;border-bottom:1px solid #f1f5f9;">${d}</div>`).join('')}
|
||||
${Array.from({length:35},(_, i) => {
|
||||
const day = i - 5;
|
||||
const isToday = day === 8;
|
||||
const hasEvent = [3,8,12,15,22].includes(day);
|
||||
return `<div style="padding:4px 2px;min-height:28px;border-bottom:1px solid #f8fafc;border-right:1px solid #f8fafc;${day<1||day>31?'color:#cbd5e1;':''}${isToday?'background:#eff6ff;':''}">
|
||||
<div style="${isToday?'background:#3b82f6;color:#fff;width:16px;height:16px;border-radius:50%;margin:0 auto;line-height:16px;':'color:#475569;'}">${day<1?'':day>31?'':day}</div>
|
||||
${hasEvent?`<div style="margin-top:1px;height:3px;background:${isToday?'#3b82f6':'#10b981'};border-radius:2px;"></div>`:''}
|
||||
</div>`;
|
||||
}).join('')}
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
// 카드 그리드
|
||||
if (key.includes('카드') && key.includes('그리드')) return `
|
||||
<div class="wf-wrap" style="padding: 12px;">
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px;">
|
||||
${Array.from({length:6},(_,i) => `<div style="border-radius:6px;overflow:hidden;border:1px solid #e2e8f0;">
|
||||
<div style="height:60px;background:${['#bfdbfe','#bbf7d0','#fde68a','#ddd6fe','#fecaca','#e0e7ff'][i]};"></div>
|
||||
<div style="padding:6px 8px;">
|
||||
<div class="wf-bar" style="width:${50+i*10}px;height:7px;margin-bottom:4px;"></div>
|
||||
<div class="wf-bar sm" style="height:6px;"></div>
|
||||
</div>
|
||||
</div>`).join('')}
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
// 가격표
|
||||
if (key.includes('가격') || key.includes('플랜')) return `
|
||||
<div class="wf-wrap" style="padding: 16px;">
|
||||
<div style="text-align: center; margin-bottom: 12px;"><span style="font-size:11px;font-weight:700;color:#1e293b;">요금제 선택</span></div>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px;">
|
||||
${[{name:'Basic',price:'₩49,000',color:'#64748b',pop:false},{name:'Pro',price:'₩99,000',color:'#3b82f6',pop:true},{name:'Enterprise',price:'문의',color:'#8b5cf6',pop:false}].map(p => `
|
||||
<div style="border-radius:8px;border:${p.pop?'2px solid #3b82f6':'1px solid #e2e8f0'};padding:12px;text-align:center;position:relative;${p.pop?'box-shadow:0 4px 12px rgba(59,130,246,.15);':''}">
|
||||
${p.pop?'<div style="position:absolute;top:-8px;left:50%;transform:translateX(-50%);background:#3b82f6;color:#fff;font-size:7px;padding:1px 8px;border-radius:8px;">인기</div>':''}
|
||||
<div style="font-size:10px;font-weight:600;color:${p.color};margin-bottom:4px;">${p.name}</div>
|
||||
<div style="font-size:16px;font-weight:800;color:#1e293b;margin-bottom:8px;">${p.price}</div>
|
||||
${['기능 A','기능 B','기능 C'].map(f => `<div style="font-size:8px;color:#64748b;padding:2px 0;">✓ ${f}</div>`).join('')}
|
||||
<div style="margin-top:8px;padding:4px;background:${p.pop?p.color:'#f1f5f9'};color:${p.pop?'#fff':'#64748b'};border-radius:4px;font-size:8px;">선택</div>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
// 채팅
|
||||
if (key.includes('채팅') || key.includes('메시징')) return `
|
||||
<div class="wf-wrap" style="display: flex; padding: 0; min-height: 220px;">
|
||||
<div style="width: 120px; background: #f8fafc; border-right: 1px solid #e2e8f0; padding: 8px 4px;">
|
||||
<div style="font-size:8px;font-weight:600;color:#94a3b8;padding:4px 6px;">채널</div>
|
||||
${['# 일반','# 영업팀','# 개발팀'].map((c,i) => `<div style="padding:4px 6px;font-size:9px;border-radius:4px;${i===1?'background:#eff6ff;color:#3b82f6;font-weight:600;':'color:#64748b;'}">${c}</div>`).join('')}
|
||||
<div style="font-size:8px;font-weight:600;color:#94a3b8;padding:4px 6px;margin-top:6px;">DM</div>
|
||||
${['김영업','이관리'].map(c => `<div style="display:flex;align-items:center;gap:4px;padding:4px 6px;font-size:9px;color:#64748b;"><div class="wf-circle" style="width:12px;height:12px;"></div>${c}</div>`).join('')}
|
||||
</div>
|
||||
<div style="flex:1;display:flex;flex-direction:column;">
|
||||
<div style="padding:6px 12px;border-bottom:1px solid #e2e8f0;font-size:10px;font-weight:600;">🔒 # 영업팀</div>
|
||||
<div style="flex:1;padding:10px 12px;display:flex;flex-direction:column;gap:8px;overflow:hidden;">
|
||||
<div style="display:flex;gap:6px;"><div class="wf-circle" style="width:20px;height:20px;flex-shrink:0;"></div><div><div style="font-size:8px;font-weight:600;color:#1e293b;">김영업 <span style="color:#94a3b8;font-weight:400;">10:30</span></div><div style="font-size:9px;color:#475569;background:#f1f5f9;padding:4px 8px;border-radius:0 8px 8px 8px;margin-top:2px;">수주건 확인 부탁드립니다</div></div></div>
|
||||
<div style="display:flex;gap:6px;justify-content:flex-end;"><div><div style="font-size:9px;color:#fff;background:#3b82f6;padding:4px 8px;border-radius:8px 0 8px 8px;">확인했습니다 👍</div></div></div>
|
||||
<div style="display:flex;gap:6px;"><div class="wf-circle" style="width:20px;height:20px;flex-shrink:0;"></div><div><div style="font-size:8px;font-weight:600;color:#1e293b;">이관리 <span style="color:#94a3b8;font-weight:400;">11:05</span></div><div style="font-size:9px;color:#475569;background:#f1f5f9;padding:4px 8px;border-radius:0 8px 8px 8px;margin-top:2px;">거래처 정보 업데이트 완료</div></div></div>
|
||||
</div>
|
||||
<div style="padding:8px;border-top:1px solid #e2e8f0;display:flex;gap:6px;align-items:center;">
|
||||
<div style="flex:1;height:28px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;padding:0 8px;font-size:9px;line-height:28px;color:#94a3b8;">메시지 입력...</div>
|
||||
<div style="font-size:10px;">📎</div><div style="font-size:10px;">😊</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
// 파일 업로드
|
||||
if (key.includes('파일') && key.includes('업로드')) return `
|
||||
<div class="wf-wrap" style="padding: 16px;">
|
||||
<div style="border: 2px dashed #cbd5e1; border-radius: 10px; padding: 20px; text-align: center; margin-bottom: 12px;">
|
||||
<div style="font-size: 24px; margin-bottom: 4px;">📁</div>
|
||||
<div style="font-size: 10px; color: #64748b;">파일을 드래그하거나 <span style="color: #3b82f6; text-decoration: underline;">클릭</span>하여 업로드</div>
|
||||
<div style="font-size: 8px; color: #94a3b8; margin-top: 4px;">PNG, JPG, PDF (최대 10MB)</div>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; gap: 6px;">
|
||||
${[{name:'견적서_v2.pdf',size:'2.4MB',pct:100,status:'완료'},{name:'도면_A-101.png',size:'5.1MB',pct:67,status:'업로드 중...'},{name:'스펙시트.xlsx',size:'1.8MB',pct:30,status:'대기'}].map(f => `
|
||||
<div style="display:flex;align-items:center;gap:8px;padding:8px;border:1px solid #e2e8f0;border-radius:6px;">
|
||||
<span style="font-size:16px;">${f.name.includes('.pdf')?'📄':f.name.includes('.png')?'🖼️':'📊'}</span>
|
||||
<div style="flex:1;">
|
||||
<div style="display:flex;justify-content:space-between;"><span style="font-size:9px;font-weight:600;color:#1e293b;">${f.name}</span><span style="font-size:8px;color:#94a3b8;">${f.size}</span></div>
|
||||
<div style="height:4px;background:#f1f5f9;border-radius:2px;margin-top:4px;overflow:hidden;"><div style="height:100%;width:${f.pct}%;background:${f.pct===100?'#10b981':'#3b82f6'};border-radius:2px;"></div></div>
|
||||
</div>
|
||||
<span style="font-size:8px;color:${f.pct===100?'#10b981':'#64748b'};">${f.status}</span>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
// 브레드크럼
|
||||
if (key.includes('브레드크럼') || key.includes('breadcrumb')) return `
|
||||
<div class="wf-wrap" style="padding: 16px;">
|
||||
<div style="display: flex; align-items: center; gap: 6px; margin-bottom: 16px;">
|
||||
${['🏠 홈','/','📋 영업관리','/','📊 수주관리','/','<b>#1024</b>'].map(b => b==='/'?`<span style="color:#cbd5e1;font-size:10px;">/</span>`:`<span style="font-size:10px;color:${b.includes('<b>')?'#1e293b':'#3b82f6'};${b.includes('<b>')?'font-weight:700;':''}">${b.replace(/<\/?b>/g,'')}</span>`).join('')}
|
||||
</div>
|
||||
<div style="font-size:14px;font-weight:700;color:#1e293b;margin-bottom:8px;">수주 #1024</div>
|
||||
<div class="wf-bar xl" style="height:8px;margin-bottom:6px;"></div>
|
||||
<div class="wf-bar lg" style="height:8px;"></div>
|
||||
</div>`;
|
||||
|
||||
// 온보딩 스테퍼
|
||||
if (key.includes('온보딩') || key.includes('스테퍼') || key.includes('위자드')) return `
|
||||
<div class="wf-wrap" style="padding: 20px;">
|
||||
<div style="display: flex; align-items: center; gap: 0; margin-bottom: 20px; padding: 0 20px;">
|
||||
${[{n:'1',t:'회사 정보',done:true},{n:'2',t:'팀 설정',active:true},{n:'3',t:'데이터 연동'},{n:'4',t:'완료'}].map((s,i) => `
|
||||
<div style="display:flex;align-items:center;gap:4px;${i>0?'flex:1;':''}">
|
||||
${i>0?`<div style="flex:1;height:2px;background:${s.done||s.active?'#3b82f6':'#e2e8f0'};"></div>`:''}
|
||||
<div style="width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:700;flex-shrink:0;
|
||||
${s.done?'background:#3b82f6;color:#fff;':s.active?'background:#fff;border:2px solid #3b82f6;color:#3b82f6;':'background:#f1f5f9;color:#94a3b8;'}">${s.done?'✓':s.n}</div>
|
||||
</div>
|
||||
`).join('')}
|
||||
</div>
|
||||
<div style="display:flex;justify-content:space-between;margin-bottom:16px;padding:0 10px;">
|
||||
${['회사 정보','팀 설정','데이터 연동','완료'].map((t,i) => `<span style="font-size:8px;color:${i<=1?'#3b82f6':'#94a3b8'};font-weight:${i===1?'600':'400'};text-align:center;">${t}</span>`).join('')}
|
||||
</div>
|
||||
<div class="wf-box" style="padding: 16px; text-align: center;">
|
||||
<div style="font-size:12px;font-weight:700;color:#1e293b;margin-bottom:4px;">팀 구성원을 초대하세요</div>
|
||||
<div style="font-size:9px;color:#64748b;margin-bottom:12px;">함께 사용할 팀원의 이메일을 입력하세요</div>
|
||||
<div style="height:28px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;margin-bottom:8px;"></div>
|
||||
<div style="height:28px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:6px;margin-bottom:12px;"></div>
|
||||
<div style="display:flex;justify-content:space-between;">
|
||||
<span style="font-size:9px;color:#3b82f6;">← 이전</span>
|
||||
<div style="padding:4px 14px;background:#3b82f6;color:#fff;border-radius:4px;font-size:9px;">다음 →</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
// 토스트
|
||||
if (key.includes('토스트') || key.includes('알림') && key.includes('시스템')) return `
|
||||
<div style="width: 100%; max-width: 380px; position: relative; height: 240px; background: #f8fafc; border-radius: 8px; border: 1px solid #e2e8f0; overflow: hidden;">
|
||||
<div style="padding: 12px; opacity: .3;">
|
||||
<div class="wf-bar xl" style="height: 10px; margin-bottom: 8px;"></div>
|
||||
<div class="wf-bar lg" style="height: 8px;"></div>
|
||||
</div>
|
||||
<div style="position: absolute; bottom: 12px; right: 12px; display: flex; flex-direction: column; gap: 6px; width: 220px;">
|
||||
<div style="background:#fff;border-radius:8px;padding:8px 12px;box-shadow:0 4px 12px rgba(0,0,0,.1);border-left:3px solid #10b981;display:flex;gap:8px;align-items:center;">
|
||||
<span style="font-size:14px;">✅</span>
|
||||
<div style="flex:1;"><div style="font-size:9px;font-weight:600;color:#1e293b;">저장 완료</div><div style="font-size:8px;color:#64748b;">수주 정보가 저장되었습니다</div></div>
|
||||
<span style="color:#94a3b8;font-size:10px;cursor:pointer;">✕</span>
|
||||
</div>
|
||||
<div style="background:#fff;border-radius:8px;padding:8px 12px;box-shadow:0 4px 12px rgba(0,0,0,.1);border-left:3px solid #ef4444;display:flex;gap:8px;align-items:center;">
|
||||
<span style="font-size:14px;">❌</span>
|
||||
<div style="flex:1;"><div style="font-size:9px;font-weight:600;color:#1e293b;">저장 실패</div><div style="font-size:8px;color:#64748b;">네트워크 오류가 발생했습니다</div></div>
|
||||
<span style="color:#94a3b8;font-size:10px;cursor:pointer;">✕</span>
|
||||
</div>
|
||||
<div style="background:#fff;border-radius:8px;padding:8px 12px;box-shadow:0 4px 12px rgba(0,0,0,.1);border-left:3px solid #f59e0b;display:flex;gap:8px;align-items:center;">
|
||||
<span style="font-size:14px;">⚠️</span>
|
||||
<div style="flex:1;"><div style="font-size:9px;font-weight:600;color:#1e293b;">저장 안 된 변경</div><div style="font-size:8px;color:#64748b;">변경사항을 저장하세요</div></div>
|
||||
<span style="font-size:8px;color:#3b82f6;cursor:pointer;">되돌리기</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
// Empty State
|
||||
if (key.includes('empty') || key.includes('빈') && key.includes('상태')) return `
|
||||
<div class="wf-wrap" style="padding: 40px; text-align: center;">
|
||||
<div style="font-size: 48px; margin-bottom: 12px; opacity: .6;">📭</div>
|
||||
<div style="font-size: 13px; font-weight: 700; color: #1e293b; margin-bottom: 6px;">아직 수주가 없습니다</div>
|
||||
<div style="font-size: 10px; color: #94a3b8; margin-bottom: 16px; line-height: 1.6;">첫 번째 수주를 등록하여<br>영업 관리를 시작하세요</div>
|
||||
<div style="display:inline-flex;align-items:center;gap:4px;padding:6px 16px;background:#3b82f6;color:#fff;border-radius:6px;font-size:10px;font-weight:600;">+ 새 수주 등록</div>
|
||||
<div style="margin-top:10px;font-size:9px;color:#3b82f6;">도움말 보기 →</div>
|
||||
</div>`;
|
||||
|
||||
// 검색 자동완성
|
||||
if (key.includes('자동완성') || key.includes('서제스트')) return `
|
||||
<div style="width: 100%; max-width: 400px;">
|
||||
<div class="wf-wrap" style="padding: 12px; overflow: visible; position: relative;">
|
||||
<div style="background:#f8fafc;border:2px solid #3b82f6;border-radius:8px;padding:8px 12px;display:flex;align-items:center;gap:8px;position:relative;z-index:2;">
|
||||
<span style="color:#94a3b8;">🔍</span><span style="font-size:11px;color:#1e293b;">블라인</span><span style="animation:blink 1s infinite;color:#3b82f6;">|</span>
|
||||
</div>
|
||||
<div style="position:absolute;left:12px;right:12px;top:44px;background:#fff;border:1px solid #e2e8f0;border-radius:8px;box-shadow:0 8px 24px rgba(0,0,0,.1);z-index:3;overflow:hidden;">
|
||||
${['블라인드 50mm 수동|품목','블라인드 25mm 전동|품목','블라인드 롤스크린 조합|품목','블라인드코리아|거래처'].map((r,i) => {
|
||||
const [name,cat] = r.split('|');
|
||||
return `<div style="display:flex;align-items:center;gap:8px;padding:7px 12px;${i===0?'background:#f8fafc;':''}cursor:pointer;">
|
||||
<span style="font-size:9px;padding:1px 6px;background:#f1f5f9;border-radius:3px;color:#64748b;">${cat}</span>
|
||||
<span style="font-size:10px;color:#1e293b;"><b style="color:#3b82f6;">블라인</b>${name.replace('블라인','')}</span>
|
||||
${i===0?'<span style="margin-left:auto;font-size:8px;color:#94a3b8;">↵</span>':''}
|
||||
</div>`;
|
||||
}).join('')}
|
||||
</div>
|
||||
</div>
|
||||
<div style="height: 80px;"></div>
|
||||
</div>`;
|
||||
|
||||
// 탭 레이아웃
|
||||
if (key.includes('탭') && key.includes('레이아웃')) return `
|
||||
<div class="wf-wrap" style="padding: 0;">
|
||||
<div style="display: flex; border-bottom: 2px solid #e2e8f0; padding: 0 12px;">
|
||||
${['📊 개요|true','📋 상세|false','📎 첨부|false','💬 댓글|false','📜 이력|false'].map(t => {
|
||||
const [n,a] = t.split('|');
|
||||
return `<div style="padding:8px 12px;font-size:9px;${a==='true'?'color:#3b82f6;border-bottom:2px solid #3b82f6;font-weight:600;margin-bottom:-2px;':'color:#94a3b8;'}">${n}${n.includes('댓글')?'<span style="background:#ef4444;color:#fff;font-size:7px;padding:0 4px;border-radius:8px;margin-left:3px;">3</span>':''}</div>`;
|
||||
}).join('')}
|
||||
</div>
|
||||
<div style="padding: 14px;">
|
||||
<div class="wf-bar xl dark" style="height: 10px; margin-bottom: 10px;"></div>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px;">
|
||||
<div class="wf-box" style="padding: 10px; height: 60px;"><div class="wf-text">요약 정보</div></div>
|
||||
<div class="wf-box" style="padding: 10px; height: 60px;"><div class="wf-text">차트</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
// 기본 와이어프레임 (매칭 안 됨)
|
||||
return `
|
||||
<div class="wf-wrap" style="padding: 24px; text-align: center;">
|
||||
<div style="font-size: 32px; margin-bottom: 12px; opacity: .5;"><i class="${this.getTypeIcon(card.type)}" style="font-size:32px;color:#94a3b8;"></i></div>
|
||||
<div style="font-size: 12px; font-weight: 600; color: #64748b; margin-bottom: 4px;">${card.title || ''}</div>
|
||||
<div style="font-size: 10px; color: #94a3b8;">이미지를 추가하면 여기에 표시됩니다</div>
|
||||
</div>`;
|
||||
},
|
||||
|
||||
openEditCardModal(card) {
|
||||
this.editingCard = {
|
||||
...card,
|
||||
|
||||
Reference in New Issue
Block a user