- Update E2E tests: announcements, auth, auth-business-flow, full-route-regression, grades, navigation, smoke-auth, teacher-web-test - Update integration tests: api-ai-chat, api-onboarding-complete, api-onboarding-status, proxy-guard, integration setup - Update visual regression tests: admin-dashboard, homepage, student-dashboard, teacher-dashboard, visual config, helpers - Update webapp tests: admin, parent, student full tests and debug scripts - Add new webapp tests: announcements_messages, settings_profile, debug scripts - Add webtest directory with test plans, screenshots, and diagnostic scripts
653 lines
74 KiB
JSON
653 lines
74 KiB
JSON
{
|
||
"test_date": "2026-06-22 19:44:12",
|
||
"test_target": "仪表公告和消息模块 (Announcements & Messages)",
|
||
"version": "0.1.0",
|
||
"base_url": "http://localhost:3000",
|
||
"summary": {
|
||
"total": 46,
|
||
"passed": 29,
|
||
"failed": 0,
|
||
"warnings": 17
|
||
},
|
||
"by_role": {
|
||
"admin": {
|
||
"total": 16,
|
||
"passed": 10,
|
||
"failed": 0,
|
||
"warnings": 6
|
||
},
|
||
"teacher": {
|
||
"total": 10,
|
||
"passed": 6,
|
||
"failed": 0,
|
||
"warnings": 4
|
||
},
|
||
"student": {
|
||
"total": 10,
|
||
"passed": 7,
|
||
"failed": 0,
|
||
"warnings": 3
|
||
},
|
||
"parent": {
|
||
"total": 10,
|
||
"passed": 6,
|
||
"failed": 0,
|
||
"warnings": 4
|
||
}
|
||
},
|
||
"test_cases": [
|
||
{
|
||
"id": "ANN-LIST-admin",
|
||
"role": "admin",
|
||
"name": "公告列表页访问",
|
||
"status": "warning",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/announcements, 标题: 公告, 卡片数: 3",
|
||
"screenshot": "tests\\webapp\\screenshots\\announcements_messages\\ann_list_admin.png",
|
||
"errors": [],
|
||
"warnings": [
|
||
"控制台错误 1 条"
|
||
],
|
||
"timestamp": "19:44:21"
|
||
},
|
||
{
|
||
"id": "ANN-DETAIL-admin",
|
||
"role": "admin",
|
||
"name": "公告详情页访问",
|
||
"status": "warning",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/announcements/ann_class_g1c1",
|
||
"screenshot": "tests\\webapp\\screenshots\\announcements_messages\\ann_detail_admin.png",
|
||
"errors": [],
|
||
"warnings": [
|
||
"控制台错误 1 条"
|
||
],
|
||
"timestamp": "19:44:27"
|
||
},
|
||
{
|
||
"id": "ANN-ADMIN-LIST-admin",
|
||
"role": "admin",
|
||
"name": "管理员公告管理页访问",
|
||
"status": "warning",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/admin/announcements",
|
||
"screenshot": "tests\\webapp\\screenshots\\announcements_messages\\ann_admin_list_admin.png",
|
||
"errors": [],
|
||
"warnings": [
|
||
"控制台错误 1 条"
|
||
],
|
||
"timestamp": "19:44:31"
|
||
},
|
||
{
|
||
"id": "ANN-CREATE-admin",
|
||
"role": "admin",
|
||
"name": "创建公告(草稿)",
|
||
"status": "passed",
|
||
"detail": "创建成功,URL: http://localhost:3000/admin/announcements",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:44:37"
|
||
},
|
||
{
|
||
"id": "ANN-EDIT-admin",
|
||
"role": "admin",
|
||
"name": "编辑公告",
|
||
"status": "passed",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/admin/announcements/nks4mojr6okoomxl2gbl9rwz, 编辑表单存在, 编辑提交成功",
|
||
"screenshot": null,
|
||
"errors": [
|
||
"PageError: Switched to client rendering because the server rendering errored:\n\nFailed to call `useTranslations` because the context from `NextIntlClientProvider` was not found.\n\nThis can happen because:\n1) You i"
|
||
],
|
||
"warnings": [],
|
||
"timestamp": "19:44:49"
|
||
},
|
||
{
|
||
"id": "ANN-PUBLISH-admin",
|
||
"role": "admin",
|
||
"name": "发布公告(编辑页访问)",
|
||
"status": "passed",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/admin/announcements/nks4mojr6okoomxl2gbl9rwz, 编辑页可访问",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:44:52"
|
||
},
|
||
{
|
||
"id": "ANN-FILTER-admin-draft",
|
||
"role": "admin",
|
||
"name": "公告过滤-draft",
|
||
"status": "passed",
|
||
"detail": "过滤=draft, HTTP 200",
|
||
"screenshot": null,
|
||
"errors": [
|
||
"PageError: Switched to client rendering because the server rendering errored:\n\nFailed to call `useTranslations` because the context from `NextIntlClientProvider` was not found.\n\nThis can happen because:\n1) You i"
|
||
],
|
||
"warnings": [],
|
||
"timestamp": "19:44:55"
|
||
},
|
||
{
|
||
"id": "ANN-FILTER-admin-published",
|
||
"role": "admin",
|
||
"name": "公告过滤-published",
|
||
"status": "passed",
|
||
"detail": "过滤=published, HTTP 200",
|
||
"screenshot": null,
|
||
"errors": [
|
||
"PageError: Switched to client rendering because the server rendering errored:\n\nFailed to call `useTranslations` because the context from `NextIntlClientProvider` was not found.\n\nThis can happen because:\n1) You i"
|
||
],
|
||
"warnings": [],
|
||
"timestamp": "19:44:59"
|
||
},
|
||
{
|
||
"id": "ANN-FILTER-admin-archived",
|
||
"role": "admin",
|
||
"name": "公告过滤-archived",
|
||
"status": "passed",
|
||
"detail": "过滤=archived, HTTP 200",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:45:02"
|
||
},
|
||
{
|
||
"id": "MSG-LIST-admin",
|
||
"role": "admin",
|
||
"name": "消息列表页访问",
|
||
"status": "passed",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/messages, 收件箱Tab存在, 已发送Tab存在, Tab切换成功, 撰写按钮存在",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:45:06"
|
||
},
|
||
{
|
||
"id": "MSG-SEARCH-admin",
|
||
"role": "admin",
|
||
"name": "消息搜索",
|
||
"status": "passed",
|
||
"detail": "搜索功能可用",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:45:10"
|
||
},
|
||
{
|
||
"id": "MSG-COMPOSE-admin",
|
||
"role": "admin",
|
||
"name": "撰写消息页访问",
|
||
"status": "passed",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/messages/compose, 收件人选择器存在, 主题输入框存在, 内容输入框存在",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:45:13"
|
||
},
|
||
{
|
||
"id": "MSG-SEND-admin",
|
||
"role": "admin",
|
||
"name": "发送消息",
|
||
"status": "passed",
|
||
"detail": "发送成功,跳转至: http://localhost:3000/messages",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:45:21"
|
||
},
|
||
{
|
||
"id": "MSG-DETAIL-admin",
|
||
"role": "admin",
|
||
"name": "消息详情页访问",
|
||
"status": "warning",
|
||
"detail": "未发现消息详情链接(可能无消息)",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:45:25"
|
||
},
|
||
{
|
||
"id": "MSG-REPLY-admin",
|
||
"role": "admin",
|
||
"name": "消息回复",
|
||
"status": "warning",
|
||
"detail": "无可用消息,跳过回复测试",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:45:28"
|
||
},
|
||
{
|
||
"id": "MSG-DELETE-BTN-admin",
|
||
"role": "admin",
|
||
"name": "消息删除按钮",
|
||
"status": "warning",
|
||
"detail": "无可用消息,跳过删除测试",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:45:31"
|
||
},
|
||
{
|
||
"id": "ANN-LIST-teacher",
|
||
"role": "teacher",
|
||
"name": "公告列表页访问",
|
||
"status": "warning",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/announcements, 标题: 公告, 卡片数: 2",
|
||
"screenshot": "tests\\webapp\\screenshots\\announcements_messages\\ann_list_teacher.png",
|
||
"errors": [
|
||
"PageError: Switched to client rendering because the server rendering errored:\n\nFailed to call `useTranslations` because the context from `NextIntlClientProvider` was not found.\n\nThis can happen because:\n1) You i"
|
||
],
|
||
"warnings": [
|
||
"控制台错误 1 条"
|
||
],
|
||
"timestamp": "19:45:41"
|
||
},
|
||
{
|
||
"id": "ANN-DETAIL-teacher",
|
||
"role": "teacher",
|
||
"name": "公告详情页访问",
|
||
"status": "passed",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/announcements/ann_grade_g1",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:45:50"
|
||
},
|
||
{
|
||
"id": "ANN-ADMIN-LIST-teacher",
|
||
"role": "teacher",
|
||
"name": "管理员公告管理页访问",
|
||
"status": "warning",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/teacher/dashboard?from=%2Fadmin%2Fannouncements&reason=forbidden",
|
||
"screenshot": "tests\\webapp\\screenshots\\announcements_messages\\ann_admin_list_teacher.png",
|
||
"errors": [],
|
||
"warnings": [
|
||
"非管理员访问管理页未显示明确的权限拒绝"
|
||
],
|
||
"timestamp": "19:45:54"
|
||
},
|
||
{
|
||
"id": "MSG-LIST-teacher",
|
||
"role": "teacher",
|
||
"name": "消息列表页访问",
|
||
"status": "passed",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/messages, 收件箱Tab存在, 已发送Tab存在, Tab切换成功, 撰写按钮存在",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:45:59"
|
||
},
|
||
{
|
||
"id": "MSG-SEARCH-teacher",
|
||
"role": "teacher",
|
||
"name": "消息搜索",
|
||
"status": "passed",
|
||
"detail": "搜索功能可用",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:46:04"
|
||
},
|
||
{
|
||
"id": "MSG-COMPOSE-teacher",
|
||
"role": "teacher",
|
||
"name": "撰写消息页访问",
|
||
"status": "warning",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/messages/compose",
|
||
"screenshot": "tests\\webapp\\screenshots\\announcements_messages\\msg_compose_teacher.png",
|
||
"errors": [],
|
||
"warnings": [
|
||
"控制台错误 1 条"
|
||
],
|
||
"timestamp": "19:46:07"
|
||
},
|
||
{
|
||
"id": "MSG-SEND-teacher",
|
||
"role": "teacher",
|
||
"name": "发送消息",
|
||
"status": "passed",
|
||
"detail": "发送成功,跳转至: http://localhost:3000/messages",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:46:15"
|
||
},
|
||
{
|
||
"id": "MSG-DETAIL-teacher",
|
||
"role": "teacher",
|
||
"name": "消息详情页访问",
|
||
"status": "warning",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/messages/ezekhvfwi58nqwus0zojv69j",
|
||
"screenshot": "tests\\webapp\\screenshots\\announcements_messages\\msg_detail_teacher.png",
|
||
"errors": [],
|
||
"warnings": [
|
||
"控制台错误 1 条"
|
||
],
|
||
"timestamp": "19:46:23"
|
||
},
|
||
{
|
||
"id": "MSG-REPLY-teacher",
|
||
"role": "teacher",
|
||
"name": "消息回复",
|
||
"status": "passed",
|
||
"detail": "回复链接正确,URL: http://localhost:3000/messages/compose?parentId=ezekhvfwi58nqwus0zojv69j&receiverId=user_p_s_g1c1_1&subject=Re%3A%20%E8%87%AA%E5%8A%A8%E5%8C%96%E6%B5%8B%E8%AF%95%E6%B6%88%E6%81%AF_194327",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:46:33"
|
||
},
|
||
{
|
||
"id": "MSG-DELETE-BTN-teacher",
|
||
"role": "teacher",
|
||
"name": "消息删除按钮",
|
||
"status": "passed",
|
||
"detail": "删除按钮存在",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:46:40"
|
||
},
|
||
{
|
||
"id": "ANN-LIST-student",
|
||
"role": "student",
|
||
"name": "公告列表页访问",
|
||
"status": "warning",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/announcements, 标题: 公告, 卡片数: 3",
|
||
"screenshot": "tests\\webapp\\screenshots\\announcements_messages\\ann_list_student.png",
|
||
"errors": [],
|
||
"warnings": [
|
||
"控制台错误 1 条"
|
||
],
|
||
"timestamp": "19:46:49"
|
||
},
|
||
{
|
||
"id": "ANN-DETAIL-student",
|
||
"role": "student",
|
||
"name": "公告详情页访问",
|
||
"status": "passed",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/announcements/ann_class_g1c1",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:46:57"
|
||
},
|
||
{
|
||
"id": "ANN-ADMIN-LIST-student",
|
||
"role": "student",
|
||
"name": "管理员公告管理页访问",
|
||
"status": "warning",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/student/dashboard?from=%2Fadmin%2Fannouncements&reason=forbidden",
|
||
"screenshot": "tests\\webapp\\screenshots\\announcements_messages\\ann_admin_list_student.png",
|
||
"errors": [],
|
||
"warnings": [
|
||
"非管理员访问管理页未显示明确的权限拒绝"
|
||
],
|
||
"timestamp": "19:47:05"
|
||
},
|
||
{
|
||
"id": "MSG-LIST-student",
|
||
"role": "student",
|
||
"name": "消息列表页访问",
|
||
"status": "passed",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/messages, 收件箱Tab存在, 已发送Tab存在, Tab切换成功, 撰写按钮存在",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:47:11"
|
||
},
|
||
{
|
||
"id": "MSG-SEARCH-student",
|
||
"role": "student",
|
||
"name": "消息搜索",
|
||
"status": "passed",
|
||
"detail": "搜索功能可用",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:47:18"
|
||
},
|
||
{
|
||
"id": "MSG-COMPOSE-student",
|
||
"role": "student",
|
||
"name": "撰写消息页访问",
|
||
"status": "passed",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/messages/compose, 收件人选择器存在, 主题输入框存在, 内容输入框存在",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:47:22"
|
||
},
|
||
{
|
||
"id": "MSG-SEND-student",
|
||
"role": "student",
|
||
"name": "发送消息",
|
||
"status": "passed",
|
||
"detail": "发送成功,跳转至: http://localhost:3000/messages",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:47:32"
|
||
},
|
||
{
|
||
"id": "MSG-DETAIL-student",
|
||
"role": "student",
|
||
"name": "消息详情页访问",
|
||
"status": "warning",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/messages/l4lxgdpkdm3twlqox85msd1f",
|
||
"screenshot": "tests\\webapp\\screenshots\\announcements_messages\\msg_detail_student.png",
|
||
"errors": [],
|
||
"warnings": [
|
||
"控制台错误 1 条"
|
||
],
|
||
"timestamp": "19:47:41"
|
||
},
|
||
{
|
||
"id": "MSG-REPLY-student",
|
||
"role": "student",
|
||
"name": "消息回复",
|
||
"status": "passed",
|
||
"detail": "回复链接正确,URL: http://localhost:3000/messages/compose?parentId=l4lxgdpkdm3twlqox85msd1f&receiverId=user_T_C1&subject=Re%3A%20%E8%87%AA%E5%8A%A8%E5%8C%96%E6%B5%8B%E8%AF%95%E6%B6%88%E6%81%AF_194612",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:47:52"
|
||
},
|
||
{
|
||
"id": "MSG-DELETE-BTN-student",
|
||
"role": "student",
|
||
"name": "消息删除按钮",
|
||
"status": "passed",
|
||
"detail": "删除按钮存在",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:48:04"
|
||
},
|
||
{
|
||
"id": "ANN-LIST-parent",
|
||
"role": "parent",
|
||
"name": "公告列表页访问",
|
||
"status": "passed",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/announcements, 标题: 公告, 卡片数: 3",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:48:15"
|
||
},
|
||
{
|
||
"id": "ANN-DETAIL-parent",
|
||
"role": "parent",
|
||
"name": "公告详情页访问",
|
||
"status": "warning",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/announcements/ann_class_g1c1",
|
||
"screenshot": "tests\\webapp\\screenshots\\announcements_messages\\ann_detail_parent.png",
|
||
"errors": [],
|
||
"warnings": [
|
||
"控制台错误 1 条"
|
||
],
|
||
"timestamp": "19:48:22"
|
||
},
|
||
{
|
||
"id": "ANN-ADMIN-LIST-parent",
|
||
"role": "parent",
|
||
"name": "管理员公告管理页访问",
|
||
"status": "warning",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/parent/dashboard?from=%2Fadmin%2Fannouncements&reason=forbidden",
|
||
"screenshot": "tests\\webapp\\screenshots\\announcements_messages\\ann_admin_list_parent.png",
|
||
"errors": [],
|
||
"warnings": [
|
||
"非管理员访问管理页未显示明确的权限拒绝"
|
||
],
|
||
"timestamp": "19:48:28"
|
||
},
|
||
{
|
||
"id": "MSG-LIST-parent",
|
||
"role": "parent",
|
||
"name": "消息列表页访问",
|
||
"status": "passed",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/messages, 收件箱Tab存在, 已发送Tab存在, Tab切换成功, 撰写按钮存在",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:48:33"
|
||
},
|
||
{
|
||
"id": "MSG-SEARCH-parent",
|
||
"role": "parent",
|
||
"name": "消息搜索",
|
||
"status": "passed",
|
||
"detail": "搜索功能可用",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:48:41"
|
||
},
|
||
{
|
||
"id": "MSG-COMPOSE-parent",
|
||
"role": "parent",
|
||
"name": "撰写消息页访问",
|
||
"status": "warning",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/messages/compose",
|
||
"screenshot": "tests\\webapp\\screenshots\\announcements_messages\\msg_compose_parent.png",
|
||
"errors": [],
|
||
"warnings": [
|
||
"控制台错误 1 条"
|
||
],
|
||
"timestamp": "19:48:46"
|
||
},
|
||
{
|
||
"id": "MSG-SEND-parent",
|
||
"role": "parent",
|
||
"name": "发送消息",
|
||
"status": "passed",
|
||
"detail": "发送成功,跳转至: http://localhost:3000/messages",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:48:54"
|
||
},
|
||
{
|
||
"id": "MSG-DETAIL-parent",
|
||
"role": "parent",
|
||
"name": "消息详情页访问",
|
||
"status": "warning",
|
||
"detail": "HTTP 200, URL: http://localhost:3000/messages/zy7o8kx9m2jm2z7t1kh4opkk",
|
||
"screenshot": "tests\\webapp\\screenshots\\announcements_messages\\msg_detail_parent.png",
|
||
"errors": [],
|
||
"warnings": [
|
||
"控制台错误 1 条"
|
||
],
|
||
"timestamp": "19:49:08"
|
||
},
|
||
{
|
||
"id": "MSG-REPLY-parent",
|
||
"role": "parent",
|
||
"name": "消息回复",
|
||
"status": "passed",
|
||
"detail": "回复链接正确,URL: http://localhost:3000/messages/compose?parentId=zy7o8kx9m2jm2z7t1kh4opkk&receiverId=user_admin&subject=Re%3A%20%E8%87%AA%E5%8A%A8%E5%8C%96%E6%B5%8B%E8%AF%95%E6%B6%88%E6%81%AF_194519",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:49:20"
|
||
},
|
||
{
|
||
"id": "MSG-DELETE-BTN-parent",
|
||
"role": "parent",
|
||
"name": "消息删除按钮",
|
||
"status": "passed",
|
||
"detail": "删除按钮存在",
|
||
"screenshot": null,
|
||
"errors": [],
|
||
"warnings": [],
|
||
"timestamp": "19:49:31"
|
||
}
|
||
],
|
||
"console_errors_global": [
|
||
{
|
||
"case": "ANN-LIST-admin",
|
||
"role": "admin",
|
||
"error": "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:\n\n- A server/client branch `if (typeof window !== 'undefined')`.\n- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n- Date formatting in a user's locale which doesn't match the server.\n- External changing data without sending a snapshot of it along with the HTML.\n- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n\n%s%s https://react.dev/link/hydration-mismatch \n\n ...\n <div className=\"flex items...\">\n <GlobalSearch>\n ...\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" size=\"icon\" className=\"relative t...\" aria-label=\"通知\" type=\"button\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded...\"}\n aria-label=\"通知\"\n type=\"button\"\n+ id=\"radix-_R_lebn6lb_\"\n- id=\"radix-_R_2lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n ...\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" className=\"relative s...\" type=\"button\" id=\"radix-_R_t...\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm...\"}\n type=\"button\"\n+ id=\"radix-_R_tebn6lb_\"\n- id=\"radix-_R_3lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n"
|
||
},
|
||
{
|
||
"case": "ANN-DETAIL-admin",
|
||
"role": "admin",
|
||
"error": "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:\n\n- A server/client branch `if (typeof window !== 'undefined')`.\n- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n- Date formatting in a user's locale which doesn't match the server.\n- External changing data without sending a snapshot of it along with the HTML.\n- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n\n%s%s https://react.dev/link/hydration-mismatch \n\n ...\n <div className=\"flex items...\">\n <GlobalSearch>\n ...\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" size=\"icon\" className=\"relative t...\" aria-label=\"通知\" type=\"button\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded...\"}\n aria-label=\"通知\"\n type=\"button\"\n+ id=\"radix-_R_lebn6lb_\"\n- id=\"radix-_R_2lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n ...\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" className=\"relative s...\" type=\"button\" id=\"radix-_R_t...\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm...\"}\n type=\"button\"\n+ id=\"radix-_R_tebn6lb_\"\n- id=\"radix-_R_3lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n"
|
||
},
|
||
{
|
||
"case": "ANN-ADMIN-LIST-admin",
|
||
"role": "admin",
|
||
"error": "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:\n\n- A server/client branch `if (typeof window !== 'undefined')`.\n- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n- Date formatting in a user's locale which doesn't match the server.\n- External changing data without sending a snapshot of it along with the HTML.\n- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n\n%s%s https://react.dev/link/hydration-mismatch \n\n ...\n <div className=\"flex items...\">\n <GlobalSearch>\n ...\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" size=\"icon\" className=\"relative t...\" aria-label=\"通知\" type=\"button\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded...\"}\n aria-label=\"通知\"\n type=\"button\"\n+ id=\"radix-_R_lebn6lb_\"\n- id=\"radix-_R_2lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n ...\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" className=\"relative s...\" type=\"button\" id=\"radix-_R_t...\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm...\"}\n type=\"button\"\n+ id=\"radix-_R_tebn6lb_\"\n- id=\"radix-_R_3lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n"
|
||
},
|
||
{
|
||
"case": "ANN-LIST-teacher",
|
||
"role": "teacher",
|
||
"error": "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:\n\n- A server/client branch `if (typeof window !== 'undefined')`.\n- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n- Date formatting in a user's locale which doesn't match the server.\n- External changing data without sending a snapshot of it along with the HTML.\n- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n\n%s%s https://react.dev/link/hydration-mismatch \n\n ...\n <div className=\"flex items...\">\n <GlobalSearch>\n ...\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" size=\"icon\" className=\"relative t...\" aria-label=\"通知\" type=\"button\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded...\"}\n aria-label=\"通知\"\n type=\"button\"\n+ id=\"radix-_R_lebn6lb_\"\n- id=\"radix-_R_2lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n ...\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" className=\"relative s...\" type=\"button\" id=\"radix-_R_t...\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm...\"}\n type=\"button\"\n+ id=\"radix-_R_tebn6lb_\"\n- id=\"radix-_R_3lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n"
|
||
},
|
||
{
|
||
"case": "MSG-COMPOSE-teacher",
|
||
"role": "teacher",
|
||
"error": "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:\n\n- A server/client branch `if (typeof window !== 'undefined')`.\n- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n- Date formatting in a user's locale which doesn't match the server.\n- External changing data without sending a snapshot of it along with the HTML.\n- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n\n%s%s https://react.dev/link/hydration-mismatch \n\n ...\n <div className=\"flex items...\">\n <GlobalSearch>\n ...\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" size=\"icon\" className=\"relative t...\" aria-label=\"通知\" type=\"button\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded...\"}\n aria-label=\"通知\"\n type=\"button\"\n+ id=\"radix-_R_lebn6lb_\"\n- id=\"radix-_R_2lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n ...\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" className=\"relative s...\" type=\"button\" id=\"radix-_R_t...\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm...\"}\n type=\"button\"\n+ id=\"radix-_R_tebn6lb_\"\n- id=\"radix-_R_3lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n"
|
||
},
|
||
{
|
||
"case": "MSG-DETAIL-teacher",
|
||
"role": "teacher",
|
||
"error": "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:\n\n- A server/client branch `if (typeof window !== 'undefined')`.\n- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n- Date formatting in a user's locale which doesn't match the server.\n- External changing data without sending a snapshot of it along with the HTML.\n- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n\n%s%s https://react.dev/link/hydration-mismatch \n\n ...\n <div className=\"flex items...\">\n <GlobalSearch>\n ...\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" size=\"icon\" className=\"relative t...\" aria-label=\"通知\" type=\"button\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded...\"}\n aria-label=\"通知\"\n type=\"button\"\n+ id=\"radix-_R_lebn6lb_\"\n- id=\"radix-_R_2lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n ...\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" className=\"relative s...\" type=\"button\" id=\"radix-_R_t...\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm...\"}\n type=\"button\"\n+ id=\"radix-_R_tebn6lb_\"\n- id=\"radix-_R_3lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n"
|
||
},
|
||
{
|
||
"case": "ANN-LIST-student",
|
||
"role": "student",
|
||
"error": "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:\n\n- A server/client branch `if (typeof window !== 'undefined')`.\n- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n- Date formatting in a user's locale which doesn't match the server.\n- External changing data without sending a snapshot of it along with the HTML.\n- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n\n%s%s https://react.dev/link/hydration-mismatch \n\n ...\n <div className=\"flex items...\">\n <GlobalSearch>\n ...\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" size=\"icon\" className=\"relative t...\" aria-label=\"通知\" type=\"button\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded...\"}\n aria-label=\"通知\"\n type=\"button\"\n+ id=\"radix-_R_lebn6lb_\"\n- id=\"radix-_R_2lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n ...\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" className=\"relative s...\" type=\"button\" id=\"radix-_R_t...\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm...\"}\n type=\"button\"\n+ id=\"radix-_R_tebn6lb_\"\n- id=\"radix-_R_3lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n"
|
||
},
|
||
{
|
||
"case": "MSG-DETAIL-student",
|
||
"role": "student",
|
||
"error": "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:\n\n- A server/client branch `if (typeof window !== 'undefined')`.\n- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n- Date formatting in a user's locale which doesn't match the server.\n- External changing data without sending a snapshot of it along with the HTML.\n- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n\n%s%s https://react.dev/link/hydration-mismatch \n\n ...\n <div className=\"flex items...\">\n <GlobalSearch>\n ...\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" size=\"icon\" className=\"relative t...\" aria-label=\"通知\" type=\"button\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded...\"}\n aria-label=\"通知\"\n type=\"button\"\n+ id=\"radix-_R_lebn6lb_\"\n- id=\"radix-_R_2lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n ...\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" className=\"relative s...\" type=\"button\" id=\"radix-_R_t...\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm...\"}\n type=\"button\"\n+ id=\"radix-_R_tebn6lb_\"\n- id=\"radix-_R_3lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n"
|
||
},
|
||
{
|
||
"case": "ANN-DETAIL-parent",
|
||
"role": "parent",
|
||
"error": "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:\n\n- A server/client branch `if (typeof window !== 'undefined')`.\n- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n- Date formatting in a user's locale which doesn't match the server.\n- External changing data without sending a snapshot of it along with the HTML.\n- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n\n%s%s https://react.dev/link/hydration-mismatch \n\n ...\n <div className=\"flex items...\">\n <GlobalSearch>\n ...\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" size=\"icon\" className=\"relative t...\" aria-label=\"通知\" type=\"button\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded...\"}\n aria-label=\"通知\"\n type=\"button\"\n+ id=\"radix-_R_lebn6lb_\"\n- id=\"radix-_R_2lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n ...\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" className=\"relative s...\" type=\"button\" id=\"radix-_R_t...\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm...\"}\n type=\"button\"\n+ id=\"radix-_R_tebn6lb_\"\n- id=\"radix-_R_3lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n"
|
||
},
|
||
{
|
||
"case": "MSG-COMPOSE-parent",
|
||
"role": "parent",
|
||
"error": "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:\n\n- A server/client branch `if (typeof window !== 'undefined')`.\n- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n- Date formatting in a user's locale which doesn't match the server.\n- External changing data without sending a snapshot of it along with the HTML.\n- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n\n%s%s https://react.dev/link/hydration-mismatch \n\n ...\n <div className=\"flex items...\">\n <GlobalSearch>\n ...\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" size=\"icon\" className=\"relative t...\" aria-label=\"通知\" type=\"button\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded...\"}\n aria-label=\"通知\"\n type=\"button\"\n+ id=\"radix-_R_lebn6lb_\"\n- id=\"radix-_R_2lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n ...\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" className=\"relative s...\" type=\"button\" id=\"radix-_R_t...\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm...\"}\n type=\"button\"\n+ id=\"radix-_R_tebn6lb_\"\n- id=\"radix-_R_3lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n"
|
||
},
|
||
{
|
||
"case": "MSG-DETAIL-parent",
|
||
"role": "parent",
|
||
"error": "A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:\n\n- A server/client branch `if (typeof window !== 'undefined')`.\n- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n- Date formatting in a user's locale which doesn't match the server.\n- External changing data without sending a snapshot of it along with the HTML.\n- Invalid HTML tag nesting.\n\nIt can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n\n%s%s https://react.dev/link/hydration-mismatch \n\n ...\n <div className=\"flex items...\">\n <GlobalSearch>\n ...\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_l...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" size=\"icon\" className=\"relative t...\" aria-label=\"通知\" type=\"button\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded...\"}\n aria-label=\"通知\"\n type=\"button\"\n+ id=\"radix-_R_lebn6lb_\"\n- id=\"radix-_R_2lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n ...\n <MenuProvider scope={{Menu:[...], ...}} onClose={function Menu.useCallback} isUsingKeyboardRef={{current:false}} ...>\n <DropdownMenuTrigger asChild={true}>\n <DropdownMenuTrigger data-slot=\"dropdown-m...\" asChild={true}>\n <MenuAnchor asChild={true} __scopeMenu={{Menu:[...], ...}}>\n <PopperAnchor __scopePopper={{Menu:[...], ...}} asChild={true} ref={null}>\n <Primitive.div asChild={true} ref={function}>\n <Primitive.div.Slot ref={function}>\n <Primitive.div.SlotClone ref={function}>\n <Primitive.button type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" aria-expanded={false} ...>\n <Primitive.button.Slot type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Primitive.button.SlotClone type=\"button\" id=\"radix-_R_t...\" aria-haspopup=\"menu\" ...>\n <Button variant=\"ghost\" className=\"relative s...\" type=\"button\" id=\"radix-_R_t...\" ...>\n <button\n data-slot=\"dropdown-menu-trigger\"\n className={\"inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm...\"}\n type=\"button\"\n+ id=\"radix-_R_tebn6lb_\"\n- id=\"radix-_R_3lqbn6lb_\"\n aria-haspopup=\"menu\"\n aria-expanded={false}\n aria-controls={undefined}\n data-state=\"closed\"\n data-disabled={undefined}\n disabled={false}\n onPointerDown={function handleEvent}\n onKeyDown={function handleEvent}\n ref={function}\n >\n ...\n"
|
||
}
|
||
],
|
||
"fixed_issues": [
|
||
{
|
||
"title": "修复 /announcements 页面 HTTP 500 错误",
|
||
"file": "src/modules/announcements/components/announcement-list.tsx, src/app/(dashboard)/announcements/page.tsx",
|
||
"problem": "Server Component (/announcements/page.tsx) 直接传递函数 detailHrefBuilder 给 Client Component (AnnouncementList),违反 Next.js 16 的序列化规则,导致 'Functions cannot be passed directly to Client Components' 错误。",
|
||
"fix": "在 AnnouncementList 中新增 detailHrefPrefix prop(字符串类型,Server Component 安全),内部通过 prefix + id 拼接构建详情链接。/announcements 和 /admin/announcements 页面改用 detailHrefPrefix 代替 detailHrefBuilder。保留 detailHrefBuilder 以兼容现有 Client Component 间调用。"
|
||
},
|
||
{
|
||
"title": "修复消息发送失败 - 数据库 schema 不同步",
|
||
"file": "数据库 messages 表",
|
||
"problem": "schema.ts 中定义了 senderDeletedAt 和 receiverDeletedAt 列(用于软删除),但数据库 messages 表缺失这两列,导致 createMessage INSERT 查询失败,返回 'Failed query: insert into messages' 错误。",
|
||
"fix": "通过 ALTER TABLE messages ADD COLUMN sender_deleted_at TIMESTAMP NULL 和 ALTER TABLE messages ADD COLUMN receiver_deleted_at TIMESTAMP NULL 手动添加缺失列,使数据库 schema 与代码 schema 保持同步。"
|
||
}
|
||
]
|
||
} |