style: 보고서 모달·일정 화면 다듬기 + 일정 컴포넌트 rem 통일
- 보고서 모달: '전체' 탭 제거(기본=항목 있는 첫 분류), 상태 드롭다운 크기 축소, 푸터 배경을 다른 모달(.bm-foot)과 동일한 #fafbfc 로 통일 - 일정 화면: 콘텐츠 영역 상하좌우 여백 1.5rem 통일, 툴바 가로 여백 정렬 + 전역 .toolbar 의 margin-bottom 상속 차단(상단 여분 마진 제거) - 일정 하위 컴포넌트 px→rem 일괄 변환(보더 width·box-shadow 는 px 유지), ScheduleAvatar 도 size 를 rem 렌더 → '전 직원' 표식과 아바타 크기 정합 유지 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -222,7 +222,10 @@ async function onDeleteCategory(id: string): Promise<void> {
|
||||
align-items: center;
|
||||
/* 프로젝트 목록 등 전역 .toolbar 와 동일한 컨트롤 간격 */
|
||||
gap: 0.625rem;
|
||||
padding: 11px 24px;
|
||||
/* 가로 여백은 .page(0 1.5rem)·콘텐츠 영역과 동일하게 1.5rem */
|
||||
padding: 0.6875rem 1.5rem;
|
||||
/* 전역 .toolbar 의 margin-bottom(0.875rem) 상속 차단 — 고정 바라 여분 마진 불필요 */
|
||||
margin-bottom: 0;
|
||||
flex-shrink: 0;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: var(--panel);
|
||||
@@ -230,14 +233,14 @@ async function onDeleteCategory(id: string): Promise<void> {
|
||||
.datenav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.datenav .range {
|
||||
font-size: 15.5px;
|
||||
font-size: 0.969rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.3px;
|
||||
letter-spacing: -0.01875rem;
|
||||
white-space: nowrap;
|
||||
margin: 0 6px;
|
||||
margin: 0 0.375rem;
|
||||
}
|
||||
.navgroup {
|
||||
display: flex;
|
||||
@@ -250,8 +253,8 @@ async function onDeleteCategory(id: string): Promise<void> {
|
||||
margin-left: -1px;
|
||||
}
|
||||
.navbtn {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
border: 1px solid var(--border-strong);
|
||||
background: #fff;
|
||||
color: var(--text-2);
|
||||
@@ -265,8 +268,8 @@ async function onDeleteCategory(id: string): Promise<void> {
|
||||
z-index: 1;
|
||||
}
|
||||
.navbtn svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
}
|
||||
.spacer {
|
||||
flex: 1;
|
||||
@@ -285,7 +288,8 @@ async function onDeleteCategory(id: string): Promise<void> {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
padding: 18px 24px 28px;
|
||||
/* 콘텐츠 영역 상하좌우 여백 통일(1.5rem) — 툴바 가로 여백·.page 와 일치 */
|
||||
padding: 1.5rem;
|
||||
}
|
||||
.detail-backdrop {
|
||||
position: absolute;
|
||||
|
||||
@@ -314,7 +314,13 @@ const CAT_COLOR: Record<ChecklistCategory, string> = {
|
||||
보안: '#dc2626',
|
||||
부가: '#b7791f',
|
||||
}
|
||||
type CatTab = ChecklistCategory | 'all'
|
||||
// 보고서 모달 탭 — 카테고리(전체 탭 없음). 기본 탭은 항목이 있는 첫 분류.
|
||||
type CatTab = ChecklistCategory
|
||||
function firstCat(items: { category: ChecklistCategory }[]): ChecklistCategory {
|
||||
return (
|
||||
CHECKLIST_CATEGORIES.find((c) => items.some((i) => i.category === c)) ?? '필수'
|
||||
)
|
||||
}
|
||||
|
||||
// KST(UTC+9) 기준 'yyyy-MM-dd' — '당일' 판정
|
||||
function kstDate(iso: string | Date): string {
|
||||
@@ -349,12 +355,11 @@ const reportItems = ref<
|
||||
>([])
|
||||
const reportNote = ref('')
|
||||
const reportSubmitting = ref(false)
|
||||
const writeTab = ref<CatTab>('all')
|
||||
const writeTab = ref<CatTab>('필수')
|
||||
|
||||
function openReportForm(cp: TaskCheckpointView) {
|
||||
if (!task.value) return
|
||||
reportingCpId.value = cp.id
|
||||
writeTab.value = 'all'
|
||||
// 현재(마지막 보고) 상태로 프리필
|
||||
reportItems.value = task.value.checklist
|
||||
.filter((c) => c.id)
|
||||
@@ -364,16 +369,11 @@ function openReportForm(cp: TaskCheckpointView) {
|
||||
category: c.category,
|
||||
workStatus: c.workStatus ?? 'todo',
|
||||
}))
|
||||
writeTab.value = firstCat(reportItems.value)
|
||||
reportNote.value = ''
|
||||
}
|
||||
// 작성 모달 — 탭별 그룹/개수/요약
|
||||
// 작성 모달 — 선택 분류의 그룹/개수/요약
|
||||
const writeGroups = computed(() => {
|
||||
if (writeTab.value === 'all') {
|
||||
return CHECKLIST_CATEGORIES.map((c) => ({
|
||||
cat: c,
|
||||
items: reportItems.value.filter((i) => i.category === c),
|
||||
})).filter((g) => g.items.length)
|
||||
}
|
||||
const c = writeTab.value
|
||||
return [{ cat: c, items: reportItems.value.filter((i) => i.category === c) }]
|
||||
})
|
||||
@@ -387,11 +387,7 @@ const writeSummary = computed(() =>
|
||||
})),
|
||||
)
|
||||
// 현재 탭에서 보이는 항목 수(0이면 빈 안내 표시)
|
||||
const writeVisible = computed(() =>
|
||||
writeTab.value === 'all'
|
||||
? reportItems.value.length
|
||||
: writeCatCount(writeTab.value),
|
||||
)
|
||||
const writeVisible = computed(() => writeCatCount(writeTab.value))
|
||||
function cancelReportForm() {
|
||||
reportingCpId.value = null
|
||||
reportItems.value = []
|
||||
@@ -418,33 +414,24 @@ async function submitReport() {
|
||||
|
||||
// --- 보고 기록 열람(모달) — 전체/카테고리 탭 ---
|
||||
const openedReport = ref<CheckpointReportView | null>(null)
|
||||
const viewTab = ref<CatTab>('all')
|
||||
const viewTab = ref<CatTab>('필수')
|
||||
// 보고 완료 체크포인트 클릭 → 해당 체크포인트의 최신 보고를 모달로 연다
|
||||
function openReportModal(cp: TaskCheckpointView) {
|
||||
const r = task.value?.checkpointReports.find((x) => x.checkpointId === cp.id)
|
||||
if (!r) return
|
||||
openedReport.value = r
|
||||
viewTab.value = 'all'
|
||||
viewTab.value = firstCat(r.items)
|
||||
}
|
||||
const viewItems = computed(() => openedReport.value?.items ?? [])
|
||||
const viewGroups = computed(() => {
|
||||
const items = viewItems.value
|
||||
if (viewTab.value === 'all') {
|
||||
return CHECKLIST_CATEGORIES.map((c) => ({
|
||||
cat: c,
|
||||
items: items.filter((i) => i.category === c),
|
||||
})).filter((g) => g.items.length)
|
||||
}
|
||||
const c = viewTab.value
|
||||
return [{ cat: c, items: items.filter((i) => i.category === c) }]
|
||||
return [{ cat: c, items: viewItems.value.filter((i) => i.category === c) }]
|
||||
})
|
||||
function viewCatCount(cat: ChecklistCategory): number {
|
||||
return viewItems.value.filter((i) => i.category === cat).length
|
||||
}
|
||||
// 현재 탭에서 보이는 항목 수(0이면 빈 안내 표시)
|
||||
const viewVisible = computed(() =>
|
||||
viewTab.value === 'all' ? viewItems.value.length : viewCatCount(viewTab.value),
|
||||
)
|
||||
const viewVisible = computed(() => viewCatCount(viewTab.value))
|
||||
const reporterInitial = computed(() => openedReport.value?.reporterName?.[0] ?? '?')
|
||||
// 담당자 액션 카드 테마(시안): changes→빨강 / review→앰버 / done→초록 / 그 외→accent
|
||||
const assigneeCardClass = computed(() => {
|
||||
@@ -1674,14 +1661,6 @@ function badgeFor(name: string | undefined): { label: string; cls: string } {
|
||||
class="pr-tabs"
|
||||
role="tablist"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="pr-tab"
|
||||
:class="{ on: viewTab === 'all' }"
|
||||
@click="viewTab = 'all'"
|
||||
>
|
||||
전체<span class="pr-tct">{{ viewItems.length }}</span>
|
||||
</button>
|
||||
<button
|
||||
v-for="c in CHECKLIST_CATEGORIES"
|
||||
:key="c"
|
||||
@@ -1702,15 +1681,6 @@ function badgeFor(name: string | undefined): { label: string; cls: string } {
|
||||
v-for="g in viewGroups"
|
||||
:key="g.cat"
|
||||
>
|
||||
<div
|
||||
v-if="viewTab === 'all'"
|
||||
class="pr-ghead"
|
||||
>
|
||||
<span
|
||||
class="pr-gdot"
|
||||
:style="{ background: CAT_COLOR[g.cat] }"
|
||||
/>{{ g.cat }}<span class="pr-gct">· {{ g.items.length }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-for="(it, i) in g.items"
|
||||
:key="g.cat + '-' + i"
|
||||
@@ -1733,7 +1703,7 @@ function badgeFor(name: string | undefined): { label: string; cls: string } {
|
||||
v-if="viewVisible === 0"
|
||||
class="pr-empty"
|
||||
>
|
||||
{{ viewTab === 'all' ? '보고된 항목이 없습니다.' : `'${viewTab}' 분류에 항목이 없습니다.` }}
|
||||
'{{ viewTab }}' 분류에 항목이 없습니다.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1810,14 +1780,6 @@ function badgeFor(name: string | undefined): { label: string; cls: string } {
|
||||
class="pr-tabs"
|
||||
role="tablist"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="pr-tab"
|
||||
:class="{ on: writeTab === 'all' }"
|
||||
@click="writeTab = 'all'"
|
||||
>
|
||||
전체<span class="pr-tct">{{ reportItems.length }}</span>
|
||||
</button>
|
||||
<button
|
||||
v-for="c in CHECKLIST_CATEGORIES"
|
||||
:key="c"
|
||||
@@ -1838,15 +1800,6 @@ function badgeFor(name: string | undefined): { label: string; cls: string } {
|
||||
v-for="g in writeGroups"
|
||||
:key="g.cat"
|
||||
>
|
||||
<div
|
||||
v-if="writeTab === 'all'"
|
||||
class="pr-ghead"
|
||||
>
|
||||
<span
|
||||
class="pr-gdot"
|
||||
:style="{ background: CAT_COLOR[g.cat] }"
|
||||
/>{{ g.cat }}<span class="pr-gct">· {{ g.items.length }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-for="it in g.items"
|
||||
:key="it.id"
|
||||
@@ -1861,7 +1814,7 @@ function badgeFor(name: string | undefined): { label: string; cls: string } {
|
||||
v-if="writeVisible === 0"
|
||||
class="pr-empty"
|
||||
>
|
||||
{{ writeTab === 'all' ? '보고할 할 일이 없습니다. 메모만 전송됩니다.' : `'${writeTab}' 분류에 할 일이 없습니다.` }}
|
||||
'{{ writeTab }}' 분류에 할 일이 없습니다.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3807,24 +3760,6 @@ a.file-info {
|
||||
padding: 0 1.5rem 0.5rem;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
.pr-ghead {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 1rem 0.125rem 0.4375rem;
|
||||
font-size: 0.719rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-3);
|
||||
}
|
||||
.pr-ghead .pr-gdot {
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.pr-ghead .pr-gct {
|
||||
color: var(--text-3);
|
||||
font-weight: 600;
|
||||
}
|
||||
.pr-task,
|
||||
.pr-titem {
|
||||
display: flex;
|
||||
@@ -3947,6 +3882,8 @@ a.file-info {
|
||||
gap: 0.75rem;
|
||||
padding: 0.875rem 1.5rem;
|
||||
border-top: 1px solid var(--border);
|
||||
/* 다른 모달 푸터(BaseModal .bm-foot)와 동일한 배경 */
|
||||
background: #fafbfc;
|
||||
}
|
||||
.pr-foot .pr-sp {
|
||||
flex: 1;
|
||||
|
||||
Reference in New Issue
Block a user