diff --git a/app/Models/Course/CourseCategory.php b/app/Models/Course/CourseCategory.php index c451e99..a4ce41b 100644 --- a/app/Models/Course/CourseCategory.php +++ b/app/Models/Course/CourseCategory.php @@ -2,10 +2,12 @@ namespace App\Models\Course; +use App\Models\ResourceLink; use App\Support\Traits\Changeable; use App\Support\Traits\HasUserGeneratedContent; use Illuminate\Database\Eloquent\Concerns\HasTimestamps; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\HasMany; use MorningTrain\Laravel\Fields\Files\Models\File; class CourseCategory extends Model @@ -51,6 +53,11 @@ public function thumbnail() return $this->belongsTo(File::class); } + public function resourceLinks(): HasMany + { + return $this->hasMany(ResourceLink::class)->orderBy('position'); + } + ////////////////////////////////// /// Attributes diff --git a/app/Models/ResourceLink.php b/app/Models/ResourceLink.php new file mode 100644 index 0000000..b066b7b --- /dev/null +++ b/app/Models/ResourceLink.php @@ -0,0 +1,15 @@ +belongsTo(CourseCategory::class); + } +} diff --git a/app/Resources/Api/Backend/Courses/Category.php b/app/Resources/Api/Backend/Courses/Category.php index 78e88eb..c845432 100644 --- a/app/Resources/Api/Backend/Courses/Category.php +++ b/app/Resources/Api/Backend/Courses/Category.php @@ -3,9 +3,12 @@ namespace App\Resources\Api\Backend\Courses; use App\Models\Course\CourseCategory as Model; +use App\Support\Enums\BasicResourceTypes; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Str; +use MorningTrain\Laravel\Fields\Fields\EnumField; use MorningTrain\Laravel\Fields\Fields\Field; +use MorningTrain\Laravel\Fields\Fields\RelationshipField; use MorningTrain\Laravel\Fields\Files\FilesField; use MorningTrain\Laravel\Filters\Filters\Filter; use MorningTrain\Laravel\Filters\Filters\Order; @@ -32,15 +35,21 @@ protected function getFields() FilesField::create('logo')->validates('file|image'), FilesField::create('thumbnail')->validates('file|image'), + + RelationshipField::create('resource_links', true)->fields([ + Field::create('text'), + Field::create('url'), + ])->removeMissing()->setOrderTo('position'), ]; } public function configureReadOperation(Read $operation) { $operation->filters([ - Filter::create()->always(function (Builder $q) { - $q->with('logo'); - $q->with('thumbnail'); + Filter::create()->always(function (Builder $query) { + $query->with('logo'); + $query->with('thumbnail'); + $query->with('resourceLinks'); }), ]); } diff --git a/app/Resources/Api/Courses/Category.php b/app/Resources/Api/Courses/Category.php index f3e1652..d772a08 100644 --- a/app/Resources/Api/Courses/Category.php +++ b/app/Resources/Api/Courses/Category.php @@ -33,8 +33,8 @@ public function operations() public function configureNewestOperation(Index $operation) { $operation->filters([ - Filter::create()->always(function (Builder $q) { - $q->where('active', '=', true); + Filter::create()->always(function (Builder $query) { + $query->where('active', '=', true); }), Order::create() @@ -51,6 +51,7 @@ protected function getFilters() Filter::create()->always(function (Builder $q) { $q->withCount('completedCourses'); $q->withCount('courses'); + $q->with('resourceLinks'); return $q->where('active', '=', true); }), diff --git a/database/factories/ResourceLinkFactory.php b/database/factories/ResourceLinkFactory.php new file mode 100644 index 0000000..72d493c --- /dev/null +++ b/database/factories/ResourceLinkFactory.php @@ -0,0 +1,15 @@ +define(ResourceLink::class, function (Faker $faker) { + return [ + 'url' => $faker->url, + 'text' => $faker->words($faker->numberBetween(1, 3), true), + 'course_category_id' => optional(CourseCategory::query()->inRandomOrder()->first('id'))->id, + ]; +}); diff --git a/database/migrations/2021_09_20_125833_create_resource_links_table.php b/database/migrations/2021_09_20_125833_create_resource_links_table.php new file mode 100644 index 0000000..999c043 --- /dev/null +++ b/database/migrations/2021_09_20_125833_create_resource_links_table.php @@ -0,0 +1,29 @@ +bigIncrements('id'); + $table->string('url'); + $table->string('text'); + $table->integer('position')->default(0); + $table->unsignedBigInteger('course_category_id'); + $table->timestamps(); + + $table->foreign('course_category_id') + ->references('id')->on('course_categories') + ->onDelete('cascade'); + }); + } + + public function down(): void + { + Schema::dropIfExists('resource_links'); + } +} diff --git a/database/seeds/CourseCategorySeeder.php b/database/seeds/CourseCategorySeeder.php index 6e9fde7..9f2b0a8 100644 --- a/database/seeds/CourseCategorySeeder.php +++ b/database/seeds/CourseCategorySeeder.php @@ -12,7 +12,7 @@ class CourseCategorySeeder extends Seeder */ public function run() { - factory(\App\Models\Course\CourseCategory::class, 1)->create([ + $categoryOne = factory(\App\Models\Course\CourseCategory::class, 1)->create([ 'title' => 'Scratch', 'description' => 'Scratch er et gratis værktøj, som du kan bruge til at programmere dine egne interaktive historier, spil og tegnefilm. Hvis du aldrig har prøvet at programmere før, er Scratch et rigtig godt sted at starte, da Scratch er meget begyndervenligt.', 'color' => '#f9d12b', @@ -24,7 +24,11 @@ public function run() }, ]); - factory(\App\Models\Course\CourseCategory::class, 1)->create([ + $categoryOne->first()->resourceLinks()->createMany( + factory(\App\Models\ResourceLink::class, 3)->make()->toArray() + ); + + $categoryTwo = factory(\App\Models\Course\CourseCategory::class, 1)->create([ 'title' => 'Javascript via p5.js', 'description' => 'p5.js er et JavaScript-bibliotek, der gør kodning let og tilgængelig for børn og unge, der interesserer sig for at kodning inden for design.', 'color' => '#F7DF1E', @@ -36,7 +40,11 @@ public function run() }, ]); - factory(\App\Models\Course\CourseCategory::class, 1)->create([ + $categoryTwo->first()->resourceLinks()->createMany( + factory(\App\Models\ResourceLink::class, 1)->make()->toArray() + ); + + $categoryThree = factory(\App\Models\Course\CourseCategory::class, 1)->create([ 'title' => 'Processing.py', 'color' => '#006673', 'active' => false, @@ -48,6 +56,10 @@ public function run() }, ]); + $categoryThree->first()->resourceLinks()->createMany( + factory(\App\Models\ResourceLink::class, 2)->make()->toArray() + ); + factory(\App\Models\Course\CourseCategory::class, 1)->create([ 'title' => 'HTML/CSS', 'description' => 'HTML og CSS er de to kode-sprog, som ligger bag enhver moderne hjemmeside. HTML og CSS sørger for, at tekst, billeder, design osv. ser flot ud i internetbrowseren.', diff --git a/resources/js/pages/backend/courses/categories/Edit.js b/resources/js/pages/backend/courses/categories/Edit.js index 818e934..7331ee0 100644 --- a/resources/js/pages/backend/courses/categories/Edit.js +++ b/resources/js/pages/backend/courses/categories/Edit.js @@ -5,6 +5,10 @@ import EditForm from "@morningtrain/react-crud/layouts/read/EditForm"; import * as Fields from "support/fields"; import Link from "widgets/navigation/Link"; import { Fieldset } from "layouts"; +import React from 'react' +import Text from '../course_resources/Text' +import Video from '../course_resources/Video' +import Questionnaire from '../course_resources/Questionnaire' export default @inject(['router']) @@ -49,6 +53,14 @@ class Edit extends CrudPage { + + + + + + ); diff --git a/resources/js/widgets/animations/worlds/CourseCoverWorld.js b/resources/js/widgets/animations/worlds/CourseCoverWorld.js index 4e1c6d7..00cf017 100644 --- a/resources/js/widgets/animations/worlds/CourseCoverWorld.js +++ b/resources/js/widgets/animations/worlds/CourseCoverWorld.js @@ -1,20 +1,18 @@ -import React from "react"; -import Section from "layouts/Section"; -import {inject} from "@morningtrain/react-decorators"; - -import World from "widgets/animations/worlds/World"; - -import Sky from "widgets/animations/partials/Sky"; -import Water from "widgets/animations/partials/Water"; -import ScratchIsland from "widgets/animations/islands/ScratchIsland"; - -import CoverScrollTransform from "helpers/CoverScrollTransform"; -import {Heading} from "support/displays"; -import WebIsland from "widgets/animations/islands/WebIsland"; -import ProcessingIsland from "widgets/animations/islands/ProcessingIsland"; -import SonicIsland from "widgets/animations/islands/SonicIsland"; -import Breadcrumbs from 'widgets/navigation/Breadcrumbs'; -import WhenModel from 'support/conditionals/WhenModel'; +import React from 'react' +import Section from 'layouts/Section' +import { inject } from '@morningtrain/react-decorators' + +import World from 'widgets/animations/worlds/World' + +import Sky from 'widgets/animations/partials/Sky' +import Water from 'widgets/animations/partials/Water' +import ScratchIsland from 'widgets/animations/islands/ScratchIsland' +import { Heading } from 'support/displays' +import WebIsland from 'widgets/animations/islands/WebIsland' +import ProcessingIsland from 'widgets/animations/islands/ProcessingIsland' +import SonicIsland from 'widgets/animations/islands/SonicIsland' +import Breadcrumbs from 'widgets/navigation/Breadcrumbs' +import WhenModel from 'support/conditionals/WhenModel' @inject(['model']) export default class CourseCoverWorld extends World { diff --git a/resources/js/widgets/courses/CourseLoop.js b/resources/js/widgets/courses/CourseLoop.js index 8a332ff..a88ade4 100644 --- a/resources/js/widgets/courses/CourseLoop.js +++ b/resources/js/widgets/courses/CourseLoop.js @@ -4,10 +4,10 @@ import React from 'react' import Course from 'widgets/courses/Course' import { router } from '@morningtrain/helpers' import * as Filters from 'support/filters' -import * as Auth from '@morningtrain/react-auth' import WaterLine from 'widgets/animations/partials/WaterLine' import CourseCoverWorld from 'widgets/animations/worlds/CourseCoverWorld' import SeaFloorWorld from 'widgets/animations/worlds/SeaFloorWorld' +import CourseResources from './CourseResources' export default class CourseLoop extends Widget { constructor (props) { @@ -22,13 +22,14 @@ export default class CourseLoop extends Widget { return (
-
- - - + +
+ + + +
+
- -
diff --git a/resources/js/widgets/courses/CourseResources.js b/resources/js/widgets/courses/CourseResources.js new file mode 100644 index 0000000..9400c9f --- /dev/null +++ b/resources/js/widgets/courses/CourseResources.js @@ -0,0 +1,23 @@ +import { Iterator } from '@morningtrain/react-resources' +import React from 'react' +import * as Displays from 'support/displays' +import { inject } from '@morningtrain/react-decorators' + +@inject(['model']) +export default class CourseResources extends React.Component { + render () { + return ( +
+
+
+ + + + + +
+
+
+ ) + } +} diff --git a/resources/sass/widgets/animations/worlds/_course-cover-world.scss b/resources/sass/widgets/animations/worlds/_course-cover-world.scss index 7ecec27..504f34c 100644 --- a/resources/sass/widgets/animations/worlds/_course-cover-world.scss +++ b/resources/sass/widgets/animations/worlds/_course-cover-world.scss @@ -6,6 +6,11 @@ } } +.course-resource-links-wrapper { + padding-top: 40px; + background: $c_water_surface; +} + .course-cover-world { position: relative; height: 0; @@ -62,4 +67,4 @@ } } } -} \ No newline at end of file +} diff --git a/resources/sass/widgets/courses/_course-wrap.scss b/resources/sass/widgets/courses/_course-wrap.scss index f3d7f32..a1830cf 100644 --- a/resources/sass/widgets/courses/_course-wrap.scss +++ b/resources/sass/widgets/courses/_course-wrap.scss @@ -17,4 +17,8 @@ &--active { z-index: 2; } -} \ No newline at end of file +} + +.course-resource-links-wrapper + .course-wrap { + padding-top: 40px; +}