@@ -459,37 +490,22 @@ const ClassroomTeacherModal = ({ containerProps, onClose }) => {
className={styles.layout}
data-testid="classroom-teacher-modal"
>
- {/* Logout lives in the title bar (top-right, before the
- close ×) so it is reachable from every teacher view. */}
- {phase !== 'teacher-login' && teacherEmail ? (
-
- {teacherEmail}
-
- ) : null}
- {phase !== 'teacher-login' && (
-
- )}
- {/* お知らせセンター (#1111) — bell + dropdown, next to the ×. */}
+ {/* タイトルバー右上(× の左): 通知ベル → アバターメニュー。
+ アバターは右上固定でアカウント(メール頭文字)を示し、
+ クリックでログアウト等のメニューを出す(#1111 レビュー)。 */}
{phase !== 'teacher-login' && notificationsCenter && (
)}
+ {phase !== 'teacher-login' && (
+
+ )}
{/* Main area */}
{renderMain()}
diff --git a/packages/scratch-gui/src/components/classroom-teacher-modal/teacher-avatar-menu.jsx b/packages/scratch-gui/src/components/classroom-teacher-modal/teacher-avatar-menu.jsx
new file mode 100644
index 00000000000..3e95852ab8d
--- /dev/null
+++ b/packages/scratch-gui/src/components/classroom-teacher-modal/teacher-avatar-menu.jsx
@@ -0,0 +1,98 @@
+/**
+ * アバターメニュー(クラス管理タイトルバー右上・#1111 レビュー)。
+ *
+ * 他アプリ(Facebook / Zenn / Google 等)に倣い、右上はユーザーを表す
+ * アバター(メール頭文字)に固定し、クリックでポップアップメニュー
+ * (現状はログアウトのみ。将来の設定項目もここに足す)。▼ でメニューが
+ * あることを示す。
+ */
+import PropTypes from 'prop-types';
+import React, { useCallback, useEffect, useRef, useState } from 'react';
+import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
+
+import { initialsFromEmail } from '../../lib/avatar-initials.js';
+import styles from './classroom-teacher-modal.css';
+
+const messages = defineMessages({
+ buttonLabel: {
+ defaultMessage: 'Account menu',
+ description: 'Aria label for the avatar/account menu button',
+ id: 'gui.classroom.avatar.buttonLabel',
+ },
+});
+
+const TeacherAvatarMenu = ({ email, onLogout }) => {
+ const intl = useIntl();
+ const [open, setOpen] = useState(false);
+ const wrapperRef = useRef(null);
+
+ const toggle = useCallback(() => setOpen(o => !o), []);
+ const handleLogout = useCallback(() => {
+ setOpen(false);
+ onLogout();
+ }, [onLogout]);
+
+ // 外側クリック / Esc で閉じる。
+ useEffect(() => {
+ if (!open) return () => {};
+ const onDocDown = e => {
+ if (wrapperRef.current && !wrapperRef.current.contains(e.target)) setOpen(false);
+ };
+ const onKey = e => {
+ if (e.key === 'Escape') setOpen(false);
+ };
+ document.addEventListener('mousedown', onDocDown);
+ document.addEventListener('keydown', onKey);
+ return () => {
+ document.removeEventListener('mousedown', onDocDown);
+ document.removeEventListener('keydown', onKey);
+ };
+ }, [open]);
+
+ return (
+
+
+
+
+ {notifications.length === 0 ? (
+
+
+
+ ) : (
+
+
+ {pageItems.map(notification => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+ )}
+
+ );
+};
+
+TeacherNotificationsList.propTypes = {
+ notifications: PropTypes.arrayOf(PropTypes.object).isRequired,
+ onOpenLink: PropTypes.func.isRequired,
+};
+
+export default TeacherNotificationsList;
diff --git a/packages/scratch-gui/src/components/classroom-teacher-modal/teacher-notifications.jsx b/packages/scratch-gui/src/components/classroom-teacher-modal/teacher-notifications.jsx
index 1d0ad660ba6..7414dbc8bda 100644
--- a/packages/scratch-gui/src/components/classroom-teacher-modal/teacher-notifications.jsx
+++ b/packages/scratch-gui/src/components/classroom-teacher-modal/teacher-notifications.jsx
@@ -1,10 +1,9 @@
/**
* お知らせセンター (notification center, EPIC #1111).
*
- * A 🔔 button pinned to the teacher modal's title bar (top-right, next to
- * logout) with an unread badge, and a dropdown panel listing the notices the
- * operators sent to this teacher. Clicking a notice jumps to the linked view
- * (e.g. the assignment board of the referenced classroom).
+ * クラス管理タイトルバー右上、アバターの左に置く**白一色のベル**ボタン
+ * (+ 未読バッジ)。クリックでドロップダウンパネルを開き、**先頭 5 件**の
+ * お知らせを表示。6 件以上あるときは「すべて見る」で全件一覧ページへ遷移。
*/
import PropTypes from 'prop-types';
import React, { useCallback } from 'react';
@@ -12,6 +11,8 @@ import { defineMessages, useIntl, FormattedMessage } from 'react-intl';
import styles from './classroom-teacher-modal.css';
+const PANEL_PREVIEW_COUNT = 5;
+
const messages = defineMessages({
buttonLabel: {
defaultMessage: 'Notifications',
@@ -21,13 +22,23 @@ const messages = defineMessages({
});
// ローカルタイムで表示する(ISO の slice だと UTC のまま出て日本では 9 時間
-// ずれる — レビュー指摘)。兄弟コンポーネントの toLocaleString 慣行に合わせる。
+// ずれる)。兄弟コンポーネントの toLocaleString 慣行に合わせる。
const formatDateTime = (iso) => {
if (!iso) return '';
const date = new Date(iso);
return isNaN(date.getTime()) ? '' : date.toLocaleString();
};
+// 白一色のベル SVG(絵文字 🔔 は主張が強いのでやめる — レビュー指摘)。
+const BellIcon = () => (
+