Update (view): redesign
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
use GrahamCampbell\Markdown\Facades\Markdown;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
if (! function_exists('epoch_now_timestamp')) {
|
||||
@@ -9,6 +10,27 @@ function epoch_now_timestamp()
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('read_duration')) {
|
||||
function read_duration($text)
|
||||
{
|
||||
return Str::readDuration($text);
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('plain_text')) {
|
||||
function plain_text($content)
|
||||
{
|
||||
return trim(preg_replace('/\s\s+/', ' ', strip_tags($content)));
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('markdown_min_read')) {
|
||||
function markdown_min_read($markdown)
|
||||
{
|
||||
return read_duration(plain_text(Markdown::convert($markdown)->getContent()));
|
||||
}
|
||||
}
|
||||
|
||||
if (! function_exists('unslug')) {
|
||||
function unslug($slug, $delimiter = '-')
|
||||
{
|
||||
|
||||
@@ -22,7 +22,7 @@ public function redirect(Request $request, $slug)
|
||||
return abort(404);
|
||||
}
|
||||
|
||||
return redirect()->route('front.post',['category_slug' => $post->category->slug, 'slug' => $post->slug]);
|
||||
return redirect()->route('front.post', ['category_slug' => $post->category->slug, 'slug' => $post->slug]);
|
||||
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ public function index(Request $request, $category_slug, $slug)
|
||||
->addValue('dateCreated', $post->created_at->format('Y-m-d'))
|
||||
->addValue('dateModified', $post->updated_at->format('Y-m-d'))
|
||||
->addValue('description', $post_description)
|
||||
->addValue('articleBody', trim(preg_replace('/\s\s+/', ' ', strip_tags($content))));
|
||||
->addValue('articleBody', plain_text($content));
|
||||
|
||||
// breadcrumb json ld
|
||||
$listItems = [];
|
||||
@@ -161,7 +161,7 @@ private function injectBootstrapClasses($content)
|
||||
|
||||
if (strpos($pNode->text(), 'Q:') === 0) {
|
||||
$currentClasses = $pNode->attr('class');
|
||||
$newClasses = trim($currentClasses.' fw-bold');
|
||||
$newClasses = trim($currentClasses.' ');
|
||||
$pNode->getNode(0)->setAttribute('class', $newClasses);
|
||||
}
|
||||
});
|
||||
@@ -234,7 +234,7 @@ private function injectPublishDateAndAuthor($post, $content)
|
||||
$authorName = $post->author->name;
|
||||
|
||||
// Create the HTML structure for publish date and author
|
||||
$publishInfo = "<div class=\"mb-4\"><span class=\"text-secondary small\">Published on {$publishedAtFormatted} by {$authorName}</span></div>";
|
||||
$publishInfo = "<div class=\"mb-4\"><span class=\"text-secondary small\">Published on {$publishedAtFormatted} by {$authorName}<i class=\"bi bi-dot\"></i>" . markdown_min_read($post->body) . "</span></div>";
|
||||
|
||||
// Inject the publish date and author information after the `h1` tag
|
||||
$content = preg_replace('/(<\/h1>)/', '$1'.$publishInfo, $content, 1);
|
||||
|
||||
@@ -42,18 +42,14 @@ public static function handle(SerpUrl $serp_url)
|
||||
|
||||
$count = 0;
|
||||
|
||||
while ((!isset($ai_suggestion?->article_type)) || (!isset($ai_suggestion?->short_title)) || (!isset($ai_suggestion?->main_keyword)) || (!isset($ai_suggestion?->photo_keywords)) || (!isset($ai_suggestion?->description)) || (!isset($ai_suggestion?->title)))
|
||||
{
|
||||
if ($count >= 3)
|
||||
{
|
||||
Log::error(serialize($ai_suggestion));
|
||||
throw new Exception('Failed to generate ai_suggestion');
|
||||
}
|
||||
else
|
||||
{
|
||||
$count++;
|
||||
$ai_suggestion = OpenAI::createNewArticleTitle($serp_url->title, $serp_url->description);
|
||||
}
|
||||
while ((! isset($ai_suggestion?->article_type)) || (! isset($ai_suggestion?->short_title)) || (! isset($ai_suggestion?->main_keyword)) || (! isset($ai_suggestion?->photo_keywords)) || (! isset($ai_suggestion?->description)) || (! isset($ai_suggestion?->title))) {
|
||||
if ($count >= 3) {
|
||||
Log::error(serialize($ai_suggestion));
|
||||
throw new Exception('Failed to generate ai_suggestion');
|
||||
} else {
|
||||
$count++;
|
||||
$ai_suggestion = OpenAI::createNewArticleTitle($serp_url->title, $serp_url->description);
|
||||
}
|
||||
}
|
||||
|
||||
$readability_content = ScrapeUrlBodyTask::handle($serp_url->url);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
@@ -19,6 +20,11 @@ public function register(): void
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
//
|
||||
Str::macro('readDuration', function (...$text) {
|
||||
$totalWords = str_word_count(implode(' ', $text));
|
||||
$minutesToRead = round($totalWords / 200);
|
||||
|
||||
return (int) max(1, $minutesToRead).' min read';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
.nav-scroller {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
height: 2.75rem;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.nav-scroller .nav {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
padding-bottom: 1rem;
|
||||
margin-top: -1px;
|
||||
overflow-x: auto;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
@@ -35,3 +32,11 @@ img.lqip-blur {
|
||||
}
|
||||
|
||||
/* lqip end */
|
||||
|
||||
a.link-body-emphasis {
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
// consistent naming. Ex: $nav-link-disabled-color and $modal-content-box-shadow-xs.
|
||||
|
||||
// Color system
|
||||
$body-bg: #f0f2f5;
|
||||
|
||||
// scss-docs-start gray-color-variables
|
||||
$white: #fff;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="container">
|
||||
<div class="w-full shadow-sm bg-white">
|
||||
<header class="border-bottom lh-1 py-3">
|
||||
<div class="row flex-nowrap justify-content-center align-items-center">
|
||||
|
||||
@@ -10,8 +10,9 @@
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="nav-scroller py-1 mb-3 border-bottom">
|
||||
<nav class="nav nav-underline justify-content-between">
|
||||
<div class="nav-scroller py-2 mb-3 border-bottom">
|
||||
|
||||
<nav class="container nav nav-underline justify-content-between">
|
||||
@foreach ($parent_categories as $category)
|
||||
<a class="fw-bold nav-item nav-link link-body-emphasis {{ active(route('front.category', ['category_slug' => $category->slug])) }}"
|
||||
href="{{ route('front.category', ['category_slug' => $category->slug]) }}">{{ $category->short_name }}</a>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<div class="p-4 mb-3 bg-body-tertiary rounded">
|
||||
<div class="p-4 mb-3 bg-white rounded-3 shadow-sm">
|
||||
<h4 class="fst-italic">About EchoScoop</h4>
|
||||
<p class="mb-0">
|
||||
EchoScoop is a streamlined news platform delivering concise global updates. Our goal is to keep you promptly
|
||||
informed with each scoop.
|
||||
EchoScoop is a streamlined news platform delivering concise global updates. We keep our news to be bite-sized within 1 to 5 minutes read. Our goal is to keep you promptly informed with each scoop.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1,18 +1,29 @@
|
||||
<div class="row g-0 border rounded overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative">
|
||||
<div class="row g-0 border-0 rounded-3 overflow-hidden flex-md-row mb-4 shadow-sm h-md-250 position-relative bg-white">
|
||||
<div class="col p-4 d-flex flex-column position-static">
|
||||
@if((isset($category)) && ($category->id != $post->category->id))
|
||||
<span class="d-inline-block mb-2 text-success-emphasis"><i>Under</i> <strong class="ms-1">{{ $category->name }} <i class="bi bi-chevron-right small"></i> {{ $post->category->name }}</strong></span>
|
||||
@if (isset($category) && $category->id != $post->category->id)
|
||||
<span class="d-inline-block mb-2 text-success-emphasis"><i>Under</i> <strong
|
||||
class="ms-1">{{ $category->name }} <i class="bi bi-chevron-right small"></i>
|
||||
{{ $post->category->name }}</strong></span>
|
||||
@else
|
||||
<span class="d-inline-block mb-2 text-success-emphasis"><i>Under</i> <strong class="ms-1">{{ $post->category->name }}</strong></span>
|
||||
@endif
|
||||
<h3 class="mb-0 h4">{{ $post->title }}</h3>
|
||||
@if (!is_empty($post->published_at))
|
||||
<div class="mb-1 text-body-secondary">{{ $post->published_at->format('M j') }}</div>
|
||||
<span class="d-inline-block mb-2 text-success-emphasis"><i>Under</i> <strong
|
||||
class="ms-1">{{ $post->category->name }}</strong></span>
|
||||
@endif
|
||||
<h3 class="mb-1 h4">{{ $post->title }}</h3>
|
||||
<div class="mb-3 text-body-secondary">
|
||||
|
||||
@if (!is_empty($post->published_at))
|
||||
{{ $post->published_at->format('M j') }}
|
||||
@endif
|
||||
|
||||
<i class="bi bi-dot"></i>
|
||||
|
||||
{{ markdown_min_read($post->body) }}
|
||||
|
||||
</div>
|
||||
<p class="mb-3">{{ $post->excerpt }}</p>
|
||||
<a href="{{ route('front.post', ['slug' => $post->slug, 'category_slug' => $post->category->slug]) }}"
|
||||
class="icon-link gap-1 icon-link-hover stretched-link">
|
||||
Continue reading
|
||||
Continue reading...
|
||||
<svg class="bi">
|
||||
<use xlink:href="#chevron-right"></use>
|
||||
</svg>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
@include('front.partials.breadcrumbs')
|
||||
|
||||
<div class="row g-5">
|
||||
<div class="row g-4">
|
||||
<div class="col-md-8">
|
||||
<h1 class="pb-2 fw-normal h2">
|
||||
@if (isset($category) && !is_null($category))
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
|
||||
@include('front.partials.breadcrumbs')
|
||||
|
||||
<div class="row g-5">
|
||||
<div class="row g-4">
|
||||
<div class="col-md-8">
|
||||
|
||||
<article class="blog-post">
|
||||
<article class="blog-post bg-white rounded-3 shadow-sm px-5 py-4
|
||||
">
|
||||
{!! $content !!}
|
||||
</article>
|
||||
|
||||
|
||||
@@ -2,18 +2,32 @@
|
||||
@section('content')
|
||||
<main class="container">
|
||||
@if (!is_null($featured_post))
|
||||
<div class="p-4 p-md-5 mb-4 rounded text-body-emphasis bg-body-secondary">
|
||||
<div class="p-4 p-md-5 mb-4 rounded-3 text-body-emphasis bg-white shadow-sm">
|
||||
<div class="col-lg-12 px-0">
|
||||
<h1 class="display-4 fst-italic">{{ $featured_post->title }}</h1>
|
||||
<p class="lead my-3">{{ $featured_post->excerpt }}</p>
|
||||
<p class="lead mb-0"><a href="{{ route('front.post', ['slug' => $featured_post->slug, 'category_slug' => $featured_post->category->slug]) }}"
|
||||
<h1 class="display-6 fw-semibold mb-1">{{ $featured_post->title }}</h1>
|
||||
|
||||
<div class="mb-1 text-body-secondary">
|
||||
|
||||
@if (!is_empty($featured_post->published_at))
|
||||
{{ $featured_post->published_at->format('M j') }}
|
||||
@endif
|
||||
|
||||
<i class="bi bi-dot"></i>
|
||||
|
||||
{{ markdown_min_read($featured_post->body) }}
|
||||
|
||||
</div>
|
||||
|
||||
<p class="lead my-3 mb-3">{{ $featured_post->excerpt }}</p>
|
||||
<p class="lead mb-0"><a
|
||||
href="{{ route('front.post', ['slug' => $featured_post->slug, 'category_slug' => $featured_post->category->slug]) }}"
|
||||
class=" fw-bold">Continue reading...</a></p>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
|
||||
<div class="row g-5">
|
||||
<div class="row g-4">
|
||||
<div class="col-md-8">
|
||||
|
||||
@if ($latest_posts->count() > 0)
|
||||
@@ -22,7 +36,7 @@ class=" fw-bold">Continue reading...</a></p>
|
||||
@endforeach
|
||||
|
||||
<div class="w-100 d-flex justify-content-center">
|
||||
<a class="btn btn-outline-primary rounded-pill px-3 text-decoration-none"
|
||||
<a class="btn btn-primary shadow rounded-pill px-3 text-decoration-none"
|
||||
href="{{ route('front.all') }}">View Latest News</a>
|
||||
</div>
|
||||
@else
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
@if ($paginator->onFirstPage())
|
||||
<div class="mx-1 page-item disabled" aria-disabled="true">
|
||||
<span
|
||||
class="text-decoration-none btn btn-light disabled border border-primary rounded-pill px-3">{!! __('pagination.previous') !!}</span>
|
||||
class="shadow-sm text-decoration-none btn btn-light disabled border border-light rounded-pill px-3">{!! __('pagination.previous') !!}</span>
|
||||
</div>
|
||||
@else
|
||||
<div class="mx-1 page-item">
|
||||
<a class="text-decoration-none btn btn-outline-primary rounded-pill px-3"
|
||||
<a class="shadow-sm text-decoration-none btn btn-primary rounded-pill px-3"
|
||||
href="{{ $paginator->previousPageUrl() }}" rel="prev">
|
||||
{!! __('pagination.previous') !!}
|
||||
</a>
|
||||
@@ -18,13 +18,13 @@ class="text-decoration-none btn btn-light disabled border border-primary rounded
|
||||
{{-- Next Page Link --}}
|
||||
@if ($paginator->hasMorePages())
|
||||
<div class="mx-1 page-item">
|
||||
<a class="text-decoration-none btn btn-outline-primary rounded-pill px-3"
|
||||
<a class="shadow-sm text-decoration-none btn btn-primary rounded-pill px-3"
|
||||
href="{{ $paginator->nextPageUrl() }}" rel="next">{!! __('pagination.next') !!}</a>
|
||||
</div>
|
||||
@else
|
||||
<div class="mx-1 page-item disabled" aria-disabled="true">
|
||||
<span
|
||||
class="text-decoration-none btn btn-light disabled border border-primary rounded-pill px-3">{!! __('pagination.next') !!}</span>
|
||||
class="shadow-sm text-decoration-none btn btn-light disabled border border-light rounded-pill px-3">{!! __('pagination.next') !!}</span>
|
||||
</div>
|
||||
@endif
|
||||
</nav>
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
|
||||
Route::get('/{category_slug}/{slug}', [App\Http\Controllers\Front\FrontPostController::class, 'index'])->name('front.post');
|
||||
|
||||
|
||||
Route::get('/{category_slug}', [App\Http\Controllers\Front\FrontListController::class, 'category'])
|
||||
->where('category_slug', '^(automotive|business|trading|information-technology|marketing|office|telecommunications|food-drink|collectibles|pets|photography|hobbies-gifts|hunting-fishing|law|politics|home-garden|shopping|fashion-clothing|real-estate|family|wedding|immigration|society|education|languages|health|beauty|psychology|wellness|religion-spirituality|tips-tricks|how-to|holiday|world-festivals|travel|outdoors|computer|phones|gadgets|technology|social-networks|ai)$')
|
||||
->name('front.category');
|
||||
|
||||
Reference in New Issue
Block a user