Просмотр исходного кода

Merge branch 'agile' of https://github.com/iapt-platform/mint into agile

bhikkhu-kosalla-china 1 год назад
Родитель
Сommit
145ae2b9cd

+ 7 - 0
dashboard/src/components/api/Comment.ts

@@ -67,11 +67,18 @@ export interface IDiscussionCountRequest {
   course_id?: string | null;
   sentences: string[][];
 }
+
+export interface IDiscussionCountWbw {
+  book_id: number;
+  paragraph: number;
+  wid: number;
+}
 export interface IDiscussionCountData {
   id: string;
   res_id: string;
   type: string;
   editor_uid: string;
+  wbw?: IDiscussionCountWbw;
 }
 export interface IDiscussionCountResponse {
   ok: boolean;

+ 91 - 13
dashboard/src/components/course/CourseMemberList.tsx

@@ -22,6 +22,11 @@ import { ISetStatus, setStatus } from "./UserAction";
 import { IChannel } from "../channel/Channel";
 import CourseInvite from "./CourseInvite";
 
+interface IParam {
+  role?: string;
+  status?: string[];
+}
+
 interface IRoleTag {
   title: string;
   color: string;
@@ -73,7 +78,7 @@ const CourseMemberListWidget = ({ courseId, onSelect }: IWidget) => {
 
   return (
     <>
-      <ProList<ICourseMember>
+      <ProList<ICourseMember, IParam>
         actionRef={ref}
         search={{
           filterType: "light",
@@ -94,6 +99,7 @@ const CourseMemberListWidget = ({ courseId, onSelect }: IWidget) => {
             search: false,
           },
           avatar: {
+            search: false,
             render(dom, entity, index, action, schema) {
               return <User {...entity.user} showName={false} />;
             },
@@ -150,14 +156,7 @@ const CourseMemberListWidget = ({ courseId, onSelect }: IWidget) => {
                 "reject",
                 "block",
               ];
-              /*
 
-              const undo = {
-                key: "undo",
-                label: "撤销上次操作",
-                disabled: !canUndo,
-              };
-              */
               const items: ItemType[] = actions.map((item) => {
                 return {
                   key: item,
@@ -224,23 +223,96 @@ const CourseMemberListWidget = ({ courseId, onSelect }: IWidget) => {
               ];
             },
           },
-          role: {
+          status: {
             // 自己扩展的字段,主要用于筛选,不在列表中显示
-            title: "角色",
-            valueType: "select",
+            title: "状态",
+            valueType: "checkbox",
             valueEnum: {
-              all: {
+              joined: {
                 text: intl.formatMessage({
-                  id: "forms.fields.publicity.all.label",
+                  id: "course.member.status.joined.label",
                 }),
                 status: "Default",
               },
+              applied: {
+                text: intl.formatMessage({
+                  id: "course.member.status.applied.label",
+                }),
+                status: "Success",
+              },
+              invited: {
+                text: intl.formatMessage({
+                  id: "course.member.status.invited.label",
+                }),
+                status: "Success",
+              },
+              canceled: {
+                text: intl.formatMessage({
+                  id: "course.member.status.canceled.label",
+                }),
+                status: "Success",
+              },
+              revoked: {
+                text: intl.formatMessage({
+                  id: "course.member.status.revoked.label",
+                }),
+                status: "Success",
+              },
+              agreed: {
+                text: intl.formatMessage({
+                  id: "course.member.status.agreed.label",
+                }),
+                status: "Success",
+              },
+              accepted: {
+                text: intl.formatMessage({
+                  id: "course.member.status.accepted.label",
+                }),
+                status: "Success",
+              },
+              disagreed: {
+                text: intl.formatMessage({
+                  id: "course.member.status.disagreed.label",
+                }),
+                status: "Success",
+              },
+              rejected: {
+                text: intl.formatMessage({
+                  id: "course.member.status.rejected.label",
+                }),
+                status: "Success",
+              },
+              left: {
+                text: intl.formatMessage({
+                  id: "course.member.status.left.label",
+                }),
+                status: "Success",
+              },
+              blocked: {
+                text: intl.formatMessage({
+                  id: "course.member.status.blocked.label",
+                }),
+                status: "Success",
+              },
+            },
+          },
+          role: {
+            // 自己扩展的字段,主要用于筛选,不在列表中显示
+            title: "角色",
+            valueType: "select",
+            valueEnum: {
               student: {
                 text: intl.formatMessage({
                   id: "auth.role.student",
                 }),
                 status: "Default",
               },
+              manager: {
+                text: intl.formatMessage({
+                  id: "auth.role.manager",
+                }),
+                status: "Success",
+              },
               assistant: {
                 text: intl.formatMessage({
                   id: "auth.role.assistant",
@@ -264,6 +336,12 @@ const CourseMemberListWidget = ({ courseId, onSelect }: IWidget) => {
           ) {
             url += "&search=" + params.keyword;
           }
+          if (params.role) {
+            url += `&role=${params.role}`;
+          }
+          if (params.status) {
+            url += `&status=${params.status}`;
+          }
           console.info("api request", url);
           const res = await get<ICourseMemberListResponse>(url);
           if (res.ok) {

+ 0 - 7
dashboard/src/components/course/RolePower.ts

@@ -53,13 +53,6 @@ const getActionsByStatus = (
   );
   const actions = data.find((value) => {
     if (value.mode.includes(mode) && value.status === status) {
-      console.debug(
-        "getActionsByStatus value",
-        value,
-        signUpStartAt,
-        signUpEndAt,
-        inSignUp
-      );
       if (inSignUp) {
         if (value.signUp && value.signUp.length > 0) {
           console.debug("getActionsByStatus got it", value.signUp);

+ 15 - 0
dashboard/src/components/discussion/DiscussionButton.tsx

@@ -7,6 +7,7 @@ import { TResType } from "./DiscussionListCard";
 import { useAppSelector } from "../../hooks";
 import { currentUser } from "../../reducers/current-user";
 import { discussionList } from "../../reducers/discussion-count";
+import { IDiscussionCountWbw } from "../api/Comment";
 
 interface IWidget {
   initCount?: number;
@@ -15,6 +16,7 @@ interface IWidget {
   hideCount?: boolean;
   hideInZero?: boolean;
   onlyMe?: boolean;
+  wbw?: IDiscussionCountWbw;
 }
 const DiscussionButton = ({
   initCount = 0,
@@ -23,12 +25,22 @@ const DiscussionButton = ({
   hideCount = false,
   hideInZero = false,
   onlyMe = false,
+  wbw,
 }: IWidget) => {
   const user = useAppSelector(currentUser);
   const discussions = useAppSelector(discussionList);
 
   const all = discussions?.filter((value) => value.res_id === resId);
   const my = all?.filter((value) => value.editor_uid === user?.id);
+  const withStudent = discussions?.filter(
+    (value) =>
+      value.wbw?.book_id === wbw?.book_id &&
+      value.wbw?.paragraph === wbw?.paragraph &&
+      value.wbw?.wid.toString() === wbw?.wid.toString()
+  );
+
+  console.debug("DiscussionButton", discussions, wbw, withStudent);
+
   let currCount = initCount;
   if (onlyMe) {
     if (my) {
@@ -42,6 +54,9 @@ const DiscussionButton = ({
     } else {
       currCount = 0;
     }
+    if (withStudent) {
+      currCount += withStudent.length;
+    }
   }
 
   let myCount = false;

+ 12 - 1
dashboard/src/components/discussion/DiscussionListCard.tsx

@@ -18,6 +18,7 @@ import { ISentenceResponse } from "../api/Corpus";
 import { TDiscussionType } from "./Discussion";
 import { courseInfo, memberInfo } from "../../reducers/current-course";
 import { courseUser } from "../../reducers/course-user";
+import TimeShow from "../general/TimeShow";
 
 export type TResType =
   | "article"
@@ -128,7 +129,17 @@ const DiscussionListCardWidget = ({
             search: false,
             render(dom, entity, index, action, schema) {
               return (
-                <span key={index}>{entity.summary ?? entity.content}</span>
+                <div>
+                  <div key={index}>{entity.summary ?? entity.content}</div>
+                  <Space>
+                    {entity.user.nickName}
+                    <TimeShow
+                      type="secondary"
+                      createdAt={entity.createdAt}
+                      updatedAt={entity.updatedAt}
+                    />
+                  </Space>
+                </div>
               );
             },
           },

+ 11 - 1
dashboard/src/components/template/Nissaya/NissayaMeaning.tsx

@@ -10,7 +10,17 @@ export interface IMeaning {
 }
 
 export const nissayaBase = (item: string, endings: string[]): IMeaning => {
-  let word = item.trim().replaceAll("။", "");
+  let word = item
+    .trim()
+    .replaceAll("။", "")
+    .replaceAll("[}", "")
+    .replaceAll("]", "")
+    .replaceAll("(", "")
+    .replaceAll(")", "")
+    .replaceAll("၊", "")
+    .replaceAll(",", "")
+    .replaceAll(".", "");
+
   let end: string[] = [];
   for (let loop = 0; loop < 3; loop++) {
     for (let i = 0; i < word.length; i++) {

+ 2 - 1
dashboard/src/components/template/Wbw/WbwCase.tsx

@@ -183,7 +183,8 @@ const WbwCaseWidget = ({
     return (
       <div
         className={
-          "wbw_word_item" + errorClass(data.case?.value, answer?.case?.value)
+          "wbw_word_item" +
+          errorClass("case", data.case?.value, answer?.case?.value)
         }
         style={{ display: "flex" }}
       >

+ 5 - 1
dashboard/src/components/template/Wbw/WbwFactorMeaning.tsx

@@ -101,7 +101,11 @@ const WbwFactorMeaningWidget = ({
       <div
         className={
           "wbw_word_item" +
-          errorClass(data.factorMeaning?.value, answer?.factorMeaning?.value)
+          errorClass(
+            "factorMeaning",
+            data.factorMeaning?.value,
+            answer?.factorMeaning?.value
+          )
         }
       >
         <Text type="secondary">

+ 1 - 1
dashboard/src/components/template/Wbw/WbwFactors.tsx

@@ -129,7 +129,7 @@ const WbwFactorsWidget = ({ data, answer, display, onChange }: IWidget) => {
       <div
         className={
           "wbw_word_item" +
-          errorClass(data.factors?.value, answer?.factors?.value)
+          errorClass("factors", data.factors?.value, answer?.factors?.value)
         }
       >
         <Text type="secondary">

+ 18 - 3
dashboard/src/components/template/Wbw/WbwMeaning.tsx

@@ -2,7 +2,7 @@ import { useState } from "react";
 import { useIntl } from "react-intl";
 import { Input, Popover, Typography } from "antd";
 
-import { IWbw, TWbwDisplayMode } from "./WbwWord";
+import { IWbw, TFieldName, TWbwDisplayMode } from "./WbwWord";
 import WbwMeaningSelect from "./WbwMeaningSelect";
 import { ArticleMode } from "../../article/Article";
 import CaseFormula from "./CaseFormula";
@@ -11,13 +11,28 @@ import EditableLabel from "../../general/EditableLabel";
 const { Text } = Typography;
 
 export const errorClass = (
+  field: TFieldName,
   data?: string | null,
   answer?: string | null
 ): string => {
   let classError = "";
   if (data && answer) {
     if (answer !== data) {
-      classError = " wbw_error";
+      classError = " wbw_check";
+      switch (field) {
+        case "case":
+          classError += " wbw_error";
+          break;
+        case "factors":
+          classError += " wbw_warning";
+          break;
+        case "factorMeaning":
+          classError += " wbw_info";
+          break;
+        case "meaning":
+          classError += " wbw_info";
+          break;
+      }
     }
   }
   return classError;
@@ -165,7 +180,7 @@ const WbwMeaningWidget = ({
       <div
         className={
           "wbw_word_item" +
-          errorClass(data.meaning?.value, answer?.meaning?.value)
+          errorClass("meaning", data.meaning?.value, answer?.meaning?.value)
         }
       >
         {editable || display === "list" ? (

+ 5 - 1
dashboard/src/components/template/Wbw/WbwPali.tsx

@@ -406,7 +406,11 @@ const WbwPaliWidget = ({
           <NoteIcon />
           <BookMarkIcon />
           <RelationIcon />
-          <WbwPaliDiscussionIcon data={data} studio={studio} />
+          <WbwPaliDiscussionIcon
+            data={data}
+            studio={studio}
+            channelId={channelId}
+          />
         </Space>
       </div>
     );

+ 17 - 1
dashboard/src/components/template/Wbw/WbwPaliDiscussionIcon.tsx

@@ -1,6 +1,8 @@
 import { useAppSelector } from "../../../hooks";
 import { courseUser } from "../../../reducers/course-user";
+import { courseInfo } from "../../../reducers/current-course";
 import { currentUser } from "../../../reducers/current-user";
+import { IDiscussionCountWbw } from "../../api/Comment";
 import { IStudio } from "../../auth/Studio";
 import DiscussionButton from "../../discussion/DiscussionButton";
 import { IWbw } from "./WbwWord";
@@ -8,11 +10,14 @@ import { IWbw } from "./WbwWord";
 interface IWidget {
   data: IWbw;
   studio?: IStudio;
+  channelId?: string;
 }
-const WbwPaliDiscussionIcon = ({ data, studio }: IWidget) => {
+const WbwPaliDiscussionIcon = ({ data, studio, channelId }: IWidget) => {
   const userInCourse = useAppSelector(courseUser);
   const currUser = useAppSelector(currentUser);
+  const course = useAppSelector(courseInfo);
 
+  let wbw: IDiscussionCountWbw | undefined;
   let onlyMe = false;
   if (userInCourse) {
     if (userInCourse.role === "student") {
@@ -23,6 +28,16 @@ const WbwPaliDiscussionIcon = ({ data, studio }: IWidget) => {
         //其他channel 只显示自己的
         onlyMe = true;
       }
+    } else {
+      if (course) {
+        if (course.channelId === channelId) {
+          wbw = {
+            book_id: data.book,
+            paragraph: data.para,
+            wid: data.sn[0],
+          };
+        }
+      }
     }
   }
 
@@ -34,6 +49,7 @@ const WbwPaliDiscussionIcon = ({ data, studio }: IWidget) => {
       onlyMe={onlyMe}
       resId={data.uid}
       resType="wbw"
+      wbw={wbw}
     />
   );
 };

+ 13 - 2
dashboard/src/components/template/Wbw/wbw.css

@@ -81,11 +81,22 @@
   padding: 0;
   cursor: pointer;
 }
-.wbw_error {
+
+.wbw_check {
   text-decoration: underline wavy;
-  text-decoration-color: red;
   text-underline-offset: 2px;
 }
+.wbw_error {
+  text-decoration-color: red;
+}
+
+.wbw_warning {
+  text-decoration-color: orange;
+}
+
+.wbw_info {
+  text-decoration-color: blue;
+}
 
 .layout-v .wbw_word_item {
   margin-right: 8px;

+ 1 - 0
dashboard/src/locales/en-US/buttons.ts

@@ -92,6 +92,7 @@ const items = {
   "buttons.forgot.password": "forgot password",
   "buttons.select.channel": "Select Channel",
   "buttons.set.display.mode": "Display Mode",
+  "buttons.manage": "Manage",
 };
 
 export default items;

+ 1 - 0
dashboard/src/locales/zh-Hans/buttons.ts

@@ -92,6 +92,7 @@ const items = {
   "buttons.forgot.password": "忘记密码",
   "buttons.select.channel": "选择版本风格",
   "buttons.set.display.mode": "显示模式",
+  "buttons.manage": "管理",
 };
 
 export default items;

+ 1 - 1
dashboard/src/pages/studio/course/list.tsx

@@ -136,7 +136,7 @@ const Widget = () => {
         >
           {intl.formatMessage({
             //编辑
-            id: "buttons.edit",
+            id: "buttons.manage",
           })}
         </Link>
       );