|
|
|
|
@@ -50,10 +50,46 @@ progress_store = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def get_user_profile_picture(user_id: UUID) -> Optional[str]:
|
|
|
|
|
"""Get profile picture URL from user profile record (cached)."""
|
|
|
|
|
"""Get profile picture URL with priority: LinkedInAccount > Profile.profile_picture.
|
|
|
|
|
|
|
|
|
|
Priority:
|
|
|
|
|
1. LinkedInAccount.linkedin_picture (if account exists and is active)
|
|
|
|
|
2. Profile.profile_picture (from setup process)
|
|
|
|
|
|
|
|
|
|
Note: session.linkedin_picture (OAuth login) should be checked by caller first.
|
|
|
|
|
"""
|
|
|
|
|
# Check for connected LinkedIn account first
|
|
|
|
|
linkedin_account = await db.get_linkedin_account(user_id)
|
|
|
|
|
if linkedin_account and linkedin_account.is_active and linkedin_account.linkedin_picture:
|
|
|
|
|
return linkedin_account.linkedin_picture
|
|
|
|
|
|
|
|
|
|
# Fall back to profile picture from setup process
|
|
|
|
|
profile = await db.get_profile(user_id)
|
|
|
|
|
if profile and profile.profile_picture:
|
|
|
|
|
return profile.profile_picture
|
|
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def get_user_avatar(session: UserSession, user_id: UUID) -> Optional[str]:
|
|
|
|
|
"""Get user avatar URL with complete priority order.
|
|
|
|
|
|
|
|
|
|
Priority:
|
|
|
|
|
1. LinkedInAccount.linkedin_picture (connected LinkedIn account for auto-posting)
|
|
|
|
|
2. Profile.profile_picture (from setup process)
|
|
|
|
|
3. session.linkedin_picture (from OAuth login)
|
|
|
|
|
|
|
|
|
|
Returns None if no avatar is available (caller should show initials).
|
|
|
|
|
"""
|
|
|
|
|
# First check LinkedInAccount and Profile
|
|
|
|
|
avatar = await get_user_profile_picture(user_id)
|
|
|
|
|
if avatar:
|
|
|
|
|
return avatar
|
|
|
|
|
|
|
|
|
|
# Fall back to session picture (OAuth login)
|
|
|
|
|
if session and session.linkedin_picture:
|
|
|
|
|
return session.linkedin_picture
|
|
|
|
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1197,11 +1233,36 @@ async def dashboard(request: Request):
|
|
|
|
|
# Company accounts have a different dashboard
|
|
|
|
|
if session.account_type == "company":
|
|
|
|
|
company = await db.get_company(UUID(session.company_id)) if session.company_id else None
|
|
|
|
|
employees = await db.get_company_employees(UUID(session.company_id)) if session.company_id else []
|
|
|
|
|
employees_raw = await db.get_company_employees(UUID(session.company_id)) if session.company_id else []
|
|
|
|
|
pending_invitations = await db.get_pending_invitations(UUID(session.company_id)) if session.company_id else []
|
|
|
|
|
quota = await db.get_company_daily_quota(UUID(session.company_id)) if session.company_id else None
|
|
|
|
|
license_key = await db.get_company_limits(UUID(session.company_id)) if session.company_id else None
|
|
|
|
|
|
|
|
|
|
# Add avatar URLs to employees
|
|
|
|
|
employees = []
|
|
|
|
|
for emp in employees_raw:
|
|
|
|
|
emp_session = UserSession(
|
|
|
|
|
user_id=str(emp.id),
|
|
|
|
|
linkedin_picture=emp.linkedin_picture,
|
|
|
|
|
email=emp.email,
|
|
|
|
|
account_type=emp.account_type.value if hasattr(emp.account_type, 'value') else emp.account_type,
|
|
|
|
|
display_name=emp.display_name
|
|
|
|
|
)
|
|
|
|
|
avatar_url = await get_user_avatar(emp_session, emp.id)
|
|
|
|
|
|
|
|
|
|
# Create employee dict with avatar
|
|
|
|
|
emp_dict = {
|
|
|
|
|
"id": emp.id,
|
|
|
|
|
"email": emp.email,
|
|
|
|
|
"display_name": emp.display_name or emp.linkedin_name or emp.email,
|
|
|
|
|
"onboarding_status": emp.onboarding_status,
|
|
|
|
|
"avatar_url": avatar_url
|
|
|
|
|
}
|
|
|
|
|
employees.append(emp_dict)
|
|
|
|
|
|
|
|
|
|
user_id = UUID(session.user_id)
|
|
|
|
|
profile_picture = await get_user_avatar(session, user_id)
|
|
|
|
|
|
|
|
|
|
return templates.TemplateResponse("company_dashboard.html", {
|
|
|
|
|
"request": request,
|
|
|
|
|
"page": "home",
|
|
|
|
|
@@ -1211,14 +1272,15 @@ async def dashboard(request: Request):
|
|
|
|
|
"total_employees": len(employees),
|
|
|
|
|
"pending_invitations": pending_invitations,
|
|
|
|
|
"quota": quota,
|
|
|
|
|
"license_key": license_key
|
|
|
|
|
"license_key": license_key,
|
|
|
|
|
"profile_picture": profile_picture
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
# Employee accounts have their own dashboard
|
|
|
|
|
user_id = UUID(session.user_id)
|
|
|
|
|
profile = await db.get_profile(user_id)
|
|
|
|
|
posts = await db.get_generated_posts(user_id)
|
|
|
|
|
profile_picture = session.linkedin_picture or await get_user_profile_picture(user_id)
|
|
|
|
|
profile_picture = await get_user_avatar(session, user_id)
|
|
|
|
|
|
|
|
|
|
if session.account_type == "employee":
|
|
|
|
|
# Count post statuses
|
|
|
|
|
@@ -1268,7 +1330,7 @@ async def posts_page(request: Request):
|
|
|
|
|
user_id = UUID(session.user_id)
|
|
|
|
|
profile = await db.get_profile(user_id)
|
|
|
|
|
posts = await db.get_generated_posts(user_id)
|
|
|
|
|
profile_picture = session.linkedin_picture or await get_user_profile_picture(user_id)
|
|
|
|
|
profile_picture = await get_user_avatar(session, user_id)
|
|
|
|
|
|
|
|
|
|
return templates.TemplateResponse("posts.html", {
|
|
|
|
|
"request": request,
|
|
|
|
|
@@ -1320,6 +1382,8 @@ async def post_types_page(request: Request):
|
|
|
|
|
categorized_by_type[type_name] = []
|
|
|
|
|
categorized_by_type[type_name].append(post)
|
|
|
|
|
|
|
|
|
|
profile_picture = await get_user_avatar(session, user_id)
|
|
|
|
|
|
|
|
|
|
return templates.TemplateResponse("post_types.html", {
|
|
|
|
|
"request": request,
|
|
|
|
|
"page": "post_types",
|
|
|
|
|
@@ -1327,10 +1391,12 @@ async def post_types_page(request: Request):
|
|
|
|
|
"post_types": post_types,
|
|
|
|
|
"uncategorized_posts": uncategorized_posts,
|
|
|
|
|
"categorized_posts": categorized_posts,
|
|
|
|
|
"categorized_by_type": categorized_by_type
|
|
|
|
|
"categorized_by_type": categorized_by_type,
|
|
|
|
|
"profile_picture": profile_picture
|
|
|
|
|
})
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.error(f"Error loading post types page: {e}")
|
|
|
|
|
profile_picture = await get_user_avatar(session, UUID(session.user_id))
|
|
|
|
|
return templates.TemplateResponse("post_types.html", {
|
|
|
|
|
"request": request,
|
|
|
|
|
"page": "post_types",
|
|
|
|
|
@@ -1339,7 +1405,8 @@ async def post_types_page(request: Request):
|
|
|
|
|
"uncategorized_posts": [],
|
|
|
|
|
"categorized_posts": [],
|
|
|
|
|
"categorized_by_type": {},
|
|
|
|
|
"error": str(e)
|
|
|
|
|
"error": str(e),
|
|
|
|
|
"profile_picture": profile_picture
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1509,15 +1576,8 @@ async def post_detail_page(request: Request, post_id: str):
|
|
|
|
|
linkedin_posts = await db.get_linkedin_posts(post.user_id)
|
|
|
|
|
reference_posts = [p.post_text for p in linkedin_posts if p.post_text and len(p.post_text) > 100][:10]
|
|
|
|
|
|
|
|
|
|
profile_picture_url = session.linkedin_picture
|
|
|
|
|
if not profile_picture_url:
|
|
|
|
|
for lp in linkedin_posts:
|
|
|
|
|
if lp.raw_data and isinstance(lp.raw_data, dict):
|
|
|
|
|
author = lp.raw_data.get("author", {})
|
|
|
|
|
if author and isinstance(author, dict):
|
|
|
|
|
profile_picture_url = author.get("profile_picture")
|
|
|
|
|
if profile_picture_url:
|
|
|
|
|
break
|
|
|
|
|
# Get avatar with priority: LinkedInAccount > profile_picture > session.linkedin_picture
|
|
|
|
|
profile_picture_url = await get_user_avatar(session, post.user_id)
|
|
|
|
|
|
|
|
|
|
profile_analysis_record = await db.get_profile_analysis(post.user_id)
|
|
|
|
|
profile_analysis = profile_analysis_record.full_analysis if profile_analysis_record else None
|
|
|
|
|
@@ -1575,13 +1635,17 @@ async def research_page(request: Request):
|
|
|
|
|
limit_reached = not can_create
|
|
|
|
|
limit_message = error_msg
|
|
|
|
|
|
|
|
|
|
user_id = UUID(session.user_id)
|
|
|
|
|
profile_picture = await get_user_avatar(session, user_id)
|
|
|
|
|
|
|
|
|
|
return templates.TemplateResponse("research.html", {
|
|
|
|
|
"request": request,
|
|
|
|
|
"page": "research",
|
|
|
|
|
"session": session,
|
|
|
|
|
"user_id": session.user_id,
|
|
|
|
|
"limit_reached": limit_reached,
|
|
|
|
|
"limit_message": limit_message
|
|
|
|
|
"limit_message": limit_message,
|
|
|
|
|
"profile_picture": profile_picture
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1600,13 +1664,17 @@ async def create_post_page(request: Request):
|
|
|
|
|
limit_reached = not can_create
|
|
|
|
|
limit_message = error_msg
|
|
|
|
|
|
|
|
|
|
user_id = UUID(session.user_id)
|
|
|
|
|
profile_picture = await get_user_avatar(session, user_id)
|
|
|
|
|
|
|
|
|
|
return templates.TemplateResponse("create_post.html", {
|
|
|
|
|
"request": request,
|
|
|
|
|
"page": "create",
|
|
|
|
|
"session": session,
|
|
|
|
|
"user_id": session.user_id,
|
|
|
|
|
"limit_reached": limit_reached,
|
|
|
|
|
"limit_message": limit_message
|
|
|
|
|
"limit_message": limit_message,
|
|
|
|
|
"profile_picture": profile_picture
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1621,7 +1689,7 @@ async def status_page(request: Request):
|
|
|
|
|
user_id = UUID(session.user_id)
|
|
|
|
|
profile = await db.get_profile(user_id)
|
|
|
|
|
status = await orchestrator.get_user_status(user_id)
|
|
|
|
|
profile_picture = session.linkedin_picture or await get_user_profile_picture(user_id)
|
|
|
|
|
profile_picture = await get_user_avatar(session, user_id)
|
|
|
|
|
|
|
|
|
|
return templates.TemplateResponse("status.html", {
|
|
|
|
|
"request": request,
|
|
|
|
|
@@ -2218,11 +2286,12 @@ async def settings_page(request: Request):
|
|
|
|
|
return RedirectResponse(url="/login", status_code=302)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
profile = await db.get_profile(UUID(session.user_id))
|
|
|
|
|
profile_picture = session.linkedin_picture or await get_user_profile_picture(UUID(session.user_id))
|
|
|
|
|
user_id = UUID(session.user_id)
|
|
|
|
|
profile = await db.get_profile(user_id)
|
|
|
|
|
profile_picture = await get_user_avatar(session, user_id)
|
|
|
|
|
|
|
|
|
|
# Get LinkedIn account if linked
|
|
|
|
|
linkedin_account = await db.get_linkedin_account(UUID(session.user_id))
|
|
|
|
|
linkedin_account = await db.get_linkedin_account(user_id)
|
|
|
|
|
|
|
|
|
|
return templates.TemplateResponse("settings.html", {
|
|
|
|
|
"request": request,
|
|
|
|
|
@@ -2480,13 +2549,17 @@ async def company_strategy_page(request: Request, success: bool = False):
|
|
|
|
|
company = await db.get_company(UUID(session.company_id))
|
|
|
|
|
strategy = company.company_strategy if company else {}
|
|
|
|
|
|
|
|
|
|
user_id = UUID(session.user_id)
|
|
|
|
|
profile_picture = await get_user_avatar(session, user_id)
|
|
|
|
|
|
|
|
|
|
return templates.TemplateResponse("company_strategy.html", {
|
|
|
|
|
"request": request,
|
|
|
|
|
"page": "strategy",
|
|
|
|
|
"session": session,
|
|
|
|
|
"company": company,
|
|
|
|
|
"strategy": strategy,
|
|
|
|
|
"success": success
|
|
|
|
|
"success": success,
|
|
|
|
|
"profile_picture": profile_picture
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -2545,7 +2618,9 @@ async def company_accounts_page(request: Request):
|
|
|
|
|
company_id = UUID(session.company_id)
|
|
|
|
|
employees = await db.get_company_employees(company_id)
|
|
|
|
|
pending_invitations = await db.get_pending_invitations(company_id)
|
|
|
|
|
profile_picture = session.linkedin_picture
|
|
|
|
|
|
|
|
|
|
user_id = UUID(session.user_id)
|
|
|
|
|
profile_picture = await get_user_avatar(session, user_id)
|
|
|
|
|
|
|
|
|
|
return templates.TemplateResponse("company_accounts.html", {
|
|
|
|
|
"request": request,
|
|
|
|
|
@@ -2576,15 +2651,25 @@ async def company_manage_page(request: Request, employee_id: str = None):
|
|
|
|
|
all_employees = await db.get_company_employees(company_id)
|
|
|
|
|
active_employees = [emp for emp in all_employees if emp.onboarding_status == "completed"]
|
|
|
|
|
|
|
|
|
|
# Build display info for employees
|
|
|
|
|
# Build display info for employees with correct avatar URLs
|
|
|
|
|
# Note: emp is a User object from get_company_employees which has linkedin_name and linkedin_picture
|
|
|
|
|
active_employees_info = []
|
|
|
|
|
for emp in active_employees:
|
|
|
|
|
# Create minimal session for employee to get avatar
|
|
|
|
|
emp_session = UserSession(
|
|
|
|
|
user_id=str(emp.id),
|
|
|
|
|
linkedin_picture=emp.linkedin_picture,
|
|
|
|
|
email=emp.email,
|
|
|
|
|
account_type=emp.account_type.value if hasattr(emp.account_type, 'value') else emp.account_type,
|
|
|
|
|
display_name=emp.display_name
|
|
|
|
|
)
|
|
|
|
|
avatar_url = await get_user_avatar(emp_session, emp.id)
|
|
|
|
|
|
|
|
|
|
active_employees_info.append({
|
|
|
|
|
"id": str(emp.id),
|
|
|
|
|
"email": emp.email,
|
|
|
|
|
"display_name": emp.linkedin_name or emp.display_name or emp.email,
|
|
|
|
|
"linkedin_picture": emp.linkedin_picture,
|
|
|
|
|
"linkedin_picture": avatar_url, # Now contains the correct avatar with priority
|
|
|
|
|
"onboarding_status": emp.onboarding_status
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
@@ -2609,6 +2694,9 @@ async def company_manage_page(request: Request, employee_id: str = None):
|
|
|
|
|
pending_posts = len([p for p in employee_posts if p.status in ['draft', 'pending']])
|
|
|
|
|
approved_posts = len([p for p in employee_posts if p.status in ['approved', 'published']])
|
|
|
|
|
|
|
|
|
|
user_id = UUID(session.user_id)
|
|
|
|
|
profile_picture = await get_user_avatar(session, user_id)
|
|
|
|
|
|
|
|
|
|
return templates.TemplateResponse("company_manage.html", {
|
|
|
|
|
"request": request,
|
|
|
|
|
"page": "manage",
|
|
|
|
|
@@ -2617,7 +2705,8 @@ async def company_manage_page(request: Request, employee_id: str = None):
|
|
|
|
|
"selected_employee": selected_employee,
|
|
|
|
|
"employee_posts": employee_posts,
|
|
|
|
|
"pending_posts": pending_posts,
|
|
|
|
|
"approved_posts": approved_posts
|
|
|
|
|
"approved_posts": approved_posts,
|
|
|
|
|
"profile_picture": profile_picture
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -2647,6 +2736,9 @@ async def company_manage_posts(request: Request, employee_id: str = None):
|
|
|
|
|
profile = await db.get_profile(emp_profile.id)
|
|
|
|
|
posts = await db.get_generated_posts(emp_profile.id)
|
|
|
|
|
|
|
|
|
|
user_id = UUID(session.user_id)
|
|
|
|
|
profile_picture = await get_user_avatar(session, user_id)
|
|
|
|
|
|
|
|
|
|
return templates.TemplateResponse("company_manage_posts.html", {
|
|
|
|
|
"request": request,
|
|
|
|
|
"page": "manage",
|
|
|
|
|
@@ -2655,7 +2747,8 @@ async def company_manage_posts(request: Request, employee_id: str = None):
|
|
|
|
|
"employee_name": emp_user.linkedin_name or emp_profile.display_name or emp_user.email,
|
|
|
|
|
"profile": profile,
|
|
|
|
|
"posts": posts,
|
|
|
|
|
"total_posts": len(posts)
|
|
|
|
|
"total_posts": len(posts),
|
|
|
|
|
"profile_picture": profile_picture
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -2688,6 +2781,17 @@ async def company_manage_post_detail(request: Request, post_id: str, employee_id
|
|
|
|
|
|
|
|
|
|
profile = await db.get_profile(emp_profile.id)
|
|
|
|
|
|
|
|
|
|
# Get employee's avatar
|
|
|
|
|
# Note: Create minimal session for the employee to use get_user_avatar
|
|
|
|
|
emp_session = UserSession(
|
|
|
|
|
user_id=str(emp_profile.id),
|
|
|
|
|
linkedin_picture=emp_user.linkedin_picture,
|
|
|
|
|
email=emp_user.email,
|
|
|
|
|
account_type=emp_user.account_type.value if hasattr(emp_user.account_type, 'value') else emp_user.account_type,
|
|
|
|
|
display_name=emp_profile.display_name
|
|
|
|
|
)
|
|
|
|
|
profile_picture_url = await get_user_avatar(emp_session, emp_profile.id)
|
|
|
|
|
|
|
|
|
|
# Convert media_items to dicts for JSON serialization in template
|
|
|
|
|
media_items_dict = []
|
|
|
|
|
if post.media_items:
|
|
|
|
|
@@ -2704,7 +2808,8 @@ async def company_manage_post_detail(request: Request, post_id: str, employee_id
|
|
|
|
|
"employee_name": emp_user.linkedin_name or emp_profile.display_name or emp_user.email,
|
|
|
|
|
"profile": profile,
|
|
|
|
|
"post": post,
|
|
|
|
|
"media_items_dict": media_items_dict
|
|
|
|
|
"media_items_dict": media_items_dict,
|
|
|
|
|
"profile_picture_url": profile_picture_url
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -2739,6 +2844,9 @@ async def company_manage_research(request: Request, employee_id: str = None):
|
|
|
|
|
limit_reached = not can_create
|
|
|
|
|
limit_message = error_msg
|
|
|
|
|
|
|
|
|
|
user_id = UUID(session.user_id)
|
|
|
|
|
profile_picture = await get_user_avatar(session, user_id)
|
|
|
|
|
|
|
|
|
|
return templates.TemplateResponse("company_manage_research.html", {
|
|
|
|
|
"request": request,
|
|
|
|
|
"page": "manage",
|
|
|
|
|
@@ -2747,7 +2855,8 @@ async def company_manage_research(request: Request, employee_id: str = None):
|
|
|
|
|
"employee_name": emp_user.linkedin_name or emp_profile.display_name or emp_user.email,
|
|
|
|
|
"user_id": str(emp_profile.id),
|
|
|
|
|
"limit_reached": limit_reached,
|
|
|
|
|
"limit_message": limit_message
|
|
|
|
|
"limit_message": limit_message,
|
|
|
|
|
"profile_picture": profile_picture
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -2782,6 +2891,9 @@ async def company_manage_create(request: Request, employee_id: str = None):
|
|
|
|
|
limit_reached = not can_create
|
|
|
|
|
limit_message = error_msg
|
|
|
|
|
|
|
|
|
|
user_id = UUID(session.user_id)
|
|
|
|
|
profile_picture = await get_user_avatar(session, user_id)
|
|
|
|
|
|
|
|
|
|
return templates.TemplateResponse("company_manage_create.html", {
|
|
|
|
|
"request": request,
|
|
|
|
|
"page": "manage",
|
|
|
|
|
@@ -2790,7 +2902,8 @@ async def company_manage_create(request: Request, employee_id: str = None):
|
|
|
|
|
"employee_name": emp_user.linkedin_name or emp_profile.display_name or emp_user.email,
|
|
|
|
|
"user_id": str(emp_profile.id),
|
|
|
|
|
"limit_reached": limit_reached,
|
|
|
|
|
"limit_message": limit_message
|
|
|
|
|
"limit_message": limit_message,
|
|
|
|
|
"profile_picture": profile_picture
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -2810,12 +2923,16 @@ async def employee_strategy_page(request: Request):
|
|
|
|
|
company = await db.get_company(UUID(session.company_id))
|
|
|
|
|
strategy = company.company_strategy if company else {}
|
|
|
|
|
|
|
|
|
|
user_id = UUID(session.user_id)
|
|
|
|
|
profile_picture = await get_user_avatar(session, user_id)
|
|
|
|
|
|
|
|
|
|
return templates.TemplateResponse("employee_strategy.html", {
|
|
|
|
|
"request": request,
|
|
|
|
|
"page": "strategy",
|
|
|
|
|
"session": session,
|
|
|
|
|
"company": company,
|
|
|
|
|
"strategy": strategy
|
|
|
|
|
"strategy": strategy,
|
|
|
|
|
"profile_picture": profile_picture
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|