Compare commits

...

4 Commits

Author SHA1 Message Date
3e0414ad1a View the review 2023-10-19 16:16:12 +11:00
9654264773 Submit review 2023-10-19 15:56:17 +11:00
46c50901e8 Leave review for appointment 2023-10-19 15:28:03 +11:00
7da2697088 View appointment detail 2023-10-19 14:43:55 +11:00
7 changed files with 292 additions and 4 deletions

View File

@ -568,5 +568,164 @@ namespace FIT5032_Assignment.Controllers {
ViewBag.image = image; ViewBag.image = image;
return View(); return View();
} }
public ActionResult Review(string id) {
// Check login
if (Request.Cookies["psg_auth_token"] == null) {
// Redirect to home page
return RedirectToAction("Index");
}
var user = psgCredentialVerify(Request.Cookies["psg_auth_token"].Value);
if (user == null) {
// Redirect to home page
Response.Cookies["psg_auth_token"].Expires = DateTime.Now.AddDays(-1);
return RedirectToAction("Index");
}
var userProfile = loginInfo(user);
if (userProfile == null) {
// Redirect to home page, and remove cookies
Response.Cookies["psg_auth_token"].Expires = DateTime.Now.AddDays(-1);
return RedirectToAction("Index");
}
// Detect user role
ViewBag.role = db.Users.Find(userProfile.uuid).role;
// Only patient can review appointment, but doctor can view
/*if (userProfile.role != 1) {
TempData["tip"] = "This operation is not allowed.";
return Redirect("/Appointments/Index");
} */
// Check if the appointment is belong to the doctor or patient
var appointment = db.Appointments.Find(id);
if (appointment == null) {
TempData["tip"] = "The appointment does not exist.";
return Redirect("/Appointments/Index");
}
if (appointment.patient != userProfile.uuid && appointment.responsibleBy != userProfile.uuid) {
TempData["tip"] = "The appointment does not exist.";
return Redirect("/Appointments/Index");
}
// Check status == 2
if (appointment.status != 2) {
TempData["tip"] = "Operation invalid";
return Redirect("/Appointments/Index");
}
ViewBag.appointment = appointment;
// Fetch doctor profile
ViewBag.doctor = db.Doctors.Where(d => d.user == appointment.responsibleBy);
ViewBag.doctorUser = db.Users.Find(appointment.responsibleBy);
// Fetch patient profile
ViewBag.patient = db.Users.Find(appointment.patient);
// Fetch review if exist
var review = db.Reviews.Where(r => r.appointment == id);
if (review.Count() != 0) {
ViewBag.review = review.First();
ViewBag.reviewAvailable = true;
} else {
ViewBag.reviewAvailable = false;
}
return View();
}
[HttpPost]
public ActionResult Review(FormCollection collection) {
Trace.WriteLine(collection["appointment"]);
// Verify login
if (Request.Cookies["psg_auth_token"] == null) {
// Redirect to home page
return RedirectToAction("Index");
}
var user = psgCredentialVerify(Request.Cookies["psg_auth_token"].Value);
if (user == null) {
// Redirect to home page
Response.Cookies["psg_auth_token"].Expires = DateTime.Now.AddDays(-1);
return RedirectToAction("Index");
}
var userProfile = loginInfo(user);
if (userProfile == null) {
// Redirect to home page, and remove cookies
Response.Cookies["psg_auth_token"].Expires = DateTime.Now.AddDays(-1);
return RedirectToAction("Index", "Home");
}
// Detect if user logined is patient
var role = db.Users.Find(userProfile.uuid).role;
if (role != 1) {
// Redirect to home page
TempData["tip"] = "Operation invalid";
return Redirect("/Appointments/Index");
}
// Check if the appointment is belong to the doctor or patient
var appointment = db.Appointments.Find(collection["appointment"]);
if (appointment == null) {
TempData["tip"] = "The appointment does not exist.";
return Redirect("/Appointments/Index");
}
if (appointment.patient != userProfile.uuid && appointment.responsibleBy != userProfile.uuid) {
TempData["tip"] = "The appointment does not exist.";
return Redirect("/Appointments/Index");
}
// Check status == 2
if (appointment.status != 2) {
TempData["tip"] = "Operation invalid";
return Redirect("/Appointments/Index");
}
// Check if the review exist
try {
var review = db.Reviews.Find(collection["appointment"]);
Trace.WriteLine(review);
if (review != null) {
TempData["tip"] = "You cannot append a new review to this appointment.";
return Redirect("/Appointments/Index");
}
} catch(Exception e) {
Trace.WriteLine(e);
TempData["tip"] = "System error";
return Redirect("/Appointments/Index");
}
// Prevent XSS attack
// Replace with < and >
var comment = collection["comment"];
comment = comment.Replace("<", "&lt;");
comment = comment.Replace(">", "&gt;");
// Prevent SQL injection
comment = comment.Replace("'", "''");
// Create review
var uuid = Guid.NewGuid().ToString();
Reviews newReview = new Reviews {
appointment = collection["appointment"],
patient = userProfile.uuid,
doctor = appointment.responsibleBy,
score = Convert.ToInt32(collection["score"]),
comment = comment,
reviewAt = DateTime.Now,
};
db.Reviews.Add(newReview);
db.SaveChanges();
ViewBag.role = role;
ViewBag.appointment = appointment;
ViewBag.doctor = db.Doctors.Where(d => d.user == appointment.responsibleBy);
ViewBag.doctorUser = db.Users.Find(appointment.responsibleBy);
ViewBag.patient = db.Users.Find(appointment.patient);
ViewBag.review = newReview;
ViewBag.reviewAvailable = true;
ViewBag.tip = "Thanks for submit your review!";
return View();
}
} }
} }

View File

@ -53,6 +53,7 @@ namespace FIT5032_Assignment.Controllers {
public DbSet<Doctors> Doctors { get; set; } public DbSet<Doctors> Doctors { get; set; }
public DbSet<Appointments> Appointments { get; set; } public DbSet<Appointments> Appointments { get; set; }
public DbSet<Images> Images { get; set; } public DbSet<Images> Images { get; set; }
public DbSet<Reviews> Reviews { get; set; }
} }
public class HomeController : Controller { public class HomeController : Controller {
public static string GetMd5Hash(string input) { public static string GetMd5Hash(string input) {

View File

@ -13,9 +13,18 @@ namespace FIT5032_Assignment.Controllers {
if (!System.IO.File.Exists(filePath)) { if (!System.IO.File.Exists(filePath)) {
return HttpNotFound(); return HttpNotFound();
} }
var fileContent = System.IO.File.ReadAllText(filePath); // Read binary data from the file
var contentType = MimeMapping.GetMimeMapping(fileName); byte[] fileBytes = System.IO.File.ReadAllBytes(filePath);
return File(fileContent, contentType);
// The binary file may png, jpg or jpeg
var fileExtension = Path.GetExtension(filePath).ToLowerInvariant();
// Set the MIME type
if (fileExtension == ".png") {
return File(fileBytes, "image/png");
} else if (fileExtension == ".jpg" || fileExtension == ".jpeg") {
return File(fileBytes, "image/jpeg");
}
return HttpNotFound();
} }
} }
} }

View File

@ -325,6 +325,7 @@
<Content Include="Views\Appointments\Create.cshtml" /> <Content Include="Views\Appointments\Create.cshtml" />
<Content Include="Views\Appointments\UploadImage.cshtml" /> <Content Include="Views\Appointments\UploadImage.cshtml" />
<Content Include="Views\Appointments\Detail.cshtml" /> <Content Include="Views\Appointments\Detail.cshtml" />
<Content Include="Views\Appointments\Review.cshtml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="App_Data\upload_images\" /> <Folder Include="App_Data\upload_images\" />

View File

@ -48,10 +48,26 @@
<div class="field"> <div class="field">
<div class="field-title">Attached image</div> <div class="field-title">Attached image</div>
<div class="field-content"> <div class="field-content">
<img src="/System/GetUploadedImage?fileName=@ViewBag.image.file" /> <img class="field-content-image" src="/System/GetUploadedImage?fileName=@ViewBag.image.file" />
</div> </div>
</div> </div>
} }
<hr>
<h3>Operations</h3>
@if (ViewBag.appointment.status == 0) {
// Cancel
<a class="btn btn-light" href="../Cancel/@ViewBag.appointment.uuid">Cancel</a>
if (ViewBag.role == 2) {
// Approve
<a class="btn btn-light" href="../Approve/@ViewBag.appointment.uuid">Approve</a>
}
} else if (ViewBag.appointment.status == 1 && ViewBag.role == 2) {
<a href="../UploadImage/@ViewBag.appointment.uuid" class="btn btn-sm btn-primary">Upload Image</a>
} else if (ViewBag.appointment.status == 2 && ViewBag.role == 1) {
<a href="../Review/@ViewBag.appointment.uuid" class="btn btn-sm btn-primary">Rate your experience</a>
} else {
<i>No operation available</i>
}
@section Scripts { @section Scripts {
<style> <style>
@ -74,6 +90,9 @@
border-radius: 50%; border-radius: 50%;
margin-right: 10px; margin-right: 10px;
} }
.field .field-content img.field-content-image {
max-width: 60%;
}
.badge { .badge {
border-radius: 0.25rem; border-radius: 0.25rem;

View File

@ -48,6 +48,8 @@
<td> <td>
@if (item.Item1.status == 0) { @if (item.Item1.status == 0) {
<a href='./Cancel/@item.Item1.uuid' class="btn btn-sm btn-danger">Cancel</a> <a href='./Cancel/@item.Item1.uuid' class="btn btn-sm btn-danger">Cancel</a>
} else if (item.Item1.status == 2) {
<a href="./Detail/@item.Item1.uuid" class="btn btn-sm btn-primary">Detail</a>
} }
</td> </td>
</tr> </tr>

View File

@ -0,0 +1,97 @@

@{
ViewBag.Title = "Review";
Layout = "~/Views/Shared/_Layout.cshtml";
}
@if (ViewBag.tip != null) {
<div class="alert alert-success" role="alert">
@ViewBag.tip
</div>
}
<h2>Rate the experience</h2>
@if (ViewBag.role == 1 && ViewBag.reviewAvailable == false) {
<p>You are about to review the Doctor <b>@ViewBag.doctorUser.displayName</b></p>
<form method="post">
<input type="hidden" id="appointment" name="appointment" required value="@ViewBag.appointment.uuid" />
<div class="form-group">
<label for="score">How about your experience?</label>
<input type="hidden" id="score" name="score" required />
<button class="btn btn-light" id="btn-good" onClick="rate(1)" type="button">
<span class="material-symbols-outlined">
thumb_up
</span>
</button>
<button class="btn btn-light" id="btn-bad" onClick="rate(0)" type="button">
<span class="material-symbols-outlined">
thumb_down
</span>
</button>
</div>
<div class="form-group">
<label for="comment">Leave your comment</label>
<div class='input-group'>
<textarea id="comment" class="form-control" name="comment"></textarea>
</div>
</div>
<button class="btn btn-primary" type="submit">Submit</button>
</form>
} else {
if (ViewBag.reviewAvailable == false) {
<p>Review not available yet.</p>
} else {
<div class="review">
<div><img src="@ViewBag.patient.avatar" style="width: 30px; border-radius: 50%; margin-right: 10px;" /><b>@ViewBag.patient.displayName</b> already reviewed this appointment.</div>
<div>
@if (ViewBag.review.score == 1) {
<span style="color: green;"><span class="material-symbols-outlined">thumb_up</span> <b>Recommended</b></span>
} else {
<span style="color: red;"><span class="material-symbols-outlined">thumb_down</span> <b>Not Recommended</b></span>
}
<span>@ViewBag.review.comment</span>
</div>
</div>
}
}
@section Scripts {
@Styles.Render("https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200")
<style>
.material-symbols-outlined {
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24
}
textarea {
width: 100%;
}
.review {
display: flex;
flex-direction: column;
/* Spacing between elements inside .review */
gap: 10px;
}
</style>
<script>
function rate(score) {
$("#score").val(score)
if (score == 1) {
$("#btn-good").removeClass("btn-light")
$("#btn-good").addClass("btn-success")
$("#btn-bad").removeClass("btn-danger")
$("#btn-bad").addClass("btn-light")
} else {
$("#btn-good").removeClass("btn-success")
$("#btn-good").addClass("btn-light")
$("#btn-bad").removeClass("btn-light")
$("#btn-bad").addClass("btn-danger")
}
}
if (@ViewBag.reviewAvailable === "True") {
alert("Review available")
}
</script>
}