Files
NextEdu/webtest/仪表公告和消息_0.1.0_.json
SpecialX d884c6d513
Some checks failed
CI / scheduled-backup (push) Failing after 36s
CI / backup-verify (push) Has been skipped
CI / weekly-dr-drill (push) Failing after 0s
CI / build-deploy (push) Has been cancelled
CI / security-scan (push) Has been cancelled
test: update and add E2E, integration, visual, and webapp tests
- 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
2026-06-23 17:39:40 +08:00

653 lines
74 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"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 保持同步。"
}
]
}