Răsfoiți Sursa

✨ feat(activities): 优化活动管理页面交互体验

- 替换原生confirm对话框为UI组件库的AlertDialog,提升视觉一致性
- 实现活动删除确认对话框,显示活动名称并提供明确操作按钮
- 实现活动状态切换确认对话框,动态显示操作类型
- 添加活动ID到状态切换按钮的data-testid,提高测试稳定性

✅ test(activities): 更新E2E测试以适应新的确认对话框

- 修改删除活动测试,适配新的确认对话框选择器
- 更新状态切换测试,使用动态操作文本匹配确认按钮
- 添加等待对话框出现的显式等待,提高测试稳定性
yourname 4 luni în urmă
părinte
comite
31512147b1

+ 66 - 12
src/client/admin/pages/Activities.tsx

@@ -12,6 +12,7 @@ import { Input } from '@/client/components/ui/input';
 import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/client/components/ui/select';
 import { Badge } from '@/client/components/ui/badge';
 import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/client/components/ui/dialog';
+import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from '@/client/components/ui/alert-dialog';
 import { ActivityForm } from '../components/ActivityForm';
 import type { CreateActivityInput, UpdateActivityInput } from '@/server/modules/activities/activity.schema';
 
@@ -50,6 +51,10 @@ export const ActivitiesPage: React.FC = () => {
   const [typeFilter, setTypeFilter] = useState<string>('all');
   const [isFormOpen, setIsFormOpen] = useState(false);
   const [editingActivity, setEditingActivity] = useState<ActivityResponse | null>(null);
+  const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
+  const [activityToDelete, setActivityToDelete] = useState<ActivityResponse | null>(null);
+  const [statusConfirmOpen, setStatusConfirmOpen] = useState(false);
+  const [activityToToggle, setActivityToToggle] = useState<ActivityResponse | null>(null);
 
   // 防抖搜索
   const debouncedSearch = useCallback(
@@ -181,15 +186,8 @@ export const ActivitiesPage: React.FC = () => {
 
   // 切换活动状态
   const handleToggleStatus = (activity: ActivityResponse) => {
-    const newStatus = activity.isDisabled === 0 ? 1 : 0;
-    const statusText = newStatus === 0 ? '启用' : '禁用';
-
-    if (confirm(`确定要${statusText}这个活动吗?`)) {
-      toggleStatusMutation.mutate({
-        id: activity.id,
-        isDisabled: newStatus
-      });
-    }
+    setActivityToToggle(activity);
+    setStatusConfirmOpen(true);
   };
 
 
@@ -366,9 +364,8 @@ export const ActivitiesPage: React.FC = () => {
                             variant="destructive"
                             size="sm"
                             onClick={() => {
-                              if (confirm('确定要删除这个活动吗?')) {
-                                deleteMutation.mutate(activity.id);
-                              }
+                              setActivityToDelete(activity);
+                              setDeleteConfirmOpen(true);
                             }}
                             data-testid={`delete-activity-${activity.id}`}
                           >
@@ -429,6 +426,63 @@ export const ActivitiesPage: React.FC = () => {
           />
         </DialogContent>
       </Dialog>
+
+      {/* 删除确认对话框 */}
+      <AlertDialog open={deleteConfirmOpen} onOpenChange={setDeleteConfirmOpen}>
+        <AlertDialogContent>
+          <AlertDialogHeader>
+            <AlertDialogTitle>确认删除</AlertDialogTitle>
+            <AlertDialogDescription>
+              确定要删除活动 "{activityToDelete?.name}" 吗?此操作不可撤销。
+            </AlertDialogDescription>
+          </AlertDialogHeader>
+          <AlertDialogFooter>
+            <AlertDialogCancel>取消</AlertDialogCancel>
+            <AlertDialogAction
+              onClick={() => {
+                if (activityToDelete) {
+                  deleteMutation.mutate(activityToDelete.id);
+                }
+                setDeleteConfirmOpen(false);
+                setActivityToDelete(null);
+              }}
+              className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
+            >
+              删除
+            </AlertDialogAction>
+          </AlertDialogFooter>
+        </AlertDialogContent>
+      </AlertDialog>
+
+      {/* 启用/禁用确认对话框 */}
+      <AlertDialog open={statusConfirmOpen} onOpenChange={setStatusConfirmOpen}>
+        <AlertDialogContent>
+          <AlertDialogHeader>
+            <AlertDialogTitle>确认状态切换</AlertDialogTitle>
+            <AlertDialogDescription>
+              确定要{activityToToggle?.isDisabled === 0 ? '禁用' : '启用'}活动 "{activityToToggle?.name}" 吗?
+            </AlertDialogDescription>
+          </AlertDialogHeader>
+          <AlertDialogFooter>
+            <AlertDialogCancel>取消</AlertDialogCancel>
+            <AlertDialogAction
+              onClick={() => {
+                if (activityToToggle) {
+                  const newStatus = activityToToggle.isDisabled === 0 ? 1 : 0;
+                  toggleStatusMutation.mutate({
+                    id: activityToToggle.id,
+                    isDisabled: newStatus
+                  });
+                }
+                setStatusConfirmOpen(false);
+                setActivityToToggle(null);
+              }}
+            >
+              {activityToToggle?.isDisabled === 0 ? '禁用' : '启用'}
+            </AlertDialogAction>
+          </AlertDialogFooter>
+        </AlertDialogContent>
+      </AlertDialog>
     </div>
   );
 };

+ 13 - 6
tests/e2e/pages/admin/activity-management.page.ts

@@ -200,8 +200,11 @@ export class ActivityManagementPage {
     await deleteButton.waitFor({ state: 'visible', timeout: 10000 });
     await deleteButton.click();
 
-    // 确认删除对话框
-    await this.page.getByRole('button', { name: '删除' }).click();
+    // 等待删除确认对话框出现
+    await this.page.waitForSelector('[role="dialog"]', { state: 'visible', timeout: 10000 });
+
+    // 确认删除 - 点击删除按钮
+    await this.page.locator('[role="dialog"]').getByRole('button', { name: '删除' }).click();
 
     // 等待删除操作完成
     try {
@@ -228,15 +231,19 @@ export class ActivityManagementPage {
     const activityRow = await this.getActivityByName(name);
     if (!activityRow) throw new Error(`Activity ${name} not found`);
 
-    // 状态切换按钮(第三个按钮)
-    const statusButton = activityRow.locator('button').nth(2);
+    // 使用data-testid定位状态切换按钮
+    const statusButton = activityRow.locator('[data-testid^="toggle-activity-"]');
     await statusButton.waitFor({ state: 'visible', timeout: 10000 });
 
     const currentStatus = await statusButton.textContent();
     await statusButton.click();
 
-    // 确认状态切换对话框
-    await this.page.getByRole('button', { name: '确认' }).click();
+    // 等待状态切换确认对话框出现
+    await this.page.waitForSelector('[role="dialog"]', { state: 'visible', timeout: 10000 });
+
+    // 确认状态切换 - 点击禁用或启用按钮
+    const actionText = currentStatus === '禁用' ? '启用' : '禁用';
+    await this.page.locator('[role="dialog"]').getByRole('button', { name: actionText }).click();
 
     // 等待操作完成
     await this.page.waitForLoadState('networkidle');