new forms added for courses

This commit is contained in:
HolgerHatGarKeineNode
2023-02-27 18:37:23 +01:00
parent d1d0d5ce3d
commit ce972e66c6
28 changed files with 612 additions and 49 deletions

View File

@@ -0,0 +1,74 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Course;
use App\Models\Lecturer;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
class CourseController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
return Course::query()
->select('id', 'name', )
->orderBy('name')
->when($request->has('user_id'),
fn(Builder $query) => $query->where('created_by', $request->user_id))
->when(
$request->search,
fn (Builder $query) => $query
->where('name', 'ilike', "%{$request->search}%")
)
->when(
$request->exists('selected'),
fn (Builder $query) => $query->whereIn('id',
$request->input('selected', [])),
fn (Builder $query) => $query->limit(10)
)
->get()
->map(function (Course $course) {
$course->image = $course->getFirstMediaUrl('logo',
'thumb');
return $course;
});
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*/
public function show(Course $course)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Course $course)
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Course $course)
{
//
}
}

View File

@@ -3,6 +3,7 @@
namespace App\Http\Livewire\City\Form; namespace App\Http\Livewire\City\Form;
use App\Models\City; use App\Models\City;
use Illuminate\Validation\Rule;
use Livewire\Component; use Livewire\Component;
class CityForm extends Component class CityForm extends Component
@@ -21,7 +22,11 @@ class CityForm extends Component
{ {
return [ return [
'city.country_id' => 'required', 'city.country_id' => 'required',
'city.name' => 'required|string', 'city.name' => [
'required',
Rule::unique('cities', 'name')
->ignore($this->city),
],
'city.longitude' => 'required', 'city.longitude' => 'required',
'city.latitude' => 'required', 'city.latitude' => 'required',
]; ];

View File

@@ -47,7 +47,6 @@ class ContentCreatorForm extends Component
$this->lecturer = new Lecturer([ $this->lecturer = new Lecturer([
'intro' => '', 'intro' => '',
'active' => true, 'active' => true,
'team_id' => true,
]); ]);
} }
if (!$this->fromUrl) { if (!$this->fromUrl) {

View File

@@ -33,7 +33,11 @@ class MeetupForm extends Component
'image' => [Rule::requiredIf(!$this->meetup->id), 'nullable', 'mimes:jpeg,png,jpg,gif', 'max:10240'], 'image' => [Rule::requiredIf(!$this->meetup->id), 'nullable', 'mimes:jpeg,png,jpg,gif', 'max:10240'],
'meetup.city_id' => 'required', 'meetup.city_id' => 'required',
'meetup.name' => 'required', 'meetup.name' => [
'required',
Rule::unique('meetups', 'name')
->ignore($this->meetup),
],
'meetup.community' => 'required', 'meetup.community' => 'required',
'meetup.telegram_link' => 'string|nullable|required_without_all:meetup.webpage,meetup.nostr,meetup.twitter_username,meetup.matrix_group', 'meetup.telegram_link' => 'string|nullable|required_without_all:meetup.webpage,meetup.nostr,meetup.twitter_username,meetup.matrix_group',
'meetup.intro' => 'string|nullable', 'meetup.intro' => 'string|nullable',

View File

@@ -0,0 +1,67 @@
<?php
namespace App\Http\Livewire\School\Form;
use App\Models\Course;
use App\Models\CourseEvent;
use Livewire\Component;
use Livewire\WithFileUploads;
use WireUi\Traits\Actions;
class CourseEventForm extends Component
{
use WithFileUploads;
use Actions;
public ?CourseEvent $courseEvent = null;
public ?string $fromUrl = '';
public $image;
protected $queryString = [
'fromUrl' => [
'except' => null,
],
];
public function rules()
{
return [
'courseEvent.course_id' => 'required',
'courseEvent.venue_id' => 'required',
'courseEvent.from' => 'required',
'courseEvent.to' => 'required',
'courseEvent.link' => 'required',
];
}
public function mount()
{
if (!$this->courseEvent) {
$this->courseEvent = new CourseEvent([]);
} elseif (
!auth()
->user()
->can('update', $this->courseEvent)
) {
abort(403);
}
if (!$this->fromUrl) {
$this->fromUrl = url()->previous();
}
}
public function submit()
{
$this->validate();
$this->courseEvent->save();
return redirect($this->fromUrl);
}
public function render()
{
return view('livewire.school.form.course-event-form');
}
}

View File

@@ -0,0 +1,103 @@
<?php
namespace App\Http\Livewire\School\Form;
use App\Models\Course;
use App\Models\Tag;
use Illuminate\Validation\Rule;
use Livewire\Component;
use Livewire\WithFileUploads;
use WireUi\Traits\Actions;
class CourseForm extends Component
{
use WithFileUploads;
use Actions;
public ?Course $course = null;
public ?string $fromUrl = '';
public $image;
public array $selectedTags = [];
protected $queryString = [
'fromUrl' => [
'except' => null,
],
];
public function rules()
{
return [
'image' => [Rule::requiredIf(!$this->course->id), 'nullable', 'mimes:jpeg,png,jpg,gif', 'max:10240'],
'course.lecturer_id' => 'required',
'course.name' => [
'required',
Rule::unique('courses', 'name')
->ignore($this->course),
],
'course.description' => 'string|nullable',
];
}
public function mount()
{
if (!$this->course) {
$this->course = new Course([
'description' => '',
]);
} elseif (
!auth()
->user()
->can('update', $this->course)
) {
abort(403);
}
if (!$this->fromUrl) {
$this->fromUrl = url()->previous();
}
}
public function submit()
{
$this->validate();
$this->course->save();
$this->course->syncTagsWithType(
$this->selectedTags,
'course'
);
if ($this->image) {
$this->course->addMedia($this->image)
->usingFileName(md5($this->image->getClientOriginalName()).'.'.$this->image->getClientOriginalExtension())
->toMediaCollection('logo');
}
return redirect($this->fromUrl);
}
public function selectTag($name)
{
$selectedTags = collect($this->selectedTags);
if ($selectedTags->contains($name)) {
$selectedTags = $selectedTags->filter(fn($tag) => $tag !== $name);
} else {
$selectedTags->push($name);
}
$this->selectedTags = $selectedTags->values()
->toArray();
}
public function render()
{
return view('livewire.school.form.course-form', [
'tags' => Tag::query()
->where('type', 'course')
->get(),
]);
}
}

View File

@@ -18,7 +18,7 @@ class CourseTable extends DataTableComponent
public function configure(): void public function configure(): void
{ {
$this->setPrimaryKey('id') $this->setPrimaryKey('id')
->setAdditionalSelects(['id']) ->setAdditionalSelects(['courses.id', 'courses.created_by'])
->setThAttributes(function (Column $column) { ->setThAttributes(function (Column $column) {
return [ return [
'class' => 'px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider dark:bg-gray-800 dark:text-gray-400', 'class' => 'px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider dark:bg-gray-800 dark:text-gray-400',
@@ -101,7 +101,7 @@ class CourseTable extends DataTableComponent
'tags', 'tags',
]) ])
->withCount([ ->withCount([
'courseEvents', 'courseEvents' => fn($query) => $query->where('course_events.from', '>', now()),
]) ])
->whereHas('courseEvents.venue.city.country', ->whereHas('courseEvents.venue.city.country',
fn ($query) => $query->where('countries.code', $this->country)) fn ($query) => $query->where('countries.code', $this->country))

View File

@@ -29,6 +29,7 @@ class EventTable extends DataTableComponent
->setDefaultSort('from', 'asc') ->setDefaultSort('from', 'asc')
->setAdditionalSelects([ ->setAdditionalSelects([
'course_events.id', 'course_events.id',
'course_events.created_by',
'course_id', 'course_id',
'venue_id', 'venue_id',
]) ])

View File

@@ -23,31 +23,25 @@ class Lecturer extends Model implements HasMedia
/** /**
* The attributes that aren't mass assignable. * The attributes that aren't mass assignable.
*
* @var array * @var array
*/ */
protected $guarded = []; protected $guarded = [];
/** /**
* The attributes that should be cast to native types. * The attributes that should be cast to native types.
*
* @var array * @var array
*/ */
protected $casts = [ protected $casts = [
'id' => 'integer', 'id' => 'integer',
'team_id' => 'integer', 'active' => 'boolean',
'active' => 'boolean',
]; ];
protected static function booted() protected static function booted()
{ {
static::creating(function ($model) { static::creating(function ($model) {
if (! $model->created_by) { if (!$model->created_by) {
$model->created_by = auth()->id(); $model->created_by = auth()->id();
} }
if (! $model->team_id) {
$model->team_id = auth()->user()->current_team_id;
}
}); });
} }

View File

@@ -42,6 +42,13 @@ class Course extends Resource
'name', 'name',
]; ];
public static $with = [
'lecturer.team',
'categories',
'tags',
'createdBy',
];
public static function label() public static function label()
{ {
return __('Course'); return __('Course');
@@ -55,7 +62,7 @@ class Course extends Resource
public static function relatableLecturers(NovaRequest $request, $query, Field $field) public static function relatableLecturers(NovaRequest $request, $query, Field $field)
{ {
if ($field instanceof BelongsTo) { if ($field instanceof BelongsTo) {
$query->where('team_id', $request->user()->current_team_id); $query->where('created_by', $request->user()->id);
} }
return $query; return $query;

View File

@@ -31,6 +31,12 @@ class CourseEvent extends Resource
'course.name', 'course.name',
]; ];
public static $with = [
'course.lecturer.team',
'venue',
'createdBy',
];
public function title() public function title()
{ {
return $this->from.' - '.$this->venue->name.' - '.$this->course->name; return $this->from.' - '.$this->venue->name.' - '.$this->course->name;

View File

@@ -39,7 +39,6 @@ class Episode extends Resource
if ($request->tags) { if ($request->tags) {
if ($model->data['link']) { if ($model->data['link']) {
$lecturer = \App\Models\Lecturer::updateOrCreate(['name' => $model->podcast->title], [ $lecturer = \App\Models\Lecturer::updateOrCreate(['name' => $model->podcast->title], [
'team_id' => 1,
'active' => true, 'active' => true,
'website' => $model->podcast->link, 'website' => $model->podcast->link,
]); ]);

View File

@@ -47,7 +47,7 @@ class CoursePolicy extends BasePolicy
*/ */
public function update(User $user, Course $course): bool public function update(User $user, Course $course): bool
{ {
return $user->belongsToTeam($course->lecturer->team) || $user->can((new \ReflectionClass($this))->getShortName().'.'.__FUNCTION__); return $course->created_by === $user->id || $user->can((new \ReflectionClass($this))->getShortName().'.'.__FUNCTION__);
} }
/** /**

View File

@@ -0,0 +1,26 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('lecturers', function (Blueprint $table) {
$table->dropForeign('lecturers_team_id_foreign');
$table->dropColumn('team_id');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
//
}
};

View File

@@ -161,25 +161,21 @@ class DatabaseSeeder extends Seeder
'created_by' => 1, 'created_by' => 1,
]); ]);
Lecturer::create([ Lecturer::create([
'team_id' => 1,
'name' => 'Markus Turm', 'name' => 'Markus Turm',
'active' => true, 'active' => true,
'created_by' => 1, 'created_by' => 1,
]); ]);
Lecturer::create([ Lecturer::create([
'team_id' => 1,
'name' => 'Beppo', 'name' => 'Beppo',
'active' => true, 'active' => true,
'created_by' => 1, 'created_by' => 1,
]); ]);
Lecturer::create([ Lecturer::create([
'team_id' => 1,
'name' => 'Helper', 'name' => 'Helper',
'active' => true, 'active' => true,
'created_by' => 1, 'created_by' => 1,
]); ]);
Lecturer::create([ Lecturer::create([
'team_id' => 1,
'name' => 'Gigi', 'name' => 'Gigi',
'active' => true, 'active' => true,
'created_by' => 1, 'created_by' => 1,

View File

@@ -1,5 +1,5 @@
<div class="w-full mb-4 md:w-auto md:mb-0"> <div class="w-full mb-4 md:w-auto md:mb-0">
<x-button href="/nova/resources/course-events"> <x-button xs :href="route('course.form.courseEvent')">
<i class="fa fa-thin fa-plus"></i> <i class="fa fa-thin fa-plus"></i>
{{ __('Register course date') }} {{ __('Register course date') }}
</x-button> </x-button>

View File

@@ -1,5 +1,23 @@
<div> <div class="flex flex-col space-y-1">
<x-button amber wire:click="courseSearch({{ $row->id }})"> @if($row->course_events_count > 0)
{{ __('Show dates') }} <div>
</x-button> <x-button xs amber wire:click="courseSearch({{ $row->id }})">
{{ __('Show dates') }} [{{ $row->course_events_count }}]
</x-button>
</div>
@else
<div>
<x-button xs outline wire:click="courseSearch({{ $row->id }})">
{{ __('Show dates') }}
</x-button>
</div>
@endif
@can('update', $row)
<div>
<x-button amber xs :href="route('course.form.course', ['course' => $row])">
<i class="fa fa-thin fa-edit"></i>
{{ __('Edit') }}
</x-buttonamber>
</div>
@endcan
</div> </div>

View File

@@ -1,5 +1,5 @@
<div class="w-full mb-4 md:w-auto md:mb-0"> <div class="w-full mb-4 md:w-auto md:mb-0">
<x-button href="/nova/resources/courses"> <x-button :href="route('course.form.course')">
<i class="fa fa-thin fa-plus"></i> <i class="fa fa-thin fa-plus"></i>
{{ __('Register course') }} {{ __('Register course') }}
</x-button> </x-button>

View File

@@ -1,3 +1,15 @@
<div> <div class="flex flex-col space-y-1">
<x-button class="whitespace-nowrap" amber wire:click="viewHistoryModal({{ $row->id }})">{{ __('Register') }}</x-button> <div>
<x-button xs class="whitespace-nowrap" amber
wire:click="viewHistoryModal({{ $row->id }})">{{ __('Register') }}</x-button>
</div>
@can('update', $row)
<div>
<x-button xs class="whitespace-nowrap" amber
:href="route('course.form.courseEvent', ['courseEvent' => $row])">
<i class="fa-thin fa-edit"></i>
{{ __('Edit') }}
</x-button>
</div>
@endcan
</div> </div>

View File

@@ -98,13 +98,15 @@
</div> </div>
@endif @endif
@if(auth()->check() && auth()->user()->meetups->contains($row)) @if(auth()->check() && auth()->user()->meetups->contains($row))
<x-button <div>
:href="route('meetup.meetup.form', ['meetup' => $row->id, 'country' => $country])" <x-button
xs :href="route('meetup.meetup.form', ['meetup' => $row->id, 'country' => $country])"
amber xs
> amber
<i class="fa fa-thin fa-edit mr-2"></i> >
{{ __('Edit') }} <i class="fa fa-thin fa-edit mr-2"></i>
</x-button> {{ __('Edit') }}
</x-button>
</div>
@endif @endif
</div> </div>

View File

@@ -77,13 +77,13 @@
{{ __('Create venue') }} {{ __('Create venue') }}
</a> </a>
<a href="/nova/resources/courses" <a href="{{ route('course.form.course') }}"
class="flex gap-x-4 py-2 text-sm font-semibold leading-6 text-gray-900"> class="flex gap-x-4 py-2 text-sm font-semibold leading-6 text-gray-900">
<i class="fa-thin fa-plus flex-none text-gray-400"></i> <i class="fa-thin fa-plus flex-none text-gray-400"></i>
{{ __('Register course') }} {{ __('Register course') }}
</a> </a>
<a href="/nova/resources/course-events" <a href="{{ route('course.form.courseEvent') }}"
class="flex gap-x-4 py-2 text-sm font-semibold leading-6 text-gray-900"> class="flex gap-x-4 py-2 text-sm font-semibold leading-6 text-gray-900">
<i class="fa-thin fa-plus flex-none text-gray-400"></i> <i class="fa-thin fa-plus flex-none text-gray-400"></i>
{{ __('Register course date') }} {{ __('Register course date') }}

View File

@@ -16,7 +16,7 @@
@endif @endif
</div> </div>
<div> <div>
<x-button :href="route('meetup.table.meetupEvent', ['country' => $country])"> <x-button :href="$fromUrl">
<i class="fa fa-thin fa-arrow-left"></i> <i class="fa fa-thin fa-arrow-left"></i>
{{ __('Back') }} {{ __('Back') }}
</x-button> </x-button>

View File

@@ -9,7 +9,7 @@
<div class="flex flex-row space-x-2 items-center"> <div class="flex flex-row space-x-2 items-center">
<div> <div>
<x-button :href="route('meetup.table.meetup', ['country' => $country])"> <x-button :href="$fromUrl">
<i class="fa fa-thin fa-arrow-left"></i> <i class="fa fa-thin fa-arrow-left"></i>
{{ __('Back') }} {{ __('Back') }}
</x-button> </x-button>

View File

@@ -24,7 +24,7 @@
@endif @endif
</div> </div>
<div> <div>
<x-button :href="route('article.overview', ['country' => null])"> <x-button :href="$fromUrl">
<i class="fa fa-thin fa-arrow-left"></i> <i class="fa fa-thin fa-arrow-left"></i>
{{ __('Back') }} {{ __('Back') }}
</x-button> </x-button>
@@ -53,14 +53,14 @@
wire:model="libraryItem.lecturer_id" wire:model="libraryItem.lecturer_id"
:searchable="true" :searchable="true"
:async-data="[ :async-data="[
'api' => route('api.lecturers.index'), 'api' => route('api.lecturers.index'),
'method' => 'GET', // default is GET 'method' => 'GET', // default is GET
'params' => ['user_id' => auth()->id()], // default is [] 'params' => ['user_id' => auth()->id()], // default is []
]" ]"
:template="[ :template="[
'name' => 'user-option', 'name' => 'user-option',
'config' => ['src' => 'image'] 'config' => ['src' => 'image']
]" ]"
option-label="name" option-label="name"
option-value="id" option-value="id"
/> />

View File

@@ -0,0 +1,127 @@
<div>
{{-- HEADER --}}
<livewire:frontend.header :country="null"/>
<div class="container p-4 mx-auto bg-21gray my-2">
<div class="pb-5 flex flex-row justify-between">
<h3 class="text-lg font-medium leading-6 text-gray-200">{{ __('Course Event') }}</h3>
<div class="flex flex-row space-x-2 items-center">
<div>
<x-button :href="$fromUrl">
<i class="fa fa-thin fa-arrow-left"></i>
{{ __('Back') }}
</x-button>
</div>
</div>
</div>
<form class="space-y-8 divide-y divide-gray-700 pb-24">
<div class="space-y-8 divide-y divide-gray-700 sm:space-y-5">
<div class="mt-6 sm:mt-5 space-y-6 sm:space-y-5">
<x-input.group :for="md5('course_id')" :label="__('Course')">
<x-slot name="label">
<div class="flex flex-row space-x-4 items-center">
<div>
{{ __('Course') }}
</div>
<x-button xs href="{{ route('course.form.course') }}">
<i class="fa fa-thin fa-plus"></i>
{{ __('Register course') }}
</x-button>
</div>
</x-slot>
<x-select
:clearable="false"
autocomplete="off"
wire:model.debounce="courseEvent.course_id"
:placeholder="__('Course')"
:async-data="[
'api' => route('api.courses.index'),
'method' => 'GET', // default is GET
'params' => ['user_id' => auth()->id()], // default is []
]"
:template="[
'name' => 'user-option',
'config' => ['src' => 'image']
]"
option-label="name"
option-value="id"
/>
</x-input.group>
<x-input.group :for="md5('venue_id')" :label="__('Venue')">
<x-slot name="label">
<div class="flex flex-row space-x-4 items-center">
<div>
{{ __('Venue') }}
</div>
<x-button xs href="{{ route('venue.form') }}">
<i class="fa fa-thin fa-plus"></i>
{{ __('Create venue') }}
</x-button>
</div>
</x-slot>
<x-select
:clearable="false"
autocomplete="off"
wire:model.debounce="courseEvent.venue_id"
:placeholder="__('Venue')"
:async-data="[
'api' => route('api.venues.index'),
'method' => 'GET', // default is GET
'params' => ['user_id' => auth()->id()], // default is []
]"
:template="[
'name' => 'user-option',
'config' => ['src' => 'flag']
]"
option-label="name"
option-value="id"
/>
</x-input.group>
<x-input.group :for="md5('courseEvent.from')" :label="__('From')">
<x-datetime-picker
:clearable="false"
time-format="24"
timezone="UTC"
user-timezone="{{ config('app.user-timezone') }}"
autocomplete="off"
wire:model.debounce="courseEvent.from"
display-format="DD-MM-YYYY HH:mm"
:placeholder="__('To')"/>
</x-input.group>
<x-input.group :for="md5('courseEvent.to')" :label="__('To')">
<x-datetime-picker
:clearable="false"
time-format="24"
timezone="UTC"
user-timezone="{{ config('app.user-timezone') }}"
autocomplete="off"
wire:model.debounce="courseEvent.to"
display-format="DD-MM-YYYY HH:mm"
:placeholder="__('To')"/>
</x-input.group>
<x-input.group :for="md5('courseEvent.link')" :label="__('Link')">
<x-input autocomplete="off" wire:model.debounce="courseEvent.link"
:placeholder="__('Link')"/>
</x-input.group>
<x-input.group :for="md5('save')" label="">
<x-button primary wire:click="submit">
<i class="fa fa-thin fa-save"></i>
{{ __('Save') }}
</x-button>
</x-input.group>
</div>
</div>
</form>
</div>
</div>

View File

@@ -0,0 +1,107 @@
<div>
{{-- HEADER --}}
<livewire:frontend.header :country="null"/>
<div class="container p-4 mx-auto bg-21gray my-2">
<div class="pb-5 flex flex-row justify-between">
<h3 class="text-lg font-medium leading-6 text-gray-200">{{ __('Course') }}</h3>
<div class="flex flex-row space-x-2 items-center">
<div>
<x-button :href="$fromUrl">
<i class="fa fa-thin fa-arrow-left"></i>
{{ __('Back') }}
</x-button>
</div>
</div>
</div>
<form class="space-y-8 divide-y divide-gray-700 pb-24">
<div class="space-y-8 divide-y divide-gray-700 sm:space-y-5">
<div class="mt-6 sm:mt-5 space-y-6 sm:space-y-5">
<x-input.group :for="md5('image')" :label="__('Logo')">
<div class="py-4">
@if ($image)
<div class="text-gray-200">{{ __('Preview') }}:</div>
<img class="h-48 object-contain" src="{{ $image->temporaryUrl() }}">
@endif
@if ($course->getFirstMediaUrl('logo'))
<div class="text-gray-200">{{ __('Current picture') }}:</div>
<img class="h-48 object-contain" src="{{ $course->getFirstMediaUrl('logo') }}">
@endif
</div>
<input class="text-gray-200" type="file" wire:model="image">
@error('image') <span class="text-red-500">{{ $message }}</span> @enderror
</x-input.group>
<x-input.group :for="md5('lecturer_id')" :label="__('Lecturer')">
<x-select
:clearable="false"
autocomplete="off"
wire:model.debounce="course.lecturer_id"
:placeholder="__('Lecturer')"
:async-data="[
'api' => route('api.lecturers.index'),
'method' => 'GET', // default is GET
'params' => ['user_id' => auth()->id()], // default is []
]"
:template="[
'name' => 'user-option',
'config' => ['src' => 'image']
]"
option-label="name"
option-value="id"
/>
</x-input.group>
<x-input.group :for="md5('course.name')" :label="__('Name')">
<x-input autocomplete="off" wire:model.debounce="course.name"
:placeholder="__('Name')"
/>
</x-input.group>
<x-input.group :for="md5('selectedTags')" :label="__('Tags')">
<div class="py-2 flex flex-wrap items-center space-x-1">
@foreach($tags as $tag)
<div class="cursor-pointer" wire:key="tag{{ $loop->index }}"
wire:click="selectTag('{{ $tag->name }}')">
@if(collect($selectedTags)->contains($tag->name))
<x-badge
amber>
{{ $tag->name }}
</x-badge>
@else
<x-badge
black>
{{ $tag->name }}
</x-badge>
@endif
</div>
@endforeach
</div>
</x-input.group>
<x-input.group :for="md5('course.description')" :label="__('Description')">
<div
class="text-amber-500 text-xs py-2">{{ __('For images in Markdown, please use eg. Imgur or another provider.') }}</div>
<x-input.simple-mde wire:model.defer="course.description"/>
@error('course.description') <span
class="text-red-500 py-2">{{ $message }}</span> @enderror
</x-input.group>
<x-input.group :for="md5('save')" label="">
<x-button primary wire:click="submit">
<i class="fa fa-thin fa-save"></i>
{{ __('Save') }}
</x-button>
</x-input.group>
</div>
</div>
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/easymde/dist/easymde.min.js"></script>
</div>

View File

@@ -29,6 +29,7 @@ Route::middleware([])
Route::resource('countries', \App\Http\Controllers\Api\CountryController::class); Route::resource('countries', \App\Http\Controllers\Api\CountryController::class);
Route::resource('meetup', \App\Http\Controllers\Api\MeetupController::class); Route::resource('meetup', \App\Http\Controllers\Api\MeetupController::class);
Route::resource('lecturers', \App\Http\Controllers\Api\LecturerController::class); Route::resource('lecturers', \App\Http\Controllers\Api\LecturerController::class);
Route::resource('courses', \App\Http\Controllers\Api\CourseController::class);
Route::resource('cities', \App\Http\Controllers\Api\CityController::class); Route::resource('cities', \App\Http\Controllers\Api\CityController::class);
Route::resource('venues', \App\Http\Controllers\Api\VenueController::class); Route::resource('venues', \App\Http\Controllers\Api\VenueController::class);
Route::resource('languages', \App\Http\Controllers\Api\LanguageController::class); Route::resource('languages', \App\Http\Controllers\Api\LanguageController::class);

View File

@@ -65,6 +65,21 @@ Route::middleware([
->name('form'); ->name('form');
}); });
/*
* Course
* */
Route::middleware([
'auth',
])
->as('course.')
->prefix('/course')
->group(function () {
Route::get('/form/course/{course?}', \App\Http\Livewire\School\Form\CourseForm::class)
->name('form.course');
Route::get('/form/course-event/{courseEvent?}', \App\Http\Livewire\School\Form\CourseEventForm::class)
->name('form.courseEvent');
});
/* /*
* Venue * Venue
* */ * */