Compare commits
No commits in common. "master" and "v0.1.4" have entirely different histories.
32 changed files with 6922 additions and 7819 deletions
|
|
@ -7,11 +7,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_DATABASE=heritage
|
||||
DB_USERNAME=heritage
|
||||
DB_PASSWORD=
|
||||
|
||||
BROADCAST_DRIVER=log
|
||||
CACHE_DRIVER=file
|
||||
|
|
|
|||
97
README.md
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).
|
||||
|
|
|
|||
|
|
@ -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,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!');
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ namespace App\Http\Middleware;
|
|||
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class Locale extends Middleware
|
||||
{
|
||||
|
|
@ -14,7 +15,7 @@ class Locale extends Middleware
|
|||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if (session()->has('locale')) {
|
||||
app()->setlocale(session()->get('locale'));
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -34,8 +34,7 @@ class Recipe extends Model
|
|||
'profession_id',
|
||||
'category_id',
|
||||
'item_id',
|
||||
'spell_id',
|
||||
'description'
|
||||
'spell_id'
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -60,8 +60,7 @@ class User extends Authenticatable
|
|||
|
||||
public function alt_characters()
|
||||
{
|
||||
$relation = $this->hasMany(Character::class);
|
||||
|
||||
$relation = $this->characters();
|
||||
if ($this->character_id) {
|
||||
$relation->where('id', '!=', $this->character_id);
|
||||
}
|
||||
|
|
@ -70,13 +69,7 @@ class User extends Authenticatable
|
|||
|
||||
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;
|
||||
return $this->hasMany(Character::class);
|
||||
}
|
||||
|
||||
public function getRoleAttribute()
|
||||
|
|
|
|||
3059
composer.lock
generated
3059
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -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,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
|
||||
10985
package-lock.json
generated
10985
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -13,7 +13,7 @@
|
|||
"@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="mysql"/>
|
||||
<server name="DB_DATABASE" value="heritage_test"/>
|
||||
<server name="MAIL_MAILER" value="array"/>
|
||||
<server name="QUEUE_CONNECTION" value="sync"/>
|
||||
<server name="SESSION_DRIVER" value="array"/>
|
||||
|
|
|
|||
|
|
@ -7,15 +7,11 @@
|
|||
"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 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",
|
||||
|
|
@ -39,6 +35,5 @@
|
|||
"of": "av",
|
||||
"or": "eller",
|
||||
"results": "resultat",
|
||||
"to": "till",
|
||||
"to generate the import string": "för att generera import strängen"
|
||||
"to": "till"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,22 @@
|
|||
</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">{{ __('addon') }}</x-link>
|
||||
</p>
|
||||
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -3,43 +3,6 @@
|
|||
<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,13 +0,0 @@
|
|||
<x-layout name="app">
|
||||
|
||||
<x-slot name="title">
|
||||
<div class="flex">
|
||||
{{ __('Import Profession') }}
|
||||
</div>
|
||||
</x-slot>
|
||||
|
||||
<div class="p-4">
|
||||
<x-profession-import-form route="{{ route('profession.store') }}" />
|
||||
</div>
|
||||
|
||||
</x-layout>
|
||||
|
|
@ -16,14 +16,10 @@
|
|||
|
||||
<x-section-heading>
|
||||
<h2 class="text-3xl">{{ __('Characters') }}</h2>
|
||||
<div class="flex space-x-2">
|
||||
<div>
|
||||
<x-button element="a" class="flex items-center" href="{{ route('character.create') }}">
|
||||
<x-icon name="plus" class="h-6 h-6 mr-1"/> {{ __('Add character') }}
|
||||
</x-button>
|
||||
|
||||
<x-button element="a" class="flex items-center" href="{{ route('profession.create') }}">
|
||||
<x-icon name="plus" class="h-6 h-6 mr-1"/> {{ __('Import profession') }}
|
||||
</x-button>
|
||||
</div>
|
||||
</x-section-heading>
|
||||
|
||||
|
|
|
|||
|
|
@ -9,10 +9,6 @@
|
|||
|
||||
<div class="p-4">
|
||||
|
||||
@if ($recipe->description)
|
||||
<p>{{ $recipe->description }}</p>
|
||||
@endif
|
||||
|
||||
@if ($recipe->craft)
|
||||
<div class="mb-4">
|
||||
<x-section-heading>
|
||||
|
|
|
|||
|
|
@ -10,8 +10,12 @@
|
|||
|
||||
@if ($user->characters->count())
|
||||
<div class="grid md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
@foreach($user->characters as $character)
|
||||
<x-character-card :character="$character" :frame="$character->isMain()" />
|
||||
@if ($user->main_character)
|
||||
<x-character-card :character="$user->main_character" frame="true" />
|
||||
@endif
|
||||
|
||||
@foreach($user->alt_characters as $character)
|
||||
<x-character-card :character="$character" />
|
||||
@endforeach
|
||||
</div>
|
||||
@else
|
||||
|
|
|
|||
|
|
@ -82,12 +82,6 @@ Route::prefix('items')->name('item.')->group(function () {
|
|||
// ----------------------------
|
||||
Route::middleware(['auth'])->group(function() {
|
||||
|
||||
// Profession
|
||||
Route::prefix('profession')->name('profession.')->group(function() {
|
||||
Route::get('/create', [ProfessionController::class, 'create'])->name('create');
|
||||
Route::post('/', [ProfessionController::class, 'store'])->name('store');
|
||||
});
|
||||
|
||||
Route::prefix('profile')->name('profile.')->group(function () {
|
||||
Route::get('/', [ProfileController::class, 'index'])->name('index');
|
||||
Route::get('/edit', [ProfileController::class, 'edit'])->name('edit');
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use App\Models\Recipe;
|
|||
use App\Models\Spell;
|
||||
use App\Jobs\ImportProfession;
|
||||
|
||||
use App\ProfessionImport\InvalidCharacterException;
|
||||
use App\ProfessionImport\InvalidProfessionException;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
|
@ -95,7 +96,7 @@ class BasicTest extends TestCase
|
|||
Profession::factory()->create([ 'name' => 'Alchemy' ]);
|
||||
$character = Character::factory()->create([ 'name' => 'Superduper']);
|
||||
|
||||
ImportProfession::dispatch($data, $character->user, $character);
|
||||
ImportProfession::dispatch($character, $data);
|
||||
|
||||
$this->assertEquals('Alchemy', $character->professions[0]->name);
|
||||
$recipes = $character->professions[0]->recipes;
|
||||
|
|
@ -130,13 +131,35 @@ class BasicTest extends TestCase
|
|||
$this->assertEquals('Imbued Vial', $recipes[1]->reagents[0]->name);
|
||||
$this->assertEquals(1, $recipes[1]->reagents[0]->quantity);
|
||||
|
||||
$this->assertEquals(22785, $recipes[1]->reagents[1]->external_id);
|
||||
$this->assertEquals('Felweed', $recipes[1]->reagents[1]->name);
|
||||
$this->assertEquals(2, $recipes[1]->reagents[1]->quantity);
|
||||
$this->assertEquals(22789, $recipes[1]->reagents[1]->external_id);
|
||||
$this->assertEquals('Terocone', $recipes[1]->reagents[1]->name);
|
||||
$this->assertEquals(1, $recipes[1]->reagents[1]->quantity);
|
||||
|
||||
$this->assertEquals(22789, $recipes[1]->reagents[2]->external_id);
|
||||
$this->assertEquals('Terocone', $recipes[1]->reagents[2]->name);
|
||||
$this->assertEquals(1, $recipes[1]->reagents[2]->quantity);
|
||||
$this->assertEquals(22785, $recipes[1]->reagents[2]->external_id);
|
||||
$this->assertEquals('Felweed', $recipes[1]->reagents[2]->name);
|
||||
$this->assertEquals(2, $recipes[1]->reagents[2]->quantity);
|
||||
}
|
||||
|
||||
public function test_wrong_character_name()
|
||||
{
|
||||
Profession::factory()->create([ 'name' => 'Alchemy' ]);
|
||||
$character = Character::factory()->create([ 'name' => 'Actualname']);
|
||||
|
||||
$data = (object) [
|
||||
"server" => "Ashbringer",
|
||||
"player" => "Wrongname",
|
||||
"guild" => "Flat Azeroth Research Team",
|
||||
"profession" => (object) [
|
||||
"name" => "Alchemy",
|
||||
"level" => 375,
|
||||
"maxLevel" => 375,
|
||||
"recipes" => []
|
||||
]
|
||||
];
|
||||
|
||||
$this->expectException(InvalidCharacterException::class);
|
||||
|
||||
ImportProfession::dispatch($character, $data);
|
||||
}
|
||||
|
||||
public function test_invalid_profession()
|
||||
|
|
@ -147,7 +170,7 @@ class BasicTest extends TestCase
|
|||
$character = Character::factory()->create();
|
||||
|
||||
$data = (object) [
|
||||
"server" => "Sulfuron",
|
||||
"server" => "Ashbringer",
|
||||
"player" => $character->name,
|
||||
"guild" => "Futuramalama",
|
||||
"profession" => (object) [
|
||||
|
|
@ -160,6 +183,6 @@ class BasicTest extends TestCase
|
|||
|
||||
$this->expectException(InvalidProfessionException::class);
|
||||
|
||||
ImportProfession::dispatch($data, $character->user, $character);
|
||||
ImportProfession::dispatch($character, $data);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,152 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\ProfessionImport;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\Character;
|
||||
use App\Models\Profession;
|
||||
use App\Jobs\ImportProfession;
|
||||
|
||||
use App\ProfessionImport\InvalidCharacterException;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Tests\TestCase;
|
||||
|
||||
class CharacterTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_character_found()
|
||||
{
|
||||
Profession::factory()->create([ 'name' => 'Alchemy' ]);
|
||||
$character = Character::factory()->create([ 'name' => 'Charfound']);
|
||||
|
||||
$data = (object) [
|
||||
"server" => "Darkshore",
|
||||
"player" => "Charfound",
|
||||
"guild" => "ABOBA",
|
||||
"profession" => (object) [
|
||||
"name" => "Alchemy",
|
||||
"level" => 375,
|
||||
"maxLevel" => 375,
|
||||
"recipes" => [
|
||||
(object) [
|
||||
"name" => "Elixir of Major Defense",
|
||||
"color" => "ffffff",
|
||||
"id" => 22834,
|
||||
"num" => 1,
|
||||
"categorie" => "Elixir",
|
||||
"items" => [
|
||||
(object) [
|
||||
"name" => "Ancient Lichen",
|
||||
"color" => "ffffff",
|
||||
"num" => 3,
|
||||
"id" => 22790
|
||||
],
|
||||
(object) [
|
||||
"name" => "Terocone",
|
||||
"color" => "ffffff",
|
||||
"num" => 1,
|
||||
"id" => 22789
|
||||
],
|
||||
(object) [
|
||||
"name" => "Imbued Vial",
|
||||
"color" => "ffffff",
|
||||
"num" => 1,
|
||||
"id" => 18256
|
||||
],
|
||||
],
|
||||
"spellId" => 28557
|
||||
],
|
||||
]
|
||||
]
|
||||
];
|
||||
|
||||
ImportProfession::dispatch($data, $character->user);
|
||||
|
||||
$this->assertEquals('Alchemy', $character->professions[0]->name);
|
||||
$recipes = $character->professions[0]->recipes;
|
||||
|
||||
$this->assertEquals(22834, $recipes[0]->craft->external_id);
|
||||
$this->assertEquals('Elixir of Major Defense', $recipes[0]->craft->name);
|
||||
|
||||
$this->assertEquals(28557, $recipes[0]->spell->id);
|
||||
$this->assertEquals('Elixir of Major Defense', $recipes[0]->spell->name);
|
||||
|
||||
$this->assertEquals(22790, $recipes[0]->reagents[0]->external_id);
|
||||
$this->assertEquals('Ancient Lichen', $recipes[0]->reagents[0]->name);
|
||||
$this->assertEquals(3, $recipes[0]->reagents[0]->quantity);
|
||||
|
||||
$this->assertEquals(18256, $recipes[0]->reagents[1]->external_id);
|
||||
$this->assertEquals('Imbued Vial', $recipes[0]->reagents[1]->name);
|
||||
$this->assertEquals(1, $recipes[0]->reagents[1]->quantity);
|
||||
|
||||
$this->assertEquals(22789, $recipes[0]->reagents[2]->external_id);
|
||||
$this->assertEquals('Terocone', $recipes[0]->reagents[2]->name);
|
||||
$this->assertEquals(1, $recipes[0]->reagents[2]->quantity);
|
||||
}
|
||||
|
||||
public function test_character_not_found()
|
||||
{
|
||||
$character = Character::factory()->create([ 'name' => 'Findme']);
|
||||
|
||||
$data = (object) [
|
||||
"server" => "Ashbringer",
|
||||
"player" => "Anotherdude",
|
||||
"guild" => "Raiders of the Last Mark",
|
||||
"profession" => (object) [
|
||||
"name" => "Alchemy",
|
||||
"level" => 375,
|
||||
"maxLevel" => 375,
|
||||
"recipes" => []
|
||||
]
|
||||
];
|
||||
|
||||
$this->expectException(InvalidCharacterException::class);
|
||||
|
||||
ImportProfession::dispatch($data, $character->user);
|
||||
}
|
||||
|
||||
public function test_other_users_characters_is_not_found()
|
||||
{
|
||||
$character = Character::factory()->create([ 'name' => 'Otherchar']);
|
||||
|
||||
$data = (object) [
|
||||
"server" => "Rattlegore",
|
||||
"player" => "Otherchar",
|
||||
"guild" => "The A-Team",
|
||||
"profession" => (object) [
|
||||
"name" => "Alchemy",
|
||||
"level" => 375,
|
||||
"maxLevel" => 375,
|
||||
"recipes" => []
|
||||
]
|
||||
];
|
||||
|
||||
$this->expectException(InvalidCharacterException::class);
|
||||
|
||||
ImportProfession::dispatch($data, User::factory()->create());
|
||||
}
|
||||
|
||||
public function test_character_is_not_the_same_as_imported_name()
|
||||
{
|
||||
$character = Character::factory()->create([ 'name' => 'Actualname']);
|
||||
|
||||
$data = (object) [
|
||||
"server" => "Benediction",
|
||||
"player" => "Wrongname",
|
||||
"guild" => "Spaceballs",
|
||||
"profession" => (object) [
|
||||
"name" => "Alchemy",
|
||||
"level" => 375,
|
||||
"maxLevel" => 375,
|
||||
"recipes" => []
|
||||
]
|
||||
];
|
||||
|
||||
$this->expectException(InvalidCharacterException::class);
|
||||
|
||||
ImportProfession::dispatch($data, $character->user, $character);
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ class RecipesTest extends TestCase
|
|||
$this->seed(\Database\Seeders\ProductionSeeders\ItemSeeder::class);
|
||||
|
||||
$data = (object) [
|
||||
"server" => "Whitemane",
|
||||
"server" => "Ashbringer",
|
||||
"player" => "Scumbag",
|
||||
"guild" => "Method",
|
||||
"profession" => (object) [
|
||||
|
|
@ -68,7 +68,7 @@ class RecipesTest extends TestCase
|
|||
Profession::factory()->create([ 'name' => 'Enchanting' ]);
|
||||
$character = Character::factory()->create([ 'name' => 'Scumbag']);
|
||||
|
||||
ImportProfession::dispatch($data, $character->user, $character);
|
||||
ImportProfession::dispatch($character, $data);
|
||||
|
||||
$this->assertEquals('Enchanting', $character->professions[0]->name);
|
||||
$recipes = $character->professions[0]->recipes;
|
||||
|
|
@ -106,9 +106,7 @@ class RecipesTest extends TestCase
|
|||
->create(['spell_id' => null]);
|
||||
|
||||
$data = (object) [
|
||||
"server" => "Mograine",
|
||||
"player" => $character->name,
|
||||
"guild" => "Odyssey",
|
||||
"profession" => (object) [
|
||||
"name" => $profession->name,
|
||||
"level" => 375,
|
||||
|
|
@ -125,7 +123,7 @@ class RecipesTest extends TestCase
|
|||
]
|
||||
];
|
||||
|
||||
ImportProfession::dispatch($data, $character->user, $character);
|
||||
ImportProfession::dispatch($character, $data);
|
||||
|
||||
$this->assertEquals(1, Recipe::count());
|
||||
$this->assertDatabaseHas('recipes', [
|
||||
|
|
|
|||
|
|
@ -22,9 +22,7 @@ class SpecializationTest extends TestCase
|
|||
$specialization = Spell::factory()->create(['id' => 28672, 'name' => 'Transmutation Master']);
|
||||
|
||||
$data = (object) [
|
||||
"server" => "Firemaw",
|
||||
"player" => $character->name,
|
||||
"guild" => "Salad Bakers",
|
||||
"profession" => (object) [
|
||||
"name" => $profession->name,
|
||||
"specializationId" => 28672,
|
||||
|
|
@ -34,7 +32,7 @@ class SpecializationTest extends TestCase
|
|||
]
|
||||
];
|
||||
|
||||
ImportProfession::dispatch($data, $character->user, $character);
|
||||
ImportProfession::dispatch($character, $data);
|
||||
|
||||
$this->assertNotNull($character->professions[0]->specialization, "specalization was not imported");
|
||||
$this->assertEquals(28672, $character->professions[0]->specialization->id);
|
||||
|
|
@ -49,9 +47,7 @@ class SpecializationTest extends TestCase
|
|||
$profession = Profession::factory()->create([ 'name' => 'Carpenter' ]);
|
||||
|
||||
$data = (object) [
|
||||
"server" => "Loatheb",
|
||||
"player" => $character->name,
|
||||
"guild" => "Progress",
|
||||
"profession" => (object) [
|
||||
"name" => $profession->name,
|
||||
"level" => 375,
|
||||
|
|
@ -59,7 +55,7 @@ class SpecializationTest extends TestCase
|
|||
]
|
||||
];
|
||||
|
||||
ImportProfession::dispatch($data, $character->user, $character);
|
||||
ImportProfession::dispatch($character, $data);
|
||||
|
||||
$this->assertNull($character->professions[0]->specialization);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,89 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Tests\TestCase;
|
||||
|
||||
use App\Models\Profession;
|
||||
use App\Models\Character;
|
||||
use App\Models\User;
|
||||
|
||||
class ProfessionTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_guest_is_not_allowed_to_create_profession()
|
||||
{
|
||||
$response = $this->get(route('profession.create'));
|
||||
|
||||
$response->assertRedirect(route('auth.login'));
|
||||
}
|
||||
|
||||
public function test_user_is_allowed_to_create_profession()
|
||||
{
|
||||
$user = User::factory()->create();
|
||||
|
||||
$response = $this->actingAs($user)
|
||||
->get(route('profession.create'));
|
||||
|
||||
$response->assertStatus(200); // OK
|
||||
}
|
||||
|
||||
public function test_user_is_allowed_to_import_profession_for_their_own_character()
|
||||
{
|
||||
Profession::factory()->create(['name' => 'Influencer']);
|
||||
$user = User::factory()->create();
|
||||
$character = Character::factory()
|
||||
->for($user)
|
||||
->create(['name' => 'Rambone']);
|
||||
|
||||
$data = '{
|
||||
"server":"Bigglesworth",
|
||||
"player":"Rambone",
|
||||
"guild":"First Blood",
|
||||
"profession": {
|
||||
"locale":"enUS",
|
||||
"name":"Influencer",
|
||||
"specializationId":null,
|
||||
"specializationName":null,
|
||||
"level":375,
|
||||
"maxLevel":375,
|
||||
"recipes": []
|
||||
}
|
||||
}';
|
||||
|
||||
$response = $this->actingAs($user)
|
||||
->post(route('profession.store'), ['data' => $data]);
|
||||
|
||||
$response->assertSessionHas(['success' => "Profession imported!"]);
|
||||
}
|
||||
|
||||
public function test_user_is_not_allowed_to_import_profession_for_someone_elses_character()
|
||||
{
|
||||
Profession::factory()->create(['name' => 'Alchemy']);
|
||||
$user = User::factory()->create();
|
||||
$character = Character::factory()->create(['name' => 'Angus']);
|
||||
|
||||
$data = '{
|
||||
"server":"Deviate Delight",
|
||||
"player":"Angus",
|
||||
"guild":"Phoenix",
|
||||
"profession": {
|
||||
"locale":"enUS",
|
||||
"name":"Alchemy",
|
||||
"specializationId":null,
|
||||
"specializationName":null,
|
||||
"level":375,
|
||||
"maxLevel":375,
|
||||
"recipes": []
|
||||
}
|
||||
}';
|
||||
|
||||
$response = $this->actingAs($user)
|
||||
->post(route('profession.store'), ['data' => $data]);
|
||||
|
||||
$response->assertSessionHas(['error' => 'Could not find character "Angus"']);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue