Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
856df0b
Import common cartridge initial commit
andreia May 20, 2026
b033096
Fix styling
andreia May 20, 2026
ad1ec24
scorm import support
swilla May 21, 2026
d3150dd
wip
swilla May 22, 2026
bfd8f1d
Fix styling
swilla May 22, 2026
6f040bb
Fix common cartridge import review issues
swilla May 28, 2026
c58a0b3
pao
swilla May 28, 2026
7e1575e
Fix CI failures for import cartridge PR
swilla May 28, 2026
4c6b49b
Address remaining Cursor review comments
swilla May 28, 2026
c4a2a7d
Fix Cursor follow-up review issues
swilla May 28, 2026
1e0880d
Address additional Cursor review feedback
swilla May 28, 2026
6043cf4
Address latest Cursor sanitizer feedback
swilla May 28, 2026
2087467
remove double courrses
swilla May 28, 2026
9c51df1
fix cursorbot
swilla May 28, 2026
db90dd9
updates to fix compatiblity for more courses
swilla May 28, 2026
188c2e2
Fix styling
swilla May 28, 2026
b527d8f
Add configurable exit LMS label
swilla Jun 5, 2026
eb0bf65
Enhance CoursesRelationManager for user course assignment.
swilla Jun 8, 2026
66c1262
cursobot knows best
swilla Jun 8, 2026
da1f8dc
Merge pull request #106 from TappNetwork/feature/user-assigned-course…
swilla Jun 8, 2026
2a14830
fixit
swilla Jun 10, 2026
a7ac69e
Fix remaining PR #105 Bugbot review issues.
swilla Jun 10, 2026
092b6f1
fix unique constraint
swilla Jun 17, 2026
0fef48b
Merge branch '4.x' into import-cartridge
swilla Jun 17, 2026
d89fbdf
phpstan
swilla Jun 17, 2026
355b975
Fix remaining Bugbot issues for HTML5 completion and launch step fall…
swilla Jun 17, 2026
c721dc9
Address new Bugbot findings for SCORM commit and cartridge import.
swilla Jun 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"require-dev": {
"filament/upgrade": "^4.0",
"larastan/larastan": "^3.0||^2.9",
"laravel/pao": "^1.0",
"laravel/pint": "^1.14",
"nunomaduro/collision": "^8.1.1||^7.10.0",
"orchestra/testbench": "^11.0.0||^10.0.0",
Expand Down
39 changes: 39 additions & 0 deletions config/filament-lms.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
// Note: This only affects the dashboard. Course pages always use sidebar navigation.
'top_navigation' => false,
'show_exit_lms_link' => true,
'exit_lms_label' => 'Exit LMS',

// If true, users only see courses they are assigned to via lms_course_user. If false, all courses are visible.
'restrict_course_visibility' => false,
Expand All @@ -43,6 +44,12 @@
'email',
],

// Course search columns for relation managers
'course_search_columns' => [
'name',
'external_id',
],

// User name columns for the course progress report. Use ['first_name', 'last_name'] when the
// users table has first_name and last_name, or ['name'] when it has a single name column.
'report_user_name_columns' => ['first_name', 'last_name'],
Expand Down Expand Up @@ -154,4 +161,36 @@
|
*/
'resources' => [],

/*
|--------------------------------------------------------------------------
| Common Cartridge / SCORM import
|--------------------------------------------------------------------------
*/
/*
|--------------------------------------------------------------------------
| Embedded player completion guards
|--------------------------------------------------------------------------
*/
'embedded_player_min_session_seconds' => 90,
'embedded_player_min_session_seconds_html5' => 300,

'common_cartridge_import' => [
'delete_after_success' => true,
// Staging disk for UI uploads and extracted packages. Must be a local filesystem disk
// because ZipArchive requires a filesystem path. Retained SCORM assets use this disk too.
'storage_disk' => 'local',
'storage_directory' => 'filament-lms/cartridge-imports',
'default_import_path' => null,
'retain_extracted_packages' => true,
'packages_directory' => 'lms-scorm-packages',
// Max ZIP upload size in kilobytes (512000 = ~500 MB). Also applied to Livewire temporary
// upload rules. Ensure php.ini upload_max_filesize and post_max_size meet or exceed this.
'max_upload_size_kb' => 512000,
// Livewire temporary upload timeout in minutes for large SCORM packages.
'max_upload_time_minutes' => 10,
],

// Optional path to Node binary for Articulate slide JSON extraction
'node_binary' => null,
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?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::table('lms_courses', function (Blueprint $table): void {
$table->boolean('embedded_player')->default(false)->after('is_private');
$table->string('completion_mode', 32)->default('native')->after('embedded_player');
});
}

public function down(): void
{
Schema::table('lms_courses', function (Blueprint $table): void {
$table->dropColumn(['embedded_player', 'completion_mode']);
});
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?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::table('lms_steps', function (Blueprint $table): void {
$table->string('player_slide_id')->nullable()->after('text');
});
}

public function down(): void
{
Schema::table('lms_steps', function (Blueprint $table): void {
$table->dropColumn('player_slide_id');
});
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?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::table('lms_documents', function (Blueprint $table): void {
$table->string('package_disk')->nullable()->after('name');
$table->string('package_path')->nullable()->after('package_disk');
$table->string('package_launch_path')->nullable()->after('package_path');
});
}

public function down(): void
{
Schema::table('lms_documents', function (Blueprint $table): void {
$table->dropColumn(['package_disk', 'package_path', 'package_launch_path']);
});
}
};
131 changes: 131 additions & 0 deletions dist/filament-lms.css
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,134 @@
.step-material-container iframe[src*=".pdf"] {
min-height: 60vh;
}

/* SCORM / HTML5 interactive packages: use full content width */
.step-material-container--interactive {
width: 100%;
max-width: 100%;
margin: 0;
height: clamp(28rem, 80vh, 64rem);
min-height: 28rem;
}

.step-material-container--interactive iframe {
display: block;
width: 100%;
height: 100%;
border: 0;
}

/* Embedded player: hide Filament sidebar and use full width */
body.lms-embedded-player .fi-sidebar {
display: none !important;
}

body.lms-embedded-player .fi-main-ctn {
margin-inline-start: 0 !important;
}

body.lms-embedded-player .fi-main {
max-width: 100%;
}

/* Knowledge checks: card-style radio options (single-choice Select fields on LMS tests). */
.lms-test-form-wrapper .lms-knowledge-check-radio-field .fi-fo-radio {
counter-reset: lms-mcq-option;
display: flex;
flex-direction: column;
gap: 0.625rem;
width: 100%;
}

.lms-test-form-wrapper .lms-knowledge-check-radio-field .fi-fo-radio-label {
counter-increment: lms-mcq-option;
position: relative;
display: flex;
align-items: stretch;
gap: 0.75rem;
width: 100%;
margin: 0;
padding: 0.75rem 1rem;
border-radius: 0.5rem;
border: 1px solid rgb(229 231 235);
background-color: rgb(249 250 251);
cursor: pointer;
transition:
background-color 0.15s ease,
border-color 0.15s ease,
box-shadow 0.15s ease;
}

.dark .lms-test-form-wrapper .lms-knowledge-check-radio-field .fi-fo-radio-label {
border-color: rgb(55 65 81);
background-color: rgb(31 41 55 / 0.45);
}

.lms-test-form-wrapper .lms-knowledge-check-radio-field .fi-fo-radio-label:has(.fi-radio-input:checked) {
border-color: rgb(251 146 60);
background-color: rgb(255 247 237);
box-shadow: 0 0 0 1px rgb(251 146 60 / 0.35);
}

.dark .lms-test-form-wrapper .lms-knowledge-check-radio-field .fi-fo-radio-label:has(.fi-radio-input:checked) {
border-color: rgb(251 146 60);
background-color: rgb(124 45 18 / 0.28);
}

.lms-test-form-wrapper .lms-knowledge-check-radio-field .fi-fo-radio-label:hover {
border-color: rgb(209 213 219);
}

.dark .lms-test-form-wrapper .lms-knowledge-check-radio-field .fi-fo-radio-label:hover {
border-color: rgb(75 85 99);
}

.lms-test-form-wrapper .lms-knowledge-check-radio-field .fi-fo-radio-label-text {
display: flex;
align-items: center;
flex: 1;
gap: 0.75rem;
min-width: 0;
}

.lms-test-form-wrapper .lms-knowledge-check-radio-field .fi-fo-radio-label-text::before {
content: counter(lms-mcq-option, upper-alpha);
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
border-radius: 0.375rem;
font-size: 0.875rem;
font-weight: 700;
background-color: rgb(209 213 219);
color: rgb(255 255 255);
}

.dark .lms-test-form-wrapper .lms-knowledge-check-radio-field .fi-fo-radio-label-text::before {
background-color: rgb(75 85 99);
}

.lms-test-form-wrapper .lms-knowledge-check-radio-field .fi-fo-radio-label:has(.fi-radio-input:checked) .fi-fo-radio-label-text::before {
background-color: rgb(249 115 22);
color: rgb(255 255 255);
}

.lms-test-form-wrapper .lms-knowledge-check-radio-field .fi-radio-input {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}

.lms-test-form-wrapper .lms-knowledge-check-radio-field .fi-fo-radio-label-text p {
margin: 0;
font-size: 0.9375rem;
line-height: 1.45;
}
Loading
Loading