Add gender requirement (آقا/خانم/فرقی نمیکند) + employee (کارجو) panel
- Gender enum + GenderRequirement on Shift/JobOpening + Gender on UserPreferences (migration) - Employer PostShift/PostJob + admin Review have a gender select; parser detects آقا/خانم/مرد/زن - Gender badge on cards + detail; gender filter on Shifts/Jobs; gender in preferences - Recommendations exclude listings whose gender requirement conflicts with the person's gender - Two panels: new /Me employee (کارجو) panel (recommendations + saved + applied + prefs) alongside /Employer; nav routes by role; /Account/Profile → /Me Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -71,7 +71,8 @@ public class InterestService
|
||||
return _db.UserPreferences.AsNoTracking().FirstOrDefaultAsync(p => p.VisitorId == id);
|
||||
}
|
||||
|
||||
public async Task SavePreferencesAsync(int? roleId, int? cityId, ShiftType? shiftType, long? minPay)
|
||||
public async Task SavePreferencesAsync(int? roleId, int? cityId, ShiftType? shiftType, long? minPay,
|
||||
Gender gender = Gender.Any)
|
||||
{
|
||||
await EnsureVisitorAsync();
|
||||
var prefs = await _db.UserPreferences.FirstOrDefaultAsync(p => p.VisitorId == VisitorId);
|
||||
@@ -84,6 +85,7 @@ public class InterestService
|
||||
prefs.CityId = cityId;
|
||||
prefs.PreferredShiftType = shiftType;
|
||||
prefs.MinPay = minPay;
|
||||
prefs.Gender = gender;
|
||||
prefs.UpdatedAt = DateTime.UtcNow;
|
||||
await _db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
@@ -107,4 +107,12 @@ public static class JalaliDate
|
||||
if (parts.Count > 1) return string.Join(" یا ", parts) + " (به انتخاب شما)";
|
||||
return parts[0];
|
||||
}
|
||||
|
||||
/// <summary>Gender label. As a listing requirement, Any → "فرقی نمیکند".</summary>
|
||||
public static string GenderLabel(Gender g, bool asRequirement = true) => g switch
|
||||
{
|
||||
Gender.Male => "آقا",
|
||||
Gender.Female => "خانم",
|
||||
_ => asRequirement ? "فرقی نمیکند" : "نامشخص",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ public class ParsedListing
|
||||
public long? PayAmount { get; set; } // shift pay or single salary figure
|
||||
public int? SharePercent { get; set; } // profit-share % (درصدی / سهم درآمد)
|
||||
public bool PayNegotiable { get; set; }
|
||||
public Gender Gender { get; set; } = Gender.Any; // جنسیت مورد نیاز
|
||||
public string? CityName { get; set; }
|
||||
public string? DistrictName { get; set; }
|
||||
public string? Phone { get; set; }
|
||||
@@ -65,6 +66,12 @@ public class HeuristicListingParser : IListingParser
|
||||
else if (text.Contains("قرارداد")) p.EmploymentType = Models.EmploymentType.Contract;
|
||||
else if (ContainsAny(text, "تمام وقت", "تماموقت")) p.EmploymentType = Models.EmploymentType.FullTime;
|
||||
|
||||
// --- Gender requirement ---
|
||||
if (ContainsAny(text, "خانم", "خانوم", "بانو", "زن ", "مامای")) p.Gender = Gender.Female;
|
||||
else if (ContainsAny(text, "آقا", "اقا", "مرد ", "مرد،", "پسر")) p.Gender = Gender.Male;
|
||||
if (p.Gender != Gender.Any)
|
||||
p.Notes.Add($"جنسیت: {(p.Gender == Gender.Female ? "خانم" : "آقا")}");
|
||||
|
||||
// --- City / district ---
|
||||
p.CityName = knownCities.FirstOrDefault(c => text.Contains(Normalize(c)));
|
||||
p.DistrictName = knownDistricts.OrderByDescending(d => d.Length)
|
||||
|
||||
@@ -79,6 +79,11 @@ public class RecommendationService
|
||||
var results = new List<Recommendation>();
|
||||
foreach (var s in candidates)
|
||||
{
|
||||
// Skip listings whose gender requirement conflicts with the person's gender.
|
||||
if (prefs?.Gender is Gender pg && pg != Gender.Any
|
||||
&& s.GenderRequirement != Gender.Any && s.GenderRequirement != pg)
|
||||
continue;
|
||||
|
||||
double score = 0;
|
||||
var reasons = new List<string>();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user