Compare commits
No commits in common. "master" and "v0.1.2" have entirely different histories.
13
.env.example
|
|
@ -1,3 +1,4 @@
|
|||
APP_NAME=Laravel
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
|
|
@ -7,11 +8,11 @@ LOG_CHANNEL=stack
|
|||
LOG_LEVEL=debug
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=laravel
|
||||
DB_USERNAME=sail
|
||||
DB_PASSWORD=password
|
||||
DB_USERNAME=root
|
||||
DB_PASSWORD=
|
||||
|
||||
BROADCAST_DRIVER=log
|
||||
CACHE_DRIVER=file
|
||||
|
|
@ -49,6 +50,6 @@ PUSHER_APP_CLUSTER=mt1
|
|||
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||
|
||||
DISCORD_CLIENT_ID=
|
||||
DISCORD_CLIENT_SECRET=
|
||||
DISCORD_REDIRECT_URI=
|
||||
DISCORD_CLIENT_ID =
|
||||
DISCORD_CLIENT_SECRET =
|
||||
DISCORD_REDIRECT_URI =
|
||||
|
|
|
|||
97
README.md
|
|
@ -1,77 +1,62 @@
|
|||
# Heritage WoW
|
||||
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400"></a></p>
|
||||
|
||||
Heritage WoW is a Laravel application for managing World of Warcraft characters and profession data.
|
||||
It supports importing profession exports, browsing recipes/items, and account login with username/password or Discord OAuth.
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.org/laravel/framework"><img src="https://travis-ci.org/laravel/framework.svg" alt="Build Status"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/dt/laravel/framework" alt="Total Downloads"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/v/laravel/framework" alt="Latest Stable Version"></a>
|
||||
<a href="https://packagist.org/packages/laravel/framework"><img src="https://img.shields.io/packagist/l/laravel/framework" alt="License"></a>
|
||||
</p>
|
||||
|
||||
## Features
|
||||
## About Laravel
|
||||
|
||||
- Character pages with owned professions and recipes
|
||||
- Profession import pipeline (queued job) for sync/update
|
||||
- Recipe and item listing pages
|
||||
- Local authentication and Discord OAuth login
|
||||
- Admin-only user management routes
|
||||
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
|
||||
|
||||
## Stack
|
||||
- [Simple, fast routing engine](https://laravel.com/docs/routing).
|
||||
- [Powerful dependency injection container](https://laravel.com/docs/container).
|
||||
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
|
||||
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
|
||||
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
|
||||
- [Robust background job processing](https://laravel.com/docs/queues).
|
||||
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
|
||||
|
||||
- PHP/Laravel: Laravel 8
|
||||
- Frontend: Blade + Tailwind CSS + Alpine.js
|
||||
- Build tooling: Laravel Mix
|
||||
- Database: MySQL (default in `.env.example`)
|
||||
Laravel is accessible, powerful, and provides tools required for large, robust applications.
|
||||
|
||||
## Local Setup
|
||||
## Learning Laravel
|
||||
|
||||
1. Install dependencies:
|
||||
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
|
||||
|
||||
```bash
|
||||
composer install
|
||||
npm install
|
||||
```
|
||||
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains over 1500 video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
|
||||
|
||||
2. Configure environment:
|
||||
## Laravel Sponsors
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
php artisan key:generate
|
||||
```
|
||||
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the Laravel [Patreon page](https://patreon.com/taylorotwell).
|
||||
|
||||
3. Update database and OAuth values in `.env` (minimum: DB_*).
|
||||
### Premium Partners
|
||||
|
||||
4. Run migrations and seeders:
|
||||
- **[Vehikl](https://vehikl.com/)**
|
||||
- **[Tighten Co.](https://tighten.co)**
|
||||
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
|
||||
- **[64 Robots](https://64robots.com)**
|
||||
- **[Cubet Techno Labs](https://cubettech.com)**
|
||||
- **[Cyber-Duck](https://cyber-duck.co.uk)**
|
||||
- **[Many](https://www.many.co.uk)**
|
||||
- **[Webdock, Fast VPS Hosting](https://www.webdock.io/en)**
|
||||
- **[DevSquad](https://devsquad.com)**
|
||||
- **[Curotec](https://www.curotec.com/services/technologies/laravel/)**
|
||||
- **[OP.GG](https://op.gg)**
|
||||
|
||||
```bash
|
||||
php artisan migrate --seed
|
||||
```
|
||||
## Contributing
|
||||
|
||||
5. Start the app and asset pipeline:
|
||||
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
|
||||
|
||||
```bash
|
||||
php artisan serve
|
||||
npm run dev
|
||||
```
|
||||
## Code of Conduct
|
||||
|
||||
6. If using an async queue connection for imports, run a worker:
|
||||
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
|
||||
|
||||
```bash
|
||||
php artisan queue:work
|
||||
```
|
||||
## Security Vulnerabilities
|
||||
|
||||
## Useful Commands
|
||||
|
||||
- Run tests: `php artisan test`
|
||||
- Build production assets: `npm run prod`
|
||||
- Create a user: `php artisan user:create <username> <password>`
|
||||
- Create an admin user: `php artisan user:create <username> <password> --admin`
|
||||
|
||||
## OAuth (Discord)
|
||||
|
||||
To enable Discord login, set these variables in `.env`:
|
||||
|
||||
- `DISCORD_CLIENT_ID`
|
||||
- `DISCORD_CLIENT_SECRET`
|
||||
- `DISCORD_REDIRECT_URI`
|
||||
|
||||
The callback route is `/oauth/discord/callback`.
|
||||
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
|
||||
|
||||
## License
|
||||
|
||||
This project is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Http\Requests\Admin\UserRequest;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return view('admin.user.index');
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
return view('admin.user.form');
|
||||
}
|
||||
|
||||
public function edit(User $user)
|
||||
{
|
||||
return view('admin.user.form', [
|
||||
'model' => $user
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete user.
|
||||
*/
|
||||
public function destroy(User $user)
|
||||
{
|
||||
$user->delete();
|
||||
|
||||
return redirect()->route('admin.users.index')
|
||||
->with(['success' => "<strong>{$user->username}</strong> was deleted!"]);
|
||||
}
|
||||
}
|
||||
|
|
@ -30,12 +30,6 @@ class OAuthController extends Controller
|
|||
{
|
||||
$oauth = Socialite::driver($driver)->stateless()->user();
|
||||
|
||||
// Check that account is not deleted before trying to find/create user and login.
|
||||
if (User::onlyTrashed()->where("{$driver}_id", $oauth->getId())->first()) {
|
||||
return redirect('/')
|
||||
->with(['error' => "Account suspended."]);
|
||||
}
|
||||
|
||||
$user = User::firstOrCreate([ "{$driver}_id" => $oauth->getId() ], [
|
||||
'username' => $oauth->getNickname()
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use App\Models\Character;
|
|||
use App\Models\Profession;
|
||||
use App\Models\Recipe;
|
||||
use App\Models\CharacterProfession;
|
||||
use App\Http\Requests\ProfessionImportRequest;
|
||||
use App\Http\Requests\CharacterProfessionRequest;
|
||||
use App\Jobs\ImportProfession;
|
||||
|
||||
class CharacterProfessionController extends Controller
|
||||
|
|
@ -32,7 +32,7 @@ class CharacterProfessionController extends Controller
|
|||
]);
|
||||
}
|
||||
|
||||
public function store(Character $character, ProfessionImportRequest $request)
|
||||
public function store(Character $character, CharacterProfessionRequest $request)
|
||||
{
|
||||
$this->authorize('import_profession', $character);
|
||||
|
||||
|
|
@ -41,7 +41,7 @@ class CharacterProfessionController extends Controller
|
|||
$data = json_decode($request->input('data'));
|
||||
|
||||
try {
|
||||
ImportProfession::dispatch($data, $request->user(), $character);
|
||||
ImportProfession::dispatch($character, $data);
|
||||
} catch(\App\ProfessionImport\Exception $e) {
|
||||
return redirect()->back()->with('error', $e->getMessage());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Item;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class ItemController extends Controller
|
||||
{
|
||||
/**
|
||||
* List all recipies
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('item.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a recipe
|
||||
*/
|
||||
public function show(Item $item)
|
||||
{
|
||||
return view('item.show', compact('item'));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class LocaleSessionController extends Controller
|
||||
{
|
||||
/**
|
||||
* Set locale
|
||||
*/
|
||||
public function store(Request $request, $locale)
|
||||
{
|
||||
if (array_key_exists($locale, config('lang'))) {
|
||||
$request->session()->put('locale', $locale);
|
||||
}
|
||||
return redirect()->back();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\ProfessionImportRequest;
|
||||
use App\Jobs\ImportProfession;
|
||||
|
||||
class ProfessionController extends Controller
|
||||
{
|
||||
public function create()
|
||||
{
|
||||
return view('profession.create');
|
||||
}
|
||||
|
||||
public function store(ProfessionImportRequest $request)
|
||||
{
|
||||
$request->validated();
|
||||
|
||||
$data = json_decode($request->input('data'));
|
||||
|
||||
try {
|
||||
ImportProfession::dispatch($data, $request->user());
|
||||
} catch(\App\ProfessionImport\Exception $e) {
|
||||
return redirect()->back()->with('error', $e->getMessage());
|
||||
}
|
||||
return redirect()->back()->with('success', 'Profession imported!');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\UserRequest;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
return view('profile.index', [
|
||||
'user' => Auth::user(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit(Request $request)
|
||||
{
|
||||
return view('profile.edit', [
|
||||
'user' => Auth::user(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(UserRequest $request)
|
||||
{
|
||||
$data = collect($request->validated());
|
||||
|
||||
$user = $request->user();
|
||||
$user->fill($data->except('password')->toArray());
|
||||
if (strlen($data['password'])) {
|
||||
$user->password = Hash::make($data['password']);
|
||||
}
|
||||
$user->save();
|
||||
|
||||
return redirect()->route('profile.index')
|
||||
->with('success', 'Your account was updated!');
|
||||
}
|
||||
}
|
||||
|
|
@ -2,17 +2,37 @@
|
|||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\User;
|
||||
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\UserRequest;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
/**
|
||||
* Show user page
|
||||
*/
|
||||
public function show(User $user)
|
||||
public function index(Request $request)
|
||||
{
|
||||
return view('user.show', compact('user'));
|
||||
return view('user.index', [
|
||||
'user' => Auth::user(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit(Request $request)
|
||||
{
|
||||
return view('user.edit', [
|
||||
'user' => Auth::user(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(UserRequest $request)
|
||||
{
|
||||
$data = $request->validated();
|
||||
|
||||
$user = $request->user();
|
||||
$user->username = $data['username'];
|
||||
$user->password = Hash::make($data['password']);
|
||||
$user->save();
|
||||
|
||||
return redirect()->route('user.index')
|
||||
->with('success', 'Your account was updated!');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ class Kernel extends HttpKernel
|
|||
\App\Http\Middleware\EncryptCookies::class,
|
||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
\App\Http\Middleware\Locale::class,
|
||||
// \Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Models\User;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class AdminUserTable extends Component
|
||||
{
|
||||
use Traits\WithPagination;
|
||||
|
||||
protected $queryString = [
|
||||
'username' => ['except' => ''],
|
||||
'role' => ['except' => '']
|
||||
];
|
||||
|
||||
/**
|
||||
* Filter by username
|
||||
*/
|
||||
public $username;
|
||||
|
||||
/**
|
||||
* Filter by role
|
||||
*/
|
||||
public $role;
|
||||
|
||||
public function render()
|
||||
{
|
||||
$query = User::query()->orderBy('username');
|
||||
|
||||
if (strlen($this->username) >= 3) {
|
||||
$query->where('username', 'LIKE', '%' . $this->username . '%');
|
||||
}
|
||||
|
||||
if (in_array($this->role, ['user','admin'])) {
|
||||
$query->where('role', $this->role);
|
||||
}
|
||||
|
||||
return view('livewire.admin-user-table', [
|
||||
'users' => $query->paginate($this->perPage)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Livewire\Form\Admin;
|
||||
|
||||
use App\Models\User;
|
||||
|
||||
use Livewire\Component;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class UserForm extends Component
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
||||
/**
|
||||
* User object.
|
||||
*/
|
||||
public User $user;
|
||||
|
||||
/**
|
||||
* Password field
|
||||
*/
|
||||
public $password;
|
||||
|
||||
/**
|
||||
* Password confirmation field.
|
||||
*/
|
||||
public $password_confirmation;
|
||||
|
||||
/**
|
||||
* Initialize the component.
|
||||
*/
|
||||
public function mount(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'user.username' => ['required', 'min:4', Rule::unique('users', 'username')->ignore($this->user) ],
|
||||
'user.role' => 'required|in:user,admin',
|
||||
'password' => ($this->user->exists ? 'nullable' : 'required') . '|min:8|confirmed',
|
||||
];
|
||||
}
|
||||
|
||||
public function updated($property)
|
||||
{
|
||||
$this->validateOnly($property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the user
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$this->authorize('administrate');
|
||||
|
||||
$this->validate();
|
||||
|
||||
if ($this->password) {
|
||||
$this->user->password = Hash::make($this->password);
|
||||
}
|
||||
$this->user->save();
|
||||
|
||||
// Livewire redirect() does not have "with" method.
|
||||
// so we call session()->flash() directly instead.
|
||||
session()->flash('success', "<strong>{$this->user->username}</strong> was "
|
||||
. ($this->user->exists ? "updated!" : "created!"));
|
||||
return redirect()->route('admin.users.index');
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.form.admin.user');
|
||||
}
|
||||
}
|
||||
|
|
@ -45,7 +45,7 @@ class CharacterForm extends Component
|
|||
public function rules()
|
||||
{
|
||||
return [
|
||||
'character.name' => [ 'required', 'min:4', Rule::unique('characters', 'name')->ignore($this->character)->whereNotNull('deleted_at') ],
|
||||
'character.name' => [ 'required', 'min:4', Rule::unique('characters', 'name')->ignore($this->character) ],
|
||||
'character.level' => [ 'required', 'integer', 'min:1', 'max:70' ],
|
||||
'character.gender' => [ 'required', 'in:M,F' ],
|
||||
'character.race' => [ 'required', 'in:' . $this->races->keys()->join(',') ],
|
||||
|
|
@ -109,7 +109,7 @@ class CharacterForm extends Component
|
|||
// Livewire redirect() does not have "with" method.
|
||||
// so we call session()->flash() directly instead.
|
||||
session()->flash('success', "<strong>{$this->character->name}</strong> was {$action}!");
|
||||
return redirect()->route('profile.index');
|
||||
return redirect()->route('user.index');
|
||||
}
|
||||
|
||||
public function render()
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Models\Item;
|
||||
|
||||
use Livewire\Component;
|
||||
|
||||
class Items extends Component
|
||||
{
|
||||
use Traits\WithPagination;
|
||||
|
||||
protected $queryString = [
|
||||
'name' => ['except' => ''],
|
||||
'reagent_for' => ['except' => false],
|
||||
];
|
||||
|
||||
/**
|
||||
* Filter by name
|
||||
*/
|
||||
public string $name = '';
|
||||
|
||||
public $reagent_for = false;
|
||||
|
||||
public function render()
|
||||
{
|
||||
$query = Item::query()
|
||||
->with(['reagent_for']);
|
||||
|
||||
if ($this->reagent_for) {
|
||||
$query->has('reagent_for');
|
||||
}
|
||||
|
||||
// Filter by name
|
||||
if (strlen($this->name) >= 3) {
|
||||
$query->where('name', 'LIKE', '%' . $this->name . '%');
|
||||
}
|
||||
|
||||
return view('livewire.items', [
|
||||
'items' => $query->paginate($this->perPage)
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -46,7 +46,7 @@ class Recipes extends Component
|
|||
return [Str::lower($item['name']) => $item['name']];
|
||||
});
|
||||
|
||||
$this->profession_options = collect(['' => '-- ' . __('Profession') . ' --'])
|
||||
$this->profession_options = collect(['' => '-- Profession --'])
|
||||
->merge($options)
|
||||
->toArray();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
|
||||
|
||||
use Closure;
|
||||
|
||||
class Locale extends Middleware
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
if (session()->has('locale')) {
|
||||
app()->setlocale(session()->get('locale'));
|
||||
}
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ namespace App\Http\Requests;
|
|||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ProfessionImportRequest extends FormRequest
|
||||
class CharacterProfessionRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
|
|
@ -36,12 +36,8 @@ class UserRequest extends FormRequest
|
|||
*/
|
||||
public function rules()
|
||||
{
|
||||
$user = auth()->user();
|
||||
$character_ids = $user->characters->pluck('id');
|
||||
|
||||
return [
|
||||
'username' => [ 'required', 'min:4', Rule::unique('users')->ignore(auth()->user()) ],
|
||||
'character_id' => 'nullable|in:' . $character_ids->join(','),
|
||||
'current_password' => 'nullable|required_with:password|current_password',
|
||||
'password' => 'nullable|min:8|confirmed',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Character;
|
||||
use App\Models\Profession;
|
||||
use App\Models\CharacterProfession;
|
||||
|
|
@ -22,8 +21,6 @@ use Illuminate\Queue\SerializesModels;
|
|||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
use Exception;
|
||||
|
||||
class ImportProfession implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
|
@ -43,22 +40,12 @@ class ImportProfession implements ShouldQueue
|
|||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($data, User $user, ?Character $character = null)
|
||||
public function __construct(Character $character, $data)
|
||||
{
|
||||
// Validate character name first.
|
||||
if ($character) {
|
||||
if ($character->name !== $data->player) {
|
||||
$message = sprintf('Wrong character: %s expected %s', $data->player, $character->name);
|
||||
throw new InvalidCharacterException($message);
|
||||
}
|
||||
} else {
|
||||
$character = Character::where('name', $data->player)
|
||||
->where('user_id', $user->id)->first();
|
||||
|
||||
if (!$character) {
|
||||
$message = sprintf('Could not find character "%s"', $data->player);
|
||||
throw new InvalidCharacterException($message);
|
||||
}
|
||||
if ($character->name !== $data->player) {
|
||||
$message = sprintf('Wrong character: %s expected %s', $data->player, $character->name);
|
||||
throw new InvalidCharacterException($message);
|
||||
}
|
||||
|
||||
// Validate profession
|
||||
|
|
@ -165,7 +152,7 @@ class ImportProfession implements ShouldQueue
|
|||
// names for reagents. so we skip those.
|
||||
|
||||
// 2021-07-08: Also skip items without id.
|
||||
if (!isset($item->name) || !isset($item->id)) {
|
||||
if (!isset($item->name) || !isset($items->id)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
|
@ -202,8 +189,7 @@ class ImportProfession implements ShouldQueue
|
|||
$recipe = $profession->recipes()->create([
|
||||
'item_id' => $crafted ? $crafted->id : null,
|
||||
'spell_id' => $spell->id,
|
||||
'category_id' => $category->id,
|
||||
'description' => isset($data->description) ? $data->description : null
|
||||
'category_id' => $category->id
|
||||
]);
|
||||
}
|
||||
// Existing record.
|
||||
|
|
@ -217,10 +203,6 @@ class ImportProfession implements ShouldQueue
|
|||
if (!$recipe->spell) {
|
||||
$recipe->spell()->associate($spell);
|
||||
}
|
||||
|
||||
if ($recipe->description === NULL && isset($data->description)) {
|
||||
$recipe->description = $data->description;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert/Update Reagents
|
||||
|
|
|
|||
|
|
@ -4,12 +4,11 @@ namespace App\Models;
|
|||
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Character extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
use HasFactory;
|
||||
|
||||
public $timestamps = false;
|
||||
|
||||
|
|
@ -26,20 +25,6 @@ class Character extends Model
|
|||
'class'
|
||||
];
|
||||
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::deleted(function($character) {
|
||||
|
||||
// Unset main character.
|
||||
if ($character->isMain()) {
|
||||
$character->user->main_character()->dissociate();
|
||||
$character->user->save();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the route key for the model.
|
||||
*
|
||||
|
|
@ -81,14 +66,6 @@ class Character extends Model
|
|||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this character is the user's main character
|
||||
*/
|
||||
public function isMain()
|
||||
{
|
||||
return $this->user->character_id == $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the character's professions.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -32,28 +32,9 @@ class Item extends Model
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the route key for the model.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRouteKeyName()
|
||||
public function recipe()
|
||||
{
|
||||
return 'slug';
|
||||
}
|
||||
|
||||
public function recipes()
|
||||
{
|
||||
return $this->hasMany(Recipe::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get recipes that this item is reagent for.
|
||||
*/
|
||||
public function reagent_for()
|
||||
{
|
||||
return $this->belongsToMany(Recipe::class, 'reagents')
|
||||
->withPivot('quantity');
|
||||
return $this->belongsTo(Recipe::class, 'id');
|
||||
}
|
||||
|
||||
public function getQuantityAttribute()
|
||||
|
|
|
|||
|
|
@ -34,8 +34,7 @@ class Recipe extends Model
|
|||
'profession_id',
|
||||
'category_id',
|
||||
'item_id',
|
||||
'spell_id',
|
||||
'description'
|
||||
'spell_id'
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -4,13 +4,12 @@ namespace App\Models;
|
|||
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
use HasFactory, Notifiable, SoftDeletes;
|
||||
use HasFactory, Notifiable;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
|
|
@ -21,8 +20,7 @@ class User extends Authenticatable
|
|||
'discord_id',
|
||||
'username',
|
||||
'password',
|
||||
'role',
|
||||
'character_id'
|
||||
'role'
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -44,46 +42,8 @@ class User extends Authenticatable
|
|||
'email_verified_at' => 'datetime',
|
||||
];
|
||||
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::deleted(function ($user) {
|
||||
$user->characters()->delete();
|
||||
});
|
||||
}
|
||||
|
||||
public function main_character()
|
||||
{
|
||||
return $this->belongsTo(Character::class, 'character_id');
|
||||
}
|
||||
|
||||
public function alt_characters()
|
||||
{
|
||||
$relation = $this->hasMany(Character::class);
|
||||
|
||||
if ($this->character_id) {
|
||||
$relation->where('id', '!=', $this->character_id);
|
||||
}
|
||||
return $relation;
|
||||
}
|
||||
|
||||
public function characters()
|
||||
{
|
||||
$relation = $this->hasMany(Character::class);
|
||||
|
||||
// sort main character first if set.
|
||||
if ($this->character_id) {
|
||||
$relation->orderByRaw('id = ' . $this->character_id . ' DESC');
|
||||
}
|
||||
return $relation;
|
||||
}
|
||||
|
||||
public function getRoleAttribute()
|
||||
{
|
||||
if (!$this->attributes['role']) {
|
||||
return 'user';
|
||||
}
|
||||
return $this->attributes['role'];
|
||||
return $this->hasMany(Character::class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace App\Providers;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
|
|
@ -26,9 +25,5 @@ class AuthServiceProvider extends ServiceProvider
|
|||
public function boot()
|
||||
{
|
||||
$this->registerPolicies();
|
||||
|
||||
Gate::define('administrate', function(User $user) {
|
||||
return $user->role === 'admin';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ class BladeServiceProvider extends ServiceProvider
|
|||
'input-password' => \App\View\Components\Form\Password::class,
|
||||
'textarea' => \App\View\Components\Form\Textarea::class,
|
||||
'select' => \App\View\Components\Form\Select::class,
|
||||
'checkbox' => \App\View\Components\Form\Checkbox::class,
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -6,5 +6,5 @@ use Illuminate\View\Component;
|
|||
|
||||
class ClassIcon extends Icon
|
||||
{
|
||||
protected string $prefix = 'classes';
|
||||
protected $prefix = 'classes';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\View\Components\Form;
|
||||
|
||||
class Checkbox extends Input
|
||||
{
|
||||
public function __construct($name, ?string $id = null, ?string $value = null, bool $disabled = false)
|
||||
{
|
||||
parent::__construct($name, $id, 'checkbox', $value, $disabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\View|\Closure|string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
return view('components.form.inputs.checkbox');
|
||||
}
|
||||
}
|
||||
|
|
@ -8,15 +8,9 @@ use Illuminate\View\Component;
|
|||
|
||||
abstract class Icon extends Component
|
||||
{
|
||||
/**
|
||||
* Path prefix.
|
||||
*/
|
||||
protected string $prefix = '';
|
||||
protected $prefix = '';
|
||||
|
||||
/**
|
||||
* Image url.
|
||||
*/
|
||||
public string $url;
|
||||
public $url;
|
||||
|
||||
/**
|
||||
* Create a new component instance.
|
||||
|
|
@ -25,11 +19,7 @@ abstract class Icon extends Component
|
|||
*/
|
||||
public function __construct(string $name)
|
||||
{
|
||||
$file = Str::of($name)->lower()->append('.jpg');
|
||||
if (strlen($this->prefix)) {
|
||||
$file = collect([$this->prefix, $file])->join('/');
|
||||
}
|
||||
|
||||
$file = sprintf('%s/%s.jpg', $this->prefix, Str::lower($name));
|
||||
$this->url = Storage::disk('images')->url($file);
|
||||
}
|
||||
|
||||
|
|
@ -40,6 +30,6 @@ abstract class Icon extends Component
|
|||
*/
|
||||
public function render()
|
||||
{
|
||||
return view('components.image-icon');
|
||||
return view('components.class-icon');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,16 @@ class Notifications extends Component
|
|||
*/
|
||||
public string $position = 'center';
|
||||
|
||||
/**
|
||||
* Type and css class mapping.
|
||||
*/
|
||||
protected $cssClasses = [
|
||||
'info' => 'info',
|
||||
'success' => 'success',
|
||||
'warning' => 'warning',
|
||||
'error' => 'danger'
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the view / contents that represent the component.
|
||||
*
|
||||
|
|
@ -33,7 +43,8 @@ class Notifications extends Component
|
|||
foreach($this->types as $type) {
|
||||
|
||||
if (Session::has($type)) {
|
||||
$messages[$type] = Session::get($type);
|
||||
$class = $this->cssClasses[$type];
|
||||
$messages[$class] = Session::get($type);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,5 +4,5 @@ namespace App\View\Components;
|
|||
|
||||
class ProfessionIcon extends Icon
|
||||
{
|
||||
protected string $prefix = 'professions';
|
||||
protected $prefix = 'professions';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\View\Components;
|
||||
|
||||
use App\Models\Character;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class RaceIcon extends Icon
|
||||
{
|
||||
protected string $prefix = 'races';
|
||||
|
||||
/**
|
||||
* Create a new component instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Character $character)
|
||||
{
|
||||
$race = Str::of($character->race)->lower()->replace(' ', '');
|
||||
$name = sprintf("%s_%s", $race, $character->gender);
|
||||
|
||||
parent::__construct($name);
|
||||
}
|
||||
}
|
||||
3475
composer.lock
generated
|
|
@ -1,17 +0,0 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Language configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Define available languages as locale (key), name (value) pairs.
|
||||
|
|
||||
*/
|
||||
|
||||
'en' => 'English',
|
||||
'se' => 'Svenska',
|
||||
|
||||
];
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddDescriptionColumnToRecipesTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('recipes', function (Blueprint $table) {
|
||||
$table->text('description')->nullable();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddSoftdeleteToUsersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropUnique(['username']);
|
||||
$table->dropUnique(['discord_id']);
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddSoftdeleteToCharactersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('characters', function (Blueprint $table) {
|
||||
$table->dropUnique(['name']);
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class AddCharacterIdToUsersTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->foreignId('character_id')
|
||||
->after('discord_id')
|
||||
->nullable()
|
||||
->constrained();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ class DatabaseSeeder extends Seeder
|
|||
$this->call(ProductionSeeders\ProfessionsTableSeeder::class);
|
||||
|
||||
$this->call(DevelopmentSeeders\UserTableSeeder::class);
|
||||
$this->call(DevelopmentSeeders\CharacterTableSeeder::class);
|
||||
$this->call(DevelopmentSeeders\ItemTableSeeder::class);
|
||||
$this->call(DevelopmentSeeders\RecipeCategoryTableSeeder::class);
|
||||
$this->call(DevelopmentSeeders\RecipeTableSeeder::class);
|
||||
|
|
|
|||
27
database/seeders/DevelopmentSeeders/CharacterTableSeeder.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders\DevelopmentSeeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
use App\Models\Profession;
|
||||
use App\Models\Character;
|
||||
use App\Models\CharacterProfession;
|
||||
|
||||
class CharacterTableSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Seed the application's database.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
for($i = 0; $i < 50; $i++) {
|
||||
|
||||
Character::factory()
|
||||
->hasRandomExistingProfessions(2)
|
||||
->create();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ namespace Database\Seeders\DevelopmentSeeders;
|
|||
use Illuminate\Database\Seeder;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Character;
|
||||
|
||||
class UserTableSeeder extends Seeder
|
||||
{
|
||||
|
|
@ -20,17 +19,5 @@ class UserTableSeeder extends Seeder
|
|||
'username' => 'admin',
|
||||
'role' => 'admin',
|
||||
]);
|
||||
|
||||
$users = User::factory()->count(50)
|
||||
->has(Character::factory()
|
||||
->hasRandomExistingProfessions(2)
|
||||
->count(3))
|
||||
->create();
|
||||
|
||||
foreach($users as $user) {
|
||||
$character = $user->characters->random();
|
||||
$user->main_character()->associate($character);
|
||||
$user->push();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,56 +0,0 @@
|
|||
services:
|
||||
laravel.test:
|
||||
build:
|
||||
context: ./vendor/laravel/sail/runtimes/8.2
|
||||
dockerfile: Dockerfile
|
||||
image: sail-8.2/app
|
||||
extra_hosts:
|
||||
- 'host.docker.internal:host-gateway'
|
||||
ports:
|
||||
- '${APP_PORT:-80}:80'
|
||||
- '${VITE_PORT:-5173}:${VITE_PORT:-5173}'
|
||||
environment:
|
||||
WWWUSER: '${WWWUSER:-1000}'
|
||||
WWWGROUP: '${WWWGROUP:-1000}'
|
||||
LARAVEL_SAIL: 1
|
||||
XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}'
|
||||
XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}'
|
||||
IGNITION_LOCAL_SITES_PATH: '${PWD}'
|
||||
volumes:
|
||||
- '.:/var/www/html:z'
|
||||
networks:
|
||||
- sail
|
||||
depends_on:
|
||||
- mysql
|
||||
mysql:
|
||||
image: 'heritage/mysql:8.0'
|
||||
build:
|
||||
context: './docker/mysql'
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- '${FORWARD_DB_PORT:-3306}:3306'
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
|
||||
MYSQL_ROOT_HOST: '%'
|
||||
MYSQL_DATABASE: '${DB_DATABASE}'
|
||||
MYSQL_USER: '${DB_USERNAME}'
|
||||
MYSQL_PASSWORD: '${DB_PASSWORD}'
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: 1
|
||||
volumes:
|
||||
- 'sail-mysql:/var/lib/mysql'
|
||||
networks:
|
||||
- sail
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- mysqladmin
|
||||
- ping
|
||||
- '-p${DB_PASSWORD}'
|
||||
retries: 3
|
||||
timeout: 5s
|
||||
networks:
|
||||
sail:
|
||||
driver: bridge
|
||||
volumes:
|
||||
sail-mysql:
|
||||
driver: local
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
FROM mysql/mysql-server:8.0
|
||||
COPY create-testing-database.sh /docker-entrypoint-initdb.d/10-create-testing-database.sh
|
||||
RUN chmod +x /docker-entrypoint-initdb.d/10-create-testing-database.sh
|
||||
RUN chown -R mysql:mysql /docker-entrypoint-initdb.d
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
mysql --user=root --password="$MYSQL_ROOT_PASSWORD" <<-EOSQL
|
||||
CREATE DATABASE IF NOT EXISTS testing;
|
||||
GRANT ALL PRIVILEGES ON \`testing%\`.* TO '$MYSQL_USER'@'%';
|
||||
EOSQL
|
||||
14815
package-lock.json
generated
|
|
@ -10,10 +10,9 @@
|
|||
"production": "mix --production"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/forms": "^0.3.3",
|
||||
"autoprefixer": "^10.2.5",
|
||||
"axios": "^0.21",
|
||||
"laravel-mix": "^6.0.49",
|
||||
"laravel-mix": "^6.0.6",
|
||||
"lodash": "^4.17.19",
|
||||
"postcss": "^8.2.14",
|
||||
"tailwindcss": "^2.1.2"
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@
|
|||
<server name="APP_ENV" value="testing"/>
|
||||
<server name="BCRYPT_ROUNDS" value="4"/>
|
||||
<server name="CACHE_DRIVER" value="array"/>
|
||||
<server name="DB_DATABASE" value="testing"/>
|
||||
<!-- <server name="DB_CONNECTION" value="sqlite"/> -->
|
||||
<!-- <server name="DB_DATABASE" value=":memory:"/> -->
|
||||
<server name="MAIL_MAILER" value="array"/>
|
||||
<server name="QUEUE_CONNECTION" value="sync"/>
|
||||
<server name="SESSION_DRIVER" value="array"/>
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 480">
|
||||
<path fill="#012169" d="M0 0h640v480H0z"/>
|
||||
<path fill="#FFF" d="M75 0l244 181L562 0h78v62L400 241l240 178v61h-80L320 301 81 480H0v-60l239-178L0 64V0h75z"/>
|
||||
<path fill="#C8102E" d="M424 281l216 159v40L369 281h55zm-184 20l6 35L54 480H0l240-179zM640 0v3L391 191l2-44L590 0h50zM0 0l239 176h-60L0 42V0z"/>
|
||||
<path fill="#FFF" d="M241 0v480h160V0H241zM0 160v160h640V160H0z"/>
|
||||
<path fill="#C8102E" d="M0 193v96h640v-96H0zM273 0v480h96V0h-96z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 516 B |
|
|
@ -1,5 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 480">
|
||||
<path fill="#066aa7" d="M0 0h640v480H0z"/>
|
||||
<path fill="#fecc00" d="M0 192h640v96H0z"/>
|
||||
<path fill="#fecc00" d="M176 0h96v480h-96z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 209 B |
|
Before Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 2 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
|
@ -1,44 +0,0 @@
|
|||
{
|
||||
"Add character": "Lägg till karaktär",
|
||||
"Character": "Karaktär",
|
||||
"Characters": "Karaktärer",
|
||||
"Class": "Klass",
|
||||
"Crafted item": "Tillverkat föremål",
|
||||
"Crafter": "Hantverkare",
|
||||
"Crafters": "Hantverkare",
|
||||
"Create": "Skapa",
|
||||
"Created by": "Skapad av",
|
||||
"Download this": "Ladda ner detta",
|
||||
"Gender": "Kön",
|
||||
"Import Profession": "Importera Yrke",
|
||||
"Import profession": "Importera yrke",
|
||||
"Import string": "Import sträng",
|
||||
"Import": "Importera",
|
||||
"Important": "Viktigt",
|
||||
"Item": "Föremål",
|
||||
"Items": "Föremål",
|
||||
"Login": "Logga in",
|
||||
"Logout": "Logga ut",
|
||||
"Name": "Namn",
|
||||
"None": "Ingen",
|
||||
"Only reagents": "Bara reagenser",
|
||||
"Owner": "Ägare",
|
||||
"Password": "Lösenord",
|
||||
"Profession": "Yrke",
|
||||
"Professions": "Yrken",
|
||||
"Race": "Ras",
|
||||
"Reagent for": "Reagens för",
|
||||
"Reagents": "Reagenser",
|
||||
"Recipe": "Recept",
|
||||
"Recipes": "Recept",
|
||||
"Roster": "Medlemmar",
|
||||
"Save": "Spara",
|
||||
"Showing": "Visar",
|
||||
"User": "Användare",
|
||||
"Username": "Användarnamn",
|
||||
"of": "av",
|
||||
"or": "eller",
|
||||
"results": "resultat",
|
||||
"to": "till",
|
||||
"to generate the import string": "för att generera import strängen"
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Language Lines
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following language lines are used during authentication for various
|
||||
| messages that we need to display to the user. You are free to modify
|
||||
| these language lines according to your application's requirements.
|
||||
|
|
||||
*/
|
||||
|
||||
'failed' => 'Dessa uppgifter stämmer inte överens med vårt register.',
|
||||
'password' => 'Lösenordet var inte korrekt',
|
||||
'throttle' => 'För många inloggningsförsök. Var vänlig försök igen om :seconds sekunder.',
|
||||
|
||||
];
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Pagination Language Lines
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following language lines are used by the paginator library to build
|
||||
| the simple pagination links. You are free to change them to anything
|
||||
| you want to customize your views to better match your application.
|
||||
|
|
||||
*/
|
||||
|
||||
'previous' => '« Föregående',
|
||||
'next' => 'Nästa »',
|
||||
|
||||
];
|
||||
|
|
@ -1,164 +0,0 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Validation Language Lines
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following language lines contain the default error messages used by
|
||||
| the validator class. Some of these rules have multiple versions such
|
||||
| as the size rules. Feel free to tweak each of these messages here.
|
||||
|
|
||||
*/
|
||||
|
||||
'accepted' => ':Attribute måste accepteras.',
|
||||
'active_url' => ':Attribute är inte en giltig webbadress.',
|
||||
'after' => ':Attribute måste vara ett datum efter :date.',
|
||||
'after_or_equal' => ':Attribute måste vara ett datum senare eller samma dag som :date.',
|
||||
'alpha' => ':Attribute får endast innehålla bokstäver.',
|
||||
'alpha_dash' => ':Attribute får endast innehålla bokstäver, siffror och bindestreck.',
|
||||
'alpha_num' => ':Attribute får endast innehålla bokstäver och siffror.',
|
||||
'array' => ':Attribute måste vara en array.',
|
||||
'attached' => 'Denna :attribute är redan bifogad.',
|
||||
'before' => ':Attribute måste vara ett datum innan :date.',
|
||||
'before_or_equal' => ':Attribute måste vara ett datum före eller samma dag som :date.',
|
||||
'between' => [
|
||||
'array' => ':Attribute måste innehålla mellan :min - :max objekt.',
|
||||
'file' => ':Attribute måste vara mellan :min till :max kilobyte stor.',
|
||||
'numeric' => ':Attribute måste vara en siffra mellan :min och :max.',
|
||||
'string' => ':Attribute måste innehålla :min till :max tecken.',
|
||||
],
|
||||
'boolean' => ':Attribute måste vara sant eller falskt.',
|
||||
'confirmed' => ':Attribute bekräftelsen matchar inte.',
|
||||
'current_password' => 'The password is incorrect.',
|
||||
'date' => ':Attribute är inte ett giltigt datum.',
|
||||
'date_equals' => ':Attribute måste vara ett datum lika med :date.',
|
||||
'date_format' => ':Attribute matchar inte formatet :format.',
|
||||
'different' => ':Attribute och :other får inte vara lika.',
|
||||
'digits' => ':Attribute måste vara :digits tecken.',
|
||||
'digits_between' => ':Attribute måste vara mellan :min och :max tecken.',
|
||||
'dimensions' => ':Attribute har felaktiga bilddimensioner.',
|
||||
'distinct' => ':Attribute innehåller fler än en repetition av samma element.',
|
||||
'email' => ':Attribute måste innehålla en korrekt e-postadress.',
|
||||
'ends_with' => ':Attribute måste sluta med en av följande: :values.',
|
||||
'exists' => ':Attribute är ogiltigt.',
|
||||
'file' => ':Attribute måste vara en fil.',
|
||||
'filled' => ':Attribute är obligatoriskt.',
|
||||
'gt' => [
|
||||
'array' => ':Attribute måste innehålla fler än :value objekt.',
|
||||
'file' => ':Attribute måste vara större än :value kilobyte stor.',
|
||||
'numeric' => ':Attribute måste vara större än :value.',
|
||||
'string' => ':Attribute måste vara längre än :value tecken.',
|
||||
],
|
||||
'gte' => [
|
||||
'array' => ':Attribute måste innehålla lika många eller fler än :value objekt.',
|
||||
'file' => ':Attribute måste vara lika med eller större än :value kilobyte stor.',
|
||||
'numeric' => ':Attribute måste vara lika med eller större än :value.',
|
||||
'string' => ':Attribute måste vara lika med eller längre än :value tecken.',
|
||||
],
|
||||
'image' => ':Attribute måste vara en bild.',
|
||||
'in' => ':Attribute är ogiltigt.',
|
||||
'in_array' => ':Attribute finns inte i :other.',
|
||||
'integer' => ':Attribute måste vara en siffra.',
|
||||
'ip' => ':Attribute måste vara en giltig IP-adress.',
|
||||
'ipv4' => ':Attribute måste vara en giltig IPv4-adress.',
|
||||
'ipv6' => ':Attribute måste vara en giltig IPv6-adress.',
|
||||
'json' => ':Attribute måste vara en giltig JSON-sträng.',
|
||||
'lt' => [
|
||||
'array' => ':Attribute måste innehålla färre än :value objekt.',
|
||||
'file' => ':Attribute måste vara mindre än :value kilobyte stor.',
|
||||
'numeric' => ':Attribute måste vara mindre än :value.',
|
||||
'string' => ':Attribute måste vara kortare än :value tecken.',
|
||||
],
|
||||
'lte' => [
|
||||
'array' => ':Attribute måste innehålla lika många eller färre än :value objekt.',
|
||||
'file' => ':Attribute måste vara lika med eller mindre än :value kilobyte stor.',
|
||||
'numeric' => ':Attribute måste vara lika med eller mindre än :value.',
|
||||
'string' => ':Attribute måste vara lika med eller kortare än :value tecken.',
|
||||
],
|
||||
'max' => [
|
||||
'array' => ':Attribute får inte innehålla mer än :max objekt.',
|
||||
'file' => ':Attribute får max vara :max kilobyte stor.',
|
||||
'numeric' => ':Attribute får inte vara större än :max.',
|
||||
'string' => ':Attribute får max innehålla :max tecken.',
|
||||
],
|
||||
'mimes' => ':Attribute måste vara en fil av typen: :values.',
|
||||
'mimetypes' => ':Attribute måste vara en fil av typen: :values.',
|
||||
'min' => [
|
||||
'array' => ':Attribute måste innehålla minst :min objekt.',
|
||||
'file' => ':Attribute måste vara minst :min kilobyte stor.',
|
||||
'numeric' => ':Attribute måste vara större än :min.',
|
||||
'string' => ':Attribute måste innehålla minst :min tecken.',
|
||||
],
|
||||
'multiple_of' => ':attribute måste vara en multipel av :value',
|
||||
'not_in' => ':Attribute är ogiltigt.',
|
||||
'not_regex' => 'Formatet för :attribute är ogiltigt.',
|
||||
'numeric' => ':Attribute måste vara en siffra.',
|
||||
'password' => 'Lösenordet är fel.',
|
||||
'present' => ':Attribute måste finnas med.',
|
||||
'prohibited' => 'Fältet :attribute är förbjudet.',
|
||||
'prohibited_if' => ':attribute är förbjudet när :other är :value.',
|
||||
'prohibited_unless' => ':attribute är förbjudet om inte :other är :values.',
|
||||
'regex' => ':Attribute har ogiltigt format.',
|
||||
'relatable' => 'Denna :attribute kanske inte är associerad med den här resursen.',
|
||||
'required' => ':Attribute är obligatoriskt.',
|
||||
'required_if' => ':Attribute är obligatoriskt när :other är :value.',
|
||||
'required_unless' => ':Attribute är obligatoriskt när inte :other finns bland :values.',
|
||||
'required_with' => ':Attribute är obligatoriskt när :values är ifyllt.',
|
||||
'required_with_all' => ':Attribute är obligatoriskt när :values är ifyllt.',
|
||||
'required_without' => ':Attribute är obligatoriskt när :values ej är ifyllt.',
|
||||
'required_without_all' => ':Attribute är obligatoriskt när ingen av :values är ifyllt.',
|
||||
'same' => ':Attribute och :other måste vara lika.',
|
||||
'size' => [
|
||||
'array' => ':Attribute måste innehålla :size objekt.',
|
||||
'file' => ':Attribute får endast vara :size kilobyte stor.',
|
||||
'numeric' => ':Attribute måste vara :size.',
|
||||
'string' => ':Attribute måste innehålla :size tecken.',
|
||||
],
|
||||
'starts_with' => ':Attribute måste börja med en av följande: :values',
|
||||
'string' => ':Attribute måste vara en sträng.',
|
||||
'timezone' => ':Attribute måste vara en giltig tidszon.',
|
||||
'unique' => ':Attribute används redan.',
|
||||
'uploaded' => ':Attribute kunde inte laddas upp.',
|
||||
'url' => ':Attribute har ett ogiltigt format.',
|
||||
'uuid' => ':Attribute måste vara ett giltigt UUID.',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Custom Validation Language Lines
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify custom validation messages for attributes using the
|
||||
| convention "attribute.rule" to name the lines. This makes it quick to
|
||||
| specify a specific custom language line for a given attribute rule.
|
||||
|
|
||||
*/
|
||||
|
||||
'custom' => [
|
||||
'attribute-name' => [
|
||||
'rule-name' => 'custom-message',
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Custom Validation Attributes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The following language lines are used to swap our attribute placeholder
|
||||
| with something more reader friendly such as "E-Mail Address" instead
|
||||
| of "email". This simply helps us make our message more expressive.
|
||||
|
|
||||
*/
|
||||
|
||||
'attributes' => [
|
||||
'username' => 'användarnamn',
|
||||
'password' => 'lösenord',
|
||||
'name' => 'namn',
|
||||
'race' => 'ras',
|
||||
'class' => 'klass',
|
||||
],
|
||||
|
||||
];
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
<x-layout name="app">
|
||||
|
||||
<x-slot name="title">{{ __('Admin') }} - {{ __('Users') }} - {{ __(isset($model) ? 'Edit' : 'New') }}</x-slot>
|
||||
|
||||
@if (isset($model))
|
||||
<livewire:form.admin.user-form :user="$model" />
|
||||
@else
|
||||
<livewire:form.admin.user-form />
|
||||
@endif
|
||||
|
||||
</x-layout>
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
<x-layout name="app">
|
||||
|
||||
<x-slot name="title">{{ __('Admin') }} - {{ __('Users') }}</x-slot>
|
||||
|
||||
<x-slot name="page_links">
|
||||
<x-button element="a" class="flex items-center" href="{{ route('admin.users.create') }}">
|
||||
{{ __('New') }}
|
||||
</x-button>
|
||||
</x-slot>
|
||||
|
||||
<livewire:admin-user-table />
|
||||
|
||||
</x-layout>
|
||||
|
|
@ -1,15 +1,19 @@
|
|||
<x-layout name="auth">
|
||||
|
||||
<h1 class="text-gray-500 text-3xl text-center mb-4">{{ __('Login') }}</h1>
|
||||
<h1 class="text-gray-500 text-3xl text-center mb-4">Login</h1>
|
||||
|
||||
<x-form action="{{ route('auth.login.store') }}">
|
||||
<x-input name="username" placeholder="{{ __('Username') }}" class="mb-2"/>
|
||||
<x-input-password placeholder="{{ __('Password') }}" class="mb-2" />
|
||||
<x-button element="input" type="submit" name="submit" value="{{ __('Login') }}" class="w-full" />
|
||||
<x-button element="input" type="submit" name="submit" value="Login" class="w-full" />
|
||||
</x-form>
|
||||
|
||||
@if (config('services.discord.client_id'))
|
||||
<x-divider>{{ __('or') }}</x-divider>
|
||||
<div class="flex items-center justify-between my-4">
|
||||
<div class="bg-gray-200 h-px w-full"></div>
|
||||
<div class="mx-4">OR</div>
|
||||
<div class="bg-gray-200 h-px w-full"></div>
|
||||
</div>
|
||||
|
||||
<x-button element="a" href="{{ route('oauth.create', ['driver' => 'discord'])}}" class="text-center w-full">
|
||||
<x-icon name="discord-text-white" class="mx-auto h-8" />
|
||||
|
|
|
|||
|
|
@ -9,8 +9,19 @@
|
|||
</div>
|
||||
</x-slot>
|
||||
|
||||
<div class="p-4">
|
||||
<x-profession-import-form route="{{ route('character.profession.store', [ 'character' => $character->slug ]) }}" />
|
||||
<div class="mx-auto max-w-sm p-4">
|
||||
<p>Download this <x-link href="https://www.curseforge.com/wow/addons/professions-exporter" target="_blank">exporter addon</x-link>
|
||||
|
||||
<x-form action="{{ route('character.profession.store', [ 'character' => $character->slug ]) }}">
|
||||
<div class="mb-2">
|
||||
<x-form.label for="data">{{ __('Import string') }}</x-form.label>
|
||||
<x-textarea class="h-64" name="data" />
|
||||
</div>
|
||||
|
||||
<div class="mt-2">
|
||||
<x-button element="input" type="submit" value="{{ __('Import') }}" />
|
||||
</div>
|
||||
</x-form>
|
||||
</div>
|
||||
|
||||
</x-layout>
|
||||
|
|
|
|||
|
|
@ -3,14 +3,8 @@
|
|||
<x-slot name="title">
|
||||
<div class="flex">
|
||||
{{ __('Character') }} -
|
||||
<div class="flex mx-2 space-x-1">
|
||||
<x-race-icon class="w-8 h-8" :character="$ch_prof->character" />
|
||||
<x-class-icon class="w-8 h-8" :name="$ch_prof->character->class" />
|
||||
</div>
|
||||
|
||||
<a href="{{ route('character.show', ['character' => $ch_prof->character ]) }}">
|
||||
{{ $ch_prof->character->level }} {{ $ch_prof->character->name }}
|
||||
</a>
|
||||
<x-class-icon class="w-8 h-8 mx-2" :name="$ch_prof->character->class" />
|
||||
{{ $ch_prof->character->level }} {{ $ch_prof->character->name }}
|
||||
</div>
|
||||
</x-slot>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,10 +3,7 @@
|
|||
<x-slot name="title">
|
||||
<div class="flex">
|
||||
{{ __('Character') }} -
|
||||
<div class="flex mx-2 space-x-1">
|
||||
<x-race-icon class="w-8 h-8" :character="$character" />
|
||||
<x-class-icon class="w-8 h-8" :name="$character->class" />
|
||||
</div>
|
||||
<x-class-icon class="w-8 h-8 mx-2" :name="$character->class" />
|
||||
{{ $character->level }} {{ $character->name }}
|
||||
</div>
|
||||
</x-slot>
|
||||
|
|
@ -22,15 +19,7 @@
|
|||
<div class="p-4">
|
||||
|
||||
<x-section-heading>
|
||||
<h2 class="text-3xl">{{ __('Owner') }}</h2>
|
||||
</x-section-heading>
|
||||
|
||||
<x-link href="{{ route('user.show', [ 'user' => $character->user ])}}">
|
||||
{{ $character->user->username }}
|
||||
</x-link>
|
||||
|
||||
<x-section-heading>
|
||||
<h2 class="text-3xl">{{ __('Professions') }}</h2>
|
||||
<h2 class="text-3xl">Professions</h2>
|
||||
<div>
|
||||
@can('import_profession', $character)
|
||||
<x-button element="a" class="flex items-center" href="{{ route('character.profession.create', [ 'character' => $character->slug ]) }}">
|
||||
|
|
|
|||
|
|
@ -1,36 +1,5 @@
|
|||
@props(['type', 'message'])
|
||||
|
||||
@php
|
||||
|
||||
switch($type) {
|
||||
case 'info':
|
||||
$border = 'border-blue-300';
|
||||
$text = 'text-blue-800';
|
||||
$bg = 'bg-blue-200';
|
||||
break;
|
||||
case 'success':
|
||||
$border = 'border-green-300';
|
||||
$text = 'text-green-800';
|
||||
$bg = 'bg-green-200';
|
||||
break;
|
||||
case 'warning':
|
||||
$border = 'border-yellow-300';
|
||||
$text = 'text-yellow-800';
|
||||
$bg = 'bg-yellow-200';
|
||||
break;
|
||||
case 'error':
|
||||
$border = 'border-red-300';
|
||||
$text = 'text-red-800';
|
||||
$bg = 'bg-red-200';
|
||||
break;
|
||||
default:
|
||||
$border = 'border-gray-300';
|
||||
$text = 'text-gray-800';
|
||||
$bg = 'bg-gray-200';
|
||||
}
|
||||
|
||||
@endphp
|
||||
|
||||
<div class="px-4 py-2 rounded border {{ $bg }} {{ $text }} {{ $border }}">
|
||||
<div class="px-4 py-2 rounded border text-{{ $type }}-800 bg-{{ $type }}-200 border-{{ $type }}-300">
|
||||
{!! $message !!}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,16 +1,9 @@
|
|||
@props(['character', 'destroy' => false, 'frame' => false ])
|
||||
@props(['character', 'destroy' => false])
|
||||
|
||||
<div class="rounded p-2 bg-class-{{ $character->class }}">
|
||||
<div class="flex justify-between">
|
||||
<div class="flex items-center">
|
||||
@if ($frame)
|
||||
<div class="w-8 h-8">
|
||||
<x-class-icon :name="$character->class" />
|
||||
<img class="transform -translate-x-2 -translate-y-6 scale-200" src="{{ url('images/elite_frame.png') }}" />
|
||||
</div>
|
||||
@else
|
||||
<x-class-icon class="w-8 h-8" :name="$character->class" />
|
||||
@endif
|
||||
<a href="{{ route('character.show', ['character' => $character ]) }}" class="text-2xl ml-2">{{ $character->level }} {{ $character->name }}</a>
|
||||
</div>
|
||||
|
||||
|
|
@ -24,10 +17,7 @@
|
|||
</div>
|
||||
|
||||
<div class="flex justify-between">
|
||||
<p>
|
||||
{{ __(Str::ucfirst($character->race)) }}
|
||||
{{ __(Str::ucfirst($character->class)) }}
|
||||
</p>
|
||||
<p>{{ Str::ucfirst($character->class) }}</p>
|
||||
|
||||
<div class="flex gap-1">
|
||||
@foreach($character->professions as $ch_prof)
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
@if (strlen($slot) > 0)
|
||||
<div class="flex items-center justify-between my-4">
|
||||
<div class="bg-gray-200 h-px w-full"></div>
|
||||
<div class="text-gray-400 mx-4 uppercase">{{ $slot }}</div>
|
||||
<div class="bg-gray-200 h-px w-full"></div>
|
||||
</div>
|
||||
@else
|
||||
<div class="bg-gray-200 h-px w-full my-6"></div>
|
||||
@endif
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
|
||||
<input type="checkbox" name="{{ $name }}" value="{{ $value }}"
|
||||
{{ $attributes->merge(['class' => ($errors->has($name) ? 'border-red-400 text-red-400 ' : '') . 'border border-gray-200 rounded p-2 focus:ring-1']) }} {{ $disabled }} />
|
||||
|
||||
@error($name)
|
||||
<p class="ml-2 text-red-400 text-sm">{{ $message }}</p>
|
||||
@enderror
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
<input type="{{ $type }}" name="{{ $name }}" value="{{ $value }}"
|
||||
{{ $attributes->merge(['class' => ($errors->has($name) ? 'border-red-400 ' : '') . 'w-full border border-gray-200 rounded px-2 py-1 focus:ring-1']) }} {{ $disabled }} />
|
||||
{{ $attributes->merge(['class' => ($errors->has($name) ? 'border-danger-400 ' : '') . 'w-full border rounded px-2 py-1 focus:ring-2']) }} {{ $disabled }} />
|
||||
|
||||
@error($name)
|
||||
<p class="ml-2 text-red-400 text-sm">{{ $message }}</p>
|
||||
<p class="ml-2 text-danger-400 text-sm">{{ $message }}</p>
|
||||
@enderror
|
||||
|
|
|
|||
|
|
@ -1,13 +1,9 @@
|
|||
<select name="{{ $name }}" {!! $attributes->merge(['class' => 'w-full border border-gray-200 rounded p-1 focus:ring-1']) !!}>
|
||||
<select {!! $attributes->merge(['class' => 'w-full border rounded p-1 focus:ring-2']) !!}>
|
||||
@forelse($options as $key => $option)
|
||||
<option value="{{ $key }}" @if($key == $value)selected @endif>
|
||||
<option value="{{ $key }}">
|
||||
{{ $option }}
|
||||
</option>
|
||||
@empty
|
||||
{!! $slot !!}
|
||||
@endforelse
|
||||
</select>
|
||||
|
||||
@error($name)
|
||||
<p class="ml-2 text-red-400 text-sm">{{ $message }}</p>
|
||||
@enderror
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
<textarea name="{{ $name }}"
|
||||
{{ $attributes->merge(['class' => ($errors->has($name) ? 'border-red-400 ' : '') . 'w-full border border-gray-200 rounded px-2 py-1 focus:ring-1']) }} {{ $disabled }}>{{ $value }}</textarea>
|
||||
{{ $attributes->merge(['class' => ($errors->has($name) ? 'border-red-400 ' : '') . 'w-full border rounded px-2 py-1 focus:ring-2']) }} {{ $disabled }}>{{ $value }}</textarea>
|
||||
|
||||
@error($name)
|
||||
<p class="ml-2 text-red-400 text-sm">{{ $message }}</p>
|
||||
|
|
|
|||
3
resources/views/components/profession-icon.blade.php
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<div>
|
||||
<!-- The only way to do great work is to love what you do. - Steve Jobs -->
|
||||
</div>
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
<p class="text-center">
|
||||
<strong>{{ __("Important") }}!</strong>
|
||||
{{ __('Download this') }}
|
||||
<x-link href="https://www.curseforge.com/wow/addons/professions-exporter" target="_blank">{{ __('addon') }}</x-link>
|
||||
{{ __('to generate the import string') }}
|
||||
</p>
|
||||
|
||||
<x-form action="{{ $route }}">
|
||||
<div class="mb-2">
|
||||
<x-form.label for="data">{{ __('Import string') }}</x-form.label>
|
||||
<x-textarea class="h-64" name="data" />
|
||||
</div>
|
||||
|
||||
<div class="mt-2">
|
||||
<x-button element="input" type="submit" value="{{ __('Import') }}" />
|
||||
</div>
|
||||
</x-form>
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
@props(['recipe'])
|
||||
@php
|
||||
if ($recipe->spell) {
|
||||
$id = $recipe->spell->id;
|
||||
$type = 'spell';
|
||||
} else {
|
||||
$id = $recipe->craft->external_id;
|
||||
$type = 'item';
|
||||
}
|
||||
@endphp
|
||||
<x-wowhead-link :type="$type" :wh-id="$id" href="{{ route('recipe.show', [ 'recipe' => $recipe ]) }}">
|
||||
{{ $recipe->name }}
|
||||
</x-wowhead-link>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<x-layout name="app">
|
||||
|
||||
<x-slot name="title">{{ __('Items') }}</x-slot>
|
||||
|
||||
<div class="p-4">
|
||||
<livewire:items />
|
||||
</div>
|
||||
</x-layout>
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
<x-layout name="app">
|
||||
|
||||
<x-slot name="title">{{ __('Item') }} - {{ $item->name }}</x-slot>
|
||||
|
||||
<div class="p-4">
|
||||
<div class="mb-4">
|
||||
<x-wowhead-link wh-id="{{ $item->external_id }}">{{ $item->name }}</x-wowhead-link>
|
||||
</div>
|
||||
|
||||
@if (count($item->recipes) > 0)
|
||||
<div class="mb-4">
|
||||
<x-section-heading>
|
||||
<h2 class="text-3xl">{{ __('Created by') }}</h2>
|
||||
</x-section-heading>
|
||||
<ul class="px-4 space-y-2">
|
||||
@foreach($item->recipes as $recipe)
|
||||
<li>
|
||||
@if ($recipe->spell_id)
|
||||
<x-wowhead-link type="spell" wh-id="{{ $recipe->spell_id }}" href="{{ route('recipe.show', [ 'recipe' => $recipe ]) }}">{{ $recipe->name }}</x-wowhead-link>
|
||||
@else
|
||||
{{ $recipe->name }}
|
||||
@endif
|
||||
</li>
|
||||
@endforeach
|
||||
</li>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (count($item->reagent_for) > 0)
|
||||
<div class="mb-4">
|
||||
<x-section-heading>
|
||||
<h2 class="text-3xl">{{ __('Reagent for') }}</h2>
|
||||
</x-section-heading>
|
||||
|
||||
<ul class="px-4 space-y-2">
|
||||
@foreach($item->reagent_for as $recipe)
|
||||
<li><x-recipe-link :recipe="$recipe" /></li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
|
||||
</x-layout>
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
</div>
|
||||
|
||||
@if (isset($page_links))
|
||||
<div class="flex space-x-2">
|
||||
<div>
|
||||
{{ $page_links }}
|
||||
</div>
|
||||
@endif
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
<div class="p-4">
|
||||
|
||||
<table class="w-full">
|
||||
|
||||
<tr>
|
||||
<th><x-input wire:model="username" name="username" placeholder="Username" /></th>
|
||||
<th><x-select wire:model="role" name="role" :options="['' => '-- Role --', 'user' => 'User', 'admin' => 'Admin']" /></th>
|
||||
</tr>
|
||||
|
||||
<tr class="border-b-2">
|
||||
<th class="py-2 text-left">Username</th>
|
||||
<th class="py-2 w-24">Role</th>
|
||||
<th class="py-2 w-44">Created</th>
|
||||
<th class="py-2 w-44">Updated</th>
|
||||
<th class="py-2 w-6"> </th>
|
||||
</tr>
|
||||
|
||||
@foreach($users as $user)
|
||||
<tr class="border-b hover:bg-gray-100">
|
||||
<td class="px-2 py-1">
|
||||
<x-link href="{{ route('admin.users.edit', [ 'user' => $user ]) }}">
|
||||
{{ $user->username }}
|
||||
</x-link>
|
||||
</td>
|
||||
<td class="px-2 py-1 text-center">{{ $user->role }}</td>
|
||||
<td class="px-2 py-1">{{ $user->created_at }}</td>
|
||||
<td class="px-2 py-1">{{ $user->updated_at }}</td>
|
||||
<td>
|
||||
<x-form :action="route('admin.users.destroy', [ 'user' => $user ])" method="DELETE">
|
||||
<a href="#" onclick="this.closest('form').submit();return false;">
|
||||
<x-icon name="cross" class="h-6 h-6 text-danger-400 hover:text-danger-600" />
|
||||
</a>
|
||||
</x-form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</table>
|
||||
|
||||
<div class="mt-4">
|
||||
{{ $users->links() }}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
<x-form class="p-4" wire:submit.prevent="save">
|
||||
|
||||
<div>
|
||||
<x-form.label for="username">{{ __('Username') }}</x-form.label>
|
||||
<x-input wire:model="user.username" name="user.username" label="username" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<x-form.label for="role">{{ __('Role') }}</x-form.label>
|
||||
<x-select wire:model="user.role" name="role" :options="['user' => 'User', 'admin' => 'Admin']" />
|
||||
</div>
|
||||
|
||||
|
||||
<div class="mb-2">
|
||||
<x-form.label for="password">{{ __('Password') }}</x-form.label>
|
||||
<x-input-password wire:model="password" name="password" />
|
||||
</div>
|
||||
|
||||
<div class="mb-2">
|
||||
<x-form.label for="password_confirmation">{{ __('Confirm Password') }}</x-form.label>
|
||||
<x-input-password wire:model="password_confirmation" name="password_confirmation" />
|
||||
</div>
|
||||
|
||||
<div class="mt-2">
|
||||
<x-button element="input" type="submit" value="{{ $user->id ? 'Save' : 'Create' }}" />
|
||||
</div>
|
||||
</x-form>
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
</div>
|
||||
|
||||
<div class="mt-2">
|
||||
<x-button element="input" type="submit" value="{{ __($this->character->exists ? 'Save' : 'Create') }}" />
|
||||
<x-button element="input" type="submit" value="{{$this->character->exists ? 'Save' : 'Create' }}" />
|
||||
</div>
|
||||
|
||||
</x-form>
|
||||
|
|
|
|||
|
|
@ -1,55 +0,0 @@
|
|||
<div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4 mb-2">
|
||||
<x-input wire:model="name" name="name" placeholder="{{ __('Name') }}" />
|
||||
<div class="flex items-center">
|
||||
<label for="reagent_for" class="mr-2">{{ __('Only reagents') }}</label>
|
||||
<x-checkbox wire:model="reagent_for" name="reagent_for" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($items->count())
|
||||
<table class="w-full whitespace-nowrap">
|
||||
<thead>
|
||||
<tr tabindex="0" class="focus:outline-none w-full text-sm leading-none text-gray-800">
|
||||
<th class="text-left w-1/4 px-3 py-4">{{ __('Name') }}</th>
|
||||
<th class="text-left w-3/4 px-3 py-4">{{ __('Reagent for') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="w-full">
|
||||
@foreach($items as $item)
|
||||
<tr class="focus:outline-none text-sm leading-none text-gray-800 bg-white hover:bg-gray-100 border-b border-t border-gray-100">
|
||||
<td class="px-3 py-4">
|
||||
<x-wowhead-link type="item" :wh-id="$item->external_id" href="{{ route('item.show', [ 'item' => $item ]) }}">
|
||||
{{ $item->name }}
|
||||
</x-wowhead-link>
|
||||
</td>
|
||||
<td class="px-3 py-4">
|
||||
@if ($item->reagent_for && $item->reagent_for->count() > 0)
|
||||
<ul class="flex flex-wrap items-center gap-2">
|
||||
@foreach($item->reagent_for->take(15) as $recipe)
|
||||
<li>
|
||||
<x-recipe-link :recipe="$recipe" />
|
||||
</li>
|
||||
@endforeach
|
||||
|
||||
@if ($item->reagent_for->count() > 15)
|
||||
<li>and <strong>{{ $item->reagent_for->count() - 15 }}</strong> more</li>
|
||||
@endif
|
||||
</ul>
|
||||
@else
|
||||
<span class="text-gray-400">{{ __('None') }}</span>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="mt-4">
|
||||
{{ $items->links() }}
|
||||
</div>
|
||||
@else
|
||||
<x-empty-result>No items found</x-empty-result>
|
||||
@endif
|
||||
</div>
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
<div>
|
||||
|
||||
<div class="grid grid-cols-3 gap-4 mb-2">
|
||||
<x-input wire:model="name" name="name" placeholder="{{ __('Recipe') }}" />
|
||||
<x-input wire:model="crafter" name="crafter" placeholder="{{ __('Crafter') }}" />
|
||||
<x-input wire:model="name" name="name" placeholder="Recipe" />
|
||||
<x-input wire:model="crafter" name="crafter" placeholder="Crafter" />
|
||||
<x-select wire:model="profession" name="profession" :options="$profession_options" />
|
||||
</div>
|
||||
|
||||
|
|
@ -10,16 +10,27 @@
|
|||
<table class="w-full whitespace-nowrap">
|
||||
<thead>
|
||||
<tr tabindex="0" class="focus:outline-none w-full text-sm leading-none text-gray-800">
|
||||
<th class="text-left px-3 py-4">{{ __('Recipe') }}</th>
|
||||
<th class="text-left px-3 py-4">{{ __('Profession') }}</th>
|
||||
<th class="text-left px-3 py-4">{{ __('Crafters') }}</th>
|
||||
<th class="text-left px-3 py-4">Recipe</th>
|
||||
<th class="text-left px-3 py-4">Profession</th>
|
||||
<th class="text-left px-3 py-4">Crafters</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="w-full">
|
||||
@foreach($recipes as $recipe)
|
||||
<tr class="focus:outline-none text-sm leading-none text-gray-800 bg-white hover:bg-gray-100 border-b border-t border-gray-100">
|
||||
<td class="px-3 py-4">
|
||||
<x-recipe-link :recipe="$recipe" />
|
||||
@php
|
||||
if ($recipe->spell) {
|
||||
$id = $recipe->spell->id;
|
||||
$type = 'spell';
|
||||
} else {
|
||||
$id = $recipe->craft->external_id;
|
||||
$type = 'item';
|
||||
}
|
||||
@endphp
|
||||
<x-wowhead-link :type="$type" :wh-id="$id" href="{{ route('recipe.show', [ 'recipe' => $recipe ]) }}">
|
||||
{{ $recipe->name }}
|
||||
</x-wowhead-link>
|
||||
</td>
|
||||
<td class="px-3 py-4">
|
||||
<div class="flex items-center">
|
||||
|
|
|
|||