From fdeefb76250b71c10a551bbbbb3861ef34f220fe Mon Sep 17 00:00:00 2001 From: "soroush.asadi" Date: Tue, 23 Jun 2026 11:41:17 +0330 Subject: [PATCH] Move recommendations to a dedicated page + consolidate preferences there MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The personalized «پیشنهادهای ویژه شما» feed lived on the homepage and its settings on a separate /Preferences page. New /Recommendations page combines both — the recommendation cards plus the preference controls (role/city/shift-type/pay/gender) that drive them, so the settings sit next to their result. Saving prefs reloads the feed in place. - Homepage: recommendation section replaced with a CTA card linking to /Recommendations; the model no longer loads recommendations. - Nav: «✨ پیشنهادها» entry added. - /Preferences now redirects to /Recommendations (old links/bookmarks keep working). - Page is NoIndex (personalized to the visitor). Co-Authored-By: Claude Opus 4.8 --- src/JobsMedical.Web/Pages/Index.cshtml | 42 ++------- src/JobsMedical.Web/Pages/Index.cshtml.cs | 13 +-- .../Pages/Preferences/Index.cshtml.cs | 26 +---- .../Pages/Recommendations/Index.cshtml | 94 +++++++++++++++++++ .../Pages/Recommendations/Index.cshtml.cs | 66 +++++++++++++ .../Pages/Shared/_Layout.cshtml | 1 + 6 files changed, 176 insertions(+), 66 deletions(-) create mode 100644 src/JobsMedical.Web/Pages/Recommendations/Index.cshtml create mode 100644 src/JobsMedical.Web/Pages/Recommendations/Index.cshtml.cs diff --git a/src/JobsMedical.Web/Pages/Index.cshtml b/src/JobsMedical.Web/Pages/Index.cshtml index 8e97cd0..8375681 100644 --- a/src/JobsMedical.Web/Pages/Index.cshtml +++ b/src/JobsMedical.Web/Pages/Index.cshtml @@ -41,39 +41,17 @@ -@if (Model.Recommendations.Count > 0) -{ -
-
- @if (Model.HasPersonalization) - { -
-
-

✨ پیشنهادهای ویژه شما

- بر اساس علاقه‌مندی‌ها و فعالیت شما انتخاب شده‌اند -
- ویرایش علاقه‌مندی‌ها -
- } - else - { -
-
-

پیشنهادها را شخصی‌سازی کن

- نقش، شهر و نوع شیفت دلخواهت را بگو تا بهترین فرصت‌ها را برایت پیدا کنیم -
- تنظیم علاقه‌مندی‌ها -
- } - +
diff --git a/src/JobsMedical.Web/Pages/Index.cshtml.cs b/src/JobsMedical.Web/Pages/Index.cshtml.cs index 6344df5..6aac4f9 100644 --- a/src/JobsMedical.Web/Pages/Index.cshtml.cs +++ b/src/JobsMedical.Web/Pages/Index.cshtml.cs @@ -9,18 +9,12 @@ namespace JobsMedical.Web.Pages; public class IndexModel : PageModel { private readonly AppDbContext _db; - private readonly RecommendationService _recs; - private readonly InterestService _interest; - public IndexModel(AppDbContext db, RecommendationService recs, InterestService interest) + public IndexModel(AppDbContext db) { _db = db; - _recs = recs; - _interest = interest; } - public List Recommendations { get; private set; } = new(); - public bool HasPersonalization { get; private set; } public List LatestShifts { get; private set; } = new(); public List LatestJobs { get; private set; } = new(); public List LatestTalent { get; private set; } = new(); @@ -35,11 +29,6 @@ public class IndexModel : PageModel { var today = DateOnly.FromDateTime(DateTime.UtcNow); - Recommendations = await _recs.GetForVisitorAsync(6); - // "Personalized" = we actually used a signal (prefs or behavior), not just cold-start freshness. - HasPersonalization = (await _interest.GetPreferencesAsync())?.HasAny == true - || (await _interest.RecentEventsAsync(1)).Count > 0; - LatestShifts = await _db.Shifts .Include(s => s.Facility).ThenInclude(f => f.City) .Include(s => s.Role) diff --git a/src/JobsMedical.Web/Pages/Preferences/Index.cshtml.cs b/src/JobsMedical.Web/Pages/Preferences/Index.cshtml.cs index 07b35ad..7300ecc 100644 --- a/src/JobsMedical.Web/Pages/Preferences/Index.cshtml.cs +++ b/src/JobsMedical.Web/Pages/Preferences/Index.cshtml.cs @@ -29,31 +29,13 @@ public class IndexModel : PageModel public bool Saved { get; private set; } - public async Task OnGetAsync() - { - await LoadListsAsync(); - var prefs = await _interest.GetPreferencesAsync(); - if (prefs is not null) - { - RoleId = prefs.RoleId; - CityId = prefs.CityId; - PreferredShiftType = prefs.PreferredShiftType; - MinPay = prefs.MinPay; - Gender = prefs.Gender; - } - } + // Preferences have moved onto the «پیشنهادهای ویژه شما» page (settings next to their result). + // Keep this route working by redirecting any old link/bookmark there. + public IActionResult OnGet() => RedirectToPage("/Recommendations/Index"); public async Task OnPostAsync() { await _interest.SavePreferencesAsync(RoleId, CityId, PreferredShiftType, MinPay, Gender); - // Back to home so the personalized feed is the immediate payoff. - TempData["prefsSaved"] = true; - return RedirectToPage("/Index"); - } - - private async Task LoadListsAsync() - { - Roles = await _db.Roles.Where(r => r.IsActive).OrderBy(r => r.SortOrder).ToListAsync(); - Cities = await _db.Cities.Where(c => c.IsActive).OrderBy(c => c.Name).ToListAsync(); + return RedirectToPage("/Recommendations/Index"); } } diff --git a/src/JobsMedical.Web/Pages/Recommendations/Index.cshtml b/src/JobsMedical.Web/Pages/Recommendations/Index.cshtml new file mode 100644 index 0000000..ed22ae0 --- /dev/null +++ b/src/JobsMedical.Web/Pages/Recommendations/Index.cshtml @@ -0,0 +1,94 @@ +@page "/Recommendations" +@model JobsMedical.Web.Pages.Recommendations.IndexModel +@{ + ViewData["Title"] = "پیشنهادهای ویژه شما"; + ViewData["Description"] = "پیشنهادهای شخصی‌سازی‌شدهٔ شیفت و استخدام برای شما در همکادر — بر اساس نقش، شهر و فعالیت شما."; + ViewData["NoIndex"] = true; // personalized to the visitor — not an indexable page +} + +
+
+

✨ پیشنهادهای ویژه شما

+

+ @(Model.HasPersonalization + ? "بر اساس علاقه‌مندی‌ها و فعالیت شما انتخاب شده‌اند. علاقه‌مندی‌ها را پایین‌تر تنظیم کن." + : "نقش، شهر و نوع شیفت دلخواهت را تنظیم کن تا بهترین فرصت‌ها را برایت پیدا کنیم.") +

+
+
+ +
+ @if (Model.Saved) + { +
✓ علاقه‌مندی‌ها ذخیره شد — پیشنهادها به‌روزرسانی شدند.
+ } + + @* Preferences — the settings that drive the feed, collapsed by default once personalized. *@ +
+ ⚙️ تنظیم علاقه‌مندی‌ها +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + @if (Model.Recommendations.Count > 0) + { +
+ @foreach (var rec in Model.Recommendations) + { + + } +
+ } + else + { +
+ هنوز پیشنهادی برای شما نیست. علاقه‌مندی‌هایت را تنظیم کن یا چند فرصت را در + استخدام و شیفت‌ها ببین تا پیشنهادها شخصی شوند. +
+ } +
diff --git a/src/JobsMedical.Web/Pages/Recommendations/Index.cshtml.cs b/src/JobsMedical.Web/Pages/Recommendations/Index.cshtml.cs new file mode 100644 index 0000000..bd6162e --- /dev/null +++ b/src/JobsMedical.Web/Pages/Recommendations/Index.cshtml.cs @@ -0,0 +1,66 @@ +using JobsMedical.Web.Data; +using JobsMedical.Web.Models; +using JobsMedical.Web.Services; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.EntityFrameworkCore; + +namespace JobsMedical.Web.Pages.Recommendations; + +/// +/// Dedicated «پیشنهادهای ویژه شما» page: the personalized recommendation feed plus the preference +/// controls that drive it (role/city/shift-type/pay/gender), in one place — moved off the homepage +/// and consolidating the old /Preferences screen so the settings live next to their result. +/// +public class IndexModel : PageModel +{ + private readonly AppDbContext _db; + private readonly RecommendationService _recs; + private readonly InterestService _interest; + + public IndexModel(AppDbContext db, RecommendationService recs, InterestService interest) + { + _db = db; + _recs = recs; + _interest = interest; + } + + public List Recommendations { get; private set; } = new(); + public bool HasPersonalization { get; private set; } + public List Roles { get; private set; } = new(); + public List Cities { get; private set; } = new(); + + [BindProperty] public int? RoleId { get; set; } + [BindProperty] public int? CityId { get; set; } + [BindProperty] public ShiftType? PreferredShiftType { get; set; } + [BindProperty] public long? MinPay { get; set; } + [BindProperty] public Gender Gender { get; set; } + [TempData] public bool Saved { get; set; } + + public async Task OnGetAsync() => await LoadAsync(); + + public async Task OnPostAsync() + { + await _interest.SavePreferencesAsync(RoleId, CityId, PreferredShiftType, MinPay, Gender); + Saved = true; + return RedirectToPage(); // reload so the feed reflects the new preferences immediately + } + + private async Task LoadAsync() + { + Roles = await _db.Roles.Where(r => r.IsActive).OrderBy(r => r.SortOrder).ToListAsync(); + Cities = await _db.Cities.Where(c => c.IsActive).OrderBy(c => c.Name).ToListAsync(); + Recommendations = await _recs.GetForVisitorAsync(12); + + var prefs = await _interest.GetPreferencesAsync(); + HasPersonalization = prefs?.HasAny == true || (await _interest.RecentEventsAsync(1)).Count > 0; + if (prefs is not null) + { + RoleId = prefs.RoleId; + CityId = prefs.CityId; + PreferredShiftType = prefs.PreferredShiftType; + MinPay = prefs.MinPay; + Gender = prefs.Gender; + } + } +} diff --git a/src/JobsMedical.Web/Pages/Shared/_Layout.cshtml b/src/JobsMedical.Web/Pages/Shared/_Layout.cshtml index 11cd060..81294f1 100644 --- a/src/JobsMedical.Web/Pages/Shared/_Layout.cshtml +++ b/src/JobsMedical.Web/Pages/Shared/_Layout.cshtml @@ -111,6 +111,7 @@