[Verify+Complaints] Facility document review + facility complaints; card location line
CI/CD / CI · dotnet build (push) Successful in 1m27s
CI/CD / Deploy · hamkadr (push) Successful in 1m13s

Card: move location to its own line above the date in the shift card (job card already did). Verification workflow: employers upload documents (license/permit) on a new Employer/Verify page; uploading marks the facility Pending. Admins see pending facilities with their documents on Admin/Facilities, can download each doc, and approve (تأیید شد) or reject with a reason. Documents stored as bytea in the DB (survives deploys via the existing volume); served only to the owner or an admin via /facility-doc/{id}. Facility model gains Verification status enum + note + requested-at; IsVerified kept in sync. Complaints: registered users/visitors can file a شکایت about a facility from shift/job detail pages (targets ReportTargetType.Facility, surfaces in Admin/Reports as مرکز). Migration backfills existing verified facilities to Verified.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-04 16:26:15 +03:30
parent 962196d5cb
commit 1f34fd126f
16 changed files with 1632 additions and 24 deletions
+3
View File
@@ -96,3 +96,6 @@ public enum IngestionMode
public enum ReportTargetType { Shift = 0, Job = 1, Facility = 2, User = 3 }
public enum ReportStatus { Open = 0, Resolved = 1, Dismissed = 2 }
/// <summary>Facility verification lifecycle. Facility.IsVerified stays in sync (true only when Verified).</summary>
public enum VerificationStatus { Unverified = 0, Pending = 1, Verified = 2, Rejected = 3 }
+9 -1
View File
@@ -33,7 +33,12 @@ public class Facility
[MaxLength(50)]
public string? BaleId { get; set; } // شناسه بله برای ارتباط
public bool IsVerified { get; set; } // نشان «تأیید شده»
public bool IsVerified { get; set; } // نشان «تأیید شده» (true only when Verification == Verified)
/// <summary>Verification workflow: employer requests review (+docs) → admin approves/rejects.</summary>
public VerificationStatus Verification { get; set; } = VerificationStatus.Unverified;
[MaxLength(500)] public string? VerificationNote { get; set; } // admin reject reason / note
public DateTime? VerificationRequestedAt { get; set; }
// Phase 2: facility self-serve. Null in MVP (admin manages).
public int? OwnerUserId { get; set; }
@@ -42,4 +47,7 @@ public class Facility
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public ICollection<Shift> Shifts { get; set; } = new List<Shift>();
/// <summary>Documents the employer uploaded to prove the facility is real (license, etc.).</summary>
public ICollection<FacilityDocument> Documents { get; set; } = new List<FacilityDocument>();
}
@@ -0,0 +1,25 @@
using System.ComponentModel.DataAnnotations;
namespace JobsMedical.Web.Models;
/// <summary>
/// A verification document an employer uploads for their facility (license, permit, ID…).
/// Stored as bytes in the DB so it survives deploys via the existing Postgres volume/backups
/// (no separate file volume to mount). Only the facility owner and admins can read it back.
/// </summary>
public class FacilityDocument
{
public int Id { get; set; }
public int FacilityId { get; set; }
public Facility Facility { get; set; } = null!;
[MaxLength(200)] public string FileName { get; set; } = "";
[MaxLength(120)] public string ContentType { get; set; } = "application/octet-stream";
public long Size { get; set; }
/// <summary>Raw file bytes (images/PDF). Capped at the upload handler (a few MB).</summary>
public byte[] Data { get; set; } = Array.Empty<byte>();
public DateTime UploadedAt { get; set; } = DateTime.UtcNow;
}