임동욱 임동욱 09-14
feat: 알림 항목을 누르면 해당 게시판으로 간다
알림에 대상 컬럼이 없어(TB_COM_NTCN: 제목·내용·읽음뿐) 제목으로 가른다 — 지금 쌓이는
알림은 학습자 1:1문의 등록 하나라 1:1문의 게시판(/boards/inquiries)으로 보낸다. 누르면
드롭다운을 닫고, 링크는 hover·focus 표시를 갖는다. 모르는 알림은 종전처럼 누를 수 없다.

내용 줄은 두 줄로 잘라 요약처럼 보이게 하고 전체는 title로 남긴다. 다만 백엔드가
내용을 고정 문구("학습자가 1:1 문의 게시판에 게시글을 등록하였습니다")로 넣고 있어
실제 게시글 요약은 CmmBbsServiceImpl.insert가 ntcnCn에 글 본문을 실어 줘야 나온다.

Co-Authored-By: Claude Opus 5 
@550f9a4bd3a255d7099b60cf6f668e6dc56e434f
app/(protected)/(basic)/_components/admin-header.module.scss
--- app/(protected)/(basic)/_components/admin-header.module.scss
+++ app/(protected)/(basic)/_components/admin-header.module.scss
@@ -123,13 +123,36 @@
   letter-spacing: -0.025em;
 }
 
+.notificationLink {
+  color: inherit;
+  text-decoration: none;
+  cursor: pointer;
+
+  &:hover {
+    background: fox.color(action-default-hover);
+  }
+
+  &.notificationUnread:hover {
+    background: fox.color(action-primary-pressed);
+  }
+
+  &:focus-visible {
+    outline: fox.border(2) solid fox.color(border-theme-primary);
+    outline-offset: calc(-1 * #{fox.border(2)});
+  }
+}
+
 .notificationContent {
+  display: -webkit-box;
+  overflow: hidden;
   color: fox.color(font-neutral-subtle);
   font-family: fox.font-family(body);
   font-size: fox.font-size(body-sm);
   line-height: 1.5;
   letter-spacing: -0.025em;
   word-break: break-word;
+  -webkit-box-orient: vertical;
+  -webkit-line-clamp: 2;
 }
 
 .notificationEmpty {
app/(protected)/(basic)/_components/admin-header.tsx
--- app/(protected)/(basic)/_components/admin-header.tsx
+++ app/(protected)/(basic)/_components/admin-header.tsx
@@ -1,9 +1,14 @@
 'use client';
 
 import Image from 'next/image';
+import Link from 'next/link';
 import { useEffect, useRef, useState } from 'react';
 import { logout } from '@/app/(protected)/_actions';
-import { countUnread, type AdminNotification } from '@/lib/domain/notification';
+import {
+  countUnread,
+  resolveNotificationHref,
+  type AdminNotification,
+} from '@/lib/domain/notification';
 import styles from './admin-header.module.scss';
 import { HeaderIconButton } from './header-icon-button';
 
@@ -107,22 +112,38 @@
                 <p className={styles.notificationEmpty}>새 알림이 없습니다.</p>
               ) : (
                 <ul className={styles.notificationList}>
-                  {notifications.map((item) => (
-                    <li key={item.id}>
-                      <div
-                        className={`${styles.notification} ${
-                          item.isRead ? '' : styles.notificationUnread
-                        }`}
-                      >
+                  {notifications.map((item) => {
+                    const href = resolveNotificationHref(item);
+                    const className = `${styles.notification} ${
+                      item.isRead ? '' : styles.notificationUnread
+                    }`;
+                    const body = (
+                      <>
                         <span className={styles.notificationTitle}>
                           {item.title}
                         </span>
-                        <span className={styles.notificationContent}>
+                        <span className={styles.notificationContent} title={item.content}>
                           {item.content}
                         </span>
-                      </div>
-                    </li>
-                  ))}
+                      </>
+                    );
+                    return (
+                      <li key={item.id}>
+                        {href ? (
+                          <Link
+                            href={href}
+                            role="menuitem"
+                            className={`${className} ${styles.notificationLink}`}
+                            onClick={() => setIsNotificationOpen(false)}
+                          >
+                            {body}
+                          </Link>
+                        ) : (
+                          <div className={className}>{body}</div>
+                        )}
+                      </li>
+                    );
+                  })}
                 </ul>
               )}
             </div>
lib/domain/notification.ts
--- lib/domain/notification.ts
+++ lib/domain/notification.ts
@@ -1,3 +1,5 @@
+import { BOARD_PATHS } from '@/lib/domain/board-post-query';
+
 /**
  * 관리자 알림 한 건. 백엔드 `CmmNtcnVo`(`TB_COM_NTCN`).
  *
@@ -20,3 +22,14 @@
 ): number {
   return notifications.filter((item) => !item.isRead).length;
 }
+
+/**
+ * 이슈: `TB_COM_NTCN`에 대상(게시판·게시글) 컬럼이 없어 제목으로 가른다. 지금 적재되는 알림은
+ * 학습자 1:1문의 등록(`CmmBbsServiceImpl.insert`, 제목 "학습자 1:1 문의 등록") 하나다.
+ * 모르는 알림은 null — 누를 곳이 없다.
+ */
+export function resolveNotificationHref(
+  notification: AdminNotification
+): string | null {
+  return notification.title.includes('1:1 문의') ? BOARD_PATHS.inquiry : null;
+}
Add a comment
List