This is an automated email from the ASF dual-hosted git repository.

robin0716 pushed a commit to branch feat/1.4.0/personal
in repository https://gitbox.apache.org/repos/asf/incubator-answer.git

commit 989261eea30c54ebe3247e18f04562a8891b09b2
Author: robin <[email protected]>
AuthorDate: Fri Aug 16 11:41:57 2024 +0800

    refactor: Update badge description styling in Admin Badges page
---
 ui/src/common/interface.ts                         | 15 ++++-
 ui/src/components/Modal/BadgeModal.tsx             | 70 ++++++++++++++++++++++
 ui/src/components/Modal/index.tsx                  |  3 +-
 ui/src/pages/Admin/Badges/index.tsx                |  6 +-
 ui/src/pages/Layout/index.tsx                      |  8 ++-
 .../components/Achievements/index.scss             |  5 +-
 .../components/Achievements/index.tsx              | 27 +++++++--
 ui/src/services/admin/badges.ts                    |  8 +--
 8 files changed, 124 insertions(+), 18 deletions(-)

diff --git a/ui/src/common/interface.ts b/ui/src/common/interface.ts
index 608a2465..a06fec28 100644
--- a/ui/src/common/interface.ts
+++ b/ui/src/common/interface.ts
@@ -220,11 +220,19 @@ export interface SetNoticeReq {
   notice_switch: boolean;
 }
 
+export interface NotificationBadgeAward {
+  notification_id: string;
+  badge_id: string;
+  name: string;
+  icon: string;
+  level: number;
+}
 export interface NotificationStatus {
   inbox: number;
   achievement: number;
   revision: number;
   can_revision: boolean;
+  badge_award: NotificationBadgeAward | null;
 }
 
 export interface QuestionDetailRes {
@@ -758,13 +766,18 @@ export interface BadgeInfo extends BadgeListItem {
   is_single: boolean;
 }
 
+export interface AdminBadgeListItem extends BadgeListItem {
+  group_name: string;
+  status: string;
+  description: string;
+}
+
 export interface BadgeDetailListReq {
   page: number;
   page_size: number;
   badge_id: string;
   username?: string | null;
 }
-
 export interface BadgeDetailListItem {
   created_at: number;
   author_user_info: UserInfoBase;
diff --git a/ui/src/components/Modal/BadgeModal.tsx 
b/ui/src/components/Modal/BadgeModal.tsx
new file mode 100644
index 00000000..214c1d67
--- /dev/null
+++ b/ui/src/components/Modal/BadgeModal.tsx
@@ -0,0 +1,70 @@
+import { FC } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useNavigate } from 'react-router-dom';
+
+import classNames from 'classnames';
+
+import type * as Type from '@/common/interface';
+import { loggedUserInfoStore } from '@/stores';
+import { readNotification, useQueryNotificationStatus } from '@/services';
+import Icon from '../Icon';
+
+import Modal from './Modal';
+
+interface BadgeModalProps {
+  badge?: Type.NotificationBadgeAward | null;
+  visible: boolean;
+}
+const BadgeModal: FC<BadgeModalProps> = ({ badge, visible }) => {
+  const { t } = useTranslation('translation', { keyPrefix: 'badges.modal' });
+  const { user } = loggedUserInfoStore();
+  const navigate = useNavigate();
+  const { data } = useQueryNotificationStatus();
+
+  const handleCancel = async () => {
+    if (!data) return;
+    await readNotification(badge?.notification_id);
+  };
+  const handleConfirm = async () => {
+    await readNotification(badge?.notification_id);
+
+    const url = `/badges/${badge?.badge_id}?username=${user.username}`;
+    navigate(url);
+  };
+
+  return (
+    <Modal
+      title={t('title')}
+      visible={visible}
+      onCancel={handleCancel}
+      onConfirm={handleConfirm}
+      cancelText={t('close')}
+      cancelBtnVariant="link"
+      confirmText={t('confirm')}
+      confirmBtnVariant="primary"
+      scrollable={false}>
+      {badge && (
+        <div className="text-center">
+          {badge.icon?.startsWith('http') ? (
+            <img src={badge.icon} width={96} height={96} alt={badge.name} />
+          ) : (
+            <Icon
+              name={badge.icon}
+              size="96px"
+              className={classNames(
+                'lh-1',
+                badge.level === 1 && 'bronze',
+                badge.level === 2 && 'silver',
+                badge.level === 3 && 'gold',
+              )}
+            />
+          )}
+          <h5 className="mt-3">{badge?.name}</h5>
+          <p>{t('content')}</p>
+        </div>
+      )}
+    </Modal>
+  );
+};
+
+export default BadgeModal;
diff --git a/ui/src/components/Modal/index.tsx 
b/ui/src/components/Modal/index.tsx
index 40459196..75d96842 100644
--- a/ui/src/components/Modal/index.tsx
+++ b/ui/src/components/Modal/index.tsx
@@ -20,6 +20,7 @@
 import DefaultModal from './Modal';
 import confirm, { Config } from './Confirm';
 import LoginToContinueModal from './LoginToContinueModal';
+import BadgeModal from './BadgeModal';
 
 type ModalType = typeof DefaultModal & {
   confirm: (config: Config) => void;
@@ -32,4 +33,4 @@ Modal.confirm = function (props: Config) {
 
 export default Modal;
 
-export { LoginToContinueModal };
+export { LoginToContinueModal, BadgeModal };
diff --git a/ui/src/pages/Admin/Badges/index.tsx 
b/ui/src/pages/Admin/Badges/index.tsx
index 448040a4..e76f90eb 100644
--- a/ui/src/pages/Admin/Badges/index.tsx
+++ b/ui/src/pages/Admin/Badges/index.tsx
@@ -39,7 +39,7 @@ const bgMap = {
 
 const PAGE_SIZE = 10;
 
-const Users: FC = () => {
+const Badges: FC = () => {
   const { t } = useTranslation('translation', { keyPrefix: 'admin.badges' });
 
   const [urlSearchParams, setUrlSearchParams] = useSearchParams();
@@ -103,7 +103,7 @@ const Users: FC = () => {
         </thead>
         <tbody className="align-middle">
           {data?.list.map((badge) => (
-            <tr>
+            <tr key={badge.id}>
               <td className="d-flex align-items-center">
                 {badge.icon?.startsWith('http') ? (
                   <img
@@ -157,4 +157,4 @@ const Users: FC = () => {
   );
 };
 
-export default Users;
+export default Badges;
diff --git a/ui/src/pages/Layout/index.tsx b/ui/src/pages/Layout/index.tsx
index 2029b0fa..e8bab279 100644
--- a/ui/src/pages/Layout/index.tsx
+++ b/ui/src/pages/Layout/index.tsx
@@ -33,8 +33,9 @@ import {
   PageTags,
   HttpErrorContent,
 } from '@/components';
-import { LoginToContinueModal } from '@/components/Modal';
+import { LoginToContinueModal, BadgeModal } from '@/components/Modal';
 import { changeTheme } from '@/utils';
+import { useQueryNotificationStatus } from '@/services';
 
 const Layout: FC = () => {
   const location = useLocation();
@@ -44,6 +45,7 @@ const Layout: FC = () => {
   };
   const { code: httpStatusCode, reset: httpStatusReset } = errorCodeStore();
   const { show: showLoginToContinueModal } = loginToContinueStore();
+  const { data: notificationData } = useQueryNotificationStatus();
 
   useEffect(() => {
     httpStatusReset();
@@ -86,6 +88,10 @@ const Layout: FC = () => {
         <Footer />
         <Customize />
         <LoginToContinueModal visible={showLoginToContinueModal} />
+        <BadgeModal
+          badge={notificationData?.badge_award}
+          visible={Boolean(notificationData?.badge_award)}
+        />
         <ScrollRestoration />
       </SWRConfig>
     </HelmetProvider>
diff --git 
a/ui/src/pages/Users/Notifications/components/Achievements/index.scss 
b/ui/src/pages/Users/Notifications/components/Achievements/index.scss
index fb91193f..9a224921 100644
--- a/ui/src/pages/Users/Notifications/components/Achievements/index.scss
+++ b/ui/src/pages/Users/Notifications/components/Achievements/index.scss
@@ -18,8 +18,9 @@
  */
 
 .achievement-wrap {
-  .num {
+  .num,
+  .icon {
     width: 60px;
     flex: none;
   }
-}
+}
\ No newline at end of file
diff --git a/ui/src/pages/Users/Notifications/components/Achievements/index.tsx 
b/ui/src/pages/Users/Notifications/components/Achievements/index.tsx
index 5f2e8f58..c171a427 100644
--- a/ui/src/pages/Users/Notifications/components/Achievements/index.tsx
+++ b/ui/src/pages/Users/Notifications/components/Achievements/index.tsx
@@ -24,10 +24,13 @@ import classNames from 'classnames';
 import isEmpty from 'lodash/isEmpty';
 
 import { Empty } from '@/components';
+import { loggedUserInfoStore } from '@/stores';
 
 import './index.scss';
 
 const Achievements = ({ data, handleReadNotification }) => {
+  const { user } = loggedUserInfoStore();
+
   if (!data) {
     return null;
   }
@@ -50,6 +53,9 @@ const Achievements = ({ data, handleReadNotification }) => {
           case 'comment':
             url = `/questions/${question}/${answer}?commentId=${comment}`;
             break;
+          case 'badge_award':
+            url = 
`/badges/${item.object_info.object_map.badge_id}?username=${user.username}`;
+            break;
           default:
             url = '';
         }
@@ -60,13 +66,22 @@ const Achievements = ({ data, handleReadNotification }) => {
               'd-flex border-start-0 border-end-0 py-3',
               !item.is_read && 'warning',
             )}>
-            {item.rank > 0 && (
-              <div className="text-success num 
text-end">{`+${item.rank}`}</div>
-            )}
-            {item.rank === 0 && <div className="num 
text-end">{item.rank}</div>}
-            {item.rank < 0 && (
-              <div className="text-danger num text-end">{`${item.rank}`}</div>
+            {item.object_info.object_type === 'badge_award' ? (
+              <div className="icon text-end">👏</div>
+            ) : (
+              <>
+                {item.rank > 0 && (
+                  <div className="text-success num 
text-end">{`+${item.rank}`}</div>
+                )}
+                {item.rank === 0 && (
+                  <div className="num text-end">{item.rank}</div>
+                )}
+                {item.rank < 0 && (
+                  <div className="text-danger num 
text-end">{`${item.rank}`}</div>
+                )}
+              </>
             )}
+
             <div className="d-flex flex-column ms-3 flex-fill">
               <Link to={url} onClick={() => handleReadNotification(item.id)}>
                 {item.object_info.title}
diff --git a/ui/src/services/admin/badges.ts b/ui/src/services/admin/badges.ts
index b984164e..64685bc5 100644
--- a/ui/src/services/admin/badges.ts
+++ b/ui/src/services/admin/badges.ts
@@ -25,10 +25,10 @@ import type * as Type from '@/common/interface';
 
 export const useQueryBadges = (params) => {
   const apiUrl = `/answer/admin/api/badges?${qs.stringify(params)}`;
-  const { data, error, mutate } = useSWR<Type.ListResult, Error>(
-    apiUrl,
-    request.instance.get,
-  );
+  const { data, error, mutate } = useSWR<
+    Type.ListResult<Type.AdminBadgeListItem>,
+    Error
+  >(apiUrl, request.instance.get);
   return {
     data,
     isLoading: !data && !error,

Reply via email to