Feeds CRUD
This commit is contained in:
parent
d4e9e27c41
commit
c7302092bb
9 changed files with 609 additions and 0 deletions
83
app/Http/Controllers/FeedsController.php
Normal file
83
app/Http/Controllers/FeedsController.php
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\Feed;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
|
||||||
|
class FeedsController extends Controller
|
||||||
|
{
|
||||||
|
public function index(): View
|
||||||
|
{
|
||||||
|
$feeds = Feed::orderBy('is_active', 'desc')
|
||||||
|
->orderBy('name')
|
||||||
|
->get();
|
||||||
|
|
||||||
|
return view('pages.feeds.index', compact('feeds'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function create(): View
|
||||||
|
{
|
||||||
|
return view('pages.feeds.create');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(Request $request): RedirectResponse
|
||||||
|
{
|
||||||
|
$validated = $request->validate([
|
||||||
|
'name' => 'required|string|max:255',
|
||||||
|
'url' => 'required|url|unique:feeds,url',
|
||||||
|
'type' => 'required|in:website,rss',
|
||||||
|
'language' => 'required|string|size:2',
|
||||||
|
'description' => 'nullable|string',
|
||||||
|
'is_active' => 'boolean'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Default is_active to true if not provided
|
||||||
|
$validated['is_active'] = $validated['is_active'] ?? true;
|
||||||
|
|
||||||
|
Feed::create($validated);
|
||||||
|
|
||||||
|
return redirect()->route('feeds.index')
|
||||||
|
->with('success', 'Feed created successfully!');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function show(Feed $feed): View
|
||||||
|
{
|
||||||
|
return view('pages.feeds.show', compact('feed'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(Feed $feed): View
|
||||||
|
{
|
||||||
|
return view('pages.feeds.edit', compact('feed'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(Request $request, Feed $feed): RedirectResponse
|
||||||
|
{
|
||||||
|
$validated = $request->validate([
|
||||||
|
'name' => 'required|string|max:255',
|
||||||
|
'url' => 'required|url|unique:feeds,url,' . $feed->id,
|
||||||
|
'type' => 'required|in:website,rss',
|
||||||
|
'language' => 'required|string|size:2',
|
||||||
|
'description' => 'nullable|string',
|
||||||
|
'is_active' => 'boolean'
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Default is_active to current value if not provided
|
||||||
|
$validated['is_active'] = $validated['is_active'] ?? $feed->is_active;
|
||||||
|
|
||||||
|
$feed->update($validated);
|
||||||
|
|
||||||
|
return redirect()->route('feeds.index')
|
||||||
|
->with('success', 'Feed updated successfully!');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function destroy(Feed $feed): RedirectResponse
|
||||||
|
{
|
||||||
|
$feed->delete();
|
||||||
|
|
||||||
|
return redirect()->route('feeds.index')
|
||||||
|
->with('success', 'Feed deleted successfully!');
|
||||||
|
}
|
||||||
|
}
|
||||||
71
app/Models/Feed.php
Normal file
71
app/Models/Feed.php
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property int $id
|
||||||
|
* @property string $name
|
||||||
|
* @property string $url
|
||||||
|
* @property string $type
|
||||||
|
* @property string $language
|
||||||
|
* @property string $description
|
||||||
|
* @property array $settings
|
||||||
|
* @property bool $is_active
|
||||||
|
* @property Carbon $last_fetched_at
|
||||||
|
* @property Carbon $created_at
|
||||||
|
* @property Carbon $updated_at
|
||||||
|
* @method static create(array $validated)
|
||||||
|
* @method static orderBy(string $string, string $string1)
|
||||||
|
*/
|
||||||
|
class Feed extends Model
|
||||||
|
{
|
||||||
|
protected $fillable = [
|
||||||
|
'name',
|
||||||
|
'url',
|
||||||
|
'type',
|
||||||
|
'language',
|
||||||
|
'description',
|
||||||
|
'settings',
|
||||||
|
'is_active',
|
||||||
|
'last_fetched_at'
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'settings' => 'array',
|
||||||
|
'is_active' => 'boolean',
|
||||||
|
'last_fetched_at' => 'datetime'
|
||||||
|
];
|
||||||
|
|
||||||
|
public function getTypeDisplayAttribute(): string
|
||||||
|
{
|
||||||
|
return match ($this->type) {
|
||||||
|
'website' => 'Website',
|
||||||
|
'rss' => 'RSS Feed',
|
||||||
|
default => 'Unknown'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStatusAttribute(): string
|
||||||
|
{
|
||||||
|
if (!$this->is_active) {
|
||||||
|
return 'Inactive';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->last_fetched_at) {
|
||||||
|
return 'Never fetched';
|
||||||
|
}
|
||||||
|
|
||||||
|
$hoursAgo = $this->last_fetched_at->diffInHours(now());
|
||||||
|
|
||||||
|
if ($hoursAgo < 2) {
|
||||||
|
return 'Recently fetched';
|
||||||
|
} elseif ($hoursAgo < 24) {
|
||||||
|
return "Fetched {$hoursAgo}h ago";
|
||||||
|
} else {
|
||||||
|
return "Fetched " . $this->last_fetched_at->diffForHumans();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
database/migrations/2025_07_05_003216_create_feeds_table.php
Normal file
31
database/migrations/2025_07_05_003216_create_feeds_table.php
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('feeds', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('name'); // "VRT News", "Belga News Agency"
|
||||||
|
$table->string('url'); // "https://vrt.be" or "https://feeds.example.com/rss.xml"
|
||||||
|
$table->enum('type', ['website', 'rss']); // Feed type
|
||||||
|
$table->string('language', 5)->default('en'); // Language code (en, nl, etc.)
|
||||||
|
$table->text('description')->nullable();
|
||||||
|
$table->json('settings')->nullable(); // Custom settings per feed type
|
||||||
|
$table->boolean('is_active')->default(true);
|
||||||
|
$table->timestamp('last_fetched_at')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->unique('url');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('feeds');
|
||||||
|
}
|
||||||
|
};
|
||||||
109
resources/views/pages/feeds/create.blade.php
Normal file
109
resources/views/pages/feeds/create.blade.php
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="max-w-2xl mx-auto py-6 sm:px-6 lg:px-8">
|
||||||
|
<div class="px-4 py-6 sm:px-0">
|
||||||
|
<div class="mb-6">
|
||||||
|
<h1 class="text-2xl font-semibold text-gray-900">Add New Feed</h1>
|
||||||
|
<p class="mt-1 text-sm text-gray-600">Create a new content feed for articles.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-white shadow sm:rounded-lg">
|
||||||
|
<form action="{{ route('feeds.store') }}" method="POST" class="px-4 py-5 sm:p-6">
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 gap-6">
|
||||||
|
<div>
|
||||||
|
<label for="name" class="block text-sm font-medium text-gray-700">Name</label>
|
||||||
|
<input type="text"
|
||||||
|
name="name"
|
||||||
|
id="name"
|
||||||
|
value="{{ old('name') }}"
|
||||||
|
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm @error('name') border-red-300 @enderror"
|
||||||
|
placeholder="VRT News">
|
||||||
|
@error('name')
|
||||||
|
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="url" class="block text-sm font-medium text-gray-700">URL</label>
|
||||||
|
<input type="url"
|
||||||
|
name="url"
|
||||||
|
id="url"
|
||||||
|
value="{{ old('url') }}"
|
||||||
|
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm @error('url') border-red-300 @enderror"
|
||||||
|
placeholder="https://example.com or https://example.com/feed.xml">
|
||||||
|
@error('url')
|
||||||
|
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="type" class="block text-sm font-medium text-gray-700">Type</label>
|
||||||
|
<select name="type"
|
||||||
|
id="type"
|
||||||
|
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm @error('type') border-red-300 @enderror">
|
||||||
|
<option value="">Select feed type...</option>
|
||||||
|
<option value="website" {{ old('type') === 'website' ? 'selected' : '' }}>Website</option>
|
||||||
|
<option value="rss" {{ old('type') === 'rss' ? 'selected' : '' }}>RSS Feed</option>
|
||||||
|
</select>
|
||||||
|
@error('type')
|
||||||
|
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="language" class="block text-sm font-medium text-gray-700">Language</label>
|
||||||
|
<select name="language"
|
||||||
|
id="language"
|
||||||
|
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm @error('language') border-red-300 @enderror">
|
||||||
|
<option value="en" {{ old('language', 'en') === 'en' ? 'selected' : '' }}>English</option>
|
||||||
|
<option value="nl" {{ old('language') === 'nl' ? 'selected' : '' }}>Dutch</option>
|
||||||
|
<option value="fr" {{ old('language') === 'fr' ? 'selected' : '' }}>French</option>
|
||||||
|
<option value="de" {{ old('language') === 'de' ? 'selected' : '' }}>German</option>
|
||||||
|
<option value="es" {{ old('language') === 'es' ? 'selected' : '' }}>Spanish</option>
|
||||||
|
</select>
|
||||||
|
@error('language')
|
||||||
|
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="description" class="block text-sm font-medium text-gray-700">Description</label>
|
||||||
|
<textarea name="description"
|
||||||
|
id="description"
|
||||||
|
rows="3"
|
||||||
|
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm @error('description') border-red-300 @enderror"
|
||||||
|
placeholder="Optional description of this feed...">{{ old('description') }}</textarea>
|
||||||
|
@error('description')
|
||||||
|
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center">
|
||||||
|
<input type="checkbox"
|
||||||
|
name="is_active"
|
||||||
|
id="is_active"
|
||||||
|
value="1"
|
||||||
|
{{ old('is_active', true) ? 'checked' : '' }}
|
||||||
|
class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded">
|
||||||
|
<label for="is_active" class="ml-2 block text-sm text-gray-900">
|
||||||
|
Active
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 flex items-center justify-end space-x-3">
|
||||||
|
<a href="{{ route('feeds.index') }}" class="bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
||||||
|
Cancel
|
||||||
|
</a>
|
||||||
|
<button type="submit" class="bg-indigo-600 py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
||||||
|
Create Feed
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
110
resources/views/pages/feeds/edit.blade.php
Normal file
110
resources/views/pages/feeds/edit.blade.php
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="max-w-2xl mx-auto py-6 sm:px-6 lg:px-8">
|
||||||
|
<div class="px-4 py-6 sm:px-0">
|
||||||
|
<div class="mb-6">
|
||||||
|
<h1 class="text-2xl font-semibold text-gray-900">Edit Feed</h1>
|
||||||
|
<p class="mt-1 text-sm text-gray-600">Update the details for {{ $feed->name }}.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-white shadow sm:rounded-lg">
|
||||||
|
<form action="{{ route('feeds.update', $feed) }}" method="POST" class="px-4 py-5 sm:p-6">
|
||||||
|
@csrf
|
||||||
|
@method('PUT')
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 gap-6">
|
||||||
|
<div>
|
||||||
|
<label for="name" class="block text-sm font-medium text-gray-700">Name</label>
|
||||||
|
<input type="text"
|
||||||
|
name="name"
|
||||||
|
id="name"
|
||||||
|
value="{{ old('name', $feed->name) }}"
|
||||||
|
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm @error('name') border-red-300 @enderror"
|
||||||
|
placeholder="VRT News">
|
||||||
|
@error('name')
|
||||||
|
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="url" class="block text-sm font-medium text-gray-700">URL</label>
|
||||||
|
<input type="url"
|
||||||
|
name="url"
|
||||||
|
id="url"
|
||||||
|
value="{{ old('url', $feed->url) }}"
|
||||||
|
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm @error('url') border-red-300 @enderror"
|
||||||
|
placeholder="https://example.com or https://example.com/feed.xml">
|
||||||
|
@error('url')
|
||||||
|
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="type" class="block text-sm font-medium text-gray-700">Type</label>
|
||||||
|
<select name="type"
|
||||||
|
id="type"
|
||||||
|
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm @error('type') border-red-300 @enderror">
|
||||||
|
<option value="">Select feed type...</option>
|
||||||
|
<option value="website" {{ old('type', $feed->type) === 'website' ? 'selected' : '' }}>Website</option>
|
||||||
|
<option value="rss" {{ old('type', $feed->type) === 'rss' ? 'selected' : '' }}>RSS Feed</option>
|
||||||
|
</select>
|
||||||
|
@error('type')
|
||||||
|
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="language" class="block text-sm font-medium text-gray-700">Language</label>
|
||||||
|
<select name="language"
|
||||||
|
id="language"
|
||||||
|
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm @error('language') border-red-300 @enderror">
|
||||||
|
<option value="en" {{ old('language', $feed->language) === 'en' ? 'selected' : '' }}>English</option>
|
||||||
|
<option value="nl" {{ old('language', $feed->language) === 'nl' ? 'selected' : '' }}>Dutch</option>
|
||||||
|
<option value="fr" {{ old('language', $feed->language) === 'fr' ? 'selected' : '' }}>French</option>
|
||||||
|
<option value="de" {{ old('language', $feed->language) === 'de' ? 'selected' : '' }}>German</option>
|
||||||
|
<option value="es" {{ old('language', $feed->language) === 'es' ? 'selected' : '' }}>Spanish</option>
|
||||||
|
</select>
|
||||||
|
@error('language')
|
||||||
|
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="description" class="block text-sm font-medium text-gray-700">Description</label>
|
||||||
|
<textarea name="description"
|
||||||
|
id="description"
|
||||||
|
rows="3"
|
||||||
|
class="mt-1 block w-full border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm @error('description') border-red-300 @enderror"
|
||||||
|
placeholder="Optional description of this feed...">{{ old('description', $feed->description) }}</textarea>
|
||||||
|
@error('description')
|
||||||
|
<p class="mt-2 text-sm text-red-600">{{ $message }}</p>
|
||||||
|
@enderror
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center">
|
||||||
|
<input type="checkbox"
|
||||||
|
name="is_active"
|
||||||
|
id="is_active"
|
||||||
|
value="1"
|
||||||
|
{{ old('is_active', $feed->is_active) ? 'checked' : '' }}
|
||||||
|
class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded">
|
||||||
|
<label for="is_active" class="ml-2 block text-sm text-gray-900">
|
||||||
|
Active
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 flex items-center justify-end space-x-3">
|
||||||
|
<a href="{{ route('feeds.index') }}" class="bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
||||||
|
Cancel
|
||||||
|
</a>
|
||||||
|
<button type="submit" class="bg-indigo-600 py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
||||||
|
Update Feed
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
87
resources/views/pages/feeds/index.blade.php
Normal file
87
resources/views/pages/feeds/index.blade.php
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="max-w-7xl mx-auto py-6 sm:px-6 lg:px-8">
|
||||||
|
<div class="px-4 py-6 sm:px-0">
|
||||||
|
<div class="flex justify-between items-center mb-6">
|
||||||
|
<h1 class="text-2xl font-semibold text-gray-900">Feeds</h1>
|
||||||
|
<a href="{{ route('feeds.create') }}" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||||
|
Add New Feed
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if(session('success'))
|
||||||
|
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded mb-4">
|
||||||
|
{{ session('success') }}
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div class="bg-white shadow overflow-hidden sm:rounded-md">
|
||||||
|
@if($feeds->count() > 0)
|
||||||
|
<ul class="divide-y divide-gray-200">
|
||||||
|
@foreach($feeds as $feed)
|
||||||
|
<li>
|
||||||
|
<div class="px-4 py-4 flex items-center justify-between">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="flex-shrink-0">
|
||||||
|
@if($feed->type === 'rss')
|
||||||
|
<i class="fas fa-rss text-orange-500 text-xl"></i>
|
||||||
|
@else
|
||||||
|
<i class="fas fa-globe text-blue-500 text-xl"></i>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<div class="ml-4">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="text-sm font-medium text-gray-900">{{ $feed->name }}</div>
|
||||||
|
<span class="ml-2 inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium
|
||||||
|
{{ $feed->is_active ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800' }}">
|
||||||
|
{{ $feed->is_active ? 'Active' : 'Inactive' }}
|
||||||
|
</span>
|
||||||
|
<span class="ml-2 inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-800">
|
||||||
|
{{ $feed->type_display }}
|
||||||
|
</span>
|
||||||
|
<span class="ml-2 inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
|
||||||
|
{{ strtoupper($feed->language) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-sm text-gray-500">{{ $feed->url }}</div>
|
||||||
|
@if($feed->description)
|
||||||
|
<div class="text-sm text-gray-500 mt-1">{{ Str::limit($feed->description, 100) }}</div>
|
||||||
|
@endif
|
||||||
|
<div class="text-xs text-gray-400 mt-1">{{ $feed->status }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center space-x-2">
|
||||||
|
<a href="{{ route('feeds.show', $feed) }}" class="text-indigo-600 hover:text-indigo-900 text-sm font-medium">
|
||||||
|
View
|
||||||
|
</a>
|
||||||
|
<a href="{{ route('feeds.edit', $feed) }}" class="text-indigo-600 hover:text-indigo-900 text-sm font-medium">
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
<form action="{{ route('feeds.destroy', $feed) }}" method="POST" class="inline"
|
||||||
|
onsubmit="return confirm('Are you sure you want to delete this feed?')">
|
||||||
|
@csrf
|
||||||
|
@method('DELETE')
|
||||||
|
<button type="submit" class="text-red-600 hover:text-red-900 text-sm font-medium">
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
@endforeach
|
||||||
|
</ul>
|
||||||
|
@else
|
||||||
|
<div class="text-center py-12">
|
||||||
|
<i class="fas fa-rss text-gray-400 text-6xl mb-4"></i>
|
||||||
|
<h3 class="text-lg font-medium text-gray-900 mb-2">No feeds yet</h3>
|
||||||
|
<p class="text-gray-500 mb-4">Get started by adding your first content feed.</p>
|
||||||
|
<a href="{{ route('feeds.create') }}" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
|
||||||
|
Add New Feed
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
113
resources/views/pages/feeds/show.blade.php
Normal file
113
resources/views/pages/feeds/show.blade.php
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
@extends('layouts.app')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
<div class="max-w-4xl mx-auto py-6 sm:px-6 lg:px-8">
|
||||||
|
<div class="px-4 py-6 sm:px-0">
|
||||||
|
<div class="mb-6">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h1 class="text-2xl font-semibold text-gray-900">{{ $feed->name }}</h1>
|
||||||
|
<p class="mt-1 text-sm text-gray-600">{{ $feed->type_display }} • {{ strtoupper($feed->language) }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center space-x-3">
|
||||||
|
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium
|
||||||
|
{{ $feed->is_active ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800' }}">
|
||||||
|
{{ $feed->is_active ? 'Active' : 'Inactive' }}
|
||||||
|
</span>
|
||||||
|
<a href="{{ route('feeds.edit', $feed) }}" class="bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-2 px-4 rounded">
|
||||||
|
Edit Feed
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-white shadow overflow-hidden sm:rounded-lg">
|
||||||
|
<div class="px-4 py-5 sm:px-6">
|
||||||
|
<h3 class="text-lg leading-6 font-medium text-gray-900">Feed Details</h3>
|
||||||
|
<p class="mt-1 max-w-2xl text-sm text-gray-500">Information about this content feed.</p>
|
||||||
|
</div>
|
||||||
|
<div class="border-t border-gray-200 px-4 py-5 sm:px-6">
|
||||||
|
<dl class="grid grid-cols-1 gap-x-4 gap-y-6 sm:grid-cols-2">
|
||||||
|
<div>
|
||||||
|
<dt class="text-sm font-medium text-gray-500">Name</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900">{{ $feed->name }}</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<dt class="text-sm font-medium text-gray-500">Type</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900">
|
||||||
|
<div class="flex items-center">
|
||||||
|
@if($feed->type === 'rss')
|
||||||
|
<i class="fas fa-rss text-orange-500 mr-2"></i>
|
||||||
|
@else
|
||||||
|
<i class="fas fa-globe text-blue-500 mr-2"></i>
|
||||||
|
@endif
|
||||||
|
{{ $feed->type_display }}
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<dt class="text-sm font-medium text-gray-500">URL</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900">
|
||||||
|
<a href="{{ $feed->url }}" target="_blank" class="text-indigo-600 hover:text-indigo-500">
|
||||||
|
{{ $feed->url }}
|
||||||
|
<i class="fas fa-external-link-alt ml-1 text-xs"></i>
|
||||||
|
</a>
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<dt class="text-sm font-medium text-gray-500">Language</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900">{{ strtoupper($feed->language) }}</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<dt class="text-sm font-medium text-gray-500">Status</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900">{{ $feed->status }}</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<dt class="text-sm font-medium text-gray-500">Created</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900">{{ $feed->created_at->format('M j, Y g:i A') }}</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if($feed->last_fetched_at)
|
||||||
|
<div>
|
||||||
|
<dt class="text-sm font-medium text-gray-500">Last Fetched</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900">{{ $feed->last_fetched_at->format('M j, Y g:i A') }}</dd>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if($feed->description)
|
||||||
|
<div class="sm:col-span-2">
|
||||||
|
<dt class="text-sm font-medium text-gray-500">Description</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900">{{ $feed->description }}</dd>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 flex items-center justify-between">
|
||||||
|
<a href="{{ route('feeds.index') }}" class="text-indigo-600 hover:text-indigo-500 font-medium">
|
||||||
|
← Back to Feeds
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<div class="flex items-center space-x-3">
|
||||||
|
<a href="{{ route('feeds.edit', $feed) }}" class="bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50">
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
<form action="{{ route('feeds.destroy', $feed) }}" method="POST" class="inline"
|
||||||
|
onsubmit="return confirm('Are you sure you want to delete this feed?')">
|
||||||
|
@csrf
|
||||||
|
@method('DELETE')
|
||||||
|
<button type="submit" class="bg-red-600 py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white hover:bg-red-700">
|
||||||
|
Delete Feed
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
|
|
@ -17,6 +17,10 @@
|
||||||
<i class="fas fa-hashtag mr-3"></i>
|
<i class="fas fa-hashtag mr-3"></i>
|
||||||
Channels
|
Channels
|
||||||
</a>
|
</a>
|
||||||
|
<a href="/feeds" class="flex items-center px-4 py-3 text-gray-300 hover:bg-gray-700 hover:text-white transition-colors {{ request()->is('feeds*') ? 'bg-gray-700 text-white' : '' }}">
|
||||||
|
<i class="fas fa-rss mr-3"></i>
|
||||||
|
Feeds
|
||||||
|
</a>
|
||||||
<a href="/logs" class="flex items-center px-4 py-3 text-gray-300 hover:bg-gray-700 hover:text-white transition-colors {{ request()->is('logs') ? 'bg-gray-700 text-white' : '' }}">
|
<a href="/logs" class="flex items-center px-4 py-3 text-gray-300 hover:bg-gray-700 hover:text-white transition-colors {{ request()->is('logs') ? 'bg-gray-700 text-white' : '' }}">
|
||||||
<i class="fas fa-list mr-3"></i>
|
<i class="fas fa-list mr-3"></i>
|
||||||
Logs
|
Logs
|
||||||
|
|
|
||||||
|
|
@ -25,3 +25,4 @@
|
||||||
Route::post('/platforms/{platformAccount}/set-active', [App\Http\Controllers\PlatformAccountsController::class, 'setActive'])->name('platforms.set-active');
|
Route::post('/platforms/{platformAccount}/set-active', [App\Http\Controllers\PlatformAccountsController::class, 'setActive'])->name('platforms.set-active');
|
||||||
|
|
||||||
Route::resource('channels', App\Http\Controllers\PlatformChannelsController::class)->names('channels');
|
Route::resource('channels', App\Http\Controllers\PlatformChannelsController::class)->names('channels');
|
||||||
|
Route::resource('feeds', App\Http\Controllers\FeedsController::class)->names('feeds');
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue