Merge pull request #168 from rico-vz/streamers

Add Streamers functionality
This commit is contained in:
Rico
2024-03-26 11:30:56 +01:00
committed by GitHub
28 changed files with 769 additions and 281 deletions

2
.gitignore vendored
View File

@@ -19,3 +19,5 @@ yarn-error.log
/.vscode /.vscode
_ide_helper.php _ide_helper.php
.phpstorm.meta.php .phpstorm.meta.php
_ide_helper_models.php

View File

@@ -2,8 +2,6 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Http\Requests\StoreChampionRequest;
use App\Http\Requests\UpdateChampionRequest;
use App\Models\Champion; use App\Models\Champion;
use App\Models\ChampionRoles; use App\Models\ChampionRoles;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
@@ -24,22 +22,6 @@ class ChampionController extends Controller
return view('champions.index', ['champions' => $champions, 'roles' => $roles]); return view('champions.index', ['champions' => $champions, 'roles' => $roles]);
} }
/**
* Show the form for creating a new resource.
*/
public function create()
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(StoreChampionRequest $request)
{
//
}
/** /**
* Display the specified resource. * Display the specified resource.
*/ */
@@ -48,7 +30,9 @@ class ChampionController extends Controller
$threeDaysInSeconds = 60 * 60 * 24 * 3; $threeDaysInSeconds = 60 * 60 * 24 * 3;
$sixMonthsInSeconds = 60 * 60 * 24 * 30 * 6; $sixMonthsInSeconds = 60 * 60 * 24 * 30 * 6;
$champion = Cache::remember('championShowCache'.$champion->slug, $threeDaysInSeconds, static fn () => $champion->load('skins', 'lanes')); $champion = Cache::remember('championShowCache'.$champion->slug, $threeDaysInSeconds, static fn () => $champion->load('streamers', 'skins', 'lanes'));
$streamers = $champion->load('streamers')->streamers;
$splashColor = Cache::remember( $splashColor = Cache::remember(
'championSplashColorCache'.$champion->slug, 'championSplashColorCache'.$champion->slug,
@@ -58,30 +42,6 @@ class ChampionController extends Controller
$champion->splash_color = $splashColor; $champion->splash_color = $splashColor;
return view('champions.show', ['champion' => $champion]); return view('champions.show', ['champion' => $champion, 'streamers' => $streamers]);
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Champion $champion)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(UpdateChampionRequest $request, Champion $champion)
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Champion $champion)
{
//
} }
} }

View File

@@ -0,0 +1,36 @@
<?php
namespace App\Http\Controllers;
use App\Models\Streamer;
use Illuminate\Support\Facades\Cache;
class StreamerController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
//
}
/**
* Display the specified resource.
*/
public function show(Streamer $streamer)
{
//
}
/**
* API: JSON response of all streamers.
* Data is cached for 12 hours.
*/
public function all()
{
$streamers = Cache::remember('streamersListAllAPICache', 60 * 60 * 12, static fn () => Streamer::orderBy('champion_id')->get());
return response()->json($streamers);
}
}

View File

@@ -0,0 +1,91 @@
<?php
namespace App\Http\Controllers;
use App\Models\Champion;
use App\Models\Streamer;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
class StreamerPanelController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
return view('streamerpanel.index', [
'streamers' => Streamer::with('champion')->get(),
]);
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
return view('streamerpanel.streamer-create', [
'champions' => Champion::all(),
]);
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$request->validate(['champion_id' => 'required|exists:champions,champion_id',
'platform' => 'required|in:twitch,youtube,kick,douyu,huya',
'username' => 'required|string',
'displayname' => 'required|string',
]);
Streamer::create($request->all());
Cache::forget('streamersListAllAPICache');
return redirect()->route('streamerpanel.index');
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Streamer $streamer)
{
return view('streamerpanel.streamer-edit', [
'streamer' => $streamer,
'champions' => Champion::all(),
]);
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, Streamer $streamer)
{
$request->validate([
'champion_id' => 'required|exists:champions,id',
'platform' => 'required|in:twitch,youtube,kick,douyu,huya',
'username' => 'required|string',
'displayname' => 'required|string',
]);
$streamer->update($request->all());
Cache::forget('streamersListAllAPICache');
return redirect()->route('streamerpanel.index');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Streamer $streamer)
{
$streamer->delete();
Cache::forget('streamersListAllAPICache');
return redirect()->route('streamerpanel.index');
}
}

View File

@@ -94,6 +94,11 @@ class Champion extends Model
return $this->hasOne(ChampionRoles::class, 'champion_id', 'champion_id'); return $this->hasOne(ChampionRoles::class, 'champion_id', 'champion_id');
} }
public function streamers()
{
return $this->hasMany(Streamer::class, 'champion_id', 'champion_id');
}
public function getChampionImageAttribute($centered = true): string public function getChampionImageAttribute($centered = true): string
{ {
$url = 'https://cdn.communitydragon.org/latest/champion/'.$this->champion_id.'/splash-art'; $url = 'https://cdn.communitydragon.org/latest/champion/'.$this->champion_id.'/splash-art';

43
app/Models/Streamer.php Normal file
View File

@@ -0,0 +1,43 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Streamer extends Model
{
use HasFactory;
protected $fillable = ['champion_id', 'platform', 'username', 'displayname'];
public function getPlatformAttribute($value): string
{
$platforms = [
'twitch' => 'Twitch',
'youtube' => 'YouTube',
'kick' => 'Kick',
'douyu' => 'Douyu',
'huya' => 'Huya',
];
return $platforms[$value];
}
public function getStreamerUrlAttribute(): string
{
return match ($this->platform) {
'Twitch' => "https://www.twitch.tv/{$this->username}",
'YouTube' => "https://www.youtube.com/@{$this->username}",
'Kick' => "https://kick.com/{$this->username}",
'Douyu' => "https://www.douyu.com/{$this->username}",
'Huya' => "https://www.huya.com/{$this->username}",
};
}
public function champion(): BelongsTo
{
return $this->belongsTo(Champion::class, 'champion_id', 'champion_id');
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\View\Components\Streamerpanel;
use Closure;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;
class StreamerCreateForm extends Component
{
/**
* Create a new component instance.
*/
public function __construct(public $champions)
{
//
}
/**
* Get the view / contents that represent the component.
*/
public function render(): View|Closure|string
{
return view('components.streamerpanel.streamer-create-form');
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\View\Components\Streamerpanel;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Collection;
use Illuminate\View\Component;
class StreamersTable extends Component
{
/**
* Create a new component instance.
*/
public function __construct(public Collection $streamers)
{
$this->streamers = $streamers;
}
/**
* Get the view / contents that represent the component.
*/
public function render(): View
{
return view('components.streamerpanel.streamers-table');
}
}

60
composer.lock generated
View File

@@ -118,16 +118,16 @@
}, },
{ {
"name": "aws/aws-sdk-php", "name": "aws/aws-sdk-php",
"version": "3.301.1", "version": "3.301.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/aws/aws-sdk-php.git", "url": "https://github.com/aws/aws-sdk-php.git",
"reference": "0a910d2b35e7087337cdf3569dc9b6ce232aafba" "reference": "1d04b11a621eaceb389d2cfbd82bcdc423903796"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/0a910d2b35e7087337cdf3569dc9b6ce232aafba", "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/1d04b11a621eaceb389d2cfbd82bcdc423903796",
"reference": "0a910d2b35e7087337cdf3569dc9b6ce232aafba", "reference": "1d04b11a621eaceb389d2cfbd82bcdc423903796",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -207,9 +207,9 @@
"support": { "support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues", "issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.301.1" "source": "https://github.com/aws/aws-sdk-php/tree/3.301.4"
}, },
"time": "2024-03-15T18:14:42+00:00" "time": "2024-03-20T18:16:55+00:00"
}, },
{ {
"name": "blade-ui-kit/blade-icons", "name": "blade-ui-kit/blade-icons",
@@ -5036,16 +5036,16 @@
}, },
{ {
"name": "spatie/laravel-package-tools", "name": "spatie/laravel-package-tools",
"version": "1.16.3", "version": "1.16.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/spatie/laravel-package-tools.git", "url": "https://github.com/spatie/laravel-package-tools.git",
"reference": "59db18c2e20d49a0b6d447bb1c654f6c123beb9e" "reference": "ddf678e78d7f8b17e5cdd99c0c3413a4a6592e53"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/59db18c2e20d49a0b6d447bb1c654f6c123beb9e", "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/ddf678e78d7f8b17e5cdd99c0c3413a4a6592e53",
"reference": "59db18c2e20d49a0b6d447bb1c654f6c123beb9e", "reference": "ddf678e78d7f8b17e5cdd99c0c3413a4a6592e53",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -5084,7 +5084,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/spatie/laravel-package-tools/issues", "issues": "https://github.com/spatie/laravel-package-tools/issues",
"source": "https://github.com/spatie/laravel-package-tools/tree/1.16.3" "source": "https://github.com/spatie/laravel-package-tools/tree/1.16.4"
}, },
"funding": [ "funding": [
{ {
@@ -5092,7 +5092,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2024-03-07T07:35:57+00:00" "time": "2024-03-20T07:29:11+00:00"
}, },
{ {
"name": "spatie/laravel-query-builder", "name": "spatie/laravel-query-builder",
@@ -8726,16 +8726,16 @@
}, },
{ {
"name": "composer/pcre", "name": "composer/pcre",
"version": "3.1.2", "version": "3.1.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/composer/pcre.git", "url": "https://github.com/composer/pcre.git",
"reference": "4775f35b2d70865807c89d32c8e7385b86eb0ace" "reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/composer/pcre/zipball/4775f35b2d70865807c89d32c8e7385b86eb0ace", "url": "https://api.github.com/repos/composer/pcre/zipball/5b16e25a5355f1f3afdfc2f954a0a80aec4826a8",
"reference": "4775f35b2d70865807c89d32c8e7385b86eb0ace", "reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -8777,7 +8777,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/composer/pcre/issues", "issues": "https://github.com/composer/pcre/issues",
"source": "https://github.com/composer/pcre/tree/3.1.2" "source": "https://github.com/composer/pcre/tree/3.1.3"
}, },
"funding": [ "funding": [
{ {
@@ -8793,7 +8793,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-03-07T15:38:35+00:00" "time": "2024-03-19T10:26:25+00:00"
}, },
{ {
"name": "doctrine/deprecations", "name": "doctrine/deprecations",
@@ -9223,16 +9223,16 @@
}, },
{ {
"name": "mockery/mockery", "name": "mockery/mockery",
"version": "1.6.9", "version": "1.6.10",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/mockery/mockery.git", "url": "https://github.com/mockery/mockery.git",
"reference": "0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06" "reference": "47065d1be1fa05def58dc14c03cf831d3884ef0b"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/mockery/mockery/zipball/0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06", "url": "https://api.github.com/repos/mockery/mockery/zipball/47065d1be1fa05def58dc14c03cf831d3884ef0b",
"reference": "0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06", "reference": "47065d1be1fa05def58dc14c03cf831d3884ef0b",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -9244,8 +9244,8 @@
"phpunit/phpunit": "<8.0" "phpunit/phpunit": "<8.0"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^8.5 || ^9.6.10", "phpunit/phpunit": "^8.5 || ^9.6.17",
"symplify/easy-coding-standard": "^12.0.8" "symplify/easy-coding-standard": "^12.1.14"
}, },
"type": "library", "type": "library",
"autoload": { "autoload": {
@@ -9302,7 +9302,7 @@
"security": "https://github.com/mockery/mockery/security/advisories", "security": "https://github.com/mockery/mockery/security/advisories",
"source": "https://github.com/mockery/mockery" "source": "https://github.com/mockery/mockery"
}, },
"time": "2023-12-10T02:24:34+00:00" "time": "2024-03-19T16:15:45+00:00"
}, },
{ {
"name": "myclabs/deep-copy", "name": "myclabs/deep-copy",
@@ -9738,16 +9738,16 @@
}, },
{ {
"name": "phpstan/phpstan", "name": "phpstan/phpstan",
"version": "1.10.62", "version": "1.10.63",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan.git", "url": "https://github.com/phpstan/phpstan.git",
"reference": "cd5c8a1660ed3540b211407c77abf4af193a6af9" "reference": "ad12836d9ca227301f5fb9960979574ed8628339"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/cd5c8a1660ed3540b211407c77abf4af193a6af9", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ad12836d9ca227301f5fb9960979574ed8628339",
"reference": "cd5c8a1660ed3540b211407c77abf4af193a6af9", "reference": "ad12836d9ca227301f5fb9960979574ed8628339",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -9796,7 +9796,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-03-13T12:27:20+00:00" "time": "2024-03-18T16:53:53+00:00"
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",

View File

@@ -0,0 +1,34 @@
<?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::create('streamers', function (Blueprint $table) {
$table->id();
$table->integer('champion_id');
$table->enum('platform', ['twitch', 'youtube', 'kick', 'douyu', 'huya']);
$table->string('username');
$table->string('displayname');
$table->foreign('champion_id')->references('champion_id')->on('champions')->onDelete('cascade');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('streamers');
}
};

1
dev_quickstart.bat Normal file
View File

@@ -0,0 +1 @@
npm run dev-all

View File

@@ -2,6 +2,78 @@
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
@font-face {
font-display: swap;
font-family: "Inter";
font-style: normal;
font-weight: 100;
src: url("/fonts/inter-v13-latin-100.woff2") format("woff2");
}
@font-face {
font-display: swap;
font-family: "Inter";
font-style: normal;
font-weight: 200;
src: url("/fonts/inter-v13-latin-200.woff2") format("woff2");
}
@font-face {
font-display: swap;
font-family: "Inter";
font-style: normal;
font-weight: 300;
src: url("/fonts/inter-v13-latin-300.woff2") format("woff2");
}
@font-face {
font-display: swap;
font-family: "Inter";
font-style: normal;
font-weight: 400;
src: url("/fonts/inter-v13-latin-regular.woff2") format("woff2");
}
@font-face {
font-display: swap;
font-family: "Inter";
font-style: normal;
font-weight: 500;
src: url("/fonts/inter-v13-latin-500.woff2") format("woff2");
}
@font-face {
font-display: swap;
font-family: "Inter";
font-style: normal;
font-weight: 600;
src: url("/fonts/inter-v13-latin-600.woff2") format("woff2");
}
@font-face {
font-display: swap;
font-family: "Inter";
font-style: normal;
font-weight: 700;
src: url("/fonts/inter-v13-latin-700.woff2") format("woff2");
}
@font-face {
font-display: swap;
font-family: "Inter";
font-style: normal;
font-weight: 800;
src: url("/fonts/inter-v13-latin-800.woff2") format("woff2");
}
@font-face {
font-display: swap;
font-family: "Inter";
font-style: normal;
font-weight: 900;
src: url("/fonts/inter-v13-latin-900.woff2") format("woff2");
}
.glow-shadow::before { .glow-shadow::before {
content: ""; content: "";
position: absolute; position: absolute;
@@ -13,13 +85,18 @@
animation: glow 4s infinite; animation: glow 4s infinite;
--tw-scale-x: 1.02; --tw-scale-x: 1.02;
--tw-scale-y: 1.02; --tw-scale-y: 1.02;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); transform: translate(var(--tw-translate-x), var(--tw-translate-y))
rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y))
scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
} }
.shadow-md-splash { .shadow-md-splash {
--tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); --tw-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1),
--tw-shadow-colored: 0 4px 6px -1px var(--splash-color), 0 2px 4px -2px var(--splash-color); 0 2px 4px -2px rgb(0 0 0 / 0.1);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); --tw-shadow-colored: 0 4px 6px -1px var(--splash-color),
0 2px 4px -2px var(--splash-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
} }
@keyframes glow { @keyframes glow {
@@ -27,10 +104,12 @@
opacity: 0.8; opacity: 0.8;
filter: blur(8px); filter: blur(8px);
} }
50% { 50% {
opacity: 0.45; opacity: 0.45;
filter: blur(11px); filter: blur(11px);
} }
100% { 100% {
opacity: 0.8; opacity: 0.8;
filter: blur(8px); filter: blur(8px);
@@ -47,75 +126,22 @@
box-shadow: 0 50vh 0 50vh #292524; box-shadow: 0 50vh 0 50vh #292524;
} }
/* inter-100 - latin */ .text-shadow-twitch {
@font-face { text-shadow: 1px 1px 2px #6441a5;
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'Inter';
font-style: normal;
font-weight: 100;
src: url('/fonts/inter-v13-latin-100.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
} }
/* inter-200 - latin */
@font-face { .text-shadow-youtube {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ text-shadow: 1px 1px 2px #ff0000;
font-family: 'Inter';
font-style: normal;
font-weight: 200;
src: url('/fonts/inter-v13-latin-200.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
} }
/* inter-300 - latin */
@font-face { .text-shadow-kick {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ text-shadow: 1px 1px 2px #53fc18;
font-family: 'Inter';
font-style: normal;
font-weight: 300;
src: url('/fonts/inter-v13-latin-300.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
} }
/* inter-regular - latin */
@font-face { .text-shadow-douyu {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ text-shadow: 1px 1px 2px #ff5f3a;
font-family: 'Inter';
font-style: normal;
font-weight: 400;
src: url('/fonts/inter-v13-latin-regular.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
} }
/* inter-500 - latin */
@font-face { .text-shadow-huya {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ text-shadow: 1px 1px 2px #ffaa06;
font-family: 'Inter';
font-style: normal;
font-weight: 500;
src: url('/fonts/inter-v13-latin-500.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
} }
/* inter-600 - latin */
@font-face {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'Inter';
font-style: normal;
font-weight: 600;
src: url('/fonts/inter-v13-latin-600.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* inter-700 - latin */
@font-face {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'Inter';
font-style: normal;
font-weight: 700;
src: url('/fonts/inter-v13-latin-700.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* inter-800 - latin */
@font-face {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'Inter';
font-style: normal;
font-weight: 800;
src: url('/fonts/inter-v13-latin-800.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}
/* inter-900 - latin */
@font-face {
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
font-family: 'Inter';
font-style: normal;
font-weight: 900;
src: url('/fonts/inter-v13-latin-900.woff2') format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
}

View File

@@ -5,7 +5,7 @@
about ' . $champion->name . ', ' . $champion->title . '. ' . substr($champion->lore, 0, 50) . '...') about ' . $champion->name . ', ' . $champion->title . '. ' . substr($champion->lore, 0, 50) . '...')
@section('content') @section('content')
<x-champions.grid_info :champion="$champion"/> <x-champions.grid_info :champion="$champion" :streamers="$streamers"/>
@endsection @endsection
@push('bottom_scripts') @push('bottom_scripts')

View File

@@ -5,125 +5,134 @@
CHAMPION DETAILS</h3> CHAMPION DETAILS</h3>
<h1 <h1
class="text-3xl font-bold text-center text-transparent uppercase sm:text-4xl bg-gradient-to-bl from-orange-300 to-orange-500 bg-clip-text"> class="text-3xl font-bold text-center text-transparent uppercase sm:text-4xl bg-gradient-to-bl from-orange-300 to-orange-500 bg-clip-text">
{{$champion->name}}</h1> {{ $champion->name }}</h1>
<h2 <h2
class="text-sm md:text-lg font-bold text-center text-transparent uppercase bg-gradient-to-bl from-orange-300 to-orange-500 bg-clip-text"> class="text-sm font-bold text-center text-transparent uppercase md:text-lg bg-gradient-to-bl from-orange-300 to-orange-500 bg-clip-text">
{{$champion->title}}</h2> {{ $champion->title }}</h2>
<div class="container mx-auto p-4 flex items-center justify-center mt-3"> <div class="container flex items-center justify-center p-4 mx-auto mt-3">
<div class="w-screen grid grid-cols-1 md-grid-cols-2 lg:grid-cols-3 gap-5"> <div class="grid w-screen grid-cols-1 gap-5 md-grid-cols-2 lg:grid-cols-3">
<div <div
class="relative rounded-2xl bg-stone-800/40 border border-neutral-300/5 shadow-sm shadow-stone-800/80 lg:col-span-2"> class="relative border shadow-sm aspect-video rounded-2xl bg-stone-800/40 border-neutral-300/5 shadow-stone-800/80 lg:col-span-2">
<div class="aspect-w-16 aspect-h-9 glow-shadow absolute inset-0 rounded-2xl" <div class="absolute inset-0 aspect-video glow-shadow rounded-2xl"
style="--splash-color: {{$champion->splash_color}}"></div> style="--splash-color: {{ $champion->splash_color }}"></div>
<div class="aspect-w-16 aspect-h-9 overflow-hidden rounded-2xl relative"> <div class="relative overflow-hidden aspect-video rounded-2xl">
<img <img src="//wsrv.nl/?url={{ $champion->getChampionImageAttribute(false) }}&w=880&output=webp&q=85&il"
src="//wsrv.nl/?url={{ $champion->getChampionImageAttribute(false) }}&w=880&output=webp&q=85&il" alt="{{ $champion->name }} Splash Art"
alt="{{$champion->name}} Splash Art" class="z-10 object-cover w-full h-full transition-transform duration-700 transform scale-100 hover:scale-105">
class="w-full h-full object-cover transform scale-100 transition-transform duration-700 hover:scale-105 z-10"
>
</div> </div>
</div> </div>
<div <div class="transition-all duration-700 border shadow-md rounded-2xl border-3 border-white/10 lg:col-start-3"
class="rounded-2xl border border-3 border-white/10 lg:col-start-3 shadow-md transition-all duration-700" style="--tw-shadow-color:{{ $champion->splash_color }}; --tw-shadow: var(--tw-shadow-colored); background-color: {{ $champion->splash_color }};">
style="--tw-shadow-color:{{$champion->splash_color}}; --tw-shadow: var(--tw-shadow-colored); background-color: {{$champion->splash_color}};">
<h4 class="text-center text-xl font-semibold text-neutral-100 uppercase mt-3.5 shadow-sm"> <h4 class="text-center text-xl font-semibold text-neutral-100 uppercase mt-3.5 shadow-sm">
{{$champion->name}} Information</h4> {{ $champion->name }} Information</h4>
<ul class="ml-7"> <ul class="ml-7">
<li class="text-neutral-100 hyphens-auto text-base font-medium leading-loose mt-8" lang="en"> <li class="mt-8 text-base font-medium leading-loose text-neutral-100 hyphens-auto" lang="en">
<span class="font-bold">Full Title:</span> {{$champion->name}}, {{$champion->title}}. <span class="font-bold">Full Title:</span> {{ $champion->name }}, {{ $champion->title }}.
</li> </li>
<li class="text-neutral-100 hyphens-auto text-base font-medium leading-loose" lang="en"> <li class="text-base font-medium leading-loose text-neutral-100 hyphens-auto" lang="en">
<span class="font-bold">Popular Positions:</span> @foreach($champion->lanes->roles as $lane) <span class="font-bold">Popular Positions:</span>
<span @foreach ($champion->lanes->roles as $lane)
class="inline-block lowercase capitalize-first">{{$lane}} @svg(getRoleIconSvg($lane), 'w-5 h-5 inline-block') <span class="inline-block lowercase capitalize-first">{{ $lane }} @svg(getRoleIconSvg($lane), 'w-5 h-5 inline-block')
@if(!$loop->last) @if (!$loop->last)
- -
@endif</span> @endif
</span>
@endforeach @endforeach
</li> </li>
<li class="text-neutral-100 hyphens-auto text-base font-medium leading-loose items-center" <li class="items-center text-base font-medium leading-loose text-neutral-100 hyphens-auto"
lang="en"> lang="en">
<span class="font-bold">Blue Essence Cost:</span> <span class="font-bold">Blue Essence Cost:</span>
<x-icon-lcu-be-svg class="inline-block w-4"/> {{$champion->price_be}} BE <x-icon-lcu-be-svg class="inline-block w-4" /> {{ $champion->price_be }} BE
</li> </li>
<li class="text-neutral-100 hyphens-auto text-medium font-medium leading-loose items-center" <li class="items-center font-medium leading-loose text-neutral-100 hyphens-auto text-medium"
lang="en"> lang="en">
<span class="font-bold">Riot Points Cost:</span> <span class="font-bold">Riot Points Cost:</span>
<x-icon-RiotPoints class="inline-block w-4"/> {{$champion->price_rp}} RP <x-icon-RiotPoints class="inline-block w-4" /> {{ $champion->price_rp }} RP
</li> </li>
<li class="text-neutral-100 hyphens-auto leading-loose font-medium" lang="en"> <li class="font-medium leading-loose text-neutral-100 hyphens-auto" lang="en">
<span class="font-bold">Roles:</span> @foreach($champion->roles as $role) <span class="font-bold">Roles:</span>
<span @foreach ($champion->roles as $role)
class="inline-block lowercase capitalize-first">{{$role}} <span class="inline-block lowercase capitalize-first">{{ $role }}
@if(!$loop->last) @if (!$loop->last)
- -
@endif</span> @endif
</span>
@endforeach @endforeach
</li> </li>
<li class="text-neutral-100 hyphens-auto leading-loose font-medium" lang="en"> <li class="font-medium leading-loose text-neutral-100 hyphens-auto" lang="en">
<span class="font-bold">Attack Type:</span> <span <span class="font-bold">Attack Type:</span> <span
class="inline-block lowercase capitalize-first">{{$champion->attack_type}}</span> class="inline-block lowercase capitalize-first">{{ $champion->attack_type }}</span>
</li> </li>
<li class="text-neutral-100 hyphens-auto leading-loose font-medium" lang="en"> <li class="font-medium leading-loose text-neutral-100 hyphens-auto" lang="en">
<span class="font-bold">Damage Type:</span> {{$champion->adaptive_type}} <span class="font-bold">Damage Type:</span> {{ $champion->adaptive_type }}
</li> </li>
<li class="text-neutral-100 hyphens-auto leading-loose font-medium" lang="en"> <li class="font-medium leading-loose text-neutral-100 hyphens-auto" lang="en">
<span class="font-bold">Resource Type:</span> {{$champion->resource_type}} <span class="font-bold">Resource Type:</span> {{ $champion->resource_type }}
</li> </li>
<li class="text-neutral-100 hyphens-auto text-base font-medium leading-loose" lang="en"> <li class="text-base font-medium leading-loose text-neutral-100 hyphens-auto" lang="en">
<span class="font-bold">Champion ID:</span> <span <span class="font-bold">Champion ID:</span> <span
class="font-mono font-medium">{{$champion->champion_id}}</span> class="font-mono font-medium">{{ $champion->champion_id }}</span>
</li> </li>
<li class="text-neutral-100 hyphens-auto leading-loose font-medium" lang="en"> <li class="font-medium leading-loose text-neutral-100 hyphens-auto" lang="en">
<span class="font-bold">Release Date:</span> {{$champion->release_date}} <span class="font-bold">Release Date:</span> {{ $champion->release_date }}
</li> </li>
<li class="text-neutral-100 hyphens-auto leading-loose font-medium" lang="en"> <li class="font-medium leading-loose text-neutral-100 hyphens-auto" lang="en">
<span class="font-bold">Release Patch:</span> Patch {{$champion->release_patch}} <span class="font-bold">Release Patch:</span> Patch {{ $champion->release_patch }}
</li> </li>
</ul> </ul>
</div> </div>
<div class="rounded-2xl border border-3 border-white/10 shadow-md shadow-stone-800/80 hover:shadow-orange-500/20 transition-all duration-700" <div class="transition-all duration-700 border shadow-md rounded-2xl border-3 border-white/10 shadow-stone-800/80 hover:shadow-orange-500/20"
style="--tw-shadow-color:{{$champion->splash_color}}; --tw-shadow: var(--tw-shadow-colored); background-color: {{$champion->splash_color}};"> style="--tw-shadow-color:{{ $champion->splash_color }}; --tw-shadow: var(--tw-shadow-colored); background-color: {{ $champion->splash_color }};">
<div class="p-4"> <div class="p-4">
<h4 class="text-center text-xl font-semibold text-neutral-100 uppercase mt-2.5 shadow-sm"> <h4 class="text-center text-xl font-semibold text-neutral-100 uppercase mt-2.5 shadow-sm">
{{$champion->name}} Lore</h4> {{ $champion->name }} Streamers</h4>
<p class="text-neutral-100 hyphens-auto text-base mt-2.5 leading-loose" lang="en"> <p class="text-neutral-100/75 hyphens-auto mt-2.5 leading-loose text-center text-sm" lang="en">
{{$champion->lore}} A list of streamers who play {{ $champion->name }} and are atleast Diamond 2 or higher.
</p> </p>
<div class="grid grid-cols-1 gap-4 mt-2.5 lg:grid-cols-2">
@foreach ($streamers as $streamer)
<div class="flex justify-center items -center">
<div class="flex flex-col items-center justify-center">
<a href="{{ $streamer->streamer_url }}" target="_blank" rel="noopener noreferrer"
class="text-center text-neutral-100 text-sm mt-1.5 items-center drop-shadow-lg text-shadow-{{ strtolower($streamer->platform) }}">
{{ $streamer->displayname }}
</a>
</div>
</div>
@endforeach
</div>
</div> </div>
</div> </div>
<div <div class="transition-all duration-700 border shadow-md rounded-2xl border-3 border-white/10 shadow-stone-800/80 lg:col-span-2 hover:shadow-orange-500/20"
class="rounded-2xl border border-3 border-white/10 shadow-md shadow-stone-800/80 lg:col-span-2 hover:shadow-orange-500/20 transition-all duration-700" style="--tw-shadow-color:{{ $champion->splash_color }}; --tw-shadow: var(--tw-shadow-colored); background-color: {{ $champion->splash_color }};">
style="--tw-shadow-color:{{$champion->splash_color}}; --tw-shadow: var(--tw-shadow-colored); background-color: {{$champion->splash_color}};">
<div class="p-4"> <div class="p-4">
<h4 class="text-center text-xl font-semibold text-neutral-100 uppercase mt-2.5 shadow-sm"> <h4 class="text-center text-xl font-semibold text-neutral-100 uppercase mt-2.5 shadow-sm">
{{$champion->name}} Skins ({{count($champion->skins)}}) </h4> {{ $champion->name }} Skins ({{ count($champion->skins) }}) </h4>
<div id="skinsElement" class="overflow-x-scroll mt-2.5"> <div id="skinsElement" class="overflow-x-scroll mt-2.5">
<div class="grid grid-flow-col grid-rows-2 w-max gap-4 mb-2.5"> <div class="grid grid-flow-col grid-rows-2 w-max gap-4 mb-2.5">
@foreach($champion->skins as $key => $skin) @foreach ($champion->skins as $key => $skin)
<div class="group flex flex-col"> <div class="flex flex-col group">
<a href="/skin/{{$skin->slug}}"> <a href="/skin/{{ $skin->slug }}">
<img <img src="//wsrv.nl/?url={{ $skin->getSkinImageAttribute() }}&w=450&output=webp&q=70&il"
src="//wsrv.nl/?url={{ $skin->getSkinImageAttribute() }}&w=450&output=webp&q=70&il" alt="{{ $champion->name }} {{ $skin->name }} Splash Art"
alt="{{$champion->name}} {{$skin->name}} Splash Art" @if ($key < 6) loading="eager" @else loading="lazy" @endif
@if($key < 6) loading="eager" @else loading="lazy" @endif
class="inline-block h-36 object-cover rounded-2xl shadow-md border border-3 border-white/10 hover:shadow-orange-500/20 transition-all duration-700 mr-2.5"> class="inline-block h-36 object-cover rounded-2xl shadow-md border border-3 border-white/10 hover:shadow-orange-500/20 transition-all duration-700 mr-2.5">
</a> </a>
<div> <div>
<p class="align-bottom text-center text-neutral-100 text-sm mt-1.5 items-center"> <p
<a href="/skin/{{$skin->slug}}" class="align-bottom text-center text-neutral-100 text-sm mt-1.5 items-center">
class="hover:text-orange-400 group-hover:text-orange-400"> <a href="/skin/{{ $skin->slug }}"
{{$skin->skin_name}} class="hover:text-orange-400 group-hover:text-orange-400">
<x-iconsax-bul-arrow-right class="inline-block w-5"/> {{ $skin->skin_name }}
<x-iconsax-bul-arrow-right class="inline-block w-5" />
</a> </a>
</p> </p>
</div> </div>
@@ -133,6 +142,17 @@
</div> </div>
</div> </div>
</div> </div>
<div class="transition-all duration-700 border shadow-md lg:col-span-3 rounded-2xl border-3 border-white/10 shadow-stone-800/80 hover:shadow-orange-500/20"
style="--tw-shadow-color:{{ $champion->splash_color }}; --tw-shadow: var(--tw-shadow-colored); background-color: {{ $champion->splash_color }};">
<div class="p-4">
<h4 class="text-center text-xl font-semibold text-neutral-100 uppercase mt-2.5 shadow-sm">
{{ $champion->name }} Lore</h4>
<p class="text-neutral-100 hyphens-auto text-base mt-2.5 leading-loose w-9/12 mx-auto"
lang="en">
{{ $champion->lore }}
</p>
</div>
</div>
</div> </div>
</div> </div>
</section> </section>

View File

@@ -1,77 +1,77 @@
<nav class="drop-shadow-md border-stone-200 bg-stone-800"> <nav class="drop-shadow-md border-stone-200 bg-stone-800">
<div class="flex flex-wrap items-center justify-between max-w-screen-xl p-4 mx-auto"> <div class="flex flex-wrap items-center justify-between max-w-screen-xl p-4 mx-auto">
<a href="/" class="flex items-center transition-transform hover:scale-105"> <a href="/" class="flex items-center transition-transform hover:scale-105">
<x-logo class="w-auto mr-2 transition-transform h-9 hover:scale-125" alt="Heimerdinger Logo"/> <x-logo class="w-auto mr-2 transition-transform h-9 hover:scale-125" alt="Heimerdinger Logo" />
<span class="self-center text-2xl font-semibold text-orange-400 whitespace-nowrap">Heimerdinger.LoL</span> <span class="self-center text-2xl font-semibold text-orange-400 whitespace-nowrap">Heimerdinger.LoL</span>
<span class="sr-only">Home</span> <span class="sr-only">Home</span>
</a> </a>
<button data-collapse-toggle="navbar-default" type="button" <button data-collapse-toggle="navbar-default" type="button"
class="inline-flex items-center justify-center w-10 h-10 p-2 text-sm rounded-lg text-stone-500 md:hidden hover:bg-stone-100 focus:outline-none focus:ring-2 focus:ring-stone-200 dark:text-stone-400 dark:hover:bg-stone-700 dark:focus:ring-stone-600" class="inline-flex items-center justify-center w-10 h-10 p-2 text-sm rounded-lg text-stone-500 md:hidden hover:bg-stone-100 focus:outline-none focus:ring-2 focus:ring-stone-200 dark:text-stone-400 dark:hover:bg-stone-700 dark:focus:ring-stone-600"
aria-controls="navbar-default" aria-expanded="false"> aria-controls="navbar-default" aria-expanded="false">
<span class="sr-only">Open main menu</span> <span class="sr-only">Open main menu</span>
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" <svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 17 14"> viewBox="0 0 17 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M1 1h15M1 7h15M1 13h15"/> d="M1 1h15M1 7h15M1 13h15" />
</svg> </svg>
</button> </button>
<div id="navbar-default" class="hidden w-full md:block md:w-auto"> <div id="navbar-default" class="hidden w-full md:block md:w-auto">
<ul <ul
class="flex flex-col p-2 items-center md:flex-row md:space-x-6 md:mt-0 md:border-0 md:bg-white dark:bg-stone-800 md:dark:bg-stone-800 dark:border-stone-700"> class="flex flex-col items-center p-2 md:flex-row md:space-x-6 md:mt-0 md:border-0 md:bg-white dark:bg-stone-800 md:dark:bg-stone-800 dark:border-stone-700">
<li> <li>
<a href="{{route('champions.index')}}" <a href="{{ route('champions.index') }}"
class="flex py-2 pl-3 pr-2 rounded hover:bg-stone-100 md:hover:bg-transparent class="flex py-2 pl-3 pr-2 rounded hover:bg-stone-100 md:hover:bg-transparent
md:border-0 md:p-0 md:dark:hover:text-orange-400 md:border-0 md:p-0 md:dark:hover:text-orange-400
dark:hover:bg-stone-700 dark:hover:text-white md:dark:hover:bg-transparent dark:hover:bg-stone-700 dark:hover:text-white md:dark:hover:bg-transparent
{{ request()->routeIs('champions.*') ? 'text-orange-400 font-medium' : 'text-white' }}"> {{ request()->routeIs('champions.*') ? 'text-orange-400 font-medium' : 'text-white' }}">
<x-iconsax-bul-people class="w-6 h-6 mr-1"/> <x-iconsax-bul-people class="w-6 h-6 mr-1" />
Champions</a> Champions</a>
</li> </li>
<li> <li>
<a href="{{route('skins.index')}}" <a href="{{ route('skins.index') }}"
class="flex py-2 pl-3 pr-2 rounded hover:bg-stone-100 md:hover:bg-transparent class="flex py-2 pl-3 pr-2 rounded hover:bg-stone-100 md:hover:bg-transparent
md:border-0 md:hover:text-orange-500 md:p-0 md:dark:hover:text-orange-400 md:border-0 md:hover:text-orange-500 md:p-0 md:dark:hover:text-orange-400
dark:hover:bg-stone-700 dark:hover:text-white md:dark:hover:bg-transparent dark:hover:bg-stone-700 dark:hover:text-white md:dark:hover:bg-transparent
{{ request()->routeIs('skins.*') ? 'text-orange-400 font-medium' : 'text-white' }}"> {{ request()->routeIs('skins.*') ? 'text-orange-400 font-medium' : 'text-white' }}">
<x-iconsax-bul-brush-2 class="w-6 h-6 mr-1"/> <x-iconsax-bul-brush-2 class="w-6 h-6 mr-1" />
Skins</a> Skins</a>
</li> </li>
<li> <li>
<a href="{{route('assets.index')}}" <a href="{{ route('assets.index') }}"
class="flex py-2 pl-3 pr-2 rounded hover:bg-stone-100 md:hover:bg-transparent class="flex py-2 pl-3 pr-2 rounded hover:bg-stone-100 md:hover:bg-transparent
md:border-0 md:hover:text-orange-500 md:p-0 md:dark:hover:text-orange-400 md:border-0 md:hover:text-orange-500 md:p-0 md:dark:hover:text-orange-400
dark:hover:bg-stone-700 dark:hover:text-white md:dark:hover:bg-transparent dark:hover:bg-stone-700 dark:hover:text-white md:dark:hover:bg-transparent
{{ request()->routeIs('assets.*') ? 'text-orange-400 font-medium' : 'text-white' }}"> {{ request()->routeIs('assets.*') ? 'text-orange-400 font-medium' : 'text-white' }}">
<x-iconsax-bul-3dcube class="w-6 h-6 mr-1"/> <x-iconsax-bul-3dcube class="w-6 h-6 mr-1" />
Assets</a> Assets</a>
</li> </li>
<li> <li>
<a href="{{route('sales.index')}}" <a href="{{ route('sales.index') }}"
class="flex py-2 pl-3 pr-2 rounded hover:bg-stone-100 md:hover:bg-transparent class="flex py-2 pl-3 pr-2 rounded hover:bg-stone-100 md:hover:bg-transparent
md:border-0 md:hover:text-orange-500 md:p-0 md:dark:hover:text-orange-400 md:border-0 md:hover:text-orange-500 md:p-0 md:dark:hover:text-orange-400
dark:hover:bg-stone-700 dark:hover:text-white md:dark:hover:bg-transparent dark:hover:bg-stone-700 dark:hover:text-white md:dark:hover:bg-transparent
{{ request()->routeIs('sales.*') ? 'text-orange-400 font-medium' : 'text-white' }}"> {{ request()->routeIs('sales.*') ? 'text-orange-400 font-medium' : 'text-white' }}">
<x-iconsax-bul-card class="w-6 h-6 mr-1"/> <x-iconsax-bul-card class="w-6 h-6 mr-1" />
Sale Rotation</a> Sale Rotation</a>
</li> </li>
<li> <li>
<a href="{{route('posts.index')}}" <a href="{{ route('posts.index') }}"
class="flex py-2 pl-3 pr-2 rounded hover:bg-stone-100 md:hover:bg-transparent class="flex py-2 pl-3 pr-2 rounded hover:bg-stone-100 md:hover:bg-transparent
md:border-0 md:hover:text-orange-500 md:p-0 md:dark:hover:text-orange-400 md:border-0 md:hover:text-orange-500 md:p-0 md:dark:hover:text-orange-400
dark:hover:bg-stone-700 dark:hover:text-white md:dark:hover:bg-transparent dark:hover:bg-stone-700 dark:hover:text-white md:dark:hover:bg-transparent
{{ request()->routeIs('posts.*') ? 'text-orange-400 font-medium' : 'text-white' }}"> {{ request()->routeIs('posts.*') ? 'text-orange-400 font-medium' : 'text-white' }}">
<x-iconsax-bul-receipt-search class="w-6 h-6 mr-1"/> <x-iconsax-bul-receipt-search class="w-6 h-6 mr-1" />
Posts</a> Posts</a>
</li> </li>
<li> <li>
<a href="{{route('about.index')}}" <a href="{{ route('about.index') }}"
class="flex py-2 pl-3 pr-2 rounded hover:bg-stone-100 md:hover:bg-transparent class="flex py-2 pl-3 pr-2 rounded hover:bg-stone-100 md:hover:bg-transparent
md:border-0 md:hover:text-orange-500 md:p-0 md:dark:hover:text-orange-400 md:border-0 md:hover:text-orange-500 md:p-0 md:dark:hover:text-orange-400
dark:hover:bg-stone-700 dark:hover:text-white md:dark:hover:bg-transparent dark:hover:bg-stone-700 dark:hover:text-white md:dark:hover:bg-transparent
{{ request()->routeIs('about.*') ? 'text-orange-400 font-medium' : 'text-white' }}"> {{ request()->routeIs('about.*') ? 'text-orange-400 font-medium' : 'text-white' }}">
<x-iconsax-bul-info-circle class="w-6 h-6 mr-1"/> <x-iconsax-bul-info-circle class="w-6 h-6 mr-1" />
About</a> About</a>
</li> </li>
</ul> </ul>

View File

@@ -0,0 +1,36 @@
<nav class="drop-shadow-md border-stone-200 bg-stone-800">
<div class="flex flex-wrap items-center justify-between max-w-screen-xl p-4 mx-auto">
<a href="/streamerpanel" class="flex items-center transition-transform hover:scale-105">
<x-logo class="w-auto mr-2 transition-transform h-9 hover:scale-125" alt="Heimerdinger Logo" />
<span class="self-center text-2xl font-semibold text-orange-400 whitespace-nowrap">Streamer Panel</span>
<span class="sr-only">Streamer Panel</span>
</a>
<button data-collapse-toggle="navbar-default" type="button"
class="inline-flex items-center justify-center w-10 h-10 p-2 text-sm rounded-lg text-stone-500 md:hidden hover:bg-stone-100 focus:outline-none focus:ring-2 focus:ring-stone-200 dark:text-stone-400 dark:hover:bg-stone-700 dark:focus:ring-stone-600"
aria-controls="navbar-default" aria-expanded="false">
<span class="sr-only">Open main menu</span>
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 17 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M1 1h15M1 7h15M1 13h15" />
</svg>
</button>
<div id="navbar-default" class="hidden w-full md:block md:w-auto">
<ul
class="flex flex-col items-center p-2 md:flex-row md:space-x-6 md:mt-0 md:border-0 md:bg-white dark:bg-stone-800 md:dark:bg-stone-800 dark:border-stone-700">
<li>
<a href="{{ route('streamerpanel.index') }}"
class="flex py-2 pl-3 pr-2 rounded hover:bg-stone-100 md:hover:bg-transparent md:border-0 md:p-0 md:dark:hover:text-orange-400 dark:hover:bg-stone-700 dark:hover:text-white md:dark:hover:bg-transparent
{{ request()->routeIs('streamerpanel.*') ? 'text-orange-400 font-medium' : 'text-white' }}">
Streamers</a>
</li>
<li>
<a href="#"
class="flex py-2 pl-3 pr-2 rounded hover:bg-stone-100 md:hover:bg-transparent md:border-0 md:hover:text-orange-500 md:p-0 md:dark:hover:text-orange-400 dark:hover:bg-stone-700 dark:hover:text-white md:dark:hover:bg-transparent
{{ request()->routeIs('streamerrequests.*') ? 'text-orange-400 font-medium' : 'text-white' }}">
Streamer Requests</a>
</li>
</ul>
</div>
</div>
</nav>

View File

@@ -1,23 +1,23 @@
@use('Carbon\Carbon') @use('Carbon\Carbon')
<div class="container mx-auto p-4 flex flex-col items-center justify-center mt-3"> <div class="container flex flex-col items-center justify-center p-4 mx-auto mt-3">
<div class="grid grid-cols-1 md:grid-cols-2 gap-12 auto-cols-max w-full"> <div class="grid w-full grid-cols-1 gap-12 md:grid-cols-2 auto-cols-max">
@foreach($posts as $post) @foreach($posts as $post)
@if($post->hidden) @if($post->hidden)
@continue @continue
@endif @endif
<article class="inline-block text-gray-200 bg-stone-800/40 shadow-md rounded-2xl border border-stone-800 hover:border-orange-500/10 hover:shadow-orange-500/10 items-center h-80 relative"> <article class="relative items-center inline-block text-gray-200 border shadow-md bg-stone-800/40 rounded-2xl border-stone-800 hover:border-orange-500/10 hover:shadow-orange-500/10 h-80">
<span <span
class="absolute top-4 left-4 text-sm text-gray-100 font-medium bg-black/60 px-1 py-1 rounded-lg"> class="absolute px-1 py-1 text-sm font-medium text-gray-100 rounded-lg top-4 left-4 bg-black/60">
<abbr itemprop="datePublished">{{ Carbon::parse($post->date)->format('F d, Y') }}</abbr> <abbr itemprop="datePublished">{{ Carbon::parse($post->date)->format('F d, Y') }}</abbr>
</span> </span>
<img src="{{ $post->thumbnail }}" alt="Post Thumbnail" class="w-full h-48 object-cover rounded-t-2xl"> <img src="{{ $post->thumbnail }}" alt="Post Thumbnail" class="object-cover w-full h-48 aspect-video rounded-t-2xl">
<div class="p-4"> <div class="p-4">
<h2 class="text-xl font-bold mb-2 line-clamp-1" itemprop="name">{{ $post->title }}</h2> <h2 class="mb-2 text-xl font-bold line-clamp-1" itemprop="name">{{ $post->title }}</h2>
<p class="text-sm line-clamp-3" itemprop="headline">{{ $post->description }}</p> <p class="text-sm line-clamp-3" itemprop="headline">{{ $post->description }}</p>
</div> </div>
<a href="{{ route('posts.show', $post->slug)}}" itemprop="url" <a href="{{ route('posts.show', $post->slug)}}" itemprop="url"
class="absolute bottom-4 right-4 text-sm text-orange-400 hover:text-orange-600">Read more</a> class="absolute text-sm text-orange-400 bottom-4 right-4 hover:text-orange-600">Read more</a>
</article> </article>
@endforeach @endforeach
</div> </div>

View File

@@ -23,7 +23,7 @@
<div class="grid w-screen grid-cols-1 gap-5 md-grid-cols-2 lg:grid-cols-3"> <div class="grid w-screen grid-cols-1 gap-5 md-grid-cols-2 lg:grid-cols-3">
<div <div
class="relative border shadow-sm rounded-2xl bg-stone-800/40 border-neutral-300/5 shadow-stone-800/80 lg:col-span-2"> class="relative border shadow-sm rounded-2xl bg-stone-800/40 border-neutral-300/5 shadow-stone-800/80 lg:col-span-2">
<div class="absolute inset-0 aspect-w-16 aspect-h-9 glow-shadow rounded-2xl" <div class="absolute inset-0 aspect-video glow-shadow rounded-2xl"
style="--splash-color: {{$skin->splash_color}}"></div> style="--splash-color: {{$skin->splash_color}}"></div>
<img src="//wsrv.nl/?url={{ $skin->getSkinImageAttribute() }}&w=840&output=webp&q=70" <img src="//wsrv.nl/?url={{ $skin->getSkinImageAttribute() }}&w=840&output=webp&q=70"
alt="{{$skin->skin_name}} Splash Art" alt="{{$skin->skin_name}} Splash Art"

View File

@@ -0,0 +1,11 @@
<section class="max-w-screen-xl mx-auto mt-12">
<h1
class="text-2xl font-bold text-center text-transparent uppercase sm:text-4xl bg-gradient-to-bl from-orange-300 to-orange-500 bg-clip-text">
Streamers</h1>
<div class="flex justify-center mt-4">
<a href="{{ route('streamerpanel.streamers.create') }}"
class="flex items-center justify-center px-4 py-2 text-sm font-medium text-white bg-orange-500 rounded-md hover:bg-orange-600 focus:outline-none focus:ring-2 focus:ring-orange-500 focus:ring-offset-2">
Add Streamer
</a>
</div>
</section>

View File

@@ -0,0 +1,37 @@
<form action="{{ route('streamerpanel.store') }}" method="POST" class="w-10/12 mx-auto ">
@csrf
<div class="flex flex-col space-y-4">
<div class="flex flex-col space-y-2">
<label for="champion_id" class="text-lg font-semibold text-orange-400">Champion</label>
<select name="champion_id" required id="champion_id" class="w-full p-2 text-white rounded-md bg-stone-800">
<option value="">Select a champion</option>
@foreach ($champions as $champion)
<option value="{{ $champion->champion_id }}">{{ $champion->name }}</option>
@endforeach
</select>
</div>
<div class="flex flex-col space-y-2">
<label for="platform" class="text-lg font-semibold text-orange-400">Platform</label>
<select name="platform" id="platform" class="w-full p-2 text-white rounded-md bg-stone-800">
<option value="twitch">Twitch</option>
<option value="youtube">YouTube</option>
<option value="kick">Kick</option>
<option value="douyu">Douyu</option>
<option value="huya">Huya</option>
</select>
</div>
<div class="flex flex-col space-y-2">
<label for="username" class="text-lg font-semibold text-orange-400">Username</label>
<input type="text" name="username" id="username"
class="w-full p-2 text-white rounded-md bg-stone-800" />
</div>
<div class="flex flex-col space-y-2">
<label for="displayname" class="text-lg font-semibold text-orange-400">Display Name</label>
<input type="text" name="displayname" id="displayname"
class="w-full p-2 text-white rounded-md bg-stone-800" />
</div>
<div class="flex justify-end">
<button type="submit" class="px-4 py-2 mt-3 text-white bg-orange-500 rounded-md">Add Streamer</button>
</div>
</div>
</form>

View File

@@ -0,0 +1,49 @@
@push('top_scripts')
<script src="https://cdn.jsdelivr.net/npm/gridjs/dist/gridjs.umd.js"></script>
<link href="https://cdn.jsdelivr.net/npm/gridjs/dist/theme/mermaid.min.css" rel="stylesheet" />
@endpush
<div class="w-10/12 mx-auto">
<div id="streamers-wrapper"></div>
</div>
@push('bottom_scripts')
<script>
new gridjs.Grid({
columns: [
"Champion",
"Streamer Name",
{
name: "URL",
formatter: (_, row) => gridjs.html(
`<a href="${row.cells[2].data}" target="_blank">${row.cells[2].data}</a>`)
},
{
name: "Actions",
formatter: (_, row) => gridjs.html(row.cells[3].data)
}
],
data: [
@foreach ($streamers as $streamer)
["{{ $streamer->champion->name }}", "{{ $streamer->displayname }}",
"{{ $streamer->streamer_url }}", `<a href="/streamerpanel/edit/{{ $streamer->id }}">✏️</a> <a href="/streamerpanel/delete/{{ $streamer->id }}" onclick="event.preventDefault(); if (confirm('Are you sure you want to delete this streamer?')) { document.getElementById('delete-form-{{ $streamer->id }}').submit(); }"></a>
<form id="delete-form-{{ $streamer->id }}" action="/streamerpanel/delete/{{ $streamer->id }}" method="POST" style="display: none;">
@csrf
@method('DELETE')
</form>`
],
@endforeach
],
search: true,
pagination: {
limit: 20
},
language: {
'search': {
'placeholder': '🔍 Search streamers...'
}
}
}).render(document.getElementById("streamers-wrapper"));
</script>
@endpush

View File

@@ -0,0 +1,58 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- DarkReader somehow thinks the website isn't dark by default,
this tells darkreader to disable on the site. -->
<meta name="darkreader-lock">
<META NAME="robots" CONTENT="noindex,nofollow">
<!-- Favicon -->
<link rel="apple-touch-icon" sizes="180x180" href="/img/icons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/img/icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/img/icons/favicon-16x16.png">
<link rel="manifest" href="/img/icons/site.webmanifest">
<link rel="mask-icon" href="/img/icons/safari-pinned-tab.svg" color="#e6855e">
<link rel="shortcut icon" href="/img/icons/favicon.ico">
<meta name="msapplication-TileColor" content="#ff7c47">
<meta name="msapplication-config" content="/img/icons/browserconfig.xml">
<meta name="theme-color" content="#ff7c47">
<title>@yield('title')</title>
<meta name="description" content="@yield('description')">
@stack('meta_tags')
<!-- OpenGraph -->
<meta property="og:site_name" content="Heimerdinger.LoL">
<meta property="og:title" content="@yield('title')">
<meta property="og:url" content="{{ url()->current() }}">
<meta property="og:description" content="@yield('description')">
<meta property="og:locale" content="en">
<meta property="og:type" content="website">
<meta property="og:image" content="https://cdn.heimerdinger.lol/og-img-home.png">
<!-- Twitter -->
<meta name="twitter:card" content="summary_large_image">
<meta property="twitter:domain" content="heimerdinger.lol">
<meta property="twitter:title" content="@yield('title')">
<meta property="twitter:description" content="@yield('description')">
<meta property="twitter:image" content="https://cdn.heimerdinger.lol/og-img-home.png">
@vite(['resources/css/app.css', 'resources/js/app.js'])
@stack('top_scripts')
<x-analytics.plausible />
</head>
<body class="antialiased bg-stone-900 dark scroll-smooth">
<x-panel-navbar />
@yield('content')
@stack('bottom_scripts')
</body>
</html>

View File

@@ -5,7 +5,7 @@
@section('description', 'Heimerdinger.LoL: ' . $post->description) @section('description', 'Heimerdinger.LoL: ' . $post->description)
@push('meta_tags') @push('meta_tags')
<link rel="canonical" href="{{config('app.HEIMER_URL') . '/post/' . $post->slug}}"> <link rel="canonical" href="{{ config('app.HEIMER_URL') . '/post/' . $post->slug }}">
<meta name="author" content="Heimerdinger.LoL"> <meta name="author" content="Heimerdinger.LoL">
<meta name="robots" content="index, follow"> <meta name="robots" content="index, follow">
@@ -13,35 +13,32 @@
@endpush @endpush
@section('content') @section('content')
<a href="{{route('posts.index')}}" <a href="{{ route('posts.index') }}"
class="block mt-8 text-center text-orange-400 text-sm uppercase font-medium hover:underline">Back class="block mt-8 text-sm font-medium text-center text-orange-400 uppercase hover:underline">Back
to to
posts</a> posts</a>
<article class="max-w-screen-md mx-auto mt-2 prose prose-stone prose-invert" itemscope <article class="max-w-screen-md mx-auto mt-2 prose prose-stone prose-invert" itemscope
itemtype="https://schema.org/BlogPosting" itemtype="https://schema.org/BlogPosting" itemid="{{ url()->current() }}">
itemid="{{url()->current()}}"> <meta itemprop="wordCount" content="{{ str_word_count($post->contents) }}">
<meta itemprop="wordCount" content="{{str_word_count($post->contents)}}"> <h3 class="text-sm font-semibold text-center text-orange-100 not-prose" itemprop="datePublished">
<h3 class="not-prose text-sm text-center text-orange-100 font-semibold" itemprop="datePublished">
{{ Carbon::parse($post->date)->format('F d, Y') }} {{ Carbon::parse($post->date)->format('F d, Y') }}
</h3> </h3>
<img src="{{$post->thumbnail}}" alt="{{$post->title}} Thumbnail" <img src="{{ $post->thumbnail }}" alt="{{ $post->title }} Thumbnail"
class="not-prose aspect-video max-h-64 w-auto mt-2 mb-2 mx-auto rounded-3xl border-orange-500/40 border-2"/> class="w-auto mx-auto mt-2 mb-2 border-2 not-prose aspect-video max-h-64 rounded-3xl border-orange-500/40" />
<meta itemprop="thumbnailUrl" content="{{$post->thumbnail}}"/> <meta itemprop="thumbnailUrl" content="{{ $post->thumbnail }}" />
<div> <div>
<h1 <h1 class="text-3xl font-bold text-center text-transparent uppercase not-prose sm:text-4xl bg-gradient-to-bl from-orange-300 to-orange-500 bg-clip-text"
class="not-prose text-3xl font-bold text-center text-transparent uppercase sm:text-4xl bg-gradient-to-bl from-orange-300 to-orange-500 bg-clip-text" itemprop="headline"> itemprop="headline">
{{$post->title}}</h1> {{ $post->title }}</h1>
<h2 class="not-prose text-center text-orange-400 text-sm italic" itemprop="description"> <h2 class="text-sm italic text-center text-orange-400 not-prose" itemprop="description">
{{ $post->description }} {{ $post->description }}
</h2> </h2>
<p> <p>
{{$post->contents}} {{ $post->contents }}
</p> </p>
<p class="mt-3 text-sm text-center"> <p class="mt-3 text-sm text-center">
Tagged with: <span itemprop="keywords" Tagged with: <span itemprop="keywords" class="italic">
class="italic"> {{ isset($post->tags) ? implode(', ', $post->tags) : 'League of Legends' }}</span>
{{ isset($post->tags) ? implode(', ', $post->tags)
: 'League of Legends' }}</span>
</p> </p>
</div> </div>
</article> </article>

View File

@@ -0,0 +1,9 @@
@extends('layouts.streamerpanel')
@section('title', 'Streamer Panel • Heimerdinger.LoL')
@section('description', 'Heimerdinger.LoL: Streamer Panel for managing your streamer requests.')
@section('content')
<x-streamerpanel.home />
<x-streamerpanel.streamerstable :streamers="$streamers" />
@endsection

View File

@@ -0,0 +1,15 @@
@extends('layouts.streamerpanel')
@section('title', 'Streamer Panel • Heimerdinger.LoL')
@section('description', 'Heimerdinger.LoL: Streamer Panel for managing your streamer requests.')
@section('content')
<section class="max-w-screen-xl mx-auto mt-12">
<h1
class="text-2xl font-bold text-center text-transparent uppercase sm:text-4xl bg-gradient-to-bl from-orange-300 to-orange-500 bg-clip-text">
Add a new Streamer</h1>
<x-streamerpanel.streamer-create-form :champions="$champions" />
</section>
@endsection

View File

@@ -1,12 +1,5 @@
<?php <?php
/* use App\Http\Controllers\StreamerController;
|--------------------------------------------------------------------------
| API Routes Route::get('/streamers', [StreamerController::class, 'all']);
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "api" middleware group. Make something great!
|
*/

View File

@@ -15,6 +15,7 @@ use App\Http\Controllers\SummonerIconController;
use App\Http\Requests\ContactSubmissionRequest; use App\Http\Requests\ContactSubmissionRequest;
use App\Models\Champion; use App\Models\Champion;
use App\Models\SummonerIcon; use App\Models\SummonerIcon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use Spatie\Honeypot\ProtectAgainstSpam; use Spatie\Honeypot\ProtectAgainstSpam;
use Spatie\Sheets\Sheet; use Spatie\Sheets\Sheet;
@@ -79,3 +80,11 @@ Route::get('/resource/sitemap', static fn () => (new HTMLSitemapController())->i
// Pulse // Pulse
Route::get(config('app.login_route'), static fn () => redirect('/pulse'))->name('login')->middleware('auth.basic'); Route::get(config('app.login_route'), static fn () => redirect('/pulse'))->name('login')->middleware('auth.basic');
// Streamer Panel
Route::get('/streamerpanel', static fn () => (new \App\Http\Controllers\StreamerPanelController())->index())->name('streamerpanel.index')->middleware('auth.basic');
Route::get('/streamerpanel/add', static fn () => (new \App\Http\Controllers\StreamerPanelController())->create())->name('streamerpanel.streamers.create')->middleware('auth.basic');
Route::post('/streamerpanel/add', static fn (Request $request) => (new \App\Http\Controllers\StreamerPanelController())->store($request))->name('streamerpanel.store')->middleware('auth.basic');
Route::get('/streamerpanel/edit/{streamer}', static fn (\App\Models\Streamer $streamer) => (new \App\Http\Controllers\StreamerPanelController())->edit($streamer))->name('streamerpanel.edit')->middleware('auth.basic');
Route::post('/streamerpanel/edit/{streamer}', static fn (Request $request, \App\Models\Streamer $streamer) => (new \App\Http\Controllers\StreamerPanelController())->update($request, $streamer))->name('streamerpanel.update')->middleware('auth.basic');
Route::delete('/streamerpanel/delete/{streamer}', static fn (\App\Models\Streamer $streamer) => (new \App\Http\Controllers\StreamerPanelController())->destroy($streamer))->name('streamerpanel.destroy')->middleware('auth.basic');

View File

@@ -5,6 +5,7 @@ module.exports = {
darkMode: 'class', darkMode: 'class',
content: [ content: [
'./resources/**/*.blade.php', './resources/**/*.blade.php',
'./resources/**/**/*.blade.php',
'./resources/**/*.js', './resources/**/*.js',
'./resources/**/*.vue', './resources/**/*.vue',
'./node_modules/flowbite/**/*.js', './node_modules/flowbite/**/*.js',
@@ -25,11 +26,15 @@ module.exports = {
fontFamily: { fontFamily: {
sans: ['Inter var', 'Inter', 'sans-serif', ...defaultTheme.fontFamily.sans], sans: ['Inter var', 'Inter', 'sans-serif', ...defaultTheme.fontFamily.sans],
}, },
colors: {
twitch: '#6441a5',
youtube: '#FF0000',
kick: '#53fc18',
douyu: '#ff5f3a',
huya: '#ffaa06'
}
}, },
}, },
corePlugins: {
aspectRatio: false,
},
variants: { variants: {
extend: { extend: {
textColor: ['group-hover'], textColor: ['group-hover'],
@@ -37,7 +42,6 @@ module.exports = {
}, },
plugins: [ plugins: [
require('flowbite/plugin'), require('flowbite/plugin'),
require('@tailwindcss/aspect-ratio'),
require('tailwind-capitalize-first-letter'), require('tailwind-capitalize-first-letter'),
require('@tailwindcss/typography'), require('@tailwindcss/typography'),
], ],