adding joined

This commit is contained in:
Marley Rae 2022-04-23 16:25:16 -07:00
parent 13c390c5af
commit 3f9376addb
37 changed files with 1162 additions and 65 deletions

View file

@ -0,0 +1,86 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreCategoryRequest;
use App\Http\Requests\UpdateCategoryRequest;
use App\Models\Category;
class CategoryController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\StoreCategoryRequest $request
* @return \Illuminate\Http\Response
*/
public function store(StoreCategoryRequest $request)
{
//
}
/**
* Display the specified resource.
*
* @param \App\Models\Category $category
* @return \Illuminate\Http\Response
*/
public function show(Category $category)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Category $category
* @return \Illuminate\Http\Response
*/
public function edit(Category $category)
{
//
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\UpdateCategoryRequest $request
* @param \App\Models\Category $category
* @return \Illuminate\Http\Response
*/
public function update(UpdateCategoryRequest $request, Category $category)
{
//
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Category $category
* @return \Illuminate\Http\Response
*/
public function destroy(Category $category)
{
//
}
}

View file

@ -0,0 +1,62 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreJoinedRequest;
use App\Http\Requests\UpdateJoinedRequest;
use App\Models\Category;
use App\Models\Joined;
class JoinedController extends Controller
{
public function __construct()
{
$this->authorizeResource(Joined::class, 'joined');
}
public function index()
{
return view('admin.joined.index');
}
public function create()
{
return view('admin.joined.create')->with([
'categories' => Category::all(),
]);
}
public function store(StoreJoinedRequest $request)
{
$validated = $request->safe()->only([
'categories',
'url',
'subject',
'image',
'approved',
]);
Joined::store($validated);
return redirect()->route('joined.index')->with('success', 'Fanlisting added.');
}
public function show(Joined $joined)
{
//
}
public function edit(Joined $joined)
{
//
}
public function update(UpdateJoinedRequest $request, Joined $joined)
{
//
}
public function destroy(Joined $joined)
{
//
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreCategoryRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return false;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreJoinedRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'categories' => ['required', 'array'],
'categories.*' => [ 'numeric', 'exists:categories,id'],
'url' => ['required', 'url'],
'subject' => ['required', 'string'],
'image' => ['nullable', 'image'],
'approved' => ['nullable', 'boolean'],
];
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UpdateCategoryRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return false;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class UpdateJoinedRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return false;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
//
];
}
}

20
app/Models/Category.php Normal file
View file

@ -0,0 +1,20 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
/* --------------------------------------------------------------------------- relationships ---- */
public function parent()
{
return $this->belongsTo(__CLASS__, 'parent_id');
}
public function joined()
{
return $this->morphedByMany(Joined::class, 'categorizable');
}
}

View file

@ -11,36 +11,35 @@ class Collective extends Authenticatable
{
use HasApiTokens, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
/* @var array<int, string> */
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
/* @var array<int, string> */
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
/* @var array<string, string> */
protected $casts = [
'email_verified_at' => 'datetime',
];
/* --------------------------------------------------------------------------- relationships ---- */
public function joined()
{
return $this->hasMany(Joined::class);
}
/* -------------------------------------------------------------------------------- password ---- */
protected function password() : Attribute

46
app/Models/Joined.php Normal file
View file

@ -0,0 +1,46 @@
<?php
namespace App\Models;
use App\Traits\Categorizable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
class Joined extends Model
{
use HasFactory, Categorizable;
protected $table = 'joined';
protected $casts = [
'approved' => 'boolean',
];
/* --------------------------------------------------------------------------- relationships ---- */
public function collective()
{
return $this->belongsTo(Collective::class);
}
// injected by trait: categories (morph many-to-many)
/* ---------------------------------------------------------------------------------- joined ---- */
public static function store($request) : Joined
{
$joined = new Joined();
$joined->url = $request['url'];
$joined->subject = $request['subject'];
$joined->image = Storage::putFile('joined', $request['image']);
$joined->approved = $request['approved'] ?? false;
$collective = auth_collective();
$collective->joined()->save($joined);
$joined->categories()->sync($request['categories']);
return $joined;
}
}

View file

@ -0,0 +1,95 @@
<?php
namespace App\Policies;
use App\Models\Category;
use App\Models\Collective;
use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Support\Facades\Auth;
class CategoryPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any models.
*
* @param \App\Models\Collective $collective
* @return \Illuminate\Auth\Access\Response|bool
*/
public function viewAny(Collective $collective)
{
return Auth::check();
}
/**
* Determine whether the user can view the model.
*
* @param \App\Models\Collective $collective
* @param \App\Models\Category $category
* @return \Illuminate\Auth\Access\Response|bool
*/
public function view(Collective $collective, Category $category)
{
return Auth::check();
}
/**
* Determine whether the user can create models.
*
* @param \App\Models\Collective $collective
* @return \Illuminate\Auth\Access\Response|bool
*/
public function create(Collective $collective)
{
return Auth::check();
}
/**
* Determine whether the user can update the model.
*
* @param \App\Models\Collective $collective
* @param \App\Models\Category $category
* @return \Illuminate\Auth\Access\Response|bool
*/
public function update(Collective $collective, Category $category)
{
return Auth::check();
}
/**
* Determine whether the user can delete the model.
*
* @param \App\Models\Collective $collective
* @param \App\Models\Category $category
* @return \Illuminate\Auth\Access\Response|bool
*/
public function delete(Collective $collective, Category $category)
{
return Auth::check();
}
/**
* Determine whether the user can restore the model.
*
* @param \App\Models\Collective $collective
* @param \App\Models\Category $category
* @return \Illuminate\Auth\Access\Response|bool
*/
public function restore(Collective $collective, Category $category)
{
return Auth::check();
}
/**
* Determine whether the user can permanently delete the model.
*
* @param \App\Models\Collective $collective
* @param \App\Models\Category $category
* @return \Illuminate\Auth\Access\Response|bool
*/
public function forceDelete(Collective $collective, Category $category)
{
return Auth::check();
}
}

View file

@ -0,0 +1,95 @@
<?php
namespace App\Policies;
use App\Models\Collective;
use App\Models\Joined;
use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Support\Facades\Auth;
class JoinedPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any models.
*
* @param \App\Models\Collective $collective
* @return \Illuminate\Auth\Access\Response|bool
*/
public function viewAny(Collective $collective)
{
return Auth::check();
}
/**
* Determine whether the user can view the model.
*
* @param \App\Models\Collective $collective
* @param \App\Models\Joined $joined
* @return \Illuminate\Auth\Access\Response|bool
*/
public function view(Collective $collective, Joined $joined)
{
return $collective->id === $joined->collective_id;
}
/**
* Determine whether the user can create models.
*
* @param \App\Models\Collective $collective
* @return \Illuminate\Auth\Access\Response|bool
*/
public function create(Collective $collective)
{
return Auth::check();
}
/**
* Determine whether the user can update the model.
*
* @param \App\Models\Collective $collective
* @param \App\Models\Joined $joined
* @return \Illuminate\Auth\Access\Response|bool
*/
public function update(Collective $collective, Joined $joined)
{
return $collective->id === $joined->collective_id;
}
/**
* Determine whether the user can delete the model.
*
* @param \App\Models\Collective $collective
* @param \App\Models\Joined $joined
* @return \Illuminate\Auth\Access\Response|bool
*/
public function delete(Collective $collective, Joined $joined)
{
return $collective->id === $joined->collective_id;
}
/**
* Determine whether the user can restore the model.
*
* @param \App\Models\Collective $collective
* @param \App\Models\Joined $joined
* @return \Illuminate\Auth\Access\Response|bool
*/
public function restore(Collective $collective, Joined $joined)
{
return $collective->id === $joined->collective_id;
}
/**
* Determine whether the user can permanently delete the model.
*
* @param \App\Models\Collective $collective
* @param \App\Models\Joined $joined
* @return \Illuminate\Auth\Access\Response|bool
*/
public function forceDelete(Collective $collective, Joined $joined)
{
return $collective->id === $joined->collective_id;
}
}

View file

@ -2,6 +2,7 @@
namespace App\Providers;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
@ -23,6 +24,10 @@ public function register()
*/
public function boot()
{
//
Relation::enforceMorphMap([
'joined' => 'App\Models\Joined',
'owned' => 'App\Models\Owned',
'wish' => 'App\Models\Wish',
]);
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace App\Traits;
use App\Models\Category;
trait Categorizable
{
public function categories()
{
return $this->morphToMany(Category::class, 'categorizable');
}
}

14
app/helpers.php Normal file
View file

@ -0,0 +1,14 @@
<?php
/* ------------------------------------------------------------------------- auth_collective ---- */
// returns the currently authenticated collective
use App\Models\Collective;
if (!function_exists('auth_collective')) {
function auth_collective() : Collective
{
return auth()->user();
}
}

View file

@ -20,6 +20,9 @@
"spatie/laravel-ignition": "^1.0"
},
"autoload": {
"files": [
"app/helpers.php"
],
"psr-4": {
"App\\": "app/",
"Database\\Factories\\": "database/factories/",

View file

@ -13,7 +13,7 @@
|
*/
'default' => env('FILESYSTEM_DISK', 'local'),
'default' => env('FILESYSTEM_DISK', 'public'),
/*
|--------------------------------------------------------------------------

View file

@ -15,13 +15,14 @@ public function up()
{
Schema::create('collectives', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->string('title');
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('title');
$table->string('password');
$table->integer('per_page')->unsigned()->default(15);
});
}

View file

@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->foreignId('parent_id')
->nullable()
->constrained('categories')
->onUpdate('cascade')
->onDelete('cascade');
$table->string('name');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('categories');
}
};

View file

@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('joined', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->foreignId('collective_id')
->constrained('collectives')
->onUpdate('cascade')
->onDelete('cascade');
$table->string('url');
$table->string('subject');
$table->string('image');
$table->boolean('approved');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('joined');
}
};

View file

@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('categorizable', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->foreignId('category_id')
->constrained('categories')
->onUpdate('cascade')
->onDelete('restrict');
$table->unsignedBigInteger('categorizable_id');
$table->string('categorizable_type');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('categorizable');
}
};

View file

@ -0,0 +1,78 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class CategorySeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
DB::table('categories')->insert([
['name' => 'Academia'],
['name' => 'Actors'],
['name' => 'Actresses'],
['name' => 'Adult'],
['name' => 'Advertising/TV Channels'],
['name' => 'Albums'],
['name' => 'Animals'],
['name' => 'Animation'],
['name' => 'Anime/Manga'],
['name' => 'Arts and Design'],
['name' => 'Authors/Writers'],
['name' => 'Calendar Events'],
['name' => 'Characters: Book/Movie'],
['name' => 'Characters: TV'],
['name' => 'Comics'],
['name' => 'Companies'],
['name' => 'Computer Miscellany and Internet'],
['name' => 'Directors/Producers'],
['name' => 'Episodes'],
['name' => 'Fan Works'],
['name' => 'Fashion/Beauty'],
['name' => 'Food/Drinks'],
['name' => 'Games'],
['name' => 'History/Royalty'],
['name' => 'Hobbies and Recreation'],
['name' => 'Literature'],
['name' => 'Miscellaneous'],
['name' => 'Models'],
['name' => 'Movies'],
['name' => 'Music Miscellany'],
['name' => 'Musicians: Bands/Groups'],
['name' => 'Musicians: Female'],
['name' => 'Musicians: Male'],
['name' => 'Mythology/Religion'],
['name' => 'Nature'],
['name' => 'Objects'],
['name' => 'People Miscellany'],
['name' => 'Personalities'],
['name' => 'Places'],
['name' => 'Politics and Organisations'],
['name' => 'Relationships: Book/Movie'],
['name' => 'Relationships: Real Life'],
['name' => 'Relationships: TV'],
['name' => 'Songs: Bands/Groups 0-M'],
['name' => 'Songs: Bands/Groups N-Z'],
['name' => 'Songs: Female Solo'],
['name' => 'Songs: Male Solo'],
['name' => 'Songs: Various'],
['name' => 'Sports'],
['name' => 'Sports Entertainment'],
['name' => 'Stage/Theatre'],
['name' => 'Toys/Collectibles'],
['name' => 'Transportation'],
['name' => 'TV Shows'],
['name' => 'TV/Movie/Book Miscellany'],
['name' => 'Webmasters'],
['name' => 'Websites'],
]);
}
}

View file

@ -14,6 +14,8 @@ class DatabaseSeeder extends Seeder
*/
public function run()
{
// \App\Models\User::factory(10)->create();
$this->call([
CategorySeeder::class
]);
}
}

View file

@ -1,17 +1,53 @@
@import url(https://fonts.googleapis.com/css2?family=Imprima&family=Satisfy&display=swap);
/* ------------------------------------------------------------------------------------ &BTN ---- */
.form__btn {
.form__input--file::-webkit-file-upload-button {
border: 1px solid #7874ff;
padding: 5px 10px;
margin-right: 5px;
line-height: normal;
cursor: pointer;
border-color: #7874ff;
background-color: #e4e3ff;
transition: background-color 0.4s, border-color 0.4s;
color: #7874ff;
-webkit-transition: background-color 0.4s, border-color 0.4s, color 0.4s;
transition: background-color 0.4s, border-color 0.4s, color 0.4s;
}
.form__btn:hover {
background-color: #f8e5ff;
.l-page-nav__link, .form__input--file::file-selector-button, .form__btn {
border: 1px solid #7874ff;
padding: 5px 10px;
margin-right: 5px;
line-height: normal;
cursor: pointer;
border-color: #7874ff;
background-color: #e4e3ff;
color: #7874ff;
transition: background-color 0.4s, border-color 0.4s, color 0.4s;
}
.form__input--file:hover::-webkit-file-upload-button {
border-color: #de7cff;
transition: background-color 0.4s, border-color 0.4s;
background-color: #f8e5ff;
color: #de7cff;
-webkit-transition: background-color 0.4s, border-color 0.4s, color 0.4s;
transition: background-color 0.4s, border-color 0.4s, color 0.4s;
}
.l-page-nav__link:hover, .form__input--file:hover::file-selector-button, .form__btn:hover {
border-color: #de7cff;
background-color: #f8e5ff;
color: #de7cff;
transition: background-color 0.4s, border-color 0.4s, color 0.4s;
}
.form__input--file:focus::-webkit-file-upload-button {
border-color: #de7cff;
background-color: #f8e5ff;
color: #de7cff;
-webkit-transition: background-color 0.4s, border-color 0.4s, color 0.4s;
transition: background-color 0.4s, border-color 0.4s, color 0.4s;
}
.l-page-nav__link:focus, .form__input--file:focus::file-selector-button, .form__btn:focus {
border-color: #de7cff;
background-color: #f8e5ff;
color: #de7cff;
transition: background-color 0.4s, border-color 0.4s, color 0.4s;
}
html {
@ -44,6 +80,7 @@ h1 {
font-weight: normal;
margin-left: 40px;
margin-bottom: 10px;
margin-top: 0;
display: inline-block;
position: relative;
}
@ -61,31 +98,97 @@ .form__fieldset {
text-align: center;
}
.form__label, .form__btns {
.form__label, .form__btns, .form__checkbox {
display: block;
margin-top: 20px;
margin-bottom: 5px;
}
.form__label:first-of-type, .form__btns:first-of-type {
.form__label:first-child, .form__btns:first-child, .form__checkbox:first-child, .form__label .form__legend + .form__label, .form__btns .form__legend + .form__label, .form__label .form__legend + .form__btns, .form__btns .form__legend + .form__btns, .form__checkbox .form__legend + .form__label, .form__checkbox .form__legend + .form__btns, .form__label .form__legend + .form__checkbox, .form__btns .form__legend + .form__checkbox, .form__checkbox .form__legend + .form__checkbox {
margin-top: 0;
}
.form__input {
.form__input, .form__select {
border: 1px solid #7874ff;
background-color: #e4e3ff;
border-color: #7874ff;
background-color: #e4e3ff;
transition: background-color 0.4s, border-color 0.4s;
color: #7874ff;
transition: background-color 0.4s, border-color 0.4s, color 0.4s;
}
.form__input:hover {
background-color: #f8e5ff;
.form__input:hover, .form__select:hover {
border-color: #de7cff;
transition: background-color 0.4s, border-color 0.4s;
background-color: #f8e5ff;
color: #de7cff;
transition: background-color 0.4s, border-color 0.4s, color 0.4s;
}
.form__input:focus {
background-color: #f8e5ff;
.form__input:focus, .form__select:focus {
border-color: #de7cff;
transition: background-color 0.4s, border-color 0.4s;
background-color: #f8e5ff;
color: #de7cff;
transition: background-color 0.4s, border-color 0.4s, color 0.4s;
}
.form__input--file {
border: 1px solid #7874ff;
border-color: #7874ff;
color: #7874ff;
transition: background-color 0.4s, border-color 0.4s, color 0.4s;
padding: 5px;
}
.form__input--file:hover {
border-color: #de7cff;
color: #de7cff;
transition: background-color 0.4s, border-color 0.4s, color 0.4s;
}
.form__input--file:focus {
border-color: #de7cff;
color: #de7cff;
transition: background-color 0.4s, border-color 0.4s, color 0.4s;
}
.form__input--checkbox {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-color: #e4e3ff;
margin: 0;
margin-top: 5px;
margin-right: 5px;
font: inherit;
color: #7874ff;
width: 15px !important;
height: 15px;
border: 1px solid #7874ff;
display: inline-grid;
place-content: center;
border-color: #7874ff;
background-color: #e4e3ff;
color: #7874ff;
transition: background-color 0.4s, border-color 0.4s, color 0.4s;
}
.form__input--checkbox::before {
content: "";
width: 9px;
height: 9px;
transform: scale(0);
transition: transform 120ms ease-in-out;
box-shadow: inset 9px 9px #7874ff;
-webkit-clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
}
.form__input--checkbox:checked::before {
transform: scale(1);
}
.form__input--checkbox:hover {
border-color: #de7cff;
background-color: #f8e5ff;
color: #de7cff;
transition: background-color 0.4s, border-color 0.4s, color 0.4s;
}
.form__input--checkbox:focus {
border-color: #de7cff;
background-color: #f8e5ff;
color: #de7cff;
transition: background-color 0.4s, border-color 0.4s, color 0.4s;
}
.form__btns:first-of-type {
@ -135,14 +238,51 @@ .l-nav__tab:last-child {
border-right: none;
}
.l-main {
padding: 10px 40px;
.l-page-nav {
width: 100%;
margin: 10px;
display: flex;
justify-content: stretch;
align-items: center;
-moz-column-gap: 20px;
column-gap: 20px;
}
.error {
background-color: #87ff5f;
.l-page-nav__links {
flex-grow: 1;
text-align: right;
margin-right: 40px;
}
.l-page-nav__link {
text-decoration: none;
}
.l-main {
padding: 10px 40px;
overflow: auto;
}
.success {
background-color: #b7ff9f;
border: 1px solid #30be00;
padding: 10px;
width: 60%;
margin: 10px auto;
}
.warning {
background-color: #ffeaad;
border: 1px solid #ebb000;
padding: 10px;
width: 60%;
margin: 10px auto;
}
.error {
background-color: #ffaaaa;
border: 1px solid #e20000;
padding: 10px;
width: 60%;
margin: 10px auto;
}

View file

@ -26,6 +26,7 @@ h1 {
font-weight: normal;
margin-left: 40px;
margin-bottom: 10px;
margin-top: 0;
display: inline-block;
position: relative;
@ -50,14 +51,63 @@ h1 {
margin-top: 20px;
margin-bottom: 5px;
&:first-of-type { margin-top: 0; }
&:first-child, .form__legend + .form__label { margin-top: 0; }
}
.form__input {
border: 1px solid $c-main;
background-color: $c-main-lightest;
@include hover($focus: true);
@include hover;
}
.form__select {
@extend .form__input;
}
.form__input--file {
border: 1px solid $c-main;
@include hover($bg: false);
padding: 5px;
&::file-selector-button {
@extend %btn;
}
}
.form__checkbox {
@extend .form__label;
}
.form__input--checkbox {
appearance: none;
background-color: $c-main-lightest;
margin: 0;
margin-top: 5px;
margin-right: 5px;
font: inherit;
color: $c-main;
width: 15px !important;
height: 15px;
border: 1px solid $c-main;
display: inline-grid;
place-content: center;
&::before {
content: '';
width: 9px;
height: 9px;
transform: scale(0);
transition: transform 120ms ease-in-out;
box-shadow: inset 9px 9px $c-main;
clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
}
&:checked::before {
transform: scale(1);
}
@include hover;
}
.form__btns {

View file

@ -42,6 +42,28 @@ body {
}
.l-page-nav {
width: 100%;
margin: 10px;
display: flex;
justify-content: stretch;
align-items: center;
column-gap: 20px;
}
.l-page-nav__links {
flex-grow: 1;
text-align: right;
margin-right: 40px;
}
.l-page-nav__link {
@extend %btn;
text-decoration: none;
}
.l-main {
padding: 10px 40px;
overflow: auto;
}

View file

@ -1,11 +1,15 @@
@use 'vars' as *;
@use 'sass:color';
.error {
background-color: color.scale($c-green, $lightness: 50%);
border: 1px solid $c-green;
@mixin msg($color) {
background-color: color.scale($color, $lightness: 70%);
border: 1px solid $color;
padding: 10px;
width: 60%;
margin: 10px auto;
}
}
.success { @include msg($c-green); }
.warning { @include msg($c-yellow); }
.error { @include msg($c-red); }

View file

@ -8,10 +8,12 @@ $f-size: 16px;
$c-main: #7874ff;
$c-main-light: color.scale($c-main, $lightness: 70%);
$c-main-lightest: color.scale($c-main, $lightness: 80%);
$c-main-dark: color.scale($c-main, $lightness: -20%);
$c-accent: #de7cff;
$c-accent-light: color.scale($c-accent, $lightness: 40%);
$c-accent-lightest: color.scale($c-accent, $lightness: 80%);
$c-accent-dark: color.scale($c-accent, $lightness: -20%);
$c-green: #30be00;
$c-yellow: #ebb000;
@ -19,22 +21,31 @@ $c-red: #e20000;
/* ------------------------------------------------------------------------------------ &BTN ---- */
@mixin hover($focus: false) {
@mixin hover($focus: true, $bg: true) {
border-color: $c-main;
background-color: $c-main-lightest;
transition: background-color 0.4s, border-color 0.4s;
@if $bg {
background-color: $c-main-lightest;
}
color: $c-main;
transition: background-color 0.4s, border-color 0.4s, color 0.4s;
&:hover {
background-color: $c-accent-lightest;
border-color: $c-accent;
transition: background-color 0.4s, border-color 0.4s;
@if $bg {
background-color: $c-accent-lightest;
}
color: $c-accent;
transition: background-color 0.4s, border-color 0.4s, color 0.4s;
}
@if $focus {
&:focus {
background-color: $c-accent-lightest;
border-color: $c-accent;
transition: background-color 0.4s, border-color 0.4s;
@if $bg {
background-color: $c-accent-lightest;
}
color: $c-accent;
transition: background-color 0.4s, border-color 0.4s, color 0.4s;
}
}
}
@ -43,6 +54,8 @@ $c-red: #e20000;
border: 1px solid $c-main;
padding: 5px 10px;
margin-right: 5px;
line-height: normal;
cursor: pointer;
@include hover;
}

View file

@ -1,5 +1,7 @@
@extends('admin.layout')
@section('pg-title', 'Dashboard')
@section('content')
<h1><span>Dashboard</span></h1>
@endsection

View file

@ -1,7 +1,8 @@
@extends('admin.layout')
@section('pg-title', 'Welcome to Fanatic!')
@section('content')
<h1><span>Welcome to Fanatic!</span></h1>
<p>This is the installation page. You will only need to complete this once, but values can be
changed after installation.</p>

View file

@ -0,0 +1,48 @@
@extends('admin.layout')
@section('pg-nav')
<x-admin.nav :section="'joined'" />
@endsection
@section('pg-title', 'Add Joined')
@section('content')
<form action="{{ route('joined.store') }}" method="POST" enctype="multipart/form-data"
autocomplete="off">
@csrf
<fieldset class="form__fieldset">
<label for="categories" class="form__label">Categories:</label>
<x-admin.form.categories :categories="$categories" name="categories[]" id="categories"
multiple required />
@error('categories') <p class="form__error">{{ $message }}</p> @enderror
<label for="url" class="form__label">URL:</label>
<input type="url" id="url" name="url" value="{{ old('url') }}" class="form__input" required>
@error('url') <p class="form__error">{{ $message }}</p> @enderror
<label for="subject" class="form__label">Subject:</label>
<input type="text" id="subject" name="subject" value="{{ old('subject') }}"
class="form__input" required>
@error('subject') <p class="form__error">{{ $message }}</p> @enderror
<label for="image" class="form__label">Image:</label>
<input type="file" id="image" name="image" value="{{ old('image') }}" accept="image/*"
class="form__input--file">
@error('image') <p class="form__error">{{ $message }}</p> @enderror
<div class="form__checkbox">
<input type="checkbox" id="approved" name="approved" value="1"
class="form__input--checkbox" @checked(old('approved'))>
<label for="approved" class="form__label--checkbox">Approved</label>
</div>
<div class="form__btns">
<input type="submit" class="form__btn" value="Submit">
<input type="reset" class="form__btn" value="Reset">
</div>
</fieldset>
</form>
@endsection

View file

@ -0,0 +1,7 @@
@extends('admin.layout')
@section('pg-nav')
<x-admin.nav :section="'joined'" />
@endsection
@section('pg-title', 'Joined')

View file

@ -14,15 +14,25 @@
@auth
<nav class="l-nav">
<a class="l-nav__tab" href="#"><span class="l-nav__link">dashboard</span></a>
<a class="l-nav__tab" href="#"><span class="l-nav__link">joined</span></a>
<a class="l-nav__tab" href="#"><span class="l-nav__link">owned</span></a>
<a class="l-nav__tab" href="#"><span class="l-nav__link">collective</span></a>
<a href="{{ route('dashboard') }}" class="l-nav__tab">
<span class="l-nav__link">dashboard</span>
</a>
<a href="{{ route('joined.index') }}" class="l-nav__tab">
<span class="l-nav__link">joined</span>
</a>
<a href="#" class="l-nav__tab"><span class="l-nav__link">owned</span></a>
<a href="#" class="l-nav__tab"><span class="l-nav__link">collective</span></a>
</nav>
@endauth
<main class="l-main">
@yield('title')
<nav class="l-page-nav">
<h1><span>@yield('pg-title')</span></h1>
<div class="l-page-nav__links">@yield('pg-nav')</div>
</nav>
@if (session()->has('success'))
<p class="success">{{ session()->get('success') }}</p>

View file

@ -0,0 +1,21 @@
{{-- expected attributes: name, id --}}
@props(['categories', 'prevCats' => false])
@php
$selected = null;
$name = rtrim($attributes['name'], '[]');
if (old($name) != null) {
$selected = collect(old($name));
} else if ($prevCats) {
$selected = $prevCats;
}
@endphp
<select {{ $attributes }} class="form__select" size="6">
@foreach ($categories as $cat)
<option value="{{ $cat->id }}"
@if(isset($selected)) @selected($selected->search($cat->id) !== false) @endif>
{{ $cat->name }}
</option>
@endforeach
</select>

View file

@ -0,0 +1,13 @@
@props(['section'])
@switch ($section)
@case('joined')
<a href="{{ route('joined.index') }}" class="l-page-nav__link">All Joined</a>
<a href="{{ route('joined.create') }}" class="l-page-nav__link">Add New</a>
@break
@case('owned')
@break
@default
@endswitch

View file

@ -1,6 +1,7 @@
<?php
use App\Http\Controllers\CollectiveController;
use App\Http\Controllers\JoinedController;
use Illuminate\Support\Facades\Route;
/*
@ -13,7 +14,15 @@
| contains the "web" middleware group. Now create something great!
|
*/
Route::middleware('guest')->group(function () {
//Route::get('/', [])
Route::get('/fanatic/install', [CollectiveController::class, 'create'])
->name('collectives.create');
Route::post('/fanatic', [CollectiveController::class, 'store'])->name('collectives.store');
});
Route::get('/fanatic', [CollectiveController::class, 'dashboard'])->name('dashboard');
Route::get('/fanatic/install', [CollectiveController::class, 'create'])->name('collectives.create');
Route::post('/fanatic', [CollectiveController::class, 'store'])->name('collectives.store');
Route::middleware('auth')->group(function () {
Route::get('/fanatic', [CollectiveController::class, 'dashboard'])->name('dashboard');
Route::resource('joined', JoinedController::class);
});