-
Database: Ensure you have a
videos
table with fields like:Schema::create('videos', function (Blueprint $table) { $table->id(); $table->string('title'); $table->text('description'); $table->string('url'); // Link to video file $table->foreignId('section_id')->constrained()->onDelete('cascade'); $table->timestamps(); });
-
Route Changes:
Route::get('/video/{id}', [VideoController::class, 'show'])->name('video.show');
-
Controller (
VideoController.php
):public function show($id) { $video = Video::findOrFail($id); return view('videos.show', compact('video')); }
-
Blade View (
resources/views/videos/show.blade.php
):<div> <h1>{{ $video->title }}</h1> <video src="{{ $video->url }}" controls></video> <p>{{ $video->description }}</p> </div>
-
Database Changes: Ensure you have a
sections
table.Schema::create('sections', function (Blueprint $table) { $table->id(); $table->string('name'); // section name $table->timestamps(); });
-
Model Changes:
-
Video.php
:public function section() { return $this->belongsTo(Section::class); }
-
Section.php
:public function videos() { return $this->hasMany(Video::class); }
-
-
Route:
Route::get('/section/{id}', [SectionController::class, 'show'])->name('section.show');
-
Controller (
SectionController.php
):public function show($id) { $section = Section::with('videos')->findOrFail($id); return view('sections.show', compact('section')); }
-
Blade View (
resources/views/sections/show.blade.php
):<h1>{{ $section->name }}</h1> @foreach($section->videos as $video) <a href="{{ route('video.show', $video->id) }}">{{ $video->title }}</a> @endforeach
-
Database: Ensure videos are ordered within each section by adding a
position
field in thevideos
table:Schema::table('videos', function (Blueprint $table) { $table->integer('position')->default(0); // Position in section });
-
JavaScript (Alpine.js): In the video player, you can add an
@ended
event to handle auto-playing the next video. -
Blade View:
<video @ended="nextVideo" src="{{ $video->url }}" controls></video> <script> function nextVideo() { window.location.href = "{{ route('video.show', $nextVideoId) }}"; } </script>
-
Controller Logic: Update the
show
method in the controller to calculate the next video in sequence:public function show($id) { $video = Video::findOrFail($id); $nextVideo = Video::where('section_id', $video->section_id) ->where('position', '>', $video->position) ->orderBy('position', 'asc') ->first(); if (!$nextVideo) { $nextSection = Section::where('id', '>', $video->section_id)->first(); if ($nextSection) { $nextVideo = $nextSection->videos()->orderBy('position', 'asc')->first(); } } return view('videos.show', compact('video', 'nextVideo')); }
-
Error Handling: The error handling for when there's no next video is included in the controller logic above.
-
Database: Create a
user_video_progress
table to track completed videos.Schema::create('user_video_progress', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained()->onDelete('cascade'); $table->foreignId('video_id')->constrained()->onDelete('cascade'); $table->boolean('completed')->default(false); $table->timestamps(); });
-
Livewire Component (
MarkComplete.php
):public function markComplete($videoId) { UserVideoProgress::updateOrCreate( ['user_id' => auth()->id(), 'video_id' => $videoId], ['completed' => true] ); }
-
Blade View:
<button wire:click="markComplete({{ $video->id }})">Mark as Complete</button>
-
Progress Tracking: Add a method to calculate overall progress:
public function getOverallProgress() { $totalVideos = Video::count(); $completedVideos = UserVideoProgress::where('user_id', auth()->id()) ->where('completed', true) ->count(); return ($completedVideos / $totalVideos) * 100; }
-
Database: Create a
comments
table:Schema::create('comments', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained()->onDelete('cascade'); $table->foreignId('video_id')->constrained()->onDelete('cascade'); $table->text('comment_text'); $table->boolean('is_approved')->default(false); $table->timestamps(); });
-
Livewire Component (
Comments.php
):public $commentText; public function addComment($videoId) { Comment::create([ 'user_id' => auth()->id(), 'video_id' => $videoId, 'comment_text' => $this->commentText, ]); }
-
Blade View:
<form wire:submit.prevent="addComment({{ $video->id }})"> <textarea wire:model="commentText"></textarea> <button type="submit">Submit</button> </form> @foreach($video->comments as $comment) <p>{{ $comment->user->name }}: {{ $comment->comment_text }}</p> @endforeach
-
Comment Moderation: Implement a basic moderation system:
public function moderateComment($commentId) { $comment = Comment::findOrFail($commentId); $comment->is_approved = !$comment->is_approved; $comment->save(); }
-
Database: Add a
code_file
field to thevideos
table.Schema::table('videos', function (Blueprint $table) { $table->string('code_file')->nullable(); });
-
Blade View:
<a href="{{ asset('storage/' . $video->code_file) }}" download>Download Code</a>
-
Frontend (Alpine.js): Add an input for the API key and textarea for chatting.
<input type="text" placeholder="Enter API Key" x-model="apiKey"> <textarea x-model="chatInput"></textarea> <button @click="sendMessage">Send Message</button>
-
Backend (Livewire): In the Livewire component, handle the API call with the provided API key:
public function sendMessage($apiKey, $message) { $response = Http::withHeaders([ 'Authorization' => "Bearer $apiKey" ])->post('https://api.openai.com/v1/completions', [ 'prompt' => $message, ]); return $response->json(); }
-
API Key Security: Ensure the API key is not stored in the database. Use session storage or encrypt it if necessary.
-
Database: Store
.cursorRules
files in a dedicated table or as part of the existing file storage setup.Schema::create('cursor_rules', function (Blueprint $table) { $table->id(); $table->string('file_name'); $table->timestamps(); });
-
Blade View:
@foreach($cursorRules as $rule) <a href="{{ asset('storage/' . $rule->file_name) }}" download>{{ $rule->file_name }}</a> @endforeach
-
Database: Create a table to store prompts for various languages/frameworks.
Schema::create('prompts', function (Blueprint $table) { $table->id(); $table->string('name'); $table->text('prompt'); $table->string('language'); $table->timestamps(); });
-
Blade View:
@foreach($prompts as $prompt) <div> <h4>{{ $prompt->name }} ({{ $prompt->language }})</h4> <p>{{ $prompt->prompt }}</p> </div> @endforeach
-
Controller: Create a controller to list the available prompts and display them on the frontend.
public function index() { $prompts = Prompt::all(); return view('prompts.index', compact('prompts')); }
-
Route: Define a route to access the prompt directory.
Route::get('/prompts', [PromptController::class, 'index'])->name('prompts.index');
-
Database: Ensure the
videos
table has a field for indicating whether a video belongs to an "advanced app build" series, such as a booleanis_advanced_build
flag.Schema::table('videos', function (Blueprint $table) { $table->boolean('is_advanced_build')->default(false); $table->foreignId('series_id')->nullable()->constrained()->onDelete('set null'); });
-
Frontend: Create a page for listing all "over-the-shoulder" advanced app builds.
<div> <h1>Advanced App Builds</h1> @foreach($advancedBuilds as $video) <a href="{{ route('video.show', $video->id) }}">{{ $video->title }}</a> @endforeach </div>
-
Controller: Add logic to the controller to query for advanced app build videos.
public function advancedBuilds() { $advancedBuilds = Video::where('is_advanced_build', true)->get(); return view('videos.advanced', compact('advancedBuilds')); }
-
Route: Define a route to access the advanced app builds.
Route::get('/advanced-builds', [VideoController::class, 'advancedBuilds'])->name('advanced.builds');
-
Series Progression: Implement a way to track progress through a series of videos:
public function getSeriesProgress($seriesId) { $totalVideos = Video::where('series_id', $seriesId)->count(); $completedVideos = UserVideoProgress::where('user_id', auth()->id()) ->whereHas('video', function ($query) use ($seriesId) { $query->where('series_id', $seriesId); }) ->where('completed', true) ->count(); return ($completedVideos / $totalVideos) * 100; }