diff --git a/__pycache__/server.cpython-312.pyc b/__pycache__/server.cpython-312.pyc index e42c0a0..8405405 100644 Binary files a/__pycache__/server.cpython-312.pyc and b/__pycache__/server.cpython-312.pyc differ diff --git a/server.py b/server.py index a10850c..4fd6df2 100644 --- a/server.py +++ b/server.py @@ -483,6 +483,31 @@ class InviteUserRequest(BaseModel): last_name: Optional[str] = None phone: Optional[str] = None +class AdminUpdateUserRequest(BaseModel): + """Admin-only endpoint for updating user profile fields""" + first_name: Optional[str] = None + last_name: Optional[str] = None + phone: Optional[str] = None + address: Optional[str] = None + city: Optional[str] = None + state: Optional[str] = None + zipcode: Optional[str] = None + date_of_birth: Optional[datetime] = None + member_since: Optional[datetime] = None + # Partner information + partner_first_name: Optional[str] = None + partner_last_name: Optional[str] = None + partner_is_member: Optional[bool] = None + partner_plan_to_become_member: Optional[bool] = None + referred_by_member_name: Optional[str] = None + + @validator('date_of_birth', 'member_since', pre=True) + def empty_str_to_none(cls, v): + """Convert empty string to None for optional datetime fields""" + if v == '' or v is None: + return None + return v + class InvitationResponse(BaseModel): id: str email: str @@ -2275,10 +2300,68 @@ async def get_user_by_id( "email_verified": user.email_verified, "newsletter_subscribed": user.newsletter_subscribed, "lead_sources": user.lead_sources, + "member_since": user.member_since.isoformat() if user.member_since else None, "created_at": user.created_at.isoformat() if user.created_at else None, "updated_at": user.updated_at.isoformat() if user.updated_at else None } +@api_router.put("/admin/users/{user_id}") +async def update_user_profile( + user_id: str, + request: AdminUpdateUserRequest, + db: Session = Depends(get_db), + current_user: User = Depends(require_permission("users.edit")) +): + """Update user profile fields (admin only)""" + user = db.query(User).filter(User.id == user_id).first() + if not user: + raise HTTPException(status_code=404, detail="User not found") + + # Update basic personal information + if request.first_name is not None: + user.first_name = request.first_name + if request.last_name is not None: + user.last_name = request.last_name + if request.phone is not None: + user.phone = request.phone + if request.address is not None: + user.address = request.address + if request.city is not None: + user.city = request.city + if request.state is not None: + user.state = request.state + if request.zipcode is not None: + user.zipcode = request.zipcode + if request.date_of_birth is not None: + user.date_of_birth = request.date_of_birth + + # Update member_since (admin only) + if request.member_since is not None: + user.member_since = request.member_since + + # Update partner information + if request.partner_first_name is not None: + user.partner_first_name = request.partner_first_name + if request.partner_last_name is not None: + user.partner_last_name = request.partner_last_name + if request.partner_is_member is not None: + user.partner_is_member = request.partner_is_member + if request.partner_plan_to_become_member is not None: + user.partner_plan_to_become_member = request.partner_plan_to_become_member + if request.referred_by_member_name is not None: + user.referred_by_member_name = request.referred_by_member_name + + user.updated_at = datetime.now(timezone.utc) + db.commit() + db.refresh(user) + + logger.info(f"Admin {current_user.email} updated profile for user {user.email}") + + return { + "message": "User profile updated successfully", + "user_id": str(user.id) + } + @api_router.put("/admin/users/{user_id}/validate") async def validate_user( user_id: str,