This commit is contained in:
Joey Kimsey
2025-01-01 22:17:38 -05:00
parent ccc134d1b2
commit 1ef85c6c2c
65 changed files with 1200 additions and 215 deletions

View File

@ -33,7 +33,7 @@ class Reviews extends Controller {
public function __construct() {
self::$title = 'Reviews - {SITENAME}';
self::$pageDescription = 'On this page you can submit a reviews for a product.';
self::$pageDescription = 'This page allows you to add a new product review or update your existing reviews.';
if ( ! App::$isLoggedIn ) {
Session::flash( 'notice', 'You must be logged in to review products.' );
@ -48,18 +48,21 @@ class Reviews extends Controller {
}
public function index() {
$reviews = Views::simpleView( 'reviews.list', self::$reviews->byUser() );
Components::set( 'reviews', $reviews );
Views::view( 'reviews.list' );
Views::view( 'reviews.list', self::$reviews->byUser() );
}
public function review( $slug = null ) {
$category = self::$categories->findBySlug( $slug );
if ( ! $category ) {
$categories = self::$categories->simpleUnreviewed();
if ( empty( $categories ) ) {
Session::flash( 'notice', 'There are no additional products to review at this time.' );
Redirect::to( 'reviews/index' );
}
$selectedCategory = '0';
$reviewCategorySelect = HoudiniForms::getSelectHtml(
'review_category_id',
self::$categories->simple(),
$categories,
$selectedCategory,
);
Components::set( 'reviewCategorySelect', $reviewCategorySelect );
@ -78,12 +81,76 @@ class Reviews extends Controller {
return Views::view( 'reviews.create' );
}
$result = self::$reviews->create( Input::post('title'), Input::post('rating'), Input::post('review'), Input::post('review_category_id') );
if ( true === $result ) {
if ( ! empty( $result ) ) {
Session::flash( 'success', 'Your review has been received.' );
Redirect::to( 'home/index' );
Redirect::to( 'reviews/index' );
} else {
Issues::add( 'error', 'There was an unresolved error while submitting your review.' );
return Views::view( 'reviews.create' );
}
}
public function view( $id = null ) {
$review = $this->confirmOwner( $id );
Views::view( 'reviews.view', $review );
}
public function edit( $id = null ) {
$review = $this->confirmOwner( $id );
if ( ! Input::exists( 'submit' ) ) {
return Views::view( 'reviews.edit', $review );
}
if ( ! Forms::check( 'reviewEditPublic' ) ) {
Issues::add( 'error', [ 'There was an error with your request.' => Check::userErrors() ] );
return Views::view( 'reviews.edit', $review );
}
$result = self::$reviews->update(
$id,
Input::post('title'),
Input::post('rating'),
Input::post('review')
);
if ( $result ) {
Session::flash( 'success', 'Your review has been updated.' );
return Redirect::to( 'reviews/index' );
}
Issues::add( 'error', [ 'There was an error with your request.' => Check::userErrors() ] );
Views::view( 'reviews.edit', $review );
}
public function delete( $id = null ) {
$this->confirmOwner( $id );
if ( self::$reviews->delete( $id ) ) {
Session::flash( 'success', 'Review deleted' );
} else {
Session::flash( 'error', 'There was an error with your request.' );
}
return Redirect::to( 'reviews/index' );
}
private function confirmOwner( $id ) {
if ( ! App::$isLoggedIn ) {
Session::flash( 'error', 'You must be logged in to review products.' );
return Redirect::home();
}
if ( !Check::id( $id ) ) {
Session::flash( 'error', 'Unknown Review' );
return Redirect::to( 'reviews/index' );
}
$review = self::$reviews->findById( $id );
if ( empty( $review ) ) {
Session::flash( 'error', 'Unknown Review' );
return Redirect::to( 'reviews/index' );
}
if ( App::$activeUser->ID != $review->createdBy ) {
Session::flash( 'error', 'Unknown Review' );
return Redirect::to( 'reviews/index' );
}
return $review;
}
}

View File

@ -25,6 +25,7 @@ class ReviewForms extends Forms {
self::addHandler( 'editReview', __CLASS__, 'editReview' );
self::addHandler( 'categoryCreate', __CLASS__, 'categoryCreate' );
self::addHandler( 'categoryEdit', __CLASS__, 'categoryEdit' );
self::addHandler( 'reviewEditPublic', __CLASS__, 'reviewEditPublic' );
}
/**
@ -88,6 +89,21 @@ class ReviewForms extends Forms {
}
return true;
}
public static function reviewEditPublic() {
if ( ! Input::exists( 'review' ) ) {
Check::addUserError( 'You must provide a review.' );
return false;
}
if ( ! Input::exists( 'title' ) ) {
Check::addUserError( 'You must provide a title.' );
return false;
}
if ( ! Input::exists( 'rating' ) ) {
Check::addUserError( 'You must provide a rating.' );
return false;
}
return true;
}
}
new ReviewForms;

View File

@ -62,21 +62,19 @@ class Review extends DatabaseModel {
return self::$db->lastId();
}
public function update( $id, $title, $rating, $review, $review_category_id = '0' ) {
public function update( $id, $title, $rating, $review, $review_category_id = null ) {
if ( ! $this->plugin->checkEnabled() ) {
Debug::info( 'Reviews are disabled in the config.' );
return false;
}
if ( !Check::dataTitle( $slug ) ) {
Debug::info( 'Review Categories: illegal title.' );
return false;
}
$fields = [
'title' => $title,
'rating' => $rating,
'review' => $review,
'review_category_id' => $review_category_id,
'review' => $review
];
if ( ! empty( $review_category_id ) ) {
$fields['review_category_id'] = $review_category_id;
}
if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
new CustomException( 'reviewUpdate' );
Debug::error( "Review: $id not updated: $fields" );
@ -133,6 +131,23 @@ class Review extends DatabaseModel {
return $this->filter( $reviews->results() );
}
public function reviewedCategoriesByUser() {
$whereClause = ['createdBy', '=', App::$activeUser->ID];
$reviews = self::$db->action( 'SELECT ID,review_category_id', $this->tableName, $whereClause );
if ( !$reviews->count() ) {
Debug::info( 'No Reviews found.' );
return false;
}
$categoryIds = [];
foreach ($reviews->results() as $key => $result) {
if ( ! in_array( $result->review_category_id, $categoryIds ) ) {
$categoryIds[] = $result->review_category_id;
}
}
return $categoryIds;
}
public function byCategory( $id, $limit = null ) {
$whereClause = [ 'review_category_id', '=', $id ];
if ( empty( $limit ) ) {

View File

@ -17,6 +17,7 @@ use TheTempusProject\Bedrock\Functions\Check;
use TheTempusProject\Canary\Bin\Canary as Debug;
use TheTempusProject\Classes\DatabaseModel;
use TheTempusProject\Plugins\Reviews as Plugin;
use TheTempusProject\Models\Review;
use TheTempusProject\TheTempusProject as App;
use TheTempusProject\Canary\Classes\CustomException;
@ -109,4 +110,29 @@ class ReviewCategory extends DatabaseModel {
}
return $out;
}
public function simpleUnreviewed() {
$categories = self::$db->get( $this->tableName, '*' );
if ( !$categories->count() ) {
Debug::warn( 'Could not find any categories' );
return false;
}
$categories = $categories->results();
$review = new Review;
$reviews = $review->reviewedCategoriesByUser();
$out = [];
foreach ( $categories as &$category ) {
if ( ! empty( $reviews) && in_array( $category->ID, $reviews ) ) {
continue;
}
$out[ $category->name ] = $category->ID;
}
return $out;
}
}

View File

@ -1,35 +1,42 @@
<div class="container col-md-4 col-lg-4">
<div class="row">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Review Category</h3>
</div>
<div class="panel-body">
<div class="row">
<div class="">
<table class="table table-user-primary">
<div class="container py-4">
<div class="row justify-content-center">
<div class="col-md-8">
{ADMIN_BREADCRUMBS}
<div class="card shadow">
<!-- Card Header -->
<div class="card-header text-center bg-dark text-white">
<h3 class="card-title mb-0">Review Category: {name}</h3>
</div>
<!-- Card Body -->
<div class="card-body">
<div class="row align-items-center">
<!-- Details -->
<table class="table table-borderless">
<tbody>
<tr>
<td align="left" width="200"><b>Name</b></td>
<td align="right">{name}</td>
<th scope="row">Name:</th>
<td>{name}</td>
</tr>
<tr>
<td><b>Slug</b></td>
<td align="right">{slug}</td>
<th scope="row">Slug:</th>
<td>{slug}</td>
</tr>
<tr>
<td><b>Created</b></td>
<td align="right">{DTC}{createdAt}{/DTC}</td>
<th scope="row">Created:</th>
<td>{DTC}{createdAt}{/DTC}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="panel-footer">
<a href="{ROOT_URL}admin/reviews/categoryEdit/{ID}" class="btn btn-md btn-warning" role="button"><i class="fa fa-fw fa-pencil"></i></a>
<a href="{ROOT_URL}admin/reviews/categoryDelete/{ID}" class="btn btn-md btn-danger" role="button"><i class="fa fa-fw fa-trash"></i></a>
<!-- Admin Controls -->
<div class="card-footer text-center">
<a href="{ROOT_URL}admin/reviews/categoryEdit/{ID}" class="btn btn-md btn-warning" role="button"><i class="fa fa-fw fa-pencil"></i></a>
<a href="{ROOT_URL}admin/reviews/categoryDelete/{ID}" class="btn btn-md btn-danger" role="button"><i class="fa fa-fw fa-trash"></i></a>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -1,22 +1,35 @@
<legend>Create Review Category</legend>
<form action="" method="post" class="form-horizontal">
<input type="hidden" name="token" value="{TOKEN}">
<div class="form-group">
<label for="name" class="col-lg-3 control-label">Name</label>
<div class="col-lg-3">
<input type="text" class="form-control" name="name" id="name">
</div>
</div>
<div class="form-group">
<label for="slug" class="col-lg-3 control-label">Slug (the public link for reviews would be {ROOT_URL}reviews/YOUR_SLUG_HERE)</label>
<div class="col-lg-3">
<input type="text" class="form-control" name="slug" id="slug">
</div>
</div>
<div class="form-group">
<label for="submit" class="col-lg-3 control-label"></label>
<div class="col-lg-3">
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block ">Submit</button>
</div>
</div>
</form>
<div class="context-main-bg context-main p-3">
<legend class="text-center">Add Review Category</legend>
<hr>
{ADMIN_BREADCRUMBS}
<form method="post">
<fieldset>
<!-- Name -->
<div class="mb-3 row">
<label for="name" class="col-lg-6 col-form-label text-end">Name:</label>
<div class="col-lg-2">
<input type="text" class="form-control" name="name" id="name" required>
</div>
</div>
<!-- Slug -->
<div class="mb-3 row">
<label for="slug" class="col-lg-6 col-form-label text-end">Slug:</label>
<div class="col-lg-2">
<input type="text" class="form-control" name="slug" id="slug" aria-describedby="slugHelp" required>
<small id="slugHelp" class="form-text text-muted">
Public links would be {ROOT_URL}reviews/YOUR_SLUG_HERE
</small>
</div>
</div>
<!-- Hidden Token -->
<input type="hidden" name="token" value="{TOKEN}">
<!-- Submit Button -->
<div class="text-center">
<button type="submit" name="submit" value="submit" class="btn btn-primary btn-lg">Create</button>
</div>
</fieldset>
</form>
</div>

View File

@ -1,22 +1,35 @@
<legend>Edit Review Category</legend>
<form action="" method="post" class="form-horizontal">
<input type="hidden" name="token" value="{TOKEN}">
<div class="form-group">
<label for="name" class="col-lg-3 control-label">Name</label>
<div class="col-lg-3">
<input type="text" class="form-control" name="name" id="name" value="{name}">
</div>
</div>
<div class="form-group">
<label for="slug" class="col-lg-3 control-label">Slug (the public link for reviews would be {ROOT_URL}reviews/YOUR_SLUG_HERE)</label>
<div class="col-lg-3">
<input type="text" class="form-control" name="slug" id="slug" value="{slug}">
</div>
</div>
<div class="form-group">
<label for="submit" class="col-lg-3 control-label"></label>
<div class="col-lg-3">
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block ">Submit</button>
</div>
</div>
</form>
<div class="context-main-bg context-main p-3">
<legend class="text-center">Edit Review Category</legend>
<hr>
{ADMIN_BREADCRUMBS}
<form method="post">
<fieldset>
<!-- Name -->
<div class="mb-3 row">
<label for="name" class="col-lg-6 col-form-label text-end">Name:</label>
<div class="col-lg-2">
<input type="text" class="form-control" name="name" id="name" value="{name}" required>
</div>
</div>
<!-- Slug -->
<div class="mb-3 row">
<label for="slug" class="col-lg-6 col-form-label text-end">Slug:</label>
<div class="col-lg-2">
<input type="text" class="form-control" name="slug" id="slug" aria-describedby="slugHelp" value="{slug}" required>
<small id="slugHelp" class="form-text text-muted">
Public links would be {ROOT_URL}reviews/YOUR_SLUG_HERE
</small>
</div>
</div>
<!-- Hidden Token -->
<input type="hidden" name="token" value="{TOKEN}">
<!-- Submit Button -->
<div class="text-center">
<button type="submit" name="submit" value="submit" class="btn btn-primary btn-lg">Create</button>
</div>
</fieldset>
</form>
</div>

View File

@ -1,28 +1,33 @@
<div class="container col-md-4 col-lg-4">
<div class="row">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">Review</h3>
</div>
<div class="panel-body">
<div class="row">
<div class="">
<table class="table table-user-primary">
<div class="container py-4">
<div class="row justify-content-center">
<div class="col-md-8">
{ADMIN_BREADCRUMBS}
<div class="card shadow">
<!-- Card Header -->
<div class="card-header text-center bg-dark text-white">
<h3 class="card-title mb-0">Review: {title}</h3>
</div>
<!-- Card Body -->
<div class="card-body">
<div class="row align-items-center">
<!-- Details -->
<table class="table table-borderless">
<tbody>
<tr>
<td align="left" width="200"><b>Title</b></td>
<td align="right">{title}</td>
<th scope="row">Title:</th>
<td>{title}</td>
</tr>
<tr>
<td><b>Rating</b></td>
<td align="right">{rating}</td>
<th scope="row">Rating:</th>
<td>{rating}</td>
</tr>
<tr>
<td><b>Created</b></td>
<td align="right">{DTC}{createdAt}{/DTC}</td>
<th scope="row">Created:</th>
<td>{DTC}{createdAt}{/DTC}</td>
</tr>
<tr>
<td align="center" colspan="2"><b>Review</b></td>
<th scope="row" colspan="2">Review:</th>
</tr>
<tr>
<td colspan="2">{review}</td>
@ -31,12 +36,14 @@
</table>
</div>
</div>
</div>
<div class="panel-footer">
<a href="{ROOT_URL}admin/reviews/reviewApprove/{ID}" class="btn btn-sm btn-success" role="button"><i class="fa fa-fw fa-info-circle"></i></a>
<a href="{ROOT_URL}admin/reviews/reviewHide/{ID}" class="btn btn-sm btn-info" role="button"><i class="fa fa-fw fa-eye-closed"></i></a>
<a href="{ROOT_URL}admin/reviews/reviewDelete/{ID}" class="btn btn-sm btn-danger" role="button"><i class="fa fa-fw fa-trash"></i></a>
<!-- Admin Controls -->
<div class="card-footer text-center">
<a href="{ROOT_URL}admin/reviews/reviewApprove/{ID}" class="btn btn-sm btn-success" role="button"><i class="fa fa-fw fa-info-circle"></i></a>
<a href="{ROOT_URL}admin/reviews/reviewHide/{ID}" class="btn btn-sm btn-info" role="button"><i class="fa fa-fw fa-eye-closed"></i></a>
<a href="{ROOT_URL}admin/reviews/reviewDelete/{ID}" class="btn btn-sm btn-danger" role="button"><i class="fa fa-fw fa-trash"></i></a>
</div>
</div>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,49 @@
<div class="container py-4">
<div class="row justify-content-center">
<div class="col-md-8">
{ADMIN_BREADCRUMBS}
<div class="card shadow">
<!-- Card Header -->
<div class="card-header text-center bg-dark text-white">
<h3 class="card-title mb-0">Review: {title}</h3>
</div>
<!-- Card Body -->
<div class="card-body">
<div class="row align-items-center">
<!-- Details -->
<table class="table table-borderless">
<tbody>
<tr>
<th scope="row">Title:</th>
<td>{title}</td>
</tr>
<tr>
<th scope="row">Rating:</th>
<td>{rating}</td>
</tr>
<tr>
<th scope="row">Created:</th>
<td>{DTC}{createdAt}{/DTC}</td>
</tr>
<tr>
<th scope="row" colspan="2">Review:</th>
</tr>
<tr>
<td colspan="2">{review}</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Admin Controls -->
<div class="card-footer text-center">
<a href="{ROOT_URL}admin/reviews/reviewApprove/{ID}" class="btn btn-sm btn-success" role="button"><i class="fa fa-fw fa-info-circle"></i></a>
<a href="{ROOT_URL}admin/reviews/reviewHide/{ID}" class="btn btn-sm btn-info" role="button"><i class="fa fa-fw fa-eye-closed"></i></a>
<a href="{ROOT_URL}admin/reviews/reviewDelete/{ID}" class="btn btn-sm btn-danger" role="button"><i class="fa fa-fw fa-trash"></i></a>
</div>
</div>
</div>
</div>
</div>

View File

@ -1,16 +1,18 @@
<div class="container mt-5">
<h2>Write a Review</h2>
<div class="col-8 mx-auto p-4 rounded shadow-sm mb-5 context-main-bg mt-4 container">
<h2 class="text-center mb-4">Share a Review</h2>
<hr>
<p>We thank you for for taking the time to review our products. We do not edit or modify reviews in any way. You, as the customer have the ability to modify your own reviews.</p>
<p>We read each and every review. You and the admin team both have the ability to comment on reviews privately. Neither your reviews or comments will be publicly shared without your permission.</p>
<form id="review-form" method="post">
<div class="form-group">
<label for="review_category_id">Review Category:</label>
<!-- Review Category -->
<div class="mb-3">
<label for="review_category_id" class="form-label">Review Category:</label>
{reviewCategorySelect}
</div>
<div class="form-group">
<label for="title">Title your review:</label>
<input type="text" class="form-control" id="title" name="title" required>
</div>
<div class="form-group">
<label>Rating:</label>
<!-- Rating -->
<div class="mb-3">
<label for="star-rating" class="form-label">Rating:</label>
<div class="star-rating">
<span class="fa fa-star" data-rating="1"></span>
<span class="fa fa-star" data-rating="2"></span>
@ -20,10 +22,31 @@
<input type="hidden" name="rating" class="rating-value" value="0">
</div>
</div>
<div class="form-group">
<label for="review">Your Review:</label>
<textarea class="form-control" id="review" name="review" rows="5" required></textarea>
<!-- Review Title -->
<div class="mb-3">
<label for="title" class="form-label">Review Title:</label>
<input type="text" class="form-control" id="title" name="title" aria-describedby="titleHelp" required>
<small id="titleHelp" class="form-text text-muted">
No need to overthink it, something as simple as "My Review" is fine.
</small>
</div>
<!-- Review -->
<div class="mb-3">
<label for="review" class="form-label">Your Review:</label>
<textarea class="form-control" name="review" id="review" rows="5" maxlength="2000" aria-describedby="reviewHelp" required></textarea>
<small id="reviewHelp" class="form-text text-muted">
(max: 2000 characters)
</small>
</div>
<!-- Hidden Token -->
<input type="hidden" name="token" value="{TOKEN}">
<!-- Submit Button -->
<div class="text-center">
<button type="submit" name="submit" value="submit" class="btn btn-primary btn-lg atb-green-bg">Submit</button>
</div>
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block ">Submit</button>
</form>
</div>

View File

@ -0,0 +1,44 @@
<div class="col-8 mx-auto p-4 rounded shadow-sm mb-5 context-main-bg mt-4 container">
<h2 class="text-center mb-4">Edit Your Review</h2>
<hr>
<form id="review-form" method="post">
<!-- Review Title -->
<div class="mb-3">
<label for="title" class="form-label">Review Title:</label>
<input type="text" class="form-control" id="title" name="title" aria-describedby="titleHelp" value="{title}" required>
<small id="titleHelp" class="form-text text-muted">
No need to overthink it, something as simple as "My Review" is fine.
</small>
</div>
<!-- Rating -->
<div class="mb-3">
<label for="star-rating" class="form-label">Rating:</label>
<div class="star-rating">
<span class="fa fa-star" data-rating="1"></span>
<span class="fa fa-star" data-rating="2"></span>
<span class="fa fa-star" data-rating="3"></span>
<span class="fa fa-star" data-rating="4"></span>
<span class="fa fa-star" data-rating="5"></span>
<input type="hidden" name="rating" class="rating-value" value="{rating}">
</div>
</div>
<!-- Review -->
<div class="mb-3">
<label for="review" class="form-label">Your Review:</label>
<textarea class="form-control" name="review" id="review" rows="5" maxlength="2000" aria-describedby="reviewHelp" required>{review}</textarea>
<small id="reviewHelp" class="form-text text-muted">
(max: 2000 characters)
</small>
</div>
<!-- Hidden Token -->
<input type="hidden" name="token" value="{TOKEN}">
<!-- Submit Button -->
<div class="text-center">
<button type="submit" name="submit" value="submit" class="btn btn-primary btn-lg atb-green-bg">Submit</button>
</div>
</form>
</div>

View File

@ -1,4 +1,4 @@
<div class="col-8 mx-auto p-4 rounded shadow-sm mb-5 context-main-bg mt-4 context-main">
<div class="col-8 mx-auto p-4 rounded shadow-sm mb-5 context-main-bg mt-4 context-main text-center">
<legend>Reviews</legend>
<hr>
<p>
@ -7,12 +7,11 @@
<p>
On this page you can find <strong>your</strong> reviews to see any responses or make edits.
</p>
<table class="table context-main">
<table class="table context-main text-center">
<thead>
<tr>
<th style="width: 60%">Title</th>
<th style="width: 10%">Rating</th>
<th style="width: 10%"></th>
<th style="width: 20%">Rating</th>
<th style="width: 10%"></th>
<th style="width: 10%"></th>
</tr>
@ -20,19 +19,20 @@
<tbody>
{LOOP}
<tr>
<td><a href="{ROOT_URL}admin/reviews/reviewView/{ID}" class="btn btn-sm btn-primary" role="button">{title}</a></td>
<td align="center">{rating}</td>
<td><a href="{ROOT_URL}admin/reviews/reviewEdit/{ID}" class="btn btn-sm btn-info" role="button"><i class="fa fa-fw fa-pencil"></i></a></td>
<td><a href="{ROOT_URL}admin/reviews/reviewDelete/{ID}" class="btn btn-sm btn-danger" role="button"><i class="fa fa-fw fa-trash"></i></a></td>
<td><a href="{ROOT_URL}reviews/view/{ID}" class="text-decoration-none atb-green" role="button">{title}</a></td>
<td>{rating}</td>
<td><a href="{ROOT_URL}reviews/edit/{ID}" class="btn btn-sm btn-warning" role="button"><i class="fa fa-fw fa-pencil"></i></a></td>
<td><a href="{ROOT_URL}reviews/delete/{ID}" class="btn btn-sm btn-danger" role="button"><i class="fa fa-fw fa-trash"></i></a></td>
</tr>
{/LOOP}
{ALT}
<tr>
<td align="center" colspan="6">
<td colspan="4">
No results to show.
</td>
</tr>
{/ALT}
</tbody>
</table>
<a href="{ROOT_URL}reviews/review" class="btn btn-md atb-green-bg" role="button">Add Review</a>
</div>

View File

@ -0,0 +1,47 @@
<div class="container py-4">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card shadow">
<!-- Card Header -->
<div class="card-header text-center bg-dark text-white">
<h3 class="card-title mb-0">Review: {title}</h3>
</div>
<!-- Card Body -->
<div class="card-body">
<div class="row align-items-center">
<!-- Details -->
<table class="table table-borderless">
<tbody>
<tr>
<th scope="row">Title:</th>
<td>{title}</td>
</tr>
<tr>
<th scope="row">Rating:</th>
<td>{rating}</td>
</tr>
<tr>
<th scope="row">Created:</th>
<td>{DTC}{createdAt}{/DTC}</td>
</tr>
<tr>
<th scope="row" colspan="2">Review:</th>
</tr>
<tr>
<td colspan="2">{review}</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- Admin Controls -->
<div class="card-footer text-center">
<a href="{ROOT_URL}reviews/edit/{ID}" class="btn btn-sm btn-warning" role="button"><i class="fa fa-fw fa-pencil"></i></a>
<a href="{ROOT_URL}reviews/delete/{ID}" class="btn btn-sm btn-danger" role="button"><i class="fa fa-fw fa-trash"></i></a>
</div>
</div>
</div>
</div>
</div>