Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>tags/1.7.1
| @@ -123,6 +123,17 @@ class AnnotationListApi(Resource): | |||
| } | |||
| return response, 200 | |||
| @setup_required | |||
| @login_required | |||
| @account_initialization_required | |||
| def delete(self, app_id): | |||
| if not current_user.is_editor: | |||
| raise Forbidden() | |||
| app_id = str(app_id) | |||
| AppAnnotationService.clear_all_annotations(app_id) | |||
| return {"result": "success"}, 204 | |||
| class AnnotationExportApi(Resource): | |||
| @setup_required | |||
| @@ -440,3 +440,27 @@ class AppAnnotationService: | |||
| "embedding_model_name": collection_binding_detail.model_name, | |||
| }, | |||
| } | |||
| @classmethod | |||
| def clear_all_annotations(cls, app_id: str) -> dict: | |||
| app = ( | |||
| db.session.query(App) | |||
| .filter(App.id == app_id, App.tenant_id == current_user.current_tenant_id, App.status == "normal") | |||
| .first() | |||
| ) | |||
| if not app: | |||
| raise NotFound("App not found") | |||
| annotations_query = db.session.query(MessageAnnotation).filter(MessageAnnotation.app_id == app_id) | |||
| for annotation in annotations_query.yield_per(100): | |||
| annotation_hit_histories_query = db.session.query(AppAnnotationHitHistory).filter( | |||
| AppAnnotationHitHistory.annotation_id == annotation.id | |||
| ) | |||
| for annotation_hit_history in annotation_hit_histories_query.yield_per(100): | |||
| db.session.delete(annotation_hit_history) | |||
| db.session.delete(annotation) | |||
| db.session.commit() | |||
| return {"result": "success"} | |||
| @@ -0,0 +1,32 @@ | |||
| 'use client' | |||
| import type { FC } from 'react' | |||
| import React from 'react' | |||
| import { useTranslation } from 'react-i18next' | |||
| import Confirm from '@/app/components/base/confirm' | |||
| type Props = { | |||
| isShow: boolean | |||
| onHide: () => void | |||
| onConfirm: () => void | |||
| } | |||
| const ClearAllAnnotationsConfirmModal: FC<Props> = ({ | |||
| isShow, | |||
| onHide, | |||
| onConfirm, | |||
| }) => { | |||
| const { t } = useTranslation() | |||
| return ( | |||
| <Confirm | |||
| isShow={isShow} | |||
| onCancel={onHide} | |||
| onConfirm={onConfirm} | |||
| type='danger' | |||
| title={t('appAnnotation.table.header.clearAllConfirm')} | |||
| /> | |||
| ) | |||
| } | |||
| export default React.memo(ClearAllAnnotationsConfirmModal) | |||
| @@ -1,9 +1,11 @@ | |||
| 'use client' | |||
| import type { FC } from 'react' | |||
| import React, { Fragment, useEffect, useState } from 'react' | |||
| import ClearAllAnnotationsConfirmModal from '../clear-all-annotations-confirm-modal' | |||
| import { useTranslation } from 'react-i18next' | |||
| import { | |||
| RiAddLine, | |||
| RiDeleteBinLine, | |||
| RiMoreFill, | |||
| } from '@remixicon/react' | |||
| import { useContext } from 'use-context-selector' | |||
| @@ -22,6 +24,7 @@ import { ChevronRight } from '@/app/components/base/icons/src/vender/line/arrows | |||
| import I18n from '@/context/i18n' | |||
| import { fetchExportAnnotationList } from '@/service/annotation' | |||
| import { clearAllAnnotations } from '@/service/annotation' | |||
| import { LanguagesSupported } from '@/i18n-config/language' | |||
| const CSV_HEADER_QA_EN = ['Question', 'Answer'] | |||
| @@ -76,7 +79,21 @@ const HeaderOptions: FC<Props> = ({ | |||
| }, [controlUpdateList]) | |||
| const [showBulkImportModal, setShowBulkImportModal] = useState(false) | |||
| const [showClearConfirm, setShowClearConfirm] = useState(false) | |||
| const handleClearAll = () => { | |||
| setShowClearConfirm(true) | |||
| } | |||
| const handleConfirmed = async () => { | |||
| try { | |||
| await clearAllAnnotations(appId) | |||
| onAdded() | |||
| } | |||
| catch (_) { | |||
| } | |||
| finally { | |||
| setShowClearConfirm(false) | |||
| } | |||
| } | |||
| const Operations = () => { | |||
| return ( | |||
| <div className="w-full py-1"> | |||
| @@ -125,6 +142,15 @@ const HeaderOptions: FC<Props> = ({ | |||
| </MenuItems> | |||
| </Transition> | |||
| </Menu> | |||
| <button | |||
| onClick={handleClearAll} | |||
| className='mx-1 flex h-9 w-[calc(100%_-_8px)] cursor-pointer items-center space-x-2 rounded-lg px-3 py-2 text-red-600 hover:bg-red-50 disabled:opacity-50' | |||
| > | |||
| <RiDeleteBinLine className='h-4 w-4'/> | |||
| <span className='system-sm-regular grow text-left'> | |||
| {t('appAnnotation.table.header.clearAll')} | |||
| </span> | |||
| </button> | |||
| </div> | |||
| ) | |||
| } | |||
| @@ -169,6 +195,15 @@ const HeaderOptions: FC<Props> = ({ | |||
| /> | |||
| ) | |||
| } | |||
| { | |||
| showClearConfirm && ( | |||
| <ClearAllAnnotationsConfirmModal | |||
| isShow={showClearConfirm} | |||
| onHide={() => setShowClearConfirm(false)} | |||
| onConfirm={handleConfirmed} | |||
| /> | |||
| ) | |||
| } | |||
| </div> | |||
| ) | |||
| } | |||
| @@ -16,7 +16,8 @@ const translation = { | |||
| addAnnotation: 'Add Annotation', | |||
| bulkImport: 'Bulk Import', | |||
| bulkExport: 'Bulk Export', | |||
| clearAll: 'Clear All Annotation', | |||
| clearAll: 'Delete All', | |||
| clearAllConfirm: 'Delete all annotations?', | |||
| }, | |||
| }, | |||
| editModal: { | |||
| @@ -18,7 +18,8 @@ const translation = { | |||
| addAnnotation: '注釈を追加', | |||
| bulkImport: '一括インポート', | |||
| bulkExport: '一括エクスポート', | |||
| clearAll: 'すべての注釈をクリア', | |||
| clearAll: 'すべて削除', | |||
| clearAllConfirm: 'すべての寸法を削除?', | |||
| }, | |||
| }, | |||
| editModal: { | |||
| @@ -18,7 +18,8 @@ const translation = { | |||
| addAnnotation: '添加标注', | |||
| bulkImport: '批量导入', | |||
| bulkExport: '批量导出', | |||
| clearAll: '删除所有标注', | |||
| clearAll: '删除所有', | |||
| clearAllConfirm: '删除所有标注?', | |||
| }, | |||
| }, | |||
| editModal: { | |||
| @@ -63,3 +63,7 @@ export const delAnnotation = (appId: string, annotationId: string) => { | |||
| export const fetchHitHistoryList = (appId: string, annotationId: string, params: Record<string, any>) => { | |||
| return get(`apps/${appId}/annotations/${annotationId}/hit-histories`, { params }) | |||
| } | |||
| export const clearAllAnnotations = (appId: string): Promise<any> => { | |||
| return del(`apps/${appId}/annotations`) | |||
| } | |||