arc/docs/SUPPORT_OPERATORS_MANAGEMENT_SCENARIO.md
2026-04-14 19:34:55 +03:30

31 KiB
Executable file
Raw Permalink Blame History

سناریوی پیاده‌سازی: مدیریت اپراتورهای پشتیبانی

📋 خلاصه اجرایی

این سند سناریوی کامل پیاده‌سازی بخش مدیریت App-Level Permissions (با تمرکز روی اپراتورهای پشتیبانی) را شامل می‌شود.


🎯 اهداف

  1. مدیریت مرکزی: ایجاد یک پنل مدیریتی برای تخصیص و لغو permissions در سطح اپلیکیشن
  2. امنیت: فقط SuperAdmin بتواند App Permissions را مدیریت کند
  3. ساده و کاربرپسند: UI ساده و واضح برای مدیریت نقش‌ها
  4. قابل توسعه: طراحی به گونه‌ای که بتوان permissions جدید اضافه کرد

🏗️ معماری کلی

┌─────────────────────────────────────────────────────────────┐
│                   SuperAdmin Panel (UI)                     │
│  ┌────────────────────────────────────────────────────────┐ │
│  │  User Management                                       │ │
│  │  ┌──────────────────────────────────────────────────┐ │ │
│  │  │  • List Users                                    │ │ │
│  │  │  • View User Details                            │ │ │
│  │  │  • Manage App Permissions ← NEW!               │ │ │
│  │  │    - Support Operator                           │ │ │
│  │  │    - System Settings                            │ │ │
│  │  │    - User Management                            │ │ │
│  │  │    - Business Management                        │ │ │
│  │  └──────────────────────────────────────────────────┘ │ │
│  └────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
                          ↕ API Calls
┌─────────────────────────────────────────────────────────────┐
│                      Backend API                            │
│  ┌────────────────────────────────────────────────────────┐ │
│  │  /api/v1/admin/users/{user_id}/app-permissions         │ │
│  │    • GET: دریافت permissions کاربر                    │ │
│  │    • PUT: به‌روزرسانی permissions کاربر ← NEW!        │ │
│  └────────────────────────────────────────────────────────┘ │
│  ┌────────────────────────────────────────────────────────┐ │
│  │  /api/v1/admin/operators ← NEW!                        │ │
│  │    • GET: لیست اپراتورهای پشتیبانی                    │ │
│  │    • POST: اضافه کردن اپراتور                         │ │
│  │    • DELETE: حذف اپراتور                              │ │
│  └────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘

📍 فاز 1: Backend API (اولویت بالا)

1.1 ایجاد Endpoint های جدید

فایل: hesabixAPI/adapters/api/v1/admin/users_permissions.py (جدید)

from fastapi import APIRouter, Depends, HTTPException, Request
from sqlalchemy.orm import Session
from typing import Dict, Any, List
from pydantic import BaseModel, Field

from adapters.db.session import get_db
from adapters.db.models.user import User
from adapters.db.repositories.user_repo import UserRepository
from app.core.auth_dependency import get_current_user, AuthContext
from app.core.permissions import require_app_permission
from app.core.responses import success_response

router = APIRouter(
    prefix="/admin/users",
    tags=["مدیریت کاربران", "مدیریت سیستم"]
)


# ========== Schemas ==========

class AppPermissionsResponse(BaseModel):
    """پاسخ دریافت App Permissions"""
    user_id: int
    email: str
    app_permissions: Dict[str, bool] = Field(default_factory=dict)
    
    class Config:
        from_attributes = True


class UpdateAppPermissionsRequest(BaseModel):
    """درخواست به‌روزرسانی App Permissions"""
    permissions: Dict[str, bool] = Field(
        ..., 
        description="دسترسی‌های سطح اپلیکیشن (مثال: {'support_operator': true, 'system_settings': false})"
    )
    
    class Config:
        json_schema_extra = {
            "example": {
                "permissions": {
                    "support_operator": True,
                    "system_settings": False,
                    "user_management": False,
                    "business_management": False
                }
            }
        }


class OperatorSummary(BaseModel):
    """خلاصه اطلاعات اپراتور"""
    id: int
    email: str
    first_name: str | None
    last_name: str | None
    full_name: str | None
    telegram_chat_id: str | None
    is_active: bool
    created_at: str
    
    class Config:
        from_attributes = True


# ========== Endpoints ==========

@router.get(
    "/{user_id}/app-permissions",
    summary="دریافت App Permissions کاربر",
    description="دریافت دسترسی‌های سطح اپلیکیشن یک کاربر. نیاز به مجوز SuperAdmin دارد.",
    response_model=AppPermissionsResponse
)
@require_app_permission("superadmin")
async def get_user_app_permissions(
    user_id: int,
    request: Request,
    ctx: AuthContext = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """دریافت App Permissions کاربر"""
    user = db.get(User, user_id)
    if not user:
        raise HTTPException(status_code=404, detail="کاربر یافت نشد")
    
    return success_response(
        data={
            "user_id": user.id,
            "email": user.email,
            "app_permissions": user.app_permissions or {}
        },
        request=request
    )


@router.put(
    "/{user_id}/app-permissions",
    summary="به‌روزرسانی App Permissions کاربر",
    description="به‌روزرسانی دسترسی‌های سطح اپلیکیشن یک کاربر. فقط SuperAdmin.",
)
@require_app_permission("superadmin")
async def update_user_app_permissions(
    user_id: int,
    permissions_request: UpdateAppPermissionsRequest,
    request: Request,
    ctx: AuthContext = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """به‌روزرسانی App Permissions کاربر"""
    
    # بررسی وجود کاربر
    user = db.get(User, user_id)
    if not user:
        raise HTTPException(status_code=404, detail="کاربر یافت نشد")
    
    # جلوگیری از تغییر permissions خودش
    if user.id == ctx.get_user_id():
        raise HTTPException(
            status_code=403, 
            detail="شما نمی‌توانید دسترسی‌های خود را تغییر دهید"
        )
    
    # فیلتر کردن فقط permissions معتبر
    valid_permissions = {
        "superadmin",
        "support_operator", 
        "system_settings",
        "user_management",
        "business_management"
    }
    
    # فقط permissions که true هستند را نگه می‌داریم
    new_permissions = {
        k: v for k, v in permissions_request.permissions.items()
        if k in valid_permissions and v is True
    }
    
    # به‌روزرسانی
    user.app_permissions = new_permissions
    db.commit()
    db.refresh(user)
    
    return success_response(
        data={
            "user_id": user.id,
            "email": user.email,
            "app_permissions": user.app_permissions or {}
        },
        request=request,
        message="دسترسی‌ها با موفقیت به‌روزرسانی شد"
    )


# ========== Operator Management Endpoints ==========

@router.get(
    "/operators",
    summary="لیست اپراتورهای پشتیبانی",
    description="دریافت لیست تمام اپراتورهای پشتیبانی فعال",
    response_model=List[OperatorSummary]
)
@require_app_permission("superadmin")
async def list_support_operators(
    request: Request,
    ctx: AuthContext = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """لیست اپراتورهای پشتیبانی"""
    user_repo = UserRepository(db)
    operators = user_repo.get_support_operators()
    
    operators_data = []
    for op in operators:
        full_name = None
        if op.first_name or op.last_name:
            parts = [p for p in [op.first_name, op.last_name] if p]
            full_name = " ".join(parts) if parts else None
        
        operators_data.append({
            "id": op.id,
            "email": op.email,
            "first_name": op.first_name,
            "last_name": op.last_name,
            "full_name": full_name,
            "telegram_chat_id": op.telegram_chat_id,
            "is_active": op.is_active,
            "created_at": op.created_at.isoformat()
        })
    
    return success_response(
        data={"items": operators_data, "total": len(operators_data)},
        request=request
    )


@router.post(
    "/operators/{user_id}",
    summary="اضافه کردن اپراتور پشتیبانی",
    description="افزودن مجوز اپراتور پشتیبانی به یک کاربر"
)
@require_app_permission("superadmin")
async def add_support_operator(
    user_id: int,
    request: Request,
    ctx: AuthContext = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """اضافه کردن اپراتور پشتیبانی"""
    user = db.get(User, user_id)
    if not user:
        raise HTTPException(status_code=404, detail="کاربر یافت نشد")
    
    # اضافه کردن permission
    permissions = user.app_permissions or {}
    permissions['support_operator'] = True
    user.app_permissions = permissions
    
    db.commit()
    db.refresh(user)
    
    return success_response(
        data={"user_id": user.id, "email": user.email},
        request=request,
        message="کاربر به عنوان اپراتور پشتیبانی اضافه شد"
    )


@router.delete(
    "/operators/{user_id}",
    summary="حذف اپراتور پشتیبانی",
    description="لغو مجوز اپراتور پشتیبانی از یک کاربر"
)
@require_app_permission("superadmin")
async def remove_support_operator(
    user_id: int,
    request: Request,
    ctx: AuthContext = Depends(get_current_user),
    db: Session = Depends(get_db)
):
    """حذف اپراتور پشتیبانی"""
    user = db.get(User, user_id)
    if not user:
        raise HTTPException(status_code=404, detail="کاربر یافت نشد")
    
    # حذف permission
    if user.app_permissions and 'support_operator' in user.app_permissions:
        permissions = user.app_permissions.copy()
        del permissions['support_operator']
        user.app_permissions = permissions if permissions else {}
        
        db.commit()
        db.refresh(user)
    
    return success_response(
        data={"user_id": user.id, "email": user.email},
        request=request,
        message="مجوز اپراتور پشتیبانی لغو شد"
    )

1.2 ثبت Router جدید

فایل: hesabixAPI/app/main.py

# اضافه کردن import
from adapters.api.v1.admin import users_permissions

# در تابع create_app():
app.include_router(
    users_permissions.router,
    prefix="/api/v1",
)

1.3 تست API

# دریافت App Permissions کاربر
curl -X GET "http://localhost:8000/api/v1/admin/users/1/app-permissions" \
  -H "Authorization: Bearer YOUR_TOKEN"

# به‌روزرسانی App Permissions
curl -X PUT "http://localhost:8000/api/v1/admin/users/1/app-permissions" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "permissions": {
      "support_operator": true,
      "system_settings": false
    }
  }'

# لیست اپراتورها
curl -X GET "http://localhost:8000/api/v1/admin/users/operators" \
  -H "Authorization: Bearer YOUR_TOKEN"

# اضافه کردن اپراتور
curl -X POST "http://localhost:8000/api/v1/admin/users/operators/1" \
  -H "Authorization: Bearer YOUR_TOKEN"

# حذف اپراتور
curl -X DELETE "http://localhost:8000/api/v1/admin/users/operators/1" \
  -H "Authorization: Bearer YOUR_TOKEN"

📍 فاز 2: Frontend UI (اولویت متوسط)

2.1 سرویس API در Flutter

فایل: hesabixUI/hesabix_ui/lib/services/admin_users_service.dart (جدید)

import '../core/api_client.dart';

class AdminUsersService {
  final ApiClient _api;
  AdminUsersService(this._api);

  /// دریافت App Permissions کاربر
  Future<Map<String, dynamic>> getUserAppPermissions(int userId) async {
    final res = await _api.get<Map<String, dynamic>>(
      '/api/v1/admin/users/$userId/app-permissions'
    );
    return Map<String, dynamic>.from(res.data?['data'] as Map? ?? const {});
  }

  /// به‌روزرسانی App Permissions کاربر
  Future<Map<String, dynamic>> updateUserAppPermissions(
    int userId,
    Map<String, bool> permissions
  ) async {
    final res = await _api.put<Map<String, dynamic>>(
      '/api/v1/admin/users/$userId/app-permissions',
      data: {'permissions': permissions}
    );
    return Map<String, dynamic>.from(res.data?['data'] as Map? ?? const {});
  }

  /// لیست اپراتورهای پشتیبانی
  Future<List<Map<String, dynamic>>> listSupportOperators() async {
    final res = await _api.get<Map<String, dynamic>>(
      '/api/v1/admin/users/operators'
    );
    final data = res.data?['data'] as Map? ?? {};
    final items = data['items'] as List? ?? [];
    return items.map((item) => Map<String, dynamic>.from(item as Map)).toList();
  }

  /// اضافه کردن اپراتور
  Future<void> addSupportOperator(int userId) async {
    await _api.post(
      '/api/v1/admin/users/operators/$userId',
    );
  }

  /// حذف اپراتور
  Future<void> removeSupportOperator(int userId) async {
    await _api.delete(
      '/api/v1/admin/users/operators/$userId',
    );
  }
}

2.2 صفحه مدیریت دسترسی‌ها

فایل: hesabixUI/hesabix_ui/lib/pages/admin/user_app_permissions_page.dart (جدید)

import 'package:flutter/material.dart';
import 'package:hesabix_ui/core/api_client.dart';
import 'package:hesabix_ui/services/admin_users_service.dart';

class UserAppPermissionsPage extends StatefulWidget {
  final int userId;
  final String userEmail;

  const UserAppPermissionsPage({
    super.key,
    required this.userId,
    required this.userEmail,
  });

  @override
  State<UserAppPermissionsPage> createState() => _UserAppPermissionsPageState();
}

class _UserAppPermissionsPageState extends State<UserAppPermissionsPage> {
  final _service = AdminUsersService(ApiClient());
  bool _isLoading = true;
  Map<String, bool> _permissions = {};

  final Map<String, String> _permissionLabels = {
    'support_operator': 'اپراتور پشتیبانی',
    'system_settings': 'تنظیمات سیستم',
    'user_management': 'مدیریت کاربران',
    'business_management': 'مدیریت کسب‌وکارها',
  };

  final Map<String, String> _permissionDescriptions = {
    'support_operator': 'دسترسی به پنل پشتیبانی و مدیریت تیکت‌ها',
    'system_settings': 'دسترسی به تنظیمات سیستم',
    'user_management': 'مدیریت کاربران در سطح اپلیکیشن',
    'business_management': 'مدیریت کسب‌وکارها',
  };

  @override
  void initState() {
    super.initState();
    _loadPermissions();
  }

  Future<void> _loadPermissions() async {
    setState(() => _isLoading = true);
    
    try {
      final data = await _service.getUserAppPermissions(widget.userId);
      final appPerms = data['app_permissions'] as Map? ?? {};
      
      // Initialize all permissions
      final permissions = <String, bool>{};
      for (var key in _permissionLabels.keys) {
        permissions[key] = appPerms[key] == true;
      }
      
      setState(() {
        _permissions = permissions;
        _isLoading = false;
      });
    } catch (e) {
      setState(() => _isLoading = false);
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('خطا در بارگذاری دسترسی‌ها: $e'),
            backgroundColor: Colors.red,
          ),
        );
      }
    }
  }

  Future<void> _savePermissions() async {
    try {
      await _service.updateUserAppPermissions(widget.userId, _permissions);
      
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(
            content: Text('دسترسی‌ها با موفقیت ذخیره شد'),
            backgroundColor: Colors.green,
          ),
        );
        Navigator.of(context).pop(true); // Return true to indicate success
      }
    } catch (e) {
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('خطا در ذخیره دسترسی‌ها: $e'),
            backgroundColor: Colors.red,
          ),
        );
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('مدیریت دسترسی‌ها'),
        actions: [
          TextButton.icon(
            onPressed: _isLoading ? null : _savePermissions,
            icon: const Icon(Icons.save, color: Colors.white),
            label: const Text('ذخیره', style: TextStyle(color: Colors.white)),
          ),
        ],
      ),
      body: _isLoading
          ? const Center(child: CircularProgressIndicator())
          : SingleChildScrollView(
              padding: const EdgeInsets.all(16),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  // User Info Card
                  Card(
                    child: ListTile(
                      leading: CircleAvatar(
                        child: Text(widget.userEmail[0].toUpperCase()),
                      ),
                      title: Text(widget.userEmail),
                      subtitle: const Text('مدیریت دسترسی‌های سطح اپلیکیشن'),
                    ),
                  ),
                  
                  const SizedBox(height: 24),
                  
                  // Permissions List
                  const Text(
                    'دسترسی‌های سطح اپلیکیشن',
                    style: TextStyle(
                      fontSize: 18,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(height: 16),
                  
                  ..._permissionLabels.entries.map((entry) {
                    return Card(
                      margin: const EdgeInsets.only(bottom: 8),
                      child: SwitchListTile(
                        title: Text(entry.value),
                        subtitle: Text(_permissionDescriptions[entry.key] ?? ''),
                        value: _permissions[entry.key] ?? false,
                        onChanged: (value) {
                          setState(() {
                            _permissions[entry.key] = value;
                          });
                        },
                        secondary: Icon(
                          _getIconForPermission(entry.key),
                          color: Theme.of(context).primaryColor,
                        ),
                      ),
                    );
                  }),
                ],
              ),
            ),
    );
  }

  IconData _getIconForPermission(String permission) {
    switch (permission) {
      case 'support_operator':
        return Icons.support_agent;
      case 'system_settings':
        return Icons.settings;
      case 'user_management':
        return Icons.people;
      case 'business_management':
        return Icons.business;
      default:
        return Icons.check_circle;
    }
  }
}

2.3 صفحه لیست اپراتورها

فایل: hesabixUI/hesabix_ui/lib/pages/admin/support_operators_page.dart (جدید)

import 'package:flutter/material.dart';
import 'package:hesabix_ui/core/api_client.dart';
import 'package:hesabix_ui/services/admin_users_service.dart';

class SupportOperatorsPage extends StatefulWidget {
  const SupportOperatorsPage({super.key});

  @override
  State<SupportOperatorsPage> createState() => _SupportOperatorsPageState();
}

class _SupportOperatorsPageState extends State<SupportOperatorsPage> {
  final _service = AdminUsersService(ApiClient());
  bool _isLoading = true;
  List<Map<String, dynamic>> _operators = [];

  @override
  void initState() {
    super.initState();
    _loadOperators();
  }

  Future<void> _loadOperators() async {
    setState(() => _isLoading = true);
    
    try {
      final operators = await _service.listSupportOperators();
      setState(() {
        _operators = operators;
        _isLoading = false;
      });
    } catch (e) {
      setState(() => _isLoading = false);
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('خطا در بارگذاری لیست اپراتورها: $e'),
            backgroundColor: Colors.red,
          ),
        );
      }
    }
  }

  Future<void> _removeOperator(int userId, String email) async {
    final confirm = await showDialog<bool>(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('حذف اپراتور'),
        content: Text('آیا مطمئن هستید که می‌خواهید دسترسی اپراتور را از $email لغو کنید؟'),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context, false),
            child: const Text('انصراف'),
          ),
          ElevatedButton(
            onPressed: () => Navigator.pop(context, true),
            style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
            child: const Text('حذف'),
          ),
        ],
      ),
    );

    if (confirm == true) {
      try {
        await _service.removeSupportOperator(userId);
        await _loadOperators();
        
        if (mounted) {
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(
              content: Text('دسترسی اپراتور با موفقیت لغو شد'),
              backgroundColor: Colors.green,
            ),
          );
        }
      } catch (e) {
        if (mounted) {
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(
              content: Text('خطا در حذف اپراتور: $e'),
              backgroundColor: Colors.red,
            ),
          );
        }
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('اپراتورهای پشتیبانی'),
        actions: [
          IconButton(
            icon: const Icon(Icons.refresh),
            onPressed: _loadOperators,
          ),
        ],
      ),
      body: _isLoading
          ? const Center(child: CircularProgressIndicator())
          : _operators.isEmpty
              ? const Center(
                  child: Text('هیچ اپراتور پشتیبانی یافت نشد'),
                )
              : ListView.builder(
                  padding: const EdgeInsets.all(16),
                  itemCount: _operators.length,
                  itemBuilder: (context, index) {
                    final operator = _operators[index];
                    final fullName = operator['full_name'] as String?;
                    final email = operator['email'] as String;
                    final telegramId = operator['telegram_chat_id'] as String?;
                    final isActive = operator['is_active'] as bool? ?? false;
                    
                    return Card(
                      margin: const EdgeInsets.only(bottom: 12),
                      child: ListTile(
                        leading: CircleAvatar(
                          backgroundColor: isActive
                              ? Colors.green
                              : Colors.grey,
                          child: Icon(
                            Icons.support_agent,
                            color: Colors.white,
                          ),
                        ),
                        title: Text(fullName ?? email),
                        subtitle: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            if (fullName != null) Text(email),
                            if (telegramId != null)
                              Row(
                                children: [
                                  const Icon(Icons.telegram, size: 16, color: Colors.blue),
                                  const SizedBox(width: 4),
                                  Text('متصل به تلگرام'),
                                ],
                              ),
                            if (!isActive)
                              const Text(
                                'غیرفعال',
                                style: TextStyle(color: Colors.red),
                              ),
                          ],
                        ),
                        trailing: IconButton(
                          icon: const Icon(Icons.delete, color: Colors.red),
                          onPressed: () => _removeOperator(
                            operator['id'] as int,
                            email,
                          ),
                        ),
                      ),
                    );
                  },
                ),
    );
  }
}

2.4 افزودن به منوی تنظیمات

فایل: hesabixUI/hesabix_ui/lib/pages/system_settings/services/settings_categorization_service.dart

// در بخش items دسته 'users_businesses' (خط 154)، اضافه کنید:

SettingsItem(
  id: 'support_operators',
  title: 'settingsSupportOperators',
  description: 'settingsSupportOperatorsDescription',
  icon: Icons.support_agent_outlined,
  color: const Color(0xFFE91E63),
  route: '/user/profile/system-settings/support-operators',
  categoryId: 'users_businesses',
  order: 3,
),

2.5 افزودن Route ها

فایل: hesabixUI/hesabix_ui/lib/main.dart

// در بخش system-settings routes
GoRoute(
  path: 'support-operators',
  name: 'system_settings_support_operators',
  builder: (context, state) {
    return const SupportOperatorsPage();
  },
),

📍 فاز 3: اضافه کردن به صفحه لیست کاربران (اولویت پایین)

در صفحه لیست کاربران (/api/v1/users) می‌توانید یک دکمه "مدیریت دسترسی‌ها" اضافه کنید که به صفحه UserAppPermissionsPage هدایت کند.


🔒 نکات امنیتی

  1. فقط SuperAdmin: تمام endpoint ها باید با @require_app_permission("superadmin") محافظت شوند
  2. جلوگیری از خودکاری: کاربر نباید بتواند permissions خودش را تغییر دهد
  3. Audit Log: تغییرات permissions باید در activity_logs ثبت شوند
  4. Validation: فقط permissions معتبر باید پذیرفته شوند

📊 اولویت‌بندی پیاده‌سازی

اولویت 1 (ضروری - همین حالا):

  1. ✅ Backend API endpoints برای مدیریت App Permissions
  2. ✅ تست کامل API ها

اولویت 2 (مهم - هفته آینده):

  1. ✅ UI صفحه مدیریت دسترسی‌ها
  2. ✅ UI صفحه لیست اپراتورها
  3. ✅ اتصال به منوی تنظیمات

اولویت 3 (خوب است داشته باشیم - ماه آینده):

  1. ⬜ Audit logging برای تغییرات
  2. ⬜ یکپارچه‌سازی با صفحه لیست کاربران
  3. ⬜ فیلتر و جستجو در لیست اپراتورها

✅ Checklist پیاده‌سازی

Backend:

  • ایجاد فایل users_permissions.py
  • اضافه کردن Schemas (Pydantic models)
  • پیاده‌سازی endpoint های CRUD
  • ثبت router در main.py
  • تست با Postman/curl
  • نوشتن Unit Tests

Frontend:

  • ایجاد admin_users_service.dart
  • پیاده‌سازی UserAppPermissionsPage
  • پیاده‌سازی SupportOperatorsPage
  • افزودن به settings_categorization_service.dart
  • افزودن routes
  • افزودن translations
  • تست در محیط توسعه

Documentation:

  • به‌روزرسانی PERMISSIONS_SYSTEM.md
  • نوشتن مستندات API
  • نوشتن راهنمای کاربر

🚀 نحوه استفاده بعد از پیاده‌سازی

برای SuperAdmin:

  1. افزودن اپراتور:

    • وارد System Settings > Support Operators شوید
    • روی "افزودن اپراتور" کلیک کنید
    • کاربر مورد نظر را انتخاب کنید
  2. مدیریت دسترسی‌ها:

    • وارد System Settings > User Management شوید
    • روی کاربر مورد نظر کلیک کنید
    • "مدیریت دسترسی‌ها" را انتخاب کنید
    • دسترسی‌های مورد نظر را فعال/غیرفعال کنید
    • ذخیره کنید
  3. حذف اپراتور:

    • وارد System Settings > Support Operators شوید
    • روی دکمه حذف کنار نام اپراتور کلیک کنید
    • تأیید کنید

📞 پشتیبانی و سوالات

در صورت بروز مشکل یا سوال:

  • مستندات API: /docs (Swagger UI)
  • لاگ‌های سیستم: journalctl -u hesabix-api -f
  • لاگ‌های اپلیکیشن: فایل‌های log در hesabixAPI/logs/

نکته مهم: این سناریو قابل توسعه است و می‌توان permissions دیگری نیز به همین روش اضافه کرد.