fix(frontend): 묶은 휴지통의 영구 삭제 글과 띄어 쓴 조사를 고친다

- 대화로 묶은 휴지통에서 줄을 골라 영구 삭제하면 확인 창 · 알림을
  "휴지통에 있는 메일 N통을 …"으로 쓴다 — 줄의 통 수는 대화 전체라서
  (옮기기 알림 "{메일함}에 있는 메일 N통을 …"과 같은 결)
- 값을 끼워 넣는 글의 띄어 쓴 조사를 붙인다(9단계 "@도메인에서",
  11단계 "25MB를" · "2GB까지" · "20MB가"), 공개 내려받기 화면의 신고
  주소는 끝 글자로 조사를 고른다

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-07 02:13:33 +09:00
parent 08aafdef5c
commit 749997cb18
16 changed files with 139 additions and 37 deletions
@@ -1,17 +1,22 @@
<script setup lang="ts">
// 영구 삭제 확인 — 휴지통에서만 (디자인 원본 BulkSelect · ListStates "휴지통 · 영구 삭제 확인")
// 제목 "메일 N통을 영구 삭제할까요?" · 글 "휴지통에서 지운 메일은 되돌릴 수 없습니다." · [영구 삭제](빨강 채움) [취소]
// 대화로 묶은 휴지통에서 줄을 골랐으면(from) 제목은 "휴지통에 있는 메일 N통을 영구 삭제할까요?" — 줄의 통 수는 대화 전체라서 (14단계, 2026-11-07)
// 열리면 「취소」에 초점이 간다 — 실수로 Enter · Space 를 눌러도 지워지지 않게. Esc 는 취소와 같다.
// 쓰는 곳: 여러 통 처리 도구 줄 · 읽기 화면(한 통) — 둘이 함께 보일 수 있어 id 앞부분(idPrefix)을 따로 받는다
// 쓰는 곳: 여러 통 처리 도구 줄 · 읽기 화면(한 통 — from 없이 지금 글) — 둘이 함께 보일 수 있어 id 앞부분(idPrefix)을 따로 받는다
import { computed } from 'vue'
import ModalDialog from '@/components/feedback/ModalDialog.vue'
import { MAIL_BULK_TEXT } from '@/shared/constants/mail'
import { formatCount } from '@/shared/utils/format'
import { purgeTitleText } from '@/shared/utils/mail-bulk'
interface Props {
open: boolean
count: number
/** 제목 · 글의 id 앞부분 — 화면에 하나뿐인 값 */
idPrefix: string
/** 대화로 묶은 목록에서 고른 줄이면 그 메일함 이름 ("휴지통") — 없으면 "메일 N통을 …" */
from?: string | null
}
interface Emits {
@@ -19,15 +24,19 @@ interface Emits {
(e: 'cancel'): void
}
defineProps<Props>()
const props = withDefaults(defineProps<Props>(), {
from: null,
})
const emit = defineEmits<Emits>()
const title = computed(() => purgeTitleText(props.count, { from: props.from }))
</script>
<template>
<ModalDialog
:open="open"
role="alertdialog"
:title="MAIL_BULK_TEXT.purgeTitle(formatCount(count))"
:title="title"
:message="MAIL_BULK_TEXT.purgeMessage"
:title-id="`${idPrefix}-title`"
:message-id="`${idPrefix}-message`"
@@ -8,7 +8,8 @@
// - role="toolbar": Tab 한 번에 들어와 화살표 · Home · End 로 옮긴다(로빙 tabindex). 이동 메뉴는 MailBulkMoveButton.
// 옮기기 · 지우기를 하는 동안 눌리지 않는 단추(이동 · 삭제)는 건너뛰고, Tab 자리 · 초점이 그 단추에 있었으면 다음 누를 수 있는 단추로 옮긴다
// - 영구 삭제는 확인 대화상자(MailPurgeDialog — 처음 초점은 「취소」)를 거친다
// - 대화로 묶은 메일함(14단계)은 "대화 N개 선택됨"이고 하는 일은 고른 줄의 메일 모두(붙잡힌 메일은 빼고) — 영구 삭제 확인의 수는 그 메일 수다.
// - 대화로 묶은 메일함(14단계)은 "대화 N개 선택됨"이고 하는 일은 고른 줄의 메일 모두(붙잡힌 메일은 빼고) — 영구 삭제 확인의 수는 그 메일 수다
// ("휴지통에 있는 메일 N통을 영구 삭제할까요?" — 줄의 통 수는 대화 전체라서, useBulkMailActions 의 conversationFrom).
// 분할 보기는 보이는 글만 "N개 선택됨"으로 줄이고 화면 낭독은 "대화 N개 선택됨"을 읽는다 — 목록 칸(384)에서 단추 일곱(280)과 한 줄에 들게
// (2026-11-06 실사용 시험: "대화 1개 선택됨" 88px 이면 도구가 두 줄 · 97px 로 넘쳤다. "N개 선택됨"은 세 자리까지 79px — 1440 의 자리 83px)
import { computed, nextTick, ref, watch } from 'vue'
@@ -251,6 +252,7 @@ watch(disabledTools, (disabled) => {
<MailPurgeDialog
:open="purgeOpen"
:count="mailStore.checkedActionIds.length"
:from="bulk.conversationFrom.value"
id-prefix="bulk-purge"
@confirm="onPurgeConfirm"
@cancel="purgeOpen = false"
@@ -587,7 +587,7 @@ describe('MessageReader — 9단계', () => {
expect(wrapper.get('#block-dialog-title').text()).toBe('보낸 사람 차단')
expect((dialog.get('[data-block-scope="address"]').element as HTMLInputElement).checked).toBe(true)
expect(dialog.text()).toContain('news@cloudbrief.example')
expect(dialog.text()).toContain('@cloudbrief.example 에서 오는 메일 모두')
expect(dialog.text()).toContain('@cloudbrief.example에서 오는 메일 모두')
expect((dialog.get('[data-block-move]').element as HTMLInputElement).checked).toBe(true)
expect(document.activeElement).toBe(wrapper.get('[data-block-confirm]').element)