Skip to content

Instantly share code, notes, and snippets.

@martinbowling
Created September 4, 2024 11:34
Show Gist options
  • Save martinbowling/d87e490c732e918d0b6499542a573f47 to your computer and use it in GitHub Desktop.
Save martinbowling/d87e490c732e918d0b6499542a573f47 to your computer and use it in GitHub Desktop.
cursor cast 09-04-2024 updates

1. Each Video has its Own Page

  1. 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();
    });
  2. Route Changes:

    Route::get('/video/{id}', [VideoController::class, 'show'])->name('video.show');
  3. Controller (VideoController.php):

    public function show($id)
    {
        $video = Video::findOrFail($id);
        return view('videos.show', compact('video'));
    }
  4. Blade View (resources/views/videos/show.blade.php):

    <div>
        <h1>{{ $video->title }}</h1>
        <video src="{{ $video->url }}" controls></video>
        <p>{{ $video->description }}</p>
    </div>

2. Create Sections (Basics, Prompt Files, UI, Tips)

  1. Database Changes: Ensure you have a sections table.

    Schema::create('sections', function (Blueprint $table) {
        $table->id();
        $table->string('name'); // section name
        $table->timestamps();
    });
  2. Model Changes:

    • Video.php:

      public function section()
      {
          return $this->belongsTo(Section::class);
      }
    • Section.php:

      public function videos()
      {
          return $this->hasMany(Video::class);
      }
  3. Route:

    Route::get('/section/{id}', [SectionController::class, 'show'])->name('section.show');
  4. Controller (SectionController.php):

    public function show($id)
    {
        $section = Section::with('videos')->findOrFail($id);
        return view('sections.show', compact('section'));
    }
  5. 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

3. Auto-Play the Next Video in Section (or Next Section)

  1. Database: Ensure videos are ordered within each section by adding a position field in the videos table:

    Schema::table('videos', function (Blueprint $table) {
        $table->integer('position')->default(0); // Position in section
    });
  2. JavaScript (Alpine.js): In the video player, you can add an @ended event to handle auto-playing the next video.

  3. Blade View:

    <video @ended="nextVideo" src="{{ $video->url }}" controls></video>
    <script>
        function nextVideo() {
            window.location.href = "{{ route('video.show', $nextVideoId) }}";
        }
    </script>
  4. 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'));
    }
  5. Error Handling: The error handling for when there's no next video is included in the controller logic above.

4. Mark Lesson/Video as Complete (Gamification)

  1. 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();
    });
  2. Livewire Component (MarkComplete.php):

    public function markComplete($videoId)
    {
        UserVideoProgress::updateOrCreate(
            ['user_id' => auth()->id(), 'video_id' => $videoId],
            ['completed' => true]
        );
    }
  3. Blade View:

    <button wire:click="markComplete({{ $video->id }})">Mark as Complete</button>
  4. 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;
    }

5. Add Comments to Build Community

  1. 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();
    });
  2. Livewire Component (Comments.php):

    public $commentText;
    public function addComment($videoId)
    {
        Comment::create([
            'user_id' => auth()->id(),
            'video_id' => $videoId,
            'comment_text' => $this->commentText,
        ]);
    }
  3. 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
  4. Comment Moderation: Implement a basic moderation system:

    public function moderateComment($commentId)
    {
        $comment = Comment::findOrFail($commentId);
        $comment->is_approved = !$comment->is_approved;
        $comment->save();
    }

6. Downloadable Code to Follow Along

  1. Database: Add a code_file field to the videos table.

    Schema::table('videos', function (Blueprint $table) {
        $table->string('code_file')->nullable();
    });
  2. Blade View:

    <a href="{{ asset('storage/' . $video->code_file) }}" download>Download Code</a>

7. Chat with the Videos Using Your Own API Key (ChatGPT/Claude)

  1. 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>
  2. 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();
    }
  3. API Key Security: Ensure the API key is not stored in the database. Use session storage or encrypt it if necessary.

8. Directory of .cursorRules Files

  1. 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();
    });
  2. Blade View:

    @foreach($cursorRules as $rule)
        <a href="{{ asset('storage/' . $rule->file_name) }}" download>{{ $rule->file_name }}</a>
    @endforeach

9. Directory of Prompts for Different Languages/Frameworks

  1. 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();
    });
  2. Blade View:

    @foreach($prompts as $prompt)
        <div>
            <h4>{{ $prompt->name }} ({{ $prompt->language }})</h4>
            <p>{{ $prompt->prompt }}</p>
        </div>
    @endforeach
  3. 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'));
    }
  4. Route: Define a route to access the prompt directory.

    Route::get('/prompts', [PromptController::class, 'index'])->name('prompts.index');

10. Full Over-the-Shoulder Advanced App Builds

  1. Database: Ensure the videos table has a field for indicating whether a video belongs to an "advanced app build" series, such as a boolean is_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');
    });
  2. 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>
  3. 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'));
    }
  4. Route: Define a route to access the advanced app builds.

    Route::get('/advanced-builds', [VideoController::class, 'advancedBuilds'])->name('advanced.builds');
  5. 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;
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment