Selaa lähdekoodia

Add more Laravel code

master
Immanuel Onyeka 3 vuotta sitten
vanhempi
commit
7f96df003c
99 muutettua tiedostoa jossa 6292 lisäystä ja 3 poistoa
  1. +3
    -3
      README.md
  2. +0
    -0
      go/view.go
  3. +300
    -0
      php-laravel/app/Console/Commands/ServicesInit.php
  4. +41
    -0
      php-laravel/app/Console/Kernel.php
  5. +41
    -0
      php-laravel/app/Exceptions/Handler.php
  6. +0
    -0
      php-laravel/app/Http/Controllers/BillingController.php
  7. +13
    -0
      php-laravel/app/Http/Controllers/Controller.php
  8. +66
    -0
      php-laravel/app/Http/Controllers/OrderController.php
  9. +14
    -0
      php-laravel/app/Http/Controllers/ServiceController.php
  10. +169
    -0
      php-laravel/app/Http/Controllers/Supply.php
  11. +38
    -0
      php-laravel/app/Http/Controllers/Ticket.php
  12. +165
    -0
      php-laravel/app/Http/Controllers/UserController.php
  13. +67
    -0
      php-laravel/app/Http/Kernel.php
  14. +21
    -0
      php-laravel/app/Http/Middleware/Authenticate.php
  15. +17
    -0
      php-laravel/app/Http/Middleware/EncryptCookies.php
  16. +17
    -0
      php-laravel/app/Http/Middleware/PreventRequestsDuringMaintenance.php
  17. +32
    -0
      php-laravel/app/Http/Middleware/RedirectIfAuthenticated.php
  18. +19
    -0
      php-laravel/app/Http/Middleware/TrimStrings.php
  19. +20
    -0
      php-laravel/app/Http/Middleware/TrustHosts.php
  20. +23
    -0
      php-laravel/app/Http/Middleware/TrustProxies.php
  21. +17
    -0
      php-laravel/app/Http/Middleware/VerifyCsrfToken.php
  22. +39
    -0
      php-laravel/app/Mail/ChangeEmail.php
  23. +49
    -0
      php-laravel/app/Mail/SupportTicket.php
  24. +30
    -0
      php-laravel/app/Models/Order.php
  25. +17
    -0
      php-laravel/app/Models/Referral.php
  26. +30
    -0
      php-laravel/app/Models/Service.php
  27. +18
    -0
      php-laravel/app/Models/Supplier.php
  28. +16
    -0
      php-laravel/app/Models/Ticket.php
  29. +16
    -0
      php-laravel/app/Models/Transaction.php
  30. +60
    -0
      php-laravel/app/Models/User.php
  31. +28
    -0
      php-laravel/app/Providers/AppServiceProvider.php
  32. +30
    -0
      php-laravel/app/Providers/AuthServiceProvider.php
  33. +21
    -0
      php-laravel/app/Providers/BroadcastServiceProvider.php
  34. +32
    -0
      php-laravel/app/Providers/EventServiceProvider.php
  35. +63
    -0
      php-laravel/app/Providers/RouteServiceProvider.php
  36. +1
    -0
      php-laravel/js/app.js
  37. +28
    -0
      php-laravel/js/bootstrap.js
  38. +8
    -0
      php-laravel/js/icons/eye-fill.vue
  39. +67
    -0
      php-laravel/js/icons/instagram.vue
  40. +3
    -0
      php-laravel/js/icons/loading.vue
  41. +5
    -0
      php-laravel/js/icons/plus-fill.vue
  42. +6
    -0
      php-laravel/js/icons/plus.vue
  43. +39
    -0
      php-laravel/js/icons/youtube.vue
  44. +116
    -0
      php-laravel/js/main.js
  45. +0
    -0
      php-laravel/js/panel/admin.vue
  46. +188
    -0
      php-laravel/js/panel/credits.vue
  47. +82
    -0
      php-laravel/js/panel/edit-cards.vue
  48. +71
    -0
      php-laravel/js/panel/order-item.vue
  49. +100
    -0
      php-laravel/js/panel/orders.vue
  50. +130
    -0
      php-laravel/js/panel/panel.vue
  51. +42
    -0
      php-laravel/js/panel/payment-card.vue
  52. +17
    -0
      php-laravel/js/panel/payment-slider.vue
  53. +28
    -0
      php-laravel/js/panel/saved-cards.vue
  54. +28
    -0
      php-laravel/js/panel/service-pane.vue
  55. +196
    -0
      php-laravel/js/panel/services.vue
  56. +119
    -0
      php-laravel/js/panel/settings.vue
  57. +46
    -0
      php-laravel/js/panel/sidebar.vue
  58. +3
    -0
      php-laravel/js/panel/summary.vue
  59. +82
    -0
      php-laravel/js/panel/support.vue
  60. +33
    -0
      php-laravel/js/panel/transaction-endpoint.vue
  61. +168
    -0
      php-laravel/js/register-area/register-area.vue
  62. +19
    -0
      php-laravel/routes/api.php
  63. +18
    -0
      php-laravel/routes/channels.php
  64. +19
    -0
      php-laravel/routes/console.php
  65. +162
    -0
      php-laravel/routes/web.php
  66. +152
    -0
      php-laravel/scss/_vars.scss
  67. +1454
    -0
      php-laravel/scss/main.scss
  68. +1
    -0
      php-laravel/views/app.blade.php
  69. +12
    -0
      php-laravel/views/change-email.blade.php
  70. +25
    -0
      php-laravel/views/email-changed.blade.php
  71. +228
    -0
      php-laravel/views/home.blade.php
  72. +61
    -0
      php-laravel/views/master.blade.php
  73. +25
    -0
      php-laravel/views/panel.blade.php
  74. +17
    -0
      php-laravel/views/request-reset.blade.php
  75. +17
    -0
      php-laravel/views/reset-password.blade.php
  76. +24
    -0
      php-laravel/views/support-ticket.blade.php
  77. +221
    -0
      php-laravel/views/terms-and-policy.blade.php
  78. +19
    -0
      php-laravel/views/vendor/mail/html/button.blade.php
  79. +11
    -0
      php-laravel/views/vendor/mail/html/footer.blade.php
  80. +11
    -0
      php-laravel/views/vendor/mail/html/header.blade.php
  81. +56
    -0
      php-laravel/views/vendor/mail/html/layout.blade.php
  82. +27
    -0
      php-laravel/views/vendor/mail/html/message.blade.php
  83. +14
    -0
      php-laravel/views/vendor/mail/html/panel.blade.php
  84. +7
    -0
      php-laravel/views/vendor/mail/html/subcopy.blade.php
  85. +3
    -0
      php-laravel/views/vendor/mail/html/table.blade.php
  86. +290
    -0
      php-laravel/views/vendor/mail/html/themes/default.css
  87. +1
    -0
      php-laravel/views/vendor/mail/text/button.blade.php
  88. +1
    -0
      php-laravel/views/vendor/mail/text/footer.blade.php
  89. +1
    -0
      php-laravel/views/vendor/mail/text/header.blade.php
  90. +9
    -0
      php-laravel/views/vendor/mail/text/layout.blade.php
  91. +27
    -0
      php-laravel/views/vendor/mail/text/message.blade.php
  92. +1
    -0
      php-laravel/views/vendor/mail/text/panel.blade.php
  93. +1
    -0
      php-laravel/views/vendor/mail/text/subcopy.blade.php
  94. +1
    -0
      php-laravel/views/vendor/mail/text/table.blade.php
  95. +62
    -0
      php-laravel/views/vendor/notifications/email.blade.php
  96. +25
    -0
      php-laravel/views/verify-email.blade.php
  97. +132
    -0
      php-laravel/views/welcome.blade.php
  98. +0
    -0
      scss/main.scss
  99. +0
    -0
      vue/panel.vue

+ 3
- 3
README.md Näytä tiedosto

@@ -1,6 +1,6 @@
# Code Examples
Some sample code taken from sites I've completed. They're written in
Javascript, PHP, and SCSS. The examples are incomplete and meant for
demonstrative purposes only as I'm not comfortable sharing complete code of a
live, potentialy profitable project.
Javascript, PHP, and SCSS. Some examples are incomplete and meant for
demonstrative purposes only as I'm not comfortable sharing complete code of some
live, potentialy profitable projects.


view.go → go/view.go Näytä tiedosto


+ 300
- 0
php-laravel/app/Console/Commands/ServicesInit.php Näytä tiedosto

@@ -0,0 +1,300 @@
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Http;

use App\Models\Service;
use App\Http\Controllers\Supply;

class ServicesInit extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'services:init';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Generate inital services';

/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}

public function handle() {
$this->youtube();
$this->tiktok();
$this->instagram();
$this->twitter();
}

protected function youtube() {
$s = new Service;
$s->name = 'Unique Views';
$s->type = 'views';
$s->site = 'youtube';
$s->maximum = 500000;
$s->minimum = 1000;
$s->price = 750;
$s->available = true;
$s->save();

$sup = Supply::smmworld( $s->id, 418);
$s->primary_supplier = $sup->id;
$s->save();

/*
$s = new Service;
$s->name = 'Language Targeted Views';
$s->type = 'views';
$s->site = 'youtube';
$s->modifier = 'language';
$s->maximum = 100000;
$s->minimum = 1000;
$s->price = 600;
$s->available = true;
$s->note = '{language: {
french: 2974, spanish: 2975, german: 2976,
}}';
$s->save();

$s = new Service;
$s->name = 'Location Targeted Views';
$s->site = 'youtube';
$s->modifier = 'location';
$s->maximum = 100000;
$s->minimum = 1000;
$s->price = 650;
$s->note = '{locations: {
india: 2600, brazil: 2597, colombia: 2608, philippines: 2609, mexico:
2673, netherlands: 2771, russia: 2772, spain: 2773, hongkong: 3065,
israel: 3119, }}';
$s->available = true;
$s->description = 'Real impressionss and profile visits';
$s->save();
*/

$s = new Service;
$s->name = 'Likes';
$s->site = 'youtube';
$s->maximum = 100000;
$s->minimum = 50;
$s->price = 850;
$s->available = true;
$s->save();

$sup = Supply::smmworld($s->id, 385);
$s->primary_supplier = $sup->id;
$s->save();

$s = new Service;
$s->name = 'Comments';
$s->site = 'youtube';
$s->maximum = 500000;
$s->minimum = 10;
$s->price = 7500;
$s->available = true;
$s->save();

$sup = Supply::smmkings($s->id, 3726);
$s->primary_supplier = $sup->id;
$s->save();

}

protected function instagram() {

$s = new Service;
$s->name = 'Likes with Profile Visits';
$s->type = 'likes';
$s->site = 'instagram';
$s->maximum = 500000;
$s->minimum = 1000;
$s->price = 700;
$s->available = true;
$s->save();

$sup = Supply::smmkings($s->id, 2997);
$s->primary_supplier = $sup->id;
$s->save();

$s = new Service;
$s->name = 'Likes';
$s->type = 'likes';
$s->site = 'instagram';
$s->maximum = 50000;
$s->minimum = 100;
$s->price = 50;
$s->available = true;
$s->save();

$sup = Supply::smmkings($s->id, 3775);
$s->primary_supplier = $sup->id;
$s->save();

$s = new Service;
$s->name = 'Post Impressions';
$s->type = 'impressions';
$s->site = 'instagram';
$s->maximum = 1000000;
$s->minimum = 100;
$s->price = 100;
$s->available = true;
$s->description = 'Post impressions and visits';
$s->save();

$sup = Supply::smmkings($s->id, 618);
$s->primary_supplier = $sup->id;
$s->save();

$s = new Service;
$s->name = 'Profile Visits';
$s->type = 'profile';
$s->site = 'instagram';
$s->maximum = 500000;
$s->minimum = 100;
$s->price = 100;
$s->available = true;
$s->save();

$sup = Supply::smmkings($s->id, 2997);
$s->primary_supplier = $sup->id;
$s->save();

$s = new Service;
$s->name = 'Post Views';
$s->type = 'views';
$s->site = 'instagram';
$s->maximum = 10000000;
$s->minimum = 100;
$s->price = 50;
$s->available = true;
$s->save();

$sup = Supply::smmkings($s->id, 2840);
$s->primary_supplier = $sup->id;
$s->save();

$s = new Service;
$s->name = 'Followers';
$s->type = 'followers';
$s->site = 'instagram';
$s->maximum = 100000;
$s->minimum = 10;
$s->price = 410;
$s->available = true;
$s->save();

$sup = Supply::smmkings($s->id, 3882);
$s->primary_supplier = $sup->id;
$s->save();

}

protected function twitter() {
$s = new Service;
$s->name = 'Video Views';
$s->type = 'views';
$s->site = 'twitter';
$s->maximum = 1000000;
$s->minimum = 100;
$s->price = 90;
$s->available = true;
$s->save();

$sup = Supply::smmkings($s->id, 287);
$s->primary_supplier = $sup->id;
$s->save();

$s = new Service;
$s->name = 'Impressions';
$s->type = 'impressions';
$s->site = 'twitter';
$s->maximum = 1000000;
$s->minimum = 100;
$s->price = 700;
$s->available = true;
$s->save();

$sup = Supply::smmkings($s->id, 288);
$s->primary_supplier = $sup->id;
$s->save();

$s = new Service;
$s->name = 'Followers';
$s->type = 'followers';
$s->site = 'twitter';
$s->maximum = 10000;
$s->minimum = 10;
$s->price = 600;
$s->available = true;
$s->save();

$sup = Supply::smmkings($s->id, 3820);
$s->primary_supplier = $sup->id;
$s->save();

}


protected function tiktok() {
$s = new Service;
$s->name = 'Views';
$s->type = 'views';
$s->site = 'tiktok';
$s->maximum = 10000000;
$s->minimum = 100;
$s->price = 70;
$s->available = true;
$s->save();

$sup = Supply::smmkings($s->id, 3826);
$s->primary_supplier = $sup->id;
$s->save();

$s = new Service;
$s->name = 'Likes';
$s->type = 'likes';
$s->site = 'tiktok';
$s->maximum = 100000;
$s->minimum = 100;
$s->price = 400;
$s->available = true;
$s->save();

$sup = Supply::smmkings($s->id, 3935);
$s->primary_supplier = $sup->id;
$s->save();

$s = new Service;
$s->name = 'Followers';
$s->type = 'followers';
$s->site = 'tiktok';
$s->maximum = 100000;
$s->minimum = 20;
$s->price = 300;
$s->available = true;
$s->save();

$sup = Supply::smmkings($s->id, 3934);
$s->primary_supplier = $sup->id;
$s->save();

}

}


+ 41
- 0
php-laravel/app/Console/Kernel.php Näytä tiedosto

@@ -0,0 +1,41 @@
<?php

namespace App\Console;

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
Commands\ServicesInit::class
];

/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
// $schedule->command('inspire')->hourly();
}

/**
* Register the commands for the application.
*
* @return void
*/
protected function commands()
{
$this->load(__DIR__.'/Commands');

require base_path('routes/console.php');
}
}

+ 41
- 0
php-laravel/app/Exceptions/Handler.php Näytä tiedosto

@@ -0,0 +1,41 @@
<?php

namespace App\Exceptions;

use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;

class Handler extends ExceptionHandler
{
/**
* A list of the exception types that are not reported.
*
* @var array
*/
protected $dontReport = [
//
];

/**
* A list of the inputs that are never flashed for validation exceptions.
*
* @var array
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];

/**
* Register the exception handling callbacks for the application.
*
* @return void
*/
public function register()
{
$this->reportable(function (Throwable $e) {
//
});
}
}

BillingController.php → php-laravel/app/Http/Controllers/BillingController.php Näytä tiedosto


+ 13
- 0
php-laravel/app/Http/Controllers/Controller.php Näytä tiedosto

@@ -0,0 +1,13 @@
<?php

namespace App\Http\Controllers;

use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;

class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}

+ 66
- 0
php-laravel/app/Http/Controllers/OrderController.php Näytä tiedosto

@@ -0,0 +1,66 @@
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Order;
use App\Models\Service;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;

class OrderController extends Controller
{
//This should also reduce user's available credits
public function newOrder(Request $request) {
$user = Auth::user();
$order = new Order;

$order->service_id = $request->service;
$order->user_id = Auth::user()->id;
$order->quantity = $request->quantity;
$order->url = $request->url;
$order->note = $request->note ?: '';
$order->status = 'processing';

if (!$this->validateOrder($request)){
abort(422);
}

$cost = ceil($order->quantity*$order->service->price/1000);
$user->credits = $user->credits - $cost;
if ($cost > $user->credits) {
abort(520, 'Insufficient Credits');
}

$user->save();
$order->save();
}

// Should probably check for other things like service availability
public function validateOrder($order) {
$service = Service::find($order->service);

if ($order->quantity < $service->minimum ||
$order->quantity > $service->maximum) {
return false;
}
return true;
}

public function changeURL(Request $request) {
$validated = $request->validate([
'order' => 'required',
'url' => 'required'
]);

$order = Order::find($request->order);
$user = Auth::user();

if (!in_array($order->status, ['processing', 'error'])) {
abort(422);
}

$order->url = $request->url;
$order->save();
}
}

+ 14
- 0
php-laravel/app/Http/Controllers/ServiceController.php Näytä tiedosto

@@ -0,0 +1,14 @@
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use App\Models\Service;

class ServiceController extends Controller
{
public function getServices(Request $request) {
return Service::all();
}
}

+ 169
- 0
php-laravel/app/Http/Controllers/Supply.php Näytä tiedosto

@@ -0,0 +1,169 @@
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

use App\Models\Supplier;
use App\Models\Order;

class Supply extends Controller
{
// Accepts a site name, service id, and integer/array of supplier rows to
// create

public static function fetch($site) {
return Http::post(config("services.$site.link"), ['key' =>
config("services.$site.key"), 'action' => 'services']);
}

public static function smmkings($service_id, $id) {
$services = Http::post(config("services.smmkings.link"), ['key' =>
config("services.smmkings.key"), 'action' => 'services'])->json();

foreach ($services as $service) {
if ($service['service'] == $id){
$s = new Supplier;
$s->service_id = $service_id;
$s->supplier = 'smmkings';
$s->supplier_id = (int) $service['service'];
$s->supplier_name = $service['name'];
$s->min = (int) $service['min'];
$s->max = (int) $service['max'];
$s->cost = $service['rate'] * 100;
$s->save();
return $s;
}
}
}

public static function smmworld($service_id, $id) {
$services = Http::post(config("services.smmworld.link"), ['key' =>
config("services.smmworld.key"), 'action' => 'services']);
$services = $services['services'];

foreach ($services as $service) {
if ($service['id'] == $id){
$s = new Supplier;
$s->service_id = $service_id;
$s->supplier = 'smmworld';
$s->supplier_id = $service['id'];
$s->supplier_name = $service['name'];
$s->min = $service['min'];
$s->max = $service['max'];
$s->cost = $service['price_per_k'] * 100;
$s->save();
return $s;
}
}
}

//Fulfills an order with only the primary supplier of it's service
public static function fulfill($order) {
if (gettype($order) == 'integer') {
$order = Order::find($order);
}
return Supply::orderPrimary($order);
}

//Fulfills an order with the first available supplier it finds. It returns
//false if none could be found and the refreshed order model otherwise.
public static function fulfillWithAny($order) {
if (gettype($order) == 'integer') {
$order = Order::find($order);
}
if (Supply::orderPrimary($order)) {
return $order->refresh();
};

$suppliers = $order->service->suppliers;

foreach ($suppliers as $s) {
switch ($s->supplier) {
case 'smmkings':
if (Supply::orderSmmkings($order, $s)){
return $order->refresh();
}
break;
case 'smmworld':
if (Supply::orderSmmkings($order, $s)){
return $order->refresh();
}
break;
}
}

return false;
}

//Fulfills an order with only the primary supplier of it's service
public static function orderPrimary($order) {
if (gettype($order) == 'integer') {
$order = Order::find($order);
}

$primary = $order->service->primary;

switch ($primary->supplier) {
case 'smmkings':
return Supply::orderSmmkings($order, $primary);
break;
case 'smmworld':
return Supply::orderSmmworld($order, $primary);
break;
}
}


//Should check that cost is not greater than price and store order
//information if completed. return false/NULL if an error occured
public static function orderSmmkings($order, $supplier) {
if ($supplier->supplier != 'smmkings') {
return false;
}

//Check that the order is not already pending. Probably by calling
//another function.
//Check supplier cost here to make sure the request is a good idea

$response = Http::post(config("services.smmkings.link"), ['key' =>
config("services.smmkings.key"), 'action' => 'add',
'service' => $supplier->supplier_id, 'link' =>
$order->url, 'quantity' => $order->quantity])->json();

//This should log the error for later reporting
if (array_key_exists('error', $response)){
return;
}

//store order information here

$status = Http::post(config("services.smmkings.link"), ['key' =>
config("services.smmkings.key"), 'action' => 'status',
'order' => $response['order']])->json();

return $response;
}

public static function orderSmmworld($order, $supplier) {
if ($supplier->supplier != 'smmworld') {
return false;
}

$response = Http::post(config("services.smmworld.link"), ['key' =>
config("services.smmworld.key"), 'action' => 'add',
'service' => $supplier->supplier_id, 'link' => $order->url, 'quantity' =>
$order->quantity])->json();

return $response;
}


}

+ 38
- 0
php-laravel/app/Http/Controllers/Ticket.php Näytä tiedosto

@@ -0,0 +1,38 @@
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Mail;
use Illuminate\Support\Facades\Log;

use App\Models;
use App\Mail\SupportTicket;

class Ticket extends Controller
{
public function send(Request $request){
$validated = $request->validate([
'topic' => 'required',
'message' => 'required'
]);

$ticket = $this->create($request->topic);

Mail::to('donotreply@trendplays.com')->send(new
SupportTicket($ticket, $request->message));
}

//Should probably have a minimum character restriction later
public function create(String $type){
$ticket = new Models\Ticket;
$ticket->user_id = Auth::user()->id;
$ticket->type = $type;
$ticket->status = 'processing';
$ticket->complete = false;
$ticket->save();

return $ticket;
}
}

+ 165
- 0
php-laravel/app/Http/Controllers/UserController.php Näytä tiedosto

@@ -0,0 +1,165 @@
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\User;
use App\Models\Order;
use App\Models\Service;
use App\Mail\ChangeEmail;

use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\URL;
use Illuminate\Auth\Events\Registered;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Facades\Auth;
use Illuminate\Notifications\Messages\MailMessage;
use Mail;

use Stripe\Stripe;
use Stripe\Customer;

class UserController extends Controller
{
public function create(Request $request) {
$validated = $request->validate([
'name' => 'required|max:30',
'email' => 'required|email|unique:users|max:255',
'password' => 'required|confirmed
|min:8|regex:/[a-z]/|regex:/[A-Z]/|regex:/[0-9]/'
]);

$user = new User;
$user->name = $request->name;
$user->email = $request->email;
$user->role = "client";
$user->active = true;
$user->password = Hash::make($request->password);
$user->save();

Auth::login($user);
event(new Registered($user));
}

public function forgotPassword(Request $request) {
$request->validate(['email' => 'required|email']);

$status = Password::sendResetLink(
$request->only('email')
);
}

public function resetPassword(Request $request) {
$request->validate([
'token' => 'required',
'email' => 'required|email',
'password' => 'required|min:8|confirmed',
]);

$status = Password::reset(
$request->only('email', 'password',
'password_confirmation', 'token'),
function ($user, $password) use ($request) {
$user->forceFill([
'password' => Hash::make($password)
])->setRememberToken(Str::random(60));
});
if ($status == Password::PASSWORD_RESET) {
return response()->json([
"status" => "success"
]);
}
}

public function login(Request $request) {
$credentials = $request->only('email', 'password');

//This should probably be changed to not return a page
if (Auth::attempt($credentials)) {
$request->session()->regenerate();
$this->clearPaying();
} else {
abort(401);
}
}

public function logout(Request $request) {
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/');
}

//It should have an orderBy clause to make sure the most recent are first
//This should limit non pending orders to 50. Should also return a json of all services
public function getOrders(Request $request) {
return Auth::user()->orders()->with('service')->withCasts(['updated_at'
=> 'datetime:d-m-Y'])->latest()->limit(100)->get();
}

public function changeName(Request $request) {
$validated = $request->validate([
'name' => 'required|max:30'
]);
$user = Auth::user();
$user->name = $request->name;
$user->save();
return $user;
}

public function changeEmail(Request $request) {
$validated = $request->validate([
'email' => 'required|email|unique:users,email',
]);

$link = URL::temporarySignedRoute('reset-email', now()->addDays(30),
['user' => Auth::user()->id, 'email' => $request->email]);

Mail::to($request->email)->send(new ChangeEmail(Auth::user()->email,
$link));


}

public function resetEmail(Request $request) {

if (! $request->hasValidSignature()) {
abort(401);
}

$validated = $request->validate([
'email' => 'required|email|unique:users,email',
]);

if (! $validated) {
abort(401);
}

$user = User::find($request->user);
$user->email = $request->email;
$user->save();

return view('email-changed');
}

public function changePassword(Request $request) {
$validated = $request->validate([
'current_password' => 'password',
'password' => 'required|confirmed|min:8|regex:/.*[a-z].*/|regex:/.*[A-Z].*/|regex:/.*[0-9].*/'
]);

$user = Auth::user();
$user->password = Hash::make($request->password);
$user->save();
}

public function clearPaying() {
$user = Auth::user();
if ($user->paying) {
$user->paying = false;
$user->save();
}
}

}

+ 67
- 0
php-laravel/app/Http/Kernel.php Näytä tiedosto

@@ -0,0 +1,67 @@
<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* These middleware are run during every request to your application.
*
* @var array
*/
protected $middleware = [
// \App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class,
\Fruitcake\Cors\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];

/**
* The application's route middleware groups.
*
* @var array
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],

'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];

/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];
}

+ 21
- 0
php-laravel/app/Http/Middleware/Authenticate.php Näytä tiedosto

@@ -0,0 +1,21 @@
<?php

namespace App\Http\Middleware;

use Illuminate\Auth\Middleware\Authenticate as Middleware;

class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* @param \Illuminate\Http\Request $request
* @return string|null
*/
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return route('login');
}
}
}

+ 17
- 0
php-laravel/app/Http/Middleware/EncryptCookies.php Näytä tiedosto

@@ -0,0 +1,17 @@
<?php

namespace App\Http\Middleware;

use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;

class EncryptCookies extends Middleware
{
/**
* The names of the cookies that should not be encrypted.
*
* @var array
*/
protected $except = [
//
];
}

+ 17
- 0
php-laravel/app/Http/Middleware/PreventRequestsDuringMaintenance.php Näytä tiedosto

@@ -0,0 +1,17 @@
<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;

class PreventRequestsDuringMaintenance extends Middleware
{
/**
* The URIs that should be reachable while maintenance mode is enabled.
*
* @var array
*/
protected $except = [
//
];
}

+ 32
- 0
php-laravel/app/Http/Middleware/RedirectIfAuthenticated.php Näytä tiedosto

@@ -0,0 +1,32 @@
<?php

namespace App\Http\Middleware;

use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|null ...$guards
* @return mixed
*/
public function handle(Request $request, Closure $next, ...$guards)
{
$guards = empty($guards) ? [null] : $guards;

foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::HOME);
}
}

return $next($request);
}
}

+ 19
- 0
php-laravel/app/Http/Middleware/TrimStrings.php Näytä tiedosto

@@ -0,0 +1,19 @@
<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;

class TrimStrings extends Middleware
{
/**
* The names of the attributes that should not be trimmed.
*
* @var array
*/
protected $except = [
'current_password',
'password',
'password_confirmation',
];
}

+ 20
- 0
php-laravel/app/Http/Middleware/TrustHosts.php Näytä tiedosto

@@ -0,0 +1,20 @@
<?php

namespace App\Http\Middleware;

use Illuminate\Http\Middleware\TrustHosts as Middleware;

class TrustHosts extends Middleware
{
/**
* Get the host patterns that should be trusted.
*
* @return array
*/
public function hosts()
{
return [
$this->allSubdomainsOfApplicationUrl(),
];
}
}

+ 23
- 0
php-laravel/app/Http/Middleware/TrustProxies.php Näytä tiedosto

@@ -0,0 +1,23 @@
<?php

namespace App\Http\Middleware;

use Fideloper\Proxy\TrustProxies as Middleware;
use Illuminate\Http\Request;

class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array|string|null
*/
protected $proxies;

/**
* The headers that should be used to detect proxies.
*
* @var int
*/
protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_AWS_ELB;
}

+ 17
- 0
php-laravel/app/Http/Middleware/VerifyCsrfToken.php Näytä tiedosto

@@ -0,0 +1,17 @@
<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array
*/
protected $except = [
'hooks/*'
];
}

+ 39
- 0
php-laravel/app/Mail/ChangeEmail.php Näytä tiedosto

@@ -0,0 +1,39 @@
<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;

class ChangeEmail extends Mailable
{
use Queueable, SerializesModels;

/**
* Create a new message instance.
*
* @return void
*/
public $current_email;
public $link;

public function __construct($current_email, $link)
{
$this->current_email = $current_email;
$this->link = $link;
}

/**
* Build the message.
*
* @return $this
*/
public function build()
{
$this->view('change-email');
$this->subject('Email change requested');
return $this;
}
}

+ 49
- 0
php-laravel/app/Mail/SupportTicket.php Näytä tiedosto

@@ -0,0 +1,49 @@
<?php

namespace App\Mail;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;

use App\Models\Ticket;

class SupportTicket extends Mailable
{
use Queueable, SerializesModels;

/**
* Create a new message instance.
*
* @return void
*/
public $name;
public $email;
public $user_message;
public $type;
public $id;

public function __construct(Ticket $ticket, String $user_message)
{
$this->name = $ticket->user->name;
$this->email = $ticket->user->email;
$this->user_message = $user_message;
$this->type = $ticket->type;
$this->id = $ticket->id;
}

/**
* Build the message.
*
* @return $this
*/
public function build()
{
$this->view('support-ticket');
$this->subject("Ticket: $this->id, $this->type");
$this->replyTo($this->email);
return $this;
}
}

+ 30
- 0
php-laravel/app/Models/Order.php Näytä tiedosto

@@ -0,0 +1,30 @@
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Models\User;
use App\Models\Service;

class Order extends Model
{
use HasFactory;

protected $fillable = [
'quantity', 'url'
];

public function service() {
return $this->belongsTo(Service::class);
}

public function user() {
return $this->belongsTo(User::class);
}

public function getServiceNameAttribute() {
return $this->service()->name();
}

}

+ 17
- 0
php-laravel/app/Models/Referral.php Näytä tiedosto

@@ -0,0 +1,17 @@
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

use App\Models\User;

class Referral extends Model
{
use HasFactory;

public function users() {
return $this->hasMany(User::class);
}
}

+ 30
- 0
php-laravel/app/Models/Service.php Näytä tiedosto

@@ -0,0 +1,30 @@
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Models\Order;
use App\Models\Supplier;

class Service extends Model
{
use HasFactory;

protected $fillable = [
'name', 'type', 'site', 'available'
];

public function order() {
return $this->hasMany(Order::class);
}

public function suppliers() {
return $this->hasMany(Supplier::class);
}

public function primary() {
return $this->belongsTo(Supplier::class, 'primary_supplier', 'id');
}

}

+ 18
- 0
php-laravel/app/Models/Supplier.php Näytä tiedosto

@@ -0,0 +1,18 @@
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

use App\Models\Service;

class Supplier extends Model
{
use HasFactory;

public function service() {
return $this->belongsTo(Service::class);
}
}


+ 16
- 0
php-laravel/app/Models/Ticket.php Näytä tiedosto

@@ -0,0 +1,16 @@
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Models\User;

class Ticket extends Model
{
use HasFactory;

public function user() {
return $this->belongsTo(User::class);
}
}

+ 16
- 0
php-laravel/app/Models/Transaction.php Näytä tiedosto

@@ -0,0 +1,16 @@
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Models\User;

class Transaction extends Model
{
use HasFactory;

public function user() {
return $this->belongsTo(User::class);
}
}

+ 60
- 0
php-laravel/app/Models/User.php Näytä tiedosto

@@ -0,0 +1,60 @@
<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use App\Models\Order;
use App\Models\Transaction;
use App\Models\Ticket;
use Laravel\Cashier\Billable;

class User extends Authenticatable implements MustVerifyEmail
{
use HasFactory, Notifiable, Billable;

/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name',
'email',
'password',
];

/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password',
'remember_token',
];

/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];

public function orders() {
return $this->hasMany(Order::class);
}

public function transactions() {
return $this->hasMany(Transaction::class);
}

public function tickets() {
return $this->hasMany(Ticket::class);
}

}

+ 28
- 0
php-laravel/app/Providers/AppServiceProvider.php Näytä tiedosto

@@ -0,0 +1,28 @@
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}

/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
//
}
}

+ 30
- 0
php-laravel/app/Providers/AuthServiceProvider.php Näytä tiedosto

@@ -0,0 +1,30 @@
<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;

class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
// 'App\Models\Model' => 'App\Policies\ModelPolicy',
];

/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();

//
}
}

+ 21
- 0
php-laravel/app/Providers/BroadcastServiceProvider.php Näytä tiedosto

@@ -0,0 +1,21 @@
<?php

namespace App\Providers;

use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\ServiceProvider;

class BroadcastServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Broadcast::routes();

require base_path('routes/channels.php');
}
}

+ 32
- 0
php-laravel/app/Providers/EventServiceProvider.php Näytä tiedosto

@@ -0,0 +1,32 @@
<?php

namespace App\Providers;

use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;

class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
],
];

/**
* Register any events for your application.
*
* @return void
*/
public function boot()
{
//
}
}

+ 63
- 0
php-laravel/app/Providers/RouteServiceProvider.php Näytä tiedosto

@@ -0,0 +1,63 @@
<?php

namespace App\Providers;

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;

class RouteServiceProvider extends ServiceProvider
{
/**
* The path to the "home" route for your application.
*
* This is used by Laravel authentication to redirect users after login.
*
* @var string
*/
public const HOME = '/home';

/**
* The controller namespace for the application.
*
* When present, controller route declarations will automatically be prefixed with this namespace.
*
* @var string|null
*/
// protected $namespace = 'App\\Http\\Controllers';

/**
* Define your route model bindings, pattern filters, etc.
*
* @return void
*/
public function boot()
{
$this->configureRateLimiting();

$this->routes(function () {
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));

Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
});
}

/**
* Configure the rate limiters for the application.
*
* @return void
*/
protected function configureRateLimiting()
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
});
}
}

+ 1
- 0
php-laravel/js/app.js Näytä tiedosto

@@ -0,0 +1 @@
require('./bootstrap');

+ 28
- 0
php-laravel/js/bootstrap.js Näytä tiedosto

@@ -0,0 +1,28 @@
window._ = require('lodash');

/**
* We'll load the axios HTTP library which allows us to easily issue requests
* to our Laravel back-end. This library automatically handles sending the
* CSRF token as a header based on the value of the "XSRF" token cookie.
*/

window.axios = require('axios');

window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

/**
* Echo exposes an expressive API for subscribing to channels and listening
* for events that are broadcast by Laravel. Echo and event broadcasting
* allows your team to easily build robust real-time web applications.
*/

// import Echo from 'laravel-echo';

// window.Pusher = require('pusher-js');

// window.Echo = new Echo({
// broadcaster: 'pusher',
// key: process.env.MIX_PUSHER_APP_KEY,
// cluster: process.env.MIX_PUSHER_APP_CLUSTER,
// forceTLS: true
// });

+ 8
- 0
php-laravel/js/icons/eye-fill.vue Näytä tiedosto

@@ -0,0 +1,8 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"
fill="currentColor" class="bi bi-eye-fill" viewBox="0 0 16 16">
<path d="M10.5 8a2.5 2.5 0 1 1-5 0 2.5 2.5 0 0 1 5 0z"/>
<path d="M0 8s3-5.5 8-5.5S16 8 16 8s-3 5.5-8 5.5S0 8 0 8zm8 3.5a3.5 3.5 0
1 0 0-7 3.5 3.5 0 0 0 0 7z"/>
</svg>
</template>

+ 67
- 0
php-laravel/js/icons/instagram.vue Näytä tiedosto

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 551.034 551.034" style="enable-background:new 0 0 551.034 551.034;" xml:space="preserve">
<g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="275.517" y1="4.57" x2="275.517" y2="549.72" gradientTransform="matrix(1 0 0 -1 0 554)">
<stop offset="0" style="stop-color:#E09B3D"/>
<stop offset="0.3" style="stop-color:#C74C4D"/>
<stop offset="0.6" style="stop-color:#C21975"/>
<stop offset="1" style="stop-color:#7024C4"/>
</linearGradient>
<path style="fill:url(#SVGID_1_);" d="M386.878,0H164.156C73.64,0,0,73.64,0,164.156v222.722
c0,90.516,73.64,164.156,164.156,164.156h222.722c90.516,0,164.156-73.64,164.156-164.156V164.156
C551.033,73.64,477.393,0,386.878,0z M495.6,386.878c0,60.045-48.677,108.722-108.722,108.722H164.156
c-60.045,0-108.722-48.677-108.722-108.722V164.156c0-60.046,48.677-108.722,108.722-108.722h222.722
c60.045,0,108.722,48.676,108.722,108.722L495.6,386.878L495.6,386.878z"/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="275.517" y1="4.57" x2="275.517" y2="549.72" gradientTransform="matrix(1 0 0 -1 0 554)">
<stop offset="0" style="stop-color:#E09B3D"/>
<stop offset="0.3" style="stop-color:#C74C4D"/>
<stop offset="0.6" style="stop-color:#C21975"/>
<stop offset="1" style="stop-color:#7024C4"/>
</linearGradient>
<path style="fill:url(#SVGID_2_);" d="M275.517,133C196.933,133,133,196.933,133,275.516s63.933,142.517,142.517,142.517
S418.034,354.1,418.034,275.516S354.101,133,275.517,133z M275.517,362.6c-48.095,0-87.083-38.988-87.083-87.083
s38.989-87.083,87.083-87.083c48.095,0,87.083,38.988,87.083,87.083C362.6,323.611,323.611,362.6,275.517,362.6z"/>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="418.31" y1="4.57" x2="418.31" y2="549.72" gradientTransform="matrix(1 0 0 -1 0 554)">
<stop offset="0" style="stop-color:#E09B3D"/>
<stop offset="0.3" style="stop-color:#C74C4D"/>
<stop offset="0.6" style="stop-color:#C21975"/>
<stop offset="1" style="stop-color:#7024C4"/>
</linearGradient>
<circle style="fill:url(#SVGID_3_);" cx="418.31" cy="134.07" r="34.15"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

+ 3
- 0
php-laravel/js/icons/loading.vue Näytä tiedosto

@@ -0,0 +1,3 @@
<template>
<svg class="loading-icon" data-set="loaders" data-loading="lazy" width="30px" height="30px" data-src="https://s2.svgbox.net/loaders.svg?ic=oval" data-icon="oval" viewBox="0 0 38 38" xmlns="http://www.w3.org/2000/svg" stroke="currentColor" color="" data-attributes-set="viewBox,xmlns,stroke,color" data-rendered="true"><g transform="translate(1 1)" stroke-width="2" fill="none" fill-rule="evenodd"><circle stroke-opacity=".5" cx="18" cy="18" r="18"></circle><path d="M36 18c0-9.94-8.06-18-18-18"><animateTransform attributeName="transform" type="rotate" from="0 18 18" to="360 18 18" dur="1s" repeatCount="indefinite"></animateTransform></path></g></svg>
</template>

+ 5
- 0
php-laravel/js/icons/plus-fill.vue Näytä tiedosto

@@ -0,0 +1,5 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-square-fill" viewBox="0 0 16 16">
<path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z"/>
</svg>
</template>

+ 6
- 0
php-laravel/js/icons/plus.vue Näytä tiedosto

@@ -0,0 +1,6 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-square" viewBox="0 0 16 16">
<path d="M14 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h12zM2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2z"/>
<path d="M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z"/>
</svg>
</template>

+ 39
- 0
php-laravel/js/icons/youtube.vue Näytä tiedosto

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 461.001 461.001" style="enable-background:new 0 0 461.001 461.001;" xml:space="preserve">
<path style="fill:#F61C0D;" d="M365.257,67.393H95.744C42.866,67.393,0,110.259,0,163.137v134.728
c0,52.878,42.866,95.744,95.744,95.744h269.513c52.878,0,95.744-42.866,95.744-95.744V163.137
C461.001,110.259,418.135,67.393,365.257,67.393z M300.506,237.056l-126.06,60.123c-3.359,1.602-7.239-0.847-7.239-4.568V168.607
c0-3.774,3.982-6.22,7.348-4.514l126.06,63.881C304.363,229.873,304.298,235.248,300.506,237.056z"/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

+ 116
- 0
php-laravel/js/main.js Näytä tiedosto

@@ -0,0 +1,116 @@
import RegisterArea from './register-area/register-area.vue'
import Panel from './panel/panel.vue'
import '../scss/main.scss'
import { createApp } from 'vue'
importAll(require.context('../images', false, /\.(png|jpe?g|svg)$/))

let heroText = document.querySelectorAll(".landing-hero h2,.landing-hero p")
let registerToggles = document.querySelectorAll(".register-btn, .register-area\
.cancel-button, .services-cards button")
let token = getCookie('XSRF-TOKEN')

function importAll(r) {
return r.keys().map(r)
}

function getCookie(name) {
var re = new RegExp(name + "=([^;]+)")
var value = re.exec(document.cookie)

return (value != null) ? unescape(value[1]) : null
}

function getToken() {
return fetch("/sanctum/csrf-cookie", {
method: 'GET'
}).then( () => {
token = getCookie('XSRF-TOKEN')
return token
})
}

function login(event) {
getToken().then(fetch("/login", {
method: 'POST',
headers: {'Content-Type': 'application/json',
'Accept': 'application/json',
'X-XSRF-TOKEN': token},
body: JSON.stringify({"email":
document.getElementById("login_email").value,
"password": document.getElementById("login_password").value}),
}).then(response => {
if (response.ok) {
window.location.assign("/panel")
} else {
document.querySelector("#login_form .error").innerText =
"Invalid credentials."
}
}))
event.preventDefault()
// event.stopPropogation()
}


//Attempt to resend the verification link
function resendLink(event) {
fetch("/resend-verification", {
method: 'POST',
headers: {'Content-Type': 'application/json',
'Accept': 'application/json',
'X-XSRF-TOKEN': token},
}).then(response => {
if (response.ok) {
event.target.parentNode.getElementsByTagName('h3')[0].innerText =
"The link has been resent."
} else {
event.target.parentNode.getElementsByTagName('h3')[0].innerText =
`${response.status} : ${response.statusText}`
}})
event.preventDefault();
}

function toggleNav() {
heroText.forEach(item => {
item.classList.toggle("hidden")
})
document.querySelector("nav form.login").classList.toggle("active")
this.classList.toggle("toggled")
}


if (window.location.pathname == '/') {
document.getElementById('nav_toggle').addEventListener('click', toggleNav)
document.querySelector('#login_form button').addEventListener('click', login)
const app = createApp(RegisterArea).mount('#app')
// app.token = token
if (!token) {app.token = getToken()}

//Triggers for registration menu
for (let i = 0; i < registerToggles.length; i++) {
registerToggles[i].addEventListener("click", function() {
document.querySelector(".register-area").classList.add("active")
app.active = 'register'
})
}
document.getElementById("forgot-password-btn").onclick = event => {
document.querySelector(".register-area").classList.add("active")
app.active = 'forgot'
event.preventDefault()
}

//FAQ collapsibles
let cols = document.getElementsByClassName("collapsible");

for (let i = 0; i < cols.length; i++) {
cols[i].addEventListener("click", function() {
this.classList.toggle("active");
});
}
} else if (window.location.pathname == '/verify-email') {
document.getElementById('resend_verification').addEventListener("click", resendLink)
} else if (window.location.pathname == '/panel') {
const app = createApp(Panel).mount('#panel')
getToken().then(()=> {app.token = token})
window.onhashchange = ()=>{app.active = location.hash}
}


+ 0
- 0
php-laravel/js/panel/admin.vue Näytä tiedosto


+ 188
- 0
php-laravel/js/panel/credits.vue Näytä tiedosto

@@ -0,0 +1,188 @@
<template>
<section class="select-credits">
<div class="credits-pane"><h2>10 Credits</h2>
<h3>$10.99</h3><div><span>Qty</span><input min="0" max="1000" v-model="packs.credits10" type="number"></div>
</div>

<div class="credits-pane"><div><h2>50 Credits</h2><span>+5 Free Credits</span></div>
<h3>$54.99 </h3><div><span>Qty</span><input min="0" max="1000" v-model="packs.credits50" type="number"></div>
</div>

<div class="credits-pane"><div><h2>100 Credits</h2><span>+10 Free Credits</span></div>
<h3>$109.99</h3> <div><span>Qty</span><input min="0" max="1000" v-model="packs.credits100" type="number"></div>
</div>

<div class="credits-pane"><div><h2>1000 Credits</h2><span>+150 Free Credits</span></div>
<h3>$1010.00</h3> <div><span>Qty</span><input min="0" max="1000" v-model="packs.credits1000" type="number"></div>
</div>
<h3>Total: ${{total.toLocaleString('en')}}</h3>
<div id="credits-errors"></div>
</section>

<section id="payment-section">
<h4>Select a payment method</h4>

<div class="sliding-menu">
<a @click="method = 'payeer'" :class="{selected: method == 'payeer'}">Payeer</a>
<a @click="method = 'pm'" :class="{selected: method == 'pm'}">Perfect Money</a>
<div :class="{right: (method == 'pm')}" class="menu-slider"><div></div></div>
</div>

<div v-if="method == 'payeer'" class="payment-window">
<img src="../../images/payeer.png" alt="">
<p>Payeer allows you to pay securely by transfering your choice of cryptocurrency
to a temporary address.
</p>
</div>

<div v-if="method == 'pm'" class="payment-window">
<img src="../../images/perfect_money.svg" alt="">
<p>Pay by transfering USD from your Perfect Money wallet.</p>
</div>

<div id="agreement-check">
<input v-model="agreed" type="checkbox"><label>I have read and agree to the <a
href="/terms-and-policy">Terms and Policy</a> and will not pursue a dispute or chargeback.</label>
<div id="payment-error"></div>
</div>
</section>

<section class="credits-confirm">
<button @click="pay" :disabled="!ready"
class="brand-btn">Buy<loading v-if="loading"></loading></button>
</section>

</template>

<script>
import Loading from '../icons/loading.vue'

function total() {
return this.packs.credits10*10.99 + this.packs.credits50*54.99
+ this.packs.credits100*109.99 + this.packs.credits1000*1010
}

//Gets the secret key specific to chosen payment amount and user
function getSecret() {
document.getElementById('credits-errors').textContent = ''
this.loading = true
return fetch('/panel/secret', {
method: 'POST',
headers: {'Content-Type': 'application/json',
'Accept': 'application/json',
'X-XSRF-TOKEN': this.token},
body: JSON.stringify({'packs': this.packs})
}).then((response) => {
if (response.ok) {
return response.text()
} else {
document.getElementById('credits-errors').textContent =
`${response.status}: ${response.statusText}`
}
}).then(secret => {
this.loading = false
return secret
})
}

function pay() {
if (this.method == 'payeer') {
this.payPayeer()
} else if (this.method == 'pm') {
this.payPm()
}
}

function makeInput(name, value) {
let input = document.createElement('input')
input.type = 'hidden'
input.name = name
input.value = value
return input
}

function payPayeer() {
fetch('/panel/payeer', {
method: 'POST',
headers: {'Content-Type': 'application/json',
'Accept': 'application/json',
'X-XSRF-TOKEN': this.token},
body: JSON.stringify({'packs': this.packs})
}).then(response => {return response.json()}).then(data => {
let form = document.createElement('form')
document.body.appendChild(form)
form.method = 'POST'
form.action = 'https://payeer.com/merchant/'
form.appendChild(this.makeInput('m_shop', data.shop))
form.appendChild(this.makeInput('m_orderid', data.transaction))
form.appendChild(this.makeInput('m_amount', data.amount))
form.appendChild(this.makeInput('m_curr', 'USD'))
form.appendChild(this.makeInput('m_desc', data.description))
form.appendChild(this.makeInput('m_sign', data.signature))
form.appendChild(this.makeInput('m_params', data.params))
form.appendChild(this.makeInput('m_cipher_method', 'AES-256-CBC'))
form.submit()
/* console.log(data.signature) */
})

}

function payPm() {
fetch('/panel/pm', {
method: 'POST',
headers: {'Content-Type': 'application/json',
'Accept': 'application/json',
'X-XSRF-TOKEN': this.token},
body: JSON.stringify({'packs': this.packs})
}).then(response => {return response.json()}).then(data => {
let form = document.createElement('form')
document.body.appendChild(form)
form.method = 'POST'
form.action = 'https://perfectmoney.is/api/step1.asp'
form.appendChild(this.makeInput('PAYEE_ACCOUNT', data.account))
form.appendChild(this.makeInput('PAYEE_NAME', 'Trendplays Network'))
form.appendChild(this.makeInput('PAYMENT_AMOUNT', data.amount))
form.appendChild(this.makeInput('PAYMENT_UNITS', 'USD'))
form.appendChild(this.makeInput('PAYMENT_ID', data.transaction))
form.appendChild(this.makeInput('STATUS_URL',
'https://trendplays.com/hooks/pm-transaction'))
form.appendChild(this.makeInput('PAYMENT_URL',
'https://trendplays.com/panel/transaction-complete'))
form.appendChild(this.makeInput('PAYMENT_URL_METHOD', 'POST'))
form.appendChild(this.makeInput('NOPAYMENT_URL',
'https://trendplays.com/panel/transaction-failed'))
form.appendChild(this.makeInput('NOPAYMENT_URL_METHOD', 'GET'))
form.appendChild(this.makeInput('SUGGESTED_MEMO', data.description))
form.appendChild(this.makeInput('SUGGESTED_MEMO_NOCHANGE', true))
form.submit()
})
}

function ready() {
if (this.packs.credis10 < 0) {
return false
} else if (this.packs.credis50 < 0) {
return false
} else if (this.packs.credis100 < 0) {
return false
} else if (this.packs.credis1000 < 0) {
return false
}

return this.total > 0 && !this.loading && this.agreed
}

export default {
components:{Loading},
data() {
return {packs: {credits10: 0, credits50: 0,
credits100: 0, credits1000: 0}, loading: false, method: 'payeer',
agreed: false
}
},
computed: {total, ready},
methods: {getSecret, pay, payPm, payPayeer, makeInput},
props: ['preferred', 'token'],
emits: ['purchaseComplete'],
}
</script>

+ 82
- 0
php-laravel/js/panel/edit-cards.vue Näytä tiedosto

@@ -0,0 +1,82 @@
<template>
<div v-if="cards && cards.length > 0">
<div class="saved-cards-heading">
<h5>Card</h5> <h5>Default</h5> <h5>Delete</h5>
</div>
<div v-for="card in cards" :key="card.id" class="saved-card">
<span>{{card.card.brand[0].toUpperCase() + card.card.brand.substring(1)}}
(••••{{card.card.last4}})</span>
<span><input :checked="card.id == preferred" :value="card.id" name="selected-card" type="radio"
@change="change(card.id)"></span>
<span><img @click="remove(card.id)" src="../../images/close-icon-black.svg"/></span>
</div>
<p id="billing-error"></p>
</div>
</template>

<script>
function get() {
fetch('/panel/cards', {
method: 'GET',
headers: {'Content-Type': 'application/json',
'Accept': 'application/json',
'X-XSRF-TOKEN': this.token}
}).then((response) => {response.json().then(data => {
this.cards = data.data
})})
}

function change(card) {
fetch('/panel/change-card', {
method: 'POST',
headers: {'Content-Type': 'application/json',
'Accept': 'application/json',
'X-XSRF-TOKEN': this.token},
body: JSON.stringify({'card': card})
}).then((response) => {
if (response.ok) {
response.json().then((data) => {
this.cards = data.data
})
} else {
console.log('bad')
document.getElementById("billing-error").textContent =
`${response.status}: ${response.statusText}`
}
})
}

function remove(card) {
if (card.length == 1) {
return
}

fetch('/panel/delete-card', {
method: 'POST',
headers: {'Content-Type': 'application/json',
'Accept': 'application/json',
'X-XSRF-TOKEN': this.token},
body: JSON.stringify({'card': card})
}).then((response) => {
if (response.ok) {
response.json().then((data) => {
this.cards = data.data
})
} else {
document.getElementById("billing-error").textContent =
`${response.status}: ${response.statusText}`
}
})
}

export default {
data() {
return {cards: null}
},
methods: {get, change, remove},
created() {
this.get()
},
props: ['token', 'preferred'],
}
</script>

+ 71
- 0
php-laravel/js/panel/order-item.vue Näytä tiedosto

@@ -0,0 +1,71 @@
<template>
<div id="overlay" v-if="selected">

<img @click="$emit('close')" class="cancel icon"
src="../../images/cancel-icon2.svg" alt=""/>
<div class="overlay-item">
<img v-if="selected.service.site == 'youtube'" class="icon"
src="../../images/youtube-icon.svg" alt=""/>
<img v-if="selected.service.site == 'instagram'" class="icon"
src="../../images/instagram-icon.svg" alt=""/>
<img v-if="selected.service.site == 'twitter'" class="icon"
src="../../images/twitter.svg" alt=""/>
<img v-if="selected.service.site == 'tiktok'" class="icon"
src="../../images/tik-tok.svg" alt=""/>
<h3>{{selected.service.name}}</h3>

<div class="details">
<ul>
<li><b>Status:</b> <span>{{selected.status.charAt(0).toUpperCase() +
selected.status.slice(1)}}</span></li>
<li><b>Quantity:</b> <span>{{selected.quantity}}</span></li>
<li><b>Remaining:</b> <span>{{selected.remaining}}</span></li>
<li><b>URL:</b> <span>{{selected.url}}</span></li>
</ul>
</div>

<div v-if="selected.status == 'processing' || selected.status ==
'error'" class="change-url">
<h4>URL</h4>
<div><input v-model="url" type="url" id="url"></div>
<button @click="saveURL" :disabled="loading">Save
<loading-icon v-if="loading"></loading-icon></button>
<p id="overlay-error">{{errorMessage}}</p>
</div>

</div>

</div>
</template>

<script>
import LoadingIcon from '../icons/loading.vue'

function saveURL() {
fetch('/panel/save-url', {
method: 'POST',
headers: {'Content-Type': 'application/json',
'Accept': 'application/json',
'X-XSRF-TOKEN': this.token},
body: JSON.stringify({'url': this.url, 'order': this.selected.id})
}).then(response => {
if (response.ok) {
this.errorMessage = 'Saved'
this.$emit('changeUrl', this.url)
} else {
this.errorMessage = 'An error occured'
}
})

}

export default {
data() {
return {loading: false, errorMessage: '', url: this.selected.url}
},
components: {LoadingIcon},
methods: {saveURL},
props: ['selected', 'token'],
emits: ['changeUrl', 'close']
}
</script>

+ 100
- 0
php-laravel/js/panel/orders.vue Näytä tiedosto

@@ -0,0 +1,100 @@
<template>
<div>
<section class="pending-pane">
<div class="actions"><a class="new-order" href="#new-order">New</a><a
class="new-order" href="#credits">Add Credits</a></div>
<h4>Pending Orders</h4>
<ul>
<template v-bind:key='order.id' v-for="order in orders">
<div class="pending-item" v-if="order.status == 'pending'">
<div class="pending-heading">
<li @click="togglePending($event)">{{order.service.name}} ({{order.updated_at}})</li>
<img class="chevron" src="../../images/chevron-down.svg" alt="">
</div>
<div class="pending-content">
<p>ID: {{order.id}}<br>URL: {{order.url}}<br>Quantity:
{{order.quantity}}<br>Note: {{order.note}}</p>
</div>
</div>
</template>
</ul>
</section>
<div class="info-grey"><p>Orders are typically completed within 1-5
days.</p><div></div></div>
<section class="history-pane">
<h4>Order History</h4>

<div class="table-scroller">
<table>
<thead><th>Date</th><th>ID</th><th>Name</th><th>Status</th>
<th>Quantity</th></thead>
<tbody>
<tr v-bind:key='order.id' v-for='order in
orders.slice(historyPage*10-10, historyPage*10)'>
<td>{{order.updated_at}}</td>
<td>{{order.id}}</td>
<td>{{order.service.name}}</td>
<td :class="order.status"
class="status"><span>{{order.status.charAt(0).toUpperCase() +
order.status.slice(1)}}</span></td>
<td>{{order.quantity}}</td>
<td> <eye @click="select(order)"></eye> </td>
</tr>
</tbody>
</table>
</div>

<img @click="moveHistory(false)" class="nav-btn left"
src="../../images/arrow-left-circle-fill.svg" alt=""/>
<p class="nav-legend">{{historyPage}}/{{Math.ceil(orders.length/10)}}</p>
<img @click="moveHistory(true)" class="nav-btn right"
src="../../images/arrow-right-circle-fill.svg" alt=""/>

</section>

<order-item v-if="selected" @close="close" :selected="selected"
:token="token" @change-url="(url) => selected.url = url"></order-item>

</div>
</template>

<script>
import Eye from '../icons/eye-fill.vue'
import OrderItem from './order-item.vue'

function togglePending(event) {
event.target.parentNode.parentNode.classList.toggle('selected')
}

function moveHistory(forward) {
if (forward) {
this.historyPage += 1
} else {
this.historyPage -= 1
}
if (this.historyPage < 1) {
this.historyPage = 1
return
} else if (this.historyPage > this.orders.length/10+1) {
this.historyPage -= 1
return
}
}

function close() {
this.selected = null
}

function select(order) {
this.selected = order
}

export default {
components: {Eye, OrderItem},
data() {return {historyPage: 1, selected: null}},
methods: {
togglePending, moveHistory, close, select
},
props: ['orders', 'token'],
}
</script>

+ 130
- 0
php-laravel/js/panel/panel.vue Näytä tiedosto

@@ -0,0 +1,130 @@
<template>
<template v-if="!loading">
<sidebar :role="user.role" :active="active"></sidebar>
<transition name="fade" mode="out-in">
<div v-if="active === ''" id="main">
<section class="welcome-pane"><h3>Welcome, {{user.name}}!</h3></section>
<section class="credits-pane"><img src="../../images/coin-stack.svg"
alt="wallet" class="icon"/><p>Credits:
{{(user.credits/100).toLocaleString('en')}}</p></section>
<section class="alerts-pane"><h4>News and Announcements</h4>
<p>We've just launched. Thanks for joining us! Some features are still
being tested. If you experience a delay in credits being added to your
account, please wait 24 hours before contacting support@trendplays.com.</p>
</section>
<section class="recent-pane"><h4>Recent Activity</h4>
<table>
<thead><th>Date</th><th>Name</th><th>Status</th></thead>
<tbody>
<tr v-bind:key='order.id' v-for='order in orders.slice(0, 10)'>
<template v-if="order.status != 'pending'">
<td>{{order.updated_at}}</td>
<td>{{order.service.name}}</td>
<td :class="order.status"
class="status"><span>{{order.status.charAt(0).toUpperCase() +
order.status.slice(1)}}</span></td>
</template>
</tr>
</tbody>
</table>
</section>
</div>

<past-orders :token="token" :orders="orders" v-else-if="active === '#orders'"
id="main">
</past-orders>

<new-order :preferred="user.payment_method" :token="token" :active="active"
:credits="user.credits" v-else-if="active ===
'#new-order' || active === '#credits'" id="main" @update-user='getUser'
@update-orders='getOrders'>
</new-order>

<div id="main" v-else-if="active === '#exit'">
<section class="logout-pane">
<h3>Are you sure you want to logout?</h3>
<a href="/logout">Logout</a>
</section>
</div>

<settings :token="token" :user="user" class="settings-page" id="main"
v-else-if="active === '#settings'">
</settings>

<transaction-end @purchase-complete="getUser" :token="token" :user="user"
:active="active" v-else-if="active ==
'#transaction-complete' || active == '#transaction-failed'">
</transaction-end>

<support v-else-if="active == '#support'" :user="user" :token="token"></support>

</transition>
</template>
</template>

<script>
import Sidebar from './sidebar.vue'
import Settings from './settings.vue'
import PastOrders from './orders.vue'
import NewOrder from './services.vue'
import TransactionEnd from './transaction-endpoint.vue'
import Support from './support.vue'

function getServices() {
return fetch("/panel/services", {
method: 'GET',
headers: {'Content-Type': 'application/json',
'Accept': 'application/json',
'X-XSRF-TOKEN': this.token}
}).then(response => {
response.json().then(data => {this.services = data})
})
}

function getUser() {
return fetch("/panel/user", {
method: 'GET',
headers: {'Content-Type': 'application/json',
'Accept': 'application/json',
'X-XSRF-TOKEN': this.token},
}).then(response => {
return response.json()
}).then(data => {
this.user = data
})
}

function getOrders() {
return fetch("/panel/orders", {
method: 'GET',
headers: {'Content-Type': 'application/json',
'Accept': 'application/json',
'X-XSRF-TOKEN': this.token},
}).then(response => {
return response.json()
}).then(data => {
this.orders = data
})
}

export default {
components: {
Sidebar, Settings, PastOrders, NewOrder,
TransactionEnd, Support
},
data() {
return {active: window.location.hash, user: null,
token: null, orders: null, loading: true,}
},
methods: {getUser, getServices, getOrders, },
created() {
let loaders = []
loaders.push(this.getUser())
loaders.push(this.getServices())
loaders.push(this.getOrders())
Promise.all(loaders).then(() => {
this.loading = false
})
}
}
</script>

+ 42
- 0
php-laravel/js/panel/payment-card.vue Näytä tiedosto

@@ -0,0 +1,42 @@
<template>
<form id="payment-form" action="">
<label for="name">Name on Card</label>
<input @input="$emit('updateBillingName', $event)" id="billing-name" type="name">
<div id="card-element"></div>
<div id="card-errors"></div>
<div id=save-card>
<input name="save-card" type="checkbox" checked="true">
<label for="">Save Card</label>
</div>
</form>
</template>

<script>
function mountPaymentForm() {
let card = this.stripe.elements().create('card')
card.mount('#card-element')
this.$emit('setCard', card)

card.on('change', function(event) {
let displayError = document.getElementById('card-errors');
if (event.error) {
displayError.textContent = event.error.message;
displayError.textContent = '';
this.$emit('cardValid', true)
} else {
displayError.textContent = '';
this.$emit('cardValid', true)
}
}.bind(this));
}

export default {
data() {
return {billingName: null}
},
methods: {mountPaymentForm},
props: ['stripe'],
mounted: mountPaymentForm,
emits: ['updateBillingName', 'cardValid', 'setCard']
}
</script>

+ 17
- 0
php-laravel/js/panel/payment-slider.vue Näytä tiedosto

@@ -0,0 +1,17 @@
<template>
<div class="services-menu">
<a href="#new-order" :class="{selected: page == 'new-order'}">Services</a>
<a href="#credits" :class="{selected: page == 'credits'}">Credits</a>
<div :class="page" class="menu-slider"><div></div></div>
</div>
</template>

<script>
export default {
data() {
return {
items: null
}
}
}
</script>

+ 28
- 0
php-laravel/js/panel/saved-cards.vue Näytä tiedosto

@@ -0,0 +1,28 @@
<template>
<div v-if="cards">
<div v-for="(card, index) in cards" :key="card.id" class="saved-card">
<span>{{card.card.brand[0].toUpperCase() + card.card.brand.substring(1)}}
(••••{{card.card.last4}})</span>
<input :checked="index === 0 || card.id == preferred" :value="card.id" name="selected-card" type="radio"
@change="$emit('update:pickedCard', card.id)">
</div>
</div>
</template>

<script>
export default {
data() {
return {}
},
mounted() {
if (this.cards && this.cards.length > 0) {
this.$emit('update:pickedCard', this.cards[0].id)
}
},
unmounted() {
this.$emit('update:pickedCard', null)
},
props: ['token', 'cards', 'preferred'],
emits: ['update:pickedCard']
}
</script>

+ 28
- 0
php-laravel/js/panel/service-pane.vue Näytä tiedosto

@@ -0,0 +1,28 @@
<template>
<section class="services-pane youtube" >
<h4>{{site.charAt(0).toUpperCase() + site.slice(1)}}</h4>
<ul :key="service.id" v-for="service in filter">
<li v-if="service.available"><span>{{service.name}}</span><span>{{(service.price/100).toLocaleString('en')}}</span><span>{{service.minimum.toLocaleString('en')}}</span><span>{{service.maximum.toLocaleString('en')}}</span>
<svg @click="$emit('select', service)" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-plus-square-fill" viewBox="0 0 16 16">
<path d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z"/>
</svg>
</li>
</ul>
</section>
</template>

<script>
function filter() {
if (!this.services || !this.site) {return}

return this.services.filter((s) => {
return s.site == this.site
})
}

export default {
props: ['services', 'site'],
emits: ['select'],
computed: {filter}
}
</script>

+ 196
- 0
php-laravel/js/panel/services.vue Näytä tiedosto

@@ -0,0 +1,196 @@
<template>
<div>
<div class="sliding-menu">
<a href="#new-order" :class="{selected: page == 'new-order'}">Services</a>
<a href="#credits" :class="{selected: page == 'credits'}">Credits</a>
<div :class="page" class="menu-slider"><div></div></div>
</div>
<h4 class="credits-display"><img class="icon" src="../../images/coin-stack.svg" alt=""><span> {{(credits/100).toLocaleString('en')}}</span></h4>

<template v-if="page == 'new-order'">

<div class="services-legend">
<h5>Name</h5><h5>Credits per 1000</h5><h5>Min Qt.</h5><h5>Max Qt.</h5>
</div>

<ServicePane :site="'youtube'" :services="services"
@select="select"></ServicePane>

<ServicePane :site="'instagram'" :services="services"
@select="select"></ServicePane>

<ServicePane :site="'twitter'" :services="services"
@select="select"></ServicePane>

<ServicePane :site="'tiktok'" :services="services"
@select="select"></ServicePane>

<div id="overlay" v-if="selected">

<div v-if="!completed" class="overlay-item">
<img @click="completed = false; selected = null" class="cancel icon"
src="../../images/cancel-icon2.svg" alt=""/>
<img v-if="selected.site == 'youtube'" class="icon"
src="../../images/youtube-icon.svg" alt=""/>
<img v-if="selected.site == 'instagram'" class="icon"
src="../../images/instagram-icon.svg" alt=""/>
<img v-if="selected.site == 'twitter'" class="icon"
src="../../images/twitter.svg" alt=""/>
<img v-if="selected.site == 'tiktok'" class="icon"
src="../../images/tik-tok.svg" alt=""/>
<h3>{{selected.name}}</h3>
<h4>Cost: {{(cost).toLocaleString('en')}}</h4>
<h4>Quantity</h4>
<div><input required :min="selected.minimum" :max="selected.maximum"
type="number" v-model="amount" id="selQty"><span> /
{{selected.maximum.toLocaleString('en')}}</span></div>

<template v-if="selected.modifier == 'location'">
<h4>Location</h4>
<div><select required id="country" name="">
<option value="usa">USA</option>
<option value="canada">Canada</option>
<option value="uk">United Kingdom</option>
<option value="germany">Germany</option>
<option value="france">France</option>
</select>
</div>
</template>

<template v-if="selected.modifier == 'language'">
<h4>Location</h4>
<div><select required id="language" name="">
<option value="english">English</option>
<option value="french">French</option>
<option value="spanish">Spanish</option>
<option value="german">German</option>
<option value="arabic">Arabic</option>
</select>
</div>
</template>

<h4>URL</h4>
<div><input required type="url" id="url" v-model="url"></div>
<button @click="buyService" :disabled="paying">Submit<loading
v-if="paying"></loading></button>
<p id="overlay-error">{{errorText}}</p>
</div>

<div class="overlay-item" v-else-if="completed">
<img @click="completed = false; selected = null" class="cancel icon"
src="../../images/cancel-icon2.svg" alt=""/>
<img class="icon" src="../../images/checked2.svg" alt=""/>
<h3>Success!</h3>
</div>

</div>

</template>

<credits @purchase-complete="$emit('updateUser')" :preferred="preferred" :token="token" v-if="page == 'credits'"></credits>

</div>
</template>

<script>
import ServicePane from './service-pane.vue'
import Credits from './credits.vue'
import Loading from '../icons/loading.vue'

function select(service) {
this.completed = false
if (this.amount < service.minimum){
this.amount = service.minimum;
}
if (this.amount > service.maximum){
this.amount = service.maximum;
}

this.selected = service
this.errorText = ''
}

function cost() {
return (this.selected.price * this.amount / 100000).toFixed(2)
}

function buyService() {
if (!this.url) {
this.errorText = "You must provide a URL."
return
} else if (Math.ceil(this.cost > this.credits)) {
this.errorText = 'Insuficient Credits'
return
} else if (this.amount < this.selected.minimum || this.amount > this.selected.maximum) {
this.errorText = 'Invalid amount'
return
}

this.paying = true
let note = ''
let country = document.getElementById('country')
let language = document.getElementById('language')

if (country) {
note = JSON.stringify({'location': country.value})
} else if (language) {
note = JSON.stringify({'language': language.value})
}
fetch('/panel/orders', {
method: 'POST',
headers: {'Content-Type': 'application/json',
'Accept': 'application/json',
'X-XSRF-TOKEN': this.token},
body: JSON.stringify({'service': this.selected.id,
'quantity': this.amount, 'url': this.url, 'note': note}), }).then(
response => {
if (response.ok) {
this.errorText = `Success!`
this.completed = true
this.$emit('updateUser')
this.$emit('updateOrders')
} else if (response.status == 520) {
this.errorText = 'Insuficient Credits'
} else {
this.errorText = `Error ${response.status}:
${response.statusText}`
}

this.paying = false
}
)

}

function page() {
switch (this.active) {
case '#new-order':
return 'new-order'
case '#credits':
return 'credits'
}
}

export default {
data() {
return {servicePane: true, services: null, selected: null, amount: 0,
paying: false, url: '', completed: false, errorText: ''}
},
components: {ServicePane, Loading, Credits},
props: ['token', 'credits', 'active', 'preferred'],
created() {
fetch("/panel/services", {
method: 'GET',
headers: {'Content-Type': 'application/json',
'Accept': 'application/json',
'X-XSRF-TOKEN': this.token}
}).then(response => {
response.json().then(data => {this.services = data})
})
},
methods: {select, buyService},
computed: {cost, page},
emits: ['updateUser', 'updateOrders']
}
</script>

+ 119
- 0
php-laravel/js/panel/settings.vue Näytä tiedosto

@@ -0,0 +1,119 @@
<template>
<div>
<h2>Settings</h2>
<section class="change-name-pane">
<h4>Name</h4>
<input :value="user.name" name="name" id="changed_name" type="text">
<button @click="changeName">Save <loading src="../../images/loading-white.svg" alt=""></loading></button>
<span></span>
</section>
<section class="change-email-pane">
<h4>Email</h4>
<input :value="user.email" name="email" type="text" id="changed_email">
<button @click="changeEmail">Save<img class="loading-icon" src="../../images/loading-white.svg" alt=""></button>
<span></span>
</section>
<section class="change-password-pane">
<h4>Change Password</h4>
<h5>Current Password</h5><input name="current_passowrd" id="current_password" type="password">
<h5>New Password</h5><input id="new_password" name="password" type="password">
<h5>Confirm Password</h5><input id="confirm_password" name="confirm_passowrd" type="password">
<button @click="changePassword">Save<img class="loading-icon" src="../../images/loading-white.svg" alt=""></button>
<span></span>
</section>
</div>
</template>

<script>
import Loading from '../icons/loading.vue'

function changeName() {
let name = document.getElementById('changed_name').value
let info = document.querySelector('.change-name-pane span')
let pane = document.querySelector('.change-name-pane')

pane.classList.add('loading')
pane.classList.remove('error')
fetch("/panel/change-name", {
method: 'POST',
headers: {'Content-Type': 'application/json',
'Accept': 'application/json',
'X-XSRF-TOKEN': this.token},
body: JSON.stringify({'name': name}),
}).then(response => {
if (response.ok) {
pane.classList.add('completed')
info.textContent = 'Completed'
} else {
pane.classList.add('error')
info.textContent = 'Error: ' + response.status
}

pane.classList.remove('loading')
})
}

function changeEmail() {
let email = document.getElementById('changed_email').value
let info = document.querySelector('.change-email-pane span')
let pane = document.querySelector('.change-email-pane')

pane.classList.add('loading')
pane.classList.remove('error')
fetch("/panel/change-email", {
method: 'POST',
headers: {'Content-Type': 'application/json',
'Accept': 'application/json',
'X-XSRF-TOKEN': this.token},
body: JSON.stringify({'email': email}),
}).then(response => {
if (response.ok) {
pane.classList.add('completed')
info.textContent = 'Verification link sent'
} else {
pane.classList.add('error')
info.textContent = 'Error: ' + response.status
}

pane.classList.remove('loading')
})
}

function changePassword() {
let info = document.querySelector('.change-password-pane span')
let pane = document.querySelector('.change-password-pane')

pane.classList.add('loading')
pane.classList.remove('error')
fetch("/panel/change-password", {
method: 'POST',
headers: {'Content-Type': 'application/json',
'Accept': 'application/json',
'X-XSRF-TOKEN': this.token},
body: JSON.stringify({'current_password':
document.getElementById('current_password').value,
'password': document.getElementById('new_password').value,
'password_confirmation':
document.getElementById('confirm_password').value}),
}).then(response => {
response.json().then(data => {console.log(data)})
if (response.ok) {
pane.classList.add('completed')
info.textContent = 'Completed'
} else {
pane.classList.add('error')
info.textContent = 'Error: ' + response.status
}

pane.classList.remove('loading')
})
}

export default {
components: {Loading,},
methods: {
changePassword, changeName, changeEmail
},
props: ['user', 'token']
}
</script>

+ 46
- 0
php-laravel/js/panel/sidebar.vue Näytä tiedosto

@@ -0,0 +1,46 @@
<template>
<nav id="sidebar">
<a :class="{selected: active == ''}" href="/panel#">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-house-door-fill" viewBox="0 0 16 16">
<path d="M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5z"/>
</svg>
</a>
<a :class="{selected: active == '#orders'}" href="/panel#orders">
<svg fill="currentColor" enable-background="new 0 0 24 24" height="512" viewBox="0 0 24 24" width="512" xmlns="http://www.w3.org/2000/svg"><path d="m16.12 1.929-10.891 5.576-4.329-2.13 10.699-5.283c.24-.122.528-.122.78 0z"/><path d="m23.088 5.375-11.082 5.49-4.15-2.045-.6-.305 10.903-5.575.6.304z"/><path d="m11.118 12.447-.012 11.553-10.614-5.539c-.3-.158-.492-.475-.492-.816v-10.688l4.498 2.216v3.896c0 .499.408.913.9.913s.9-.414.9-.913v-2.995l.6.292z"/><path d="m23.988 6.969-11.07 5.466-.012 11.553 11.094-5.793z"/></svg>
</a>

<a :class="{selected: active == '#new-order' || active == '#credits'}" href="/panel#new-order">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor"><path d="M12,2C6.486,2,2,6.486,2,12s4.486,10,10,10c5.514,0,10-4.486,10-10S17.514,2,12,2z M17,13h-4v4h-2v-4H7v-2h4V7h2v4h4V13z"></path></svg>
</a>

<a :class="{selected: active == '#settings'}" href="/panel#settings">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-gear-fill" viewBox="0 0 16 16">
<path d="M9.405 1.05c-.413-1.4-2.397-1.4-2.81 0l-.1.34a1.464 1.464 0 0 1-2.105.872l-.31-.17c-1.283-.698-2.686.705-1.987 1.987l.169.311c.446.82.023 1.841-.872 2.105l-.34.1c-1.4.413-1.4 2.397 0 2.81l.34.1a1.464 1.464 0 0 1 .872 2.105l-.17.31c-.698 1.283.705 2.686 1.987 1.987l.311-.169a1.464 1.464 0 0 1 2.105.872l.1.34c.413 1.4 2.397 1.4 2.81 0l.1-.34a1.464 1.464 0 0 1 2.105-.872l.31.17c1.283.698 2.686-.705 1.987-1.987l-.169-.311a1.464 1.464 0 0 1 .872-2.105l.34-.1c1.4-.413 1.4-2.397 0-2.81l-.34-.1a1.464 1.464 0 0 1-.872-2.105l.17-.31c.698-1.283-.705-2.686-1.987-1.987l-.311.169a1.464 1.464 0 0 1-2.105-.872l-.1-.34zM8 10.93a2.929 2.929 0 1 1 0-5.86 2.929 2.929 0 0 1 0 5.858z"/>
</svg>
</a>

<a :class="{selected: active == '#support'}" href="/panel#support">
<svg width="1em" height="1em" viewBox="0 0 16 16" class="bi bi-life-preserver" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="M14.43 10.772l-2.788-1.115a4.015 4.015 0 0 1-1.985 1.985l1.115 2.788a7.025 7.025 0 0 0 3.658-3.658zM5.228 14.43l1.115-2.788a4.015 4.015 0 0 1-1.985-1.985L1.57 10.772a7.025 7.025 0 0 0 3.658 3.658zm9.202-9.202a7.025 7.025 0 0 0-3.658-3.658L9.657 4.358a4.015 4.015 0 0 1 1.985 1.985l2.788-1.115zm-8.087-.87L5.228 1.57A7.025 7.025 0 0 0 1.57 5.228l2.788 1.115a4.015 4.015 0 0 1 1.985-1.985zM8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm0-5a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/>
</svg>
</a>

<a v-if="role == 'admin'" :class="{selected: active == '#admin'}" href="/telescope">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-key-fill" viewBox="0 0 16 16">
<path d="M3.5 11.5a3.5 3.5 0 1 1 3.163-5H14L15.5 8 14 9.5l-1-1-1 1-1-1-1 1-1-1-1 1H6.663a3.5 3.5 0 0 1-3.163 2zM2.5 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
</svg>
</a>
<a :class="{selected: active == '#exit'}" href="/panel#exit">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-door-open-fill" viewBox="0 0 16 16">
<path d="M1.5 15a.5.5 0 0 0 0 1h13a.5.5 0 0 0 0-1H13V2.5A1.5 1.5 0 0 0 11.5 1H11V.5a.5.5 0 0 0-.57-.495l-7 1A.5.5 0 0 0 3 1.5V15H1.5zM11 2h.5a.5.5 0 0 1 .5.5V15h-1V2zm-2.5 8c-.276 0-.5-.448-.5-1s.224-1 .5-1 .5.448.5 1-.224 1-.5 1z"/>
</svg>
</a>
</nav>
</template>

<script>
export default {
props: ['active', 'role']
}
</script>


+ 3
- 0
php-laravel/js/panel/summary.vue Näytä tiedosto

@@ -0,0 +1,3 @@
<template>
<div id="main">important info here</div>
</template>

+ 82
- 0
php-laravel/js/panel/support.vue Näytä tiedosto

@@ -0,0 +1,82 @@
<template>
<div class="support-section" id="main">

<h2>Support</h2>

<loading v-if="loading"></loading>

<div v-if="!loading && complete" class="dialog">
<img class="icon" src="../../images/checked2.svg" alt="">
<h3>Ticket sent. An administrator will contact you soon.</h3>
</div>

<div v-if="!loading && !complete" id="support-form">

<label for="">Topic</label>
<select id="support-topic" name="" v-model="topic">
<option value="order">Order</option>
<option value="service">Service</option>
<option value="credits">Credits</option>
<option value="payment">Payment</option>
<option value="other">Other</option>
</select>

<label for="">Details</label>
<textarea id="" name="" cols="30" rows="10" v-model="message"></textarea>
<span class="note-grey">Include any relevant information like the order number,
service name, etc</span>

<button @click="send">Submit</button>

<p class="error-message">{{errorMessage}}</p>

</div>

</div>
</template>


<script>
import Loading from '../icons/loading.vue'

function send() {
this.errorMessage = ''

if (!this.topic || !this.message) {
this.errorMessage = 'Topic and details cannot be blank.'
return
}

this.loading = true

fetch("/panel/support", {
method: 'POST',
headers: {'Content-Type': 'application/json',
'Accept': 'application/json',
'X-XSRF-TOKEN': this.token},
body: JSON.stringify({'topic': this.topic, 'message': this.message})}).
then(response => {
if (response.ok) {
this.complete = true
} else {
this.complete = false
this.error = true
this.errorMessage = `${response.status}:
${response.statusText}`
}

this.loading = false
})

}

export default {
components: {Loading},
props: ['user', 'token'],
data() {
return {loading: false, complete: false, error: false, errorMessage:
'', topic: null, message: ''}
},
methods: {send}
}
</script>

+ 33
- 0
php-laravel/js/panel/transaction-endpoint.vue Näytä tiedosto

@@ -0,0 +1,33 @@
<template>
<div v-once id="main">

<div v-if="active == '#transaction-complete' && user.paying" class="status-dialog">
<img class="icon" src="../../images/checked2.svg" alt=""/>
<h3>Purchase complete.</h3>
</div>

<div v-if="active == '#transaction-failed' && user.paying" class="status-dialog">
<img class="icon" src="../../images/warning-colored.svg" alt=""/>
<h3>Purchase failed.</h3>
</div>

</div>
</template>

<script>
export default {
props: ['token', 'user', 'active'],
emits: ['purchaseComplete'],

//Should check that user is actualling in the paying state. If so, send get
//request to panel/transaction-end. It's then() should emit a payment
//complete to panel for a user state refresh.
mounted() {
fetch('/panel/clear-paying', {
method: 'GET',
headers: {'Content-Type': 'application/json',
'Accept': 'application/json',
'X-XSRF-TOKEN': this.token}}).then(() => this.$emit('purchaseComplete'))
}
}
</script>

+ 168
- 0
php-laravel/js/register-area/register-area.vue Näytä tiedosto

@@ -0,0 +1,168 @@
<template>
<form v-if="active === 'register'" onsubmit = "event.preventDefault(); return false" id="register-form">
<h3>Registration</h3>
<div>{{errorMessage}}</div>
<div>
<label for='sender_name'>Name</label>
<input id='register-name' required type='name' name='sender_name' placeholder=''
spellcheck='false'>
</div>
<div>
<label for='sender_email'>Email</label>
<input v-on:input="checkPasswords" id='register-email' required type='email' name='sender_email' placeholder=''
spellcheck='false'>
</div>
<div>
<label for='sender_password'>Password</label>
<input v-on:input="checkPasswords" id='register-password' required type='password' name='sender_password'
placeholder='' spellcheck='false'>
</div>
<div>
<label for='confirm_password'>Confirm Password</label>
<input v-on:input="checkPasswords" id='confirm-password' required type='password'
name='sender_password' placeholder='' spellcheck='false'>
</div>
<button @click="register($event)" class="submit-btn" type="submit">Submit</button>
</form>
<form v-if="active === 'forgot'" v-on:submit="forgotPassword" id="forgot-form">
<h3>Forgot Password</h3>
<div>
<label for='sender_email'>Email</label>
<input id='forgot-email' required type='email' name='sender_email' placeholder=''
spellcheck='false'>
</div>
<button class="submit-btn" type="submit">Submit</button>
</form>
<img v-if="active === 'loading'" type="image/svg+xml" class="loading-icon" src="../../images/loading.svg" alt=""/>
<div v-if="active === 'register-completed'">
<img class="medium-icon" src="../../images/checked2.svg" alt="">
<h3>Success!</h3>
<p>A verification link has been sent to your inbox.</p>
</div>
<div v-if="active === 'forgot-completed'">
<img class="medium-icon" src="../../images/checked2.svg" alt="">
<h3>Success!</h3>
<p>A password reset link has been sent.</p>
</div>
<div v-if="active === 'error'">
<img class="medium-icon" src="../../images/warning-colored.svg" alt="">
<h3>An Error Occured.</h3>
<p>{{`${error}: ${errorMessage}`}}</p>
</div>
<div v-on:click="closeArea" class="cancel-button"></div>
</template>

<script>
function getCookie(name) {
var re = new RegExp(name + "=([^;]+)")
var value = re.exec(document.cookie)

let v = (value != null) ? unescape(value[1]) : null
return v
}

function getToken() {
return fetch("/sanctum/csrf-cookie", {
method: 'GET'
}).then( () => {
this.token = this.getCookie('XSRF-TOKEN')
return this.token
})
}
function register(event) {
event.preventDefault();
event.stopPropagation();

this.errorMessage = ''
let name = document.getElementById("register-name").value
let email = document.getElementById("register-email").value
let password = document.getElementById("register-password").value
let password_confirmation = document.getElementById("confirm-password").value

this.active = 'loading'

this.getToken().then(() => {fetch("/register", {
method: 'POST',
headers: {'Content-Type': 'application/json',
'Accept': 'application/json',
'X-XSRF-TOKEN': this.token},
body: JSON.stringify({"name": name,
"email": email,
"password": password,
"password_confirmation": password_confirmation})})
.then(response => {
//Give completed or error
if (response.ok) {
this.active = 'register-completed'
} else if (response.status != 500) {
response.json().then((e) => {
try {
for (let i in e.errors) {
this.errorMessage = e.errors[i].flat().join("\n") +
"\n" + this.errorMessage
}
} catch (x) {
this.errorMessage = e.message
}
})
this.active = 'register'
} else {
this.errorMessage = response.statusText
this.active = 'register'
}
});
})
return false
}

function checkPasswords() {
let passInput = document.getElementById('register-password')
let passInput2 = document.getElementById('confirm-password')
if (passInput.value != passInput2.value) {
passInput2.setCustomValidity('Passwords must be matching')
} else {
passInput2.setCustomValidity('');
}
}

function forgotPassword(event) {
let email = document.getElementById("forgot-email").value
this.active = 'loading'
fetch("/forgot-password", {
method: 'POST',
headers: {'Content-Type': 'application/json',
'Accept': 'application/json',
'X-XSRF-TOKEN': this.token},
body: JSON.stringify({"email": email})})
.then(response => {
if (response.ok) {
this.active = 'forgot-completed'
} else {
this.error = response.status
this.errorMessage = response.statusText
this.active = 'error'
}
/* console.log(response.json()) */
});
event.preventDefault();
}

module.exports = {
data() {
return {active: 'register', token: '', errorMessage: ''}
},
methods: {
getToken,
getCookie,
register,
checkPasswords,
forgotPassword,
closeArea() {
document.querySelector(".register-area").classList.remove("active")
},
},
mounted() {
this.getToken()
}
}
</script>

+ 19
- 0
php-laravel/routes/api.php Näytä tiedosto

@@ -0,0 +1,19 @@
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});

+ 18
- 0
php-laravel/routes/channels.php Näytä tiedosto

@@ -0,0 +1,18 @@
<?php

use Illuminate\Support\Facades\Broadcast;

/*
|--------------------------------------------------------------------------
| Broadcast Channels
|--------------------------------------------------------------------------
|
| Here you may register all of the event broadcasting channels that your
| application supports. The given channel authorization callbacks are
| used to check if an authenticated user can listen to the channel.
|
*/

Broadcast::channel('App.Models.User.{id}', function ($user, $id) {
return (int) $user->id === (int) $id;
});

+ 19
- 0
php-laravel/routes/console.php Näytä tiedosto

@@ -0,0 +1,19 @@
<?php

use Illuminate\Foundation\Inspiring;
use Illuminate\Support\Facades\Artisan;

/*
|--------------------------------------------------------------------------
| Console Routes
|--------------------------------------------------------------------------
|
| This file is where you may define all of your Closure based console
| commands. Each Closure is bound to a command instance allowing a
| simple approach to interacting with each command's IO methods.
|
*/

Artisan::command('inspire', function () {
$this->comment(Inspiring::quote());
})->purpose('Display an inspiring quote');

+ 162
- 0
php-laravel/routes/web.php Näytä tiedosto

@@ -0,0 +1,162 @@
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserController;
use App\Http\Controllers\ServiceController;
use App\Http\Controllers\OrderController;
use App\Http\Controllers\BillingController;
use App\Http\Controllers\Ticket;

use Illuminate\Foundation\Auth\EmailVerificationRequest;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
if (Auth::check()) {
return redirect('panel');
}
return view('home');
})->name('login');

Route::view('/panel', 'panel')->middleware([ 'auth', 'verified' ])->name('panel');

Route::view('/terms-and-policy', 'terms-and-policy');

//Verification routes
Route::get('/verify-email', function() {
if (Auth::user()->email_verified_at) {
abort(404);
} else {
return view('verify-email');
}
})->middleware('auth')->name('verification.notice');

Route::get('/verify/{id}/{hash}', function (EmailVerificationRequest $request) {
$request->fulfill();

return redirect('/panel');
})->middleware(['auth', 'signed'])->name('verification.verify');

Route::post('/resend-verification', function (Request $request) {
if (Auth::user()->email_verified_at) {
abort(404);
}

$request->user()->sendEmailVerificationNotification();
return back()->with('message', 'Verification link sent!');
})->middleware(['auth', 'throttle:6,1'])->name('verification.send');


//Creation and recovery
Route::post('/register', [UserController::class, 'create']);

Route::post('/forgot-password', [UserController::class,
'forgotPassword'])->middleware('guest')->name('password.email');

Route::get('/reset-password/{token}', function ($token) {
return view('reset-password', ['token' => $token]);
})->middleware('guest')->name('password.reset');

Route::post('/reset-passowrd', [UserController::class,
'resetPassword'])->middleware('guest');


Route::post('/login', [UserController::class,
'login'])->middleware('guest');

Route::get('/logout', [UserController::class,
'logout'])->middleware('auth');

//These should probably be grouped by name later
Route::get('/panel/user', function (Request $request) {
return $request->user();
})->middleware([ 'auth', 'verified' ]);

Route::post('/panel/change-card', function (Request $request) {
return App::make(BillingController::class)->changeDefaultCard($request->card);
})->middleware([ 'auth', 'verified' ]);

Route::post('/panel/delete-card', [BillingController::class,
'deleteCard'])->middleware([ 'auth', 'verified' ]);

Route::get('/panel/orders', [UserController::class,
'getOrders'])->middleware([ 'auth', 'verified' ]);

Route::get('/panel/services', [ServiceController::class,
'getServices'])->middleware([ 'auth', 'verified' ]);

Route::post('/panel/change-name', [UserController::class,
'changeName'])->middleware([ 'auth', 'verified' ]);

Route::post('/panel/change-email', [UserController::class,
'changeEmail'])->middleware([ 'auth', 'verified' ]);

Route::post('/panel/change-password', [UserController::class,
'changePassword'])->middleware([ 'auth', 'verified' ]);

Route::get('/reset-email', [UserController::class,
'resetEmail'])->name('reset-email');

Route::post('/panel/orders', [OrderController::class,
'newOrder'])->middleware([ 'auth', 'verified' ]);

Route::post('/panel/secret', [BillingController::class,
'stripeSecret'])->middleware([ 'auth', 'verified' ]);

//Initiate a payeer payment
Route::post('/panel/payeer', [BillingController::class,
'payeer'])->middleware([ 'auth', 'verified' ]);

//Initiate a Perfect Money payment
Route::post('/panel/pm', [BillingController::class,
'pm'])->middleware([ 'auth', 'verified' ]);

Route::post('/panel/pm-complete', [BillingController::class,
'completePM'])->middleware([ 'auth', 'verified' ]);

Route::post('/panel/pm-fail', [BillingController::class,
'failPM'])->middleware([ 'auth', 'verified' ]);

Route::get('/panel/cards', [BillingController::class,
'getCards'])->middleware([ 'auth', 'verified' ]);

Route::post('/panel/save-url', [OrderController::class,
'changeURL'])->middleware([ 'auth', 'verified' ]);

//Stripe webhooks
Route::post('/hooks/charge',
[BillingController::class, 'chargeEvent']);

//Payeer handler function
Route::post('/hooks/payeer-transaction',
[BillingController::class, 'processPayeer']);

//PM handler function
Route::post('/hooks/pm-transaction',
[BillingController::class, 'processPM']);

//Payment attempt is over
Route::get('/panel/clear-paying',
[UserController::class, 'clearPaying'])->middleware(['auth', 'verified']);

Route::post('/panel/support',
[Ticket::class, 'send'])->middleware(['auth', 'verified']);

Route::get('/panel/transaction-failed', function (Request $request) {
return redirect('/panel#transaction-failed');
})->middleware(['auth', 'verified']);

Route::get('/panel/transaction-complete', function (Request $request) {
return redirect('/panel#transaction-complete');
})->middleware(['auth', 'verified']);

+ 152
- 0
php-laravel/scss/_vars.scss Näytä tiedosto

@@ -0,0 +1,152 @@
@use "sass:map";
@use "sass:color";

$theme-colors: (
"red-alert": #ce0018,
"red-orangish": #de493b,
"blue": #0736a4,
"light-blue": #a4bfd9,
"medium-blue": #1877f2,
"dark-blue": #09c6f9,
"orange": #ec9f05,
"dark-orange": #ff4e00,
"brand-orange": #f67602,
"grey": #e6ebf1,
"light-grey": #e9e9e9,
"light2-grey": #bebebe,
"light3-grey": rgb(245,245,245),
"dark-grey": #212121,
"faded-text": #425466,
"faded-text2": #6c757d,
"dark-green": #3bb78f,
"light-green": #0bab64,
"green": #63b521,
"alt-blue-light": #e2f5ff,
"alt-blue-medium": #2b96cc,
"text-blue-medium": #425466,
"alt-red-light": #f3beb8,
"alt-red-medium": #f09898,
);

@function getColor($col) {
@return map.get($theme-colors, $col);
}

@function darkenColor($col) {
$oldCol : map.get($theme-colors, $col);
$newCol : color.scale($oldCol, $lightness: -45%);
@return $newCol;
}

@function lightenColor($col) {
$oldCol : map.get($theme-colors, $col);
$newCol : color.scale($oldCol, $lightness: 15%);
@return $newCol;
}

@mixin button($col) {
display: inline-block;
//font-family: $btn-font-family;
font-weight: 400;
background-color: getColor($col);
text-align: center;
vertical-align: middle;
user-select: none;
border: none;
padding: 7px 15px;
border-radius: 5px;
&:hover{
background-color: darkenColor($col);
}
}

// A linear gradient with the light color at the top left and dark color at
// bottom right, that darkens on hover
@mixin special-button($start, $end) {
font-family: "PatuaOne";
height: 2em;
border: none;
border-radius: 4px;
padding: 3px;
color: white;
background-color: #045de9;
background-image: linear-gradient(315deg, map.get($theme-colors, $start) 0%, map.get($theme-colors, $end) 74%);
&:hover {
background-image: linear-gradient(315deg, darkenColor($start) 0%, darkenColor($end) 74%);
}
}

// A white button with a transparent background that becomes white and orange on hover
@mixin transparent-button {
text-align: center;
font-size: 20px;
border: 2px solid white;
border-radius: 4px;
padding: 10px;
background-color: transparent;
color: white;
&:hover {
background-color: white;
color: map.get($theme-colors, "orange");
}
}

@mixin inverting-button($primary, $background) {
text-align: center;
border: 2px solid $primary;
border-radius: 4px;
padding: 5px 10px;
background-color: $background;
color: $primary;
&:hover {
background-color: $primary;
color: $background;
}
}

// A button with a white background and specified accent color that reverses on hover
@mixin brand-button($col) {
width: 10em;
height: 2.5em;;
background-color: white;
border: 2px solid map.get($theme-colors, $col);
color: map.get($theme-colors, $col);
border-radius: 4px;
font-size: 16px;
&:hover {
background-color: map.get($theme-colors, $col);
color: white;
}
}

@mixin hovering {
padding: 18px 9px;
border-radius: 4px;
background: white;
box-shadow: 7px 10px 8px rgb(156 166 175 / 22%);
transition: transform 0.2s;
}

@mixin hovering2 {
padding: 18px 9px;
border-radius: 4px;
background: white;
box-shadow: rgba(0, 0, 0, 0.25) 0px 54px 55px, rgba(0, 0, 0, 0.12) 0px -12px 30px, rgba(0, 0, 0, 0.12) 0px 4px 6px, rgba(0, 0, 0, 0.17) 0px 12px 13px, rgba(0, 0, 0, 0.09) 0px -3px 5px;
transition: transform 0.2s;
}

@mixin hovering3 {
padding: 18px 9px;
border-radius: 4px;
background: white;
box-shadow: rgba(0, 0, 0, 0.16) 0px 10px 36px 0px, rgba(0, 0, 0, 0.06) 0px 0px 0px 1px;
transition: transform 0.2s;
}

@mixin hovering-light {
padding: 18px 9px;
border-radius: 4px;
background: white;
box-shadow: rgba(0, 0, 0, 0.16) 0px 1px 4px;
transition: transform 0.2s;
}

+ 1454
- 0
php-laravel/scss/main.scss
File diff suppressed because it is too large
Näytä tiedosto


+ 1
- 0
php-laravel/views/app.blade.php Näytä tiedosto

@@ -0,0 +1 @@
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Vue App</title><link href="/css/app.a9fd8694.css" rel="preload" as="style"><link href="/js/app.0225a575.js" rel="preload" as="script"><link href="/js/chunk-vendors.b6c5b7d4.js" rel="preload" as="script"><link href="/css/app.a9fd8694.css" rel="stylesheet"></head><body><div id="app"></div><script src="/js/chunk-vendors.b6c5b7d4.js"></script><script src="/js/app.0225a575.js"></script></body></html>

+ 12
- 0
php-laravel/views/change-email.blade.php Näytä tiedosto

@@ -0,0 +1,12 @@
<main class="body" id="ticket-email">
<h3>An email change has been requested from {{$current_email}}</h3>

<div>
<a href="{{$link}}"><h4>Confirm</h4></a>
</div>

<div>
<small>If you do not recognize this activity, please disregard this
message.</small>
</div>
</main>

+ 25
- 0
php-laravel/views/email-changed.blade.php Näytä tiedosto

@@ -0,0 +1,25 @@
@extends('master')

@section('title', 'Settings')
@section('heading-style', 'info-page')

@section('head-metas')
@parent
<link rel="stylesheet" href="{{asset('panel.css')}}">
@endsection

@section('content')
<main>
<div class="info-heading">
<h3>Your email has been reset.</h3>
<a href="/panel"><button class="brand-button">Return</button></a>
</div>
</main>
@endsection

@section('scripts')
@parent
<script src="js/app.js"></script>
<script src="js/chunk-vendors.js"></script>
<!-- <script src="main.js"></script> -->
@endsection

+ 228
- 0
php-laravel/views/home.blade.php Näytä tiedosto

@@ -0,0 +1,228 @@
@extends('master')

@section('title', 'Home')
@section('heading-style', 'Home')

@section('head-metas')
@parent
@endsection

@section('login-form')
@guest
<form id='login_form' class= 'login' method='POST'>
<div>
<label for='sender_email'>Email</label>
<input required id='login_email' type='email' name='sender_email' placeholder=''
spellcheck='false'>
</div>
<div>
<label for='sender_password'>Password</label>
<input required id='login_password' type='password'
name='sender_password' placeholder='' spellcheck='false'>
</div>
<button class='login-btn'type="login">Log in</button>
<div><p class="error"></p></div>
</form>
<div id="nav_toggle" class="nav-toggle"><div>
@endguest
@endsection
@section('content')
@parent
<main>
<div id="app" class="register-area"></div>
<div class='landing-hero'>
<div class='hero-filter'>
<h2>The web's #1 content promoter</h2>
<p>Give your brand the boost it needs for oganic growth with the
web's most trusted social media marketing panel.</p>
<a class='register-btn'>Register Now</a>
</div>
</div>
<div class='blue-background'><section class="about-us">
<p>
We're a social media content promoter based in Canada, helping
entertainers and brands perfect their marketing campaigns through
targeted views and engagements. By providing impressions and
retention, we'll help boost your
rankings and get you noticed.
</p>
</section>
<section class='services-cards'>
<h2>Our Services</h2>

<div class="cards">
<div class="card">
<img src="/img/youtube-icon.svg" alt="youtube-icon">
<h3>Youtube</h3>
<ul>
<li>Views increase within 24 hours</li>
<li>Boost to rankings</li>
<li>High quality accounts</li>
<li>Likes and Comments</li>
</ul>
<button>Select</button>
</div>

<div class="card">
<img src="/img/twitter.svg" alt="spotify-icon">
<h3>Twitter</h3>
<ul>
<li>Impressions and favorites</li>
<li>Activity increases within 24 hours</li>
<li>Followers</li>
</ul>
<button>Select</button>
</div>

<div class="card">
<img src="/img/instagram-icon.svg" alt="twitter-icon">
<h3>Instagram</h3>
<ul>
<li>Active user accounts</li>
<li>Activity increases within 24 hours</li>
<li>Likes</li>
<li>Followers</li>
</ul>
<button>Select</button>
</div>

<div class="card">
<img src="/img/tik-tok.svg" alt="twitter-icon">
<h3>TikTok</h3>
<ul>
<li>Active user accounts</li>
<li>Views</li>
<li>Likes</li>
<li>Followers</li>
</ul>
<button>Select</button>
</div>
</div></div>
</section>
<section class='features-info'>
<h2>Why We Are the Best</h2>
<div class="circle-prop">
<svg height="100%" preserveAspectRatio="none" viewBox="0 0 500 500"fill='white' version="1.1" id="Layer_1" x="0px" y="0px"
xml:space="preserve">
<path stroke-width="20" fill="none" stroke="#ddd" d="M0,10 C400,0,500,256,250,500"/>
<!- <path fill="none" stroke="white" d="M256,10C115.03,0,0,115.05,0,256c0,140.97,115.05,256,256,256c140.97,0,256-115.05,256-256C512,115.03,396.95,0,256,0z "/> -->
</svg>
<img src="img/teamwork2.png" alt="teamwork">
</div>
<div class="info-cards">

<div class="info-card"><p>
We help you save time managing your onlince presence while still providing
a high quality, and reliable service.
</p>
</div>

<div class="info-card"><p>All orders are proccessed within one day of
placing your order. Many services available are instant.</p>
</div>

<div class="info-card"><p>By combining our services to create trending
content, you'll improve the quality of viewer engagements and boost your
rankings significantly. We're the best marketing panel around.</p> </div>
<div class="info-card"><p>

Improve your brand's image, increase your social proof, and build a stronger
relationship with your audience.</p>
</div></div>
</section>
<section class='panel-infos'>
<h2>A User-Friendly Control Panel</h2>
<img class="panel-preview" src="/images/panel-preview.png" alt="panel-preview">
<div class="info-items">
<div class="panel-info">
<img src="./img/tap.svg" alt="">
<h3>Easy to Use</h3>
<p>We provide an extremely user-friendly and intuitive SPA interface to
manage and review your SMM orders.</p>
</div>
<div class="panel-info">
<img src="./img/lighting.svg" alt="">
<h3>Powerful</h3>
<p>Our panel contains a variety of features for managing payment
methods, and automatically queuing orders.</p>
</div>
<div class="panel-info">
<img src="./img/statistics.svg" alt="">
<h3>Scalable</h3>
<p>We make it easy to organize your marketing campaign with a variety
of accounts and social networks</p>
</div>
<div>
</section>
<section class='benefits-info'>
<h2>Benefits of Buying Views</h2>
<div>
<p>We know that getting people to see your content can be an uphill
battle. Reaching just 10,000 organic views can be tough because social networks give
the lion's share of exposure to the personalities who already have the biggest
following. The more views and impressions a post has, the more people are
willing to watch it, that's why consistent and high engagement is
essential to your brand's online growth. </p>
<p>Give your content the fair chance it deserves with a boost to your
account's traffic. Our SMM panel will help you build your audience,
develop your brand, and launch your new business.</p>
</div>
</section>
<section class='faq-info'>

<h2>FAQ</h2>
<div class="collapsibles">

<div class="collapsible"><button>Why should I choose Trendplays?</button>
<div class="content"><p>Buying views and engagements is easy and safe
if you buy from a reliable source. The traffic we send to your content is
from accounts with years of history, viewing
gradually over a few days.</p></div></div>

<div class="collapsible"><button>What if my audience finds out?</button>
<div class="content"><p> At Trendplays, we
pride ourselves on our capacity for discretion and professionalism.
</p></div></div>

<div class="collapsible"><button>Will my account be safe?</button>
<div class="content"><p>
While there is some risk of disciplinary action,
it is rare and mild because, if buying views were made a breach in
the TOS that resulted in a ban, then anybody could send those views to
their competitor's account.
</p></div></div>

<div class="collapsible"><button>Is buying views illegal?</button>
<div class="content"><p>
No, and there is no good reason it should be.
</p></div></div>

<div class="collapsible"><button>How can I pay?</button>
<div class="content"><p>
We accept Debit/Credit or your choice of crytocurrency
through our Payeer and Perfect Money payment providers.
</p></div></div>

</div>
</section>
</main>
@endsection

@section('footer')
<footer class='footer text-center'>
<div class='foot-links'>
<a href="/terms-and-policy">Terms & Policy</a>
<a href="mail:support@trendplays.com">Help</a>
<a id="forgot-password-btn" href="#">Reset Password</a>
</div>
<small>©Copyright 2021 Trendplays Network, Inc.</small>
</footer>
@endsection

@section('scripts')
@parent
<script src="js/app.js"></script>
<script src="js/chunk-vendors.js"></script>
<!-- <script src="main.js"></script> -->
@endsection

+ 61
- 0
php-laravel/views/master.blade.php Näytä tiedosto

@@ -0,0 +1,61 @@
<!DOCTYPE html>
<head>
{{-- This section may need to be overwritten in descendants --}}
@section('head-metas')
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="">
<link rel="shortcut icon" type="image/jpg" href="/img/arrow-up.svg"/>
<link rel="stylesheet" href="/css/app.css">
@if (config('app.env') == 'production')
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-LH36E9P3P5"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());

gtag('config', 'G-LH36E9P3P5');
</script>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-200591850-1">
</script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());

gtag('config', 'UA-200591850-1');
</script>
@endif
@show
<title>Trendplays - @yield('title')</title>
</head>

<body>
<nav class="navigator @yield('heading-style')" role="navigation">
<a id='logo' href='/'>
<h1>Trendplays</h1>
<img src="/img/arrow-up.svg" height="30px" alt="">
</a>
@yield('login-form')
<span class="icon-bar"></span>
</nav>

@yield('content')

@section('footer')
<footer class='footer text-center'>
<div class='foot-links'>
<a href="/terms-and-policy">Terms & Policy</a>
<a href="mail:support@trendplays.com">Help</a>
@auth <a href="/logout">Logout</a> @endauth
</div>
<small>©Copyright 2021 Trendplays Network, Inc.</small>
</footer>
@show

@section('scripts')
@show

</body>

+ 25
- 0
php-laravel/views/panel.blade.php Näytä tiedosto

@@ -0,0 +1,25 @@
@extends('master')

@section('title', 'Panel')
@section('child-type', 'panel')

@section('head-metas')
@parent
@endsection

@section('content')
@parent
<main class="panel">
<div id="panel">
</div>
</main>
@endsection

@section('footer')
@endsection

@section('scripts')
@parent
<script src="js/app.js"></script>
<script src="js/chunk-vendors.js"></script>
@endsection

+ 17
- 0
php-laravel/views/request-reset.blade.php Näytä tiedosto

@@ -0,0 +1,17 @@
@EXTEnds('master')

@section('title', 'Password Reset')

@section('head-metas')
@parent
<link rel="stylesheet" href="{{asset('main.css')}}">
@endsection

@section('content')
Reset form goes here.
@endsection

@section('scripts')
@parent
<script src="main.js"></script>
@endsection

+ 17
- 0
php-laravel/views/reset-password.blade.php Näytä tiedosto

@@ -0,0 +1,17 @@
@extends('master')

@section('title', 'Password Reset')

@section('head-metas')
@parent
<link rel="stylesheet" href="{{asset('main.css')}}">
@endsection

@section('content')
Reset form goes here.
@endsection

@section('scripts')
@parent
<script src="main.js"></script>
@endsection

+ 24
- 0
php-laravel/views/support-ticket.blade.php Näytä tiedosto

@@ -0,0 +1,24 @@
@extends('master')

@section('title', 'Ticket')

@section('head-metas')
@parent
@endsection

@section('content')
@parent

<main id="ticket-email">
<h3>Name: {{$name}}</h3>

<h3>Email: {{$email}}</h3>

<h3>Message:</h3>
<p> {{$user_message}} </p>
</main>

@endsection

@section('footer')
@endsection

+ 221
- 0
php-laravel/views/terms-and-policy.blade.php Näytä tiedosto

@@ -0,0 +1,221 @@
@extends('master')

@section('title', 'Terms & Policy')
@section('heading-style', 'info-page')

@section('head-metas')
@parent
<link rel="stylesheet" href="{{asset('panel.css')}}">
@endsection

@section('content')
@parent
<main class="terms">

<section>
<h2>Terms and Conditions</h2>
<h4>SITE USAGE</h4>
<p>These terms and conditions outline the rules and regulations for the
use of this Website. By accessing this website we assume you accept these terms
and conditions in full. The following terminology applies to these Terms and
Conditions, Privacy Statement and Disclaimer Notice and any or all Agreements:
"Client", "You" and "Your" refers to you, the person accessing this website and
accepting the Company's terms and conditions. "The Company", "Ourselves", "We",
"Our" and "Us", refers to our Company. "Party", "Parties", or "Us", refers to
both the client and ourselves, or either the client or ourselves. </p>

<p> All terms refer to the offer, acceptance and consideration of payment necessary to
undertake the process of our assistance to the client in the most appropriate
manner, whether by formal meetings of a fixed duration, or any other means, for
the express purpose of meeting the client's needs in respect of provision of
the Company's stated services/products. Any use of the above terminology or
other words in the singular, plural, capitalisation and/or he/she or they, are
taken as interchangeable.
</p>

<p>Trendplays Network Inc. and any third-party providers make no warranty of any kind
regarding this site and/or any data or content provided on this site, all of
which are provided on an “as is” “as available” basis. We and any third party
providers do not warrant the accuracy, completeness, currency or reliability of
any of the content or data found on this site and such parties expressly
disclaim, to the fullest extent provided by law, all warranties and conditions,
including implied warranties and conditions of merchantability, fitness for a
particular purpose and non-infringement, and those arising by statute or
otherwise in law or from course of dealing or usage of trade. Neither We nor
any third party providers warrant that this site, its servers or any email sent
from us are free of viruses or other harmful components.
</p>

<h4>SITE CHANGES</h4>
<p> Trendplays Network Inc. reserves the right to change, edit, or delete
any documents, information, or other content appearing on the site or these
Terms and Conditions from time to time without notice and in its sole
discretion. All changes are effective immediately when posted and apply to all
access to, and use of, this site thereafter. However, any changes to the
governing law and dispute resolution provisions set forth below will not apply
to any disputes for which parties have actual notice on or before the date the
change is posted on the site.
</p>

<h4>COOKIES</h4>
<p>We employ the use of cookies. By using this website you consent to
the use of cookies in accordance with our privacy policy. Cookies are used in
some areas of this site to enable special functionality in those areas and
improve ease of use for our visitors.</p>

<h4>INDEMNIFICATION</h4>
<p>
You agree to indemnify, defend and hold harmless Trendplays Network Inc. and its
directors, officers, associates, agents, successors and assigns from and
against any claims, liabilities, damages, judgments, awards, losses, costs,
expenses and fees (including reasonable attorney fees) arising out of or
relating to your violation of these Terms and Conditions or your use of this
site.
</p>

<h4>NO JOINT VENTURE OR DEROGATION OF RIGHTS</h4>
<p>
You agree that no joint venture, partnership, employment, or agency
relationship exists between you and Trendplays Network Inc. as a result of these
Terms and Conditions or your use of the Site. Our performance of these
Terms and Conditions is subject to existing laws and legal process, and nothing
contained herein is in derogation of our right to comply with governmental,
court and law enforcement requests or requirements relating to your use of the
Site or information provided to or gathered by us with respect to such
use.
</p>

<h4>VISITOR COMMUNICATION</h4>
<p>
Except where expressly provided otherwise by Trendplays Network Inc., all comments, feedback,
information, or materials that you submit through or in association with the
Site shall be treated by you as confidential. By submitting such comments,
feedback, information, or materials to Trendplays Network Inc.:
</p>
<p>
i. You represent and warrant that Trendplays Network Inc.’s use of your
submission does not and will not breach any agreement, violate any law, or
infringe any third party’s
rights; </p>

<p> ii. You represent and warrant that you have all rights to enter into
these Terms and Conditions;</p>

<p> iii. Trendplays Network Inc. is free to use in any manner all or part of
the content of any such communications on an unrestricted basis without the
obligation to notify, identify or compensate you or anyone else; and</p>

<p>iv. You grant Trendplays Network Inc. all necessary rights, including a
waiver of all privacy and moral rights, to use all comments, feedback,
information, or materials, in whole or in part, or as a derivative work,
without any duty by Trendplays Network Inc. to anyone whatsoever.</p>

<p> Trendplays Network Inc. does not accept unsolicited ideas, works, or other
materials, and you acknowledge that you are responsible for and bear all risk
as to the use or distribution of any such ideas, works, or materials.</p>

<h4>SERVICE</h4>
<p>The rates shown on this site are subject to change at any time without notice.</p>

<p>Trendplays Network Ink, does not guarantee a delivery time for any
order, and acknowledge that some orders may not be feasible to complete.
The information provided is only the best estimation of when the order will be
completed.</p>

<p>Trendplays Network Inc., reserves the right to make modifications to a
service type if deemed necessary to complete an order.</p>

<p>Trendplays Network Inc., reserves the right to refund orders or
transactions at it's discretion.</p>

<p>Trendplays Network Inc, reserves the right to ban or delete an account
at any time without notice for any perceived breach of our terms.</p>
<p>All deposits are final. No refund will be made once credits have been
deposited to any account. All disputes or charge-backs will result in a ban
and termination of all future orders.</p>

<p>Fraudulent activity such as the use of a stolen credit card will result
in a ban without exceptions.</p>

<p> You may not run other marketing campaigns while your Trendplays Network
Inc., marketing campaign is running. Trendplays Network Inc., uses public
statistics to measure the results of it's campaigns, which other campaigns may
interfere with. If you do run other marketing campaigns simultaneously with a
Trendplays Network Inc., marketing campaign, then you agree that the Company is
responsible for every fan, follower, view, comment, like, visit, and/or vote
that you gain during the duration of the campaign.</p>

<p>The company shall in no way be liable for any accounts, photos, videos,
and/or tracks and that are removed due to a service being implemented or
your interaction with Trendplays Network Inc., It is nearly impossible to
determine why social media account providers suspend or delete accounts or
content. All purchases of Services are made at your own risk. For this reason,
Trendplays Network Inc., cannot refund credits after an order has completed.
</p>
</section>

</section>

<section>
<h2>Privacy Policy</h2>
<h4>YOUR PRIVACY MATTERS</h4>
<p> It is our policy to respect your privacy regarding any information we
may collect while operating our website. This Privacy Policy applies to any
of our products (hereinafter, "us", "we", or "any Trendplays Network Inc. products"). We
respect your privacy and are committed to protecting personally identifiable
information you may provide us through the Website. We have adopted this
privacy policy to explain what information may be collected
on our Website, how we use this information, and under what circumstances we
may disclose the information to third parties. This Privacy Policy applies only
to information we collect through the Website and does not apply to our
collection of information from other sources.</p>

<p>This Privacy Policy, together with the Terms and Conditions posted on
our Website, set forth the general rules and policies governing your use of our
Website. Depending on your activities when visiting our Website, you may be
required to agree to additional terms and conditions.</p>

<h4>VISITORS</h4>
<p>We collect non-personally-identifying information that web browsers and
servers typically make available, such as the browser type, language
preference, referring site, and the date and time of each visitor request. The
purpose of collecting this non-personally identifying information is to better
understand how our visitors use this website. From time to time, we may release
aggregate user statistics.</p>

<h4>IDENTIFYING INFORMATION</h4>
<p> Certain visitors to this site choose to interact with it in ways
that require us to gather personally-identifying information. The amount and
type of information that Trendplays Network Inc. gathers depends on the nature of
the interaction. For example, we ask visitors who sign up to provide an email
address.</p>

<h4>PROTECTION OF PERSONAL INFORMATION</h4>
<p>Trendplays Network Inc. discloses potentially personally-identifying
and personally-identifying information only to those of its employees,
contractors and affiliated organizations that (i) need to know that information
in order to process it on our behalf or to provide services available on this
website, and (ii) that have agreed not to disclose it to others. Some of those
employees, contractors and affiliated organizations may be located outside of
your home country; by using this website, you consent to the transfer of such
information to them.</p>
<p>Trendplays Network Inc. will not rent or sell potentially
personally-identifying and personally-identifying information to anyone. Other
than to its employees, contractors and affiliated organizations, as described
above, we disclose potentially personally-identifying and
personally-identifying information only in response to a subpoena, court order
or other governmental request, or when we believe in good faith that disclosure
is reasonably necessary to protect our property or rights. We take all
measures reasonably necessary to protect against the unauthorized access, use,
alteration or destruction of potentially personally-identifying information.</p>
</section>
</main>
@endsection

@section('scripts')
@parent
<script src="js/app.js"></script>
<script src="js/chunk-vendors.js"></script>
<!-- <script src="main.js"></script> -->
@endsection

+ 19
- 0
php-laravel/views/vendor/mail/html/button.blade.php Näytä tiedosto

@@ -0,0 +1,19 @@
<table class="action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="center">
<table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="center">
<table border="0" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td>
<a href="{{ $url }}" class="button button-{{ $color ?? 'primary' }}" target="_blank" rel="noopener">{{ $slot }}</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>

+ 11
- 0
php-laravel/views/vendor/mail/html/footer.blade.php Näytä tiedosto

@@ -0,0 +1,11 @@
<tr>
<td>
<table class="footer" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="content-cell" align="center">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>
</td>
</tr>

+ 11
- 0
php-laravel/views/vendor/mail/html/header.blade.php Näytä tiedosto

@@ -0,0 +1,11 @@
<tr>
<td class="header">
<a href="{{ $url }}" style="display: inline-block;">
@if (trim($slot) === 'Laravel')
<img src="https://laravel.com/img/notification-logo.png" class="logo" alt="Laravel Logo">
@else
{{ $slot }}
@endif
</a>
</td>
</tr>

+ 56
- 0
php-laravel/views/vendor/mail/html/layout.blade.php Näytä tiedosto

@@ -0,0 +1,56 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="color-scheme" content="light">
<meta name="supported-color-schemes" content="light">
<style>
@media only screen and (max-width: 600px) {
.inner-body {
width: 100% !important;
}

.footer {
width: 100% !important;
}
}

@media only screen and (max-width: 500px) {
.button {
width: 100% !important;
}
}
</style>
</head>
<body>

<table class="wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="center">
<table class="content" width="100%" cellpadding="0" cellspacing="0" role="presentation">
{{ $header ?? '' }}

<!-- Email Body -->
<tr>
<td class="body" width="100%" cellpadding="0" cellspacing="0">
<table class="inner-body" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
<!-- Body content -->
<tr>
<td class="content-cell">
{{ Illuminate\Mail\Markdown::parse($slot) }}

{{ $subcopy ?? '' }}
</td>
</tr>
</table>
</td>
</tr>

{{ $footer ?? '' }}
</table>
</td>
</tr>
</table>
</body>
</html>

+ 27
- 0
php-laravel/views/vendor/mail/html/message.blade.php Näytä tiedosto

@@ -0,0 +1,27 @@
@component('mail::layout')
{{-- Header --}}
@slot('header')
@component('mail::header', ['url' => config('app.url')])
{{ config('app.name') }}
@endcomponent
@endslot

{{-- Body --}}
{{ $slot }}

{{-- Subcopy --}}
@isset($subcopy)
@slot('subcopy')
@component('mail::subcopy')
{{ $subcopy }}
@endcomponent
@endslot
@endisset

{{-- Footer --}}
@slot('footer')
@component('mail::footer')
© {{ date('Y') }} {{ config('app.name') }}. @lang('All rights reserved.')
@endcomponent
@endslot
@endcomponent

+ 14
- 0
php-laravel/views/vendor/mail/html/panel.blade.php Näytä tiedosto

@@ -0,0 +1,14 @@
<table class="panel" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="panel-content">
<table width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="panel-item">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>
</td>
</tr>
</table>


+ 7
- 0
php-laravel/views/vendor/mail/html/subcopy.blade.php Näytä tiedosto

@@ -0,0 +1,7 @@
<table class="subcopy" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td>
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>

+ 3
- 0
php-laravel/views/vendor/mail/html/table.blade.php Näytä tiedosto

@@ -0,0 +1,3 @@
<div class="table">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</div>

+ 290
- 0
php-laravel/views/vendor/mail/html/themes/default.css Näytä tiedosto

@@ -0,0 +1,290 @@
/* Base */

body,
body *:not(html):not(style):not(br):not(tr):not(code) {
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
position: relative;
}

body {
-webkit-text-size-adjust: none;
background-color: #ffffff;
color: #718096;
height: 100%;
line-height: 1.4;
margin: 0;
padding: 0;
width: 100% !important;
}

p,
ul,
ol,
blockquote {
line-height: 1.4;
text-align: left;
}

a {
color: #3869d4;
}

a img {
border: none;
}

/* Typography */

h1 {
color: #3d4852;
font-size: 18px;
font-weight: bold;
margin-top: 0;
text-align: left;
}

h2 {
font-size: 16px;
font-weight: bold;
margin-top: 0;
text-align: left;
}

h3 {
font-size: 14px;
font-weight: bold;
margin-top: 0;
text-align: left;
}

p {
font-size: 16px;
line-height: 1.5em;
margin-top: 0;
text-align: left;
}

p.sub {
font-size: 12px;
}

img {
max-width: 100%;
}

/* Layout */

.wrapper {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
background-color: #edf2f7;
margin: 0;
padding: 0;
width: 100%;
}

.content {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
margin: 0;
padding: 0;
width: 100%;
}

/* Header */

.header {
padding: 25px 0;
text-align: center;
}

.header a {
color: #3d4852;
font-size: 19px;
font-weight: bold;
text-decoration: none;
}

/* Logo */

.logo {
height: 75px;
max-height: 75px;
width: 75px;
}

/* Body */

.body {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
background-color: #edf2f7;
border-bottom: 1px solid #edf2f7;
border-top: 1px solid #edf2f7;
margin: 0;
padding: 0;
width: 100%;
}

.inner-body {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 570px;
background-color: #ffffff;
border-color: #e8e5ef;
border-radius: 2px;
border-width: 1px;
box-shadow: 0 2px 0 rgba(0, 0, 150, 0.025), 2px 4px 0 rgba(0, 0, 150, 0.015);
margin: 0 auto;
padding: 0;
width: 570px;
}

/* Subcopy */

.subcopy {
border-top: 1px solid #e8e5ef;
margin-top: 25px;
padding-top: 25px;
}

.subcopy p {
font-size: 14px;
}

/* Footer */

.footer {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 570px;
margin: 0 auto;
padding: 0;
text-align: center;
width: 570px;
}

.footer p {
color: #b0adc5;
font-size: 12px;
text-align: center;
}

.footer a {
color: #b0adc5;
text-decoration: underline;
}

/* Tables */

.table table {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
margin: 30px auto;
width: 100%;
}

.table th {
border-bottom: 1px solid #edeff2;
margin: 0;
padding-bottom: 8px;
}

.table td {
color: #74787e;
font-size: 15px;
line-height: 18px;
margin: 0;
padding: 10px 0;
}

.content-cell {
max-width: 100vw;
padding: 32px;
}

/* Buttons */

.action {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
margin: 30px auto;
padding: 0;
text-align: center;
width: 100%;
}

.button {
-webkit-text-size-adjust: none;
border-radius: 4px;
color: #fff;
display: inline-block;
overflow: hidden;
text-decoration: none;
}

.button-blue,
.button-primary {
background-color: #2d3748;
border-bottom: 8px solid #2d3748;
border-left: 18px solid #2d3748;
border-right: 18px solid #2d3748;
border-top: 8px solid #2d3748;
}

.button-green,
.button-success {
background-color: #48bb78;
border-bottom: 8px solid #48bb78;
border-left: 18px solid #48bb78;
border-right: 18px solid #48bb78;
border-top: 8px solid #48bb78;
}

.button-red,
.button-error {
background-color: #e53e3e;
border-bottom: 8px solid #e53e3e;
border-left: 18px solid #e53e3e;
border-right: 18px solid #e53e3e;
border-top: 8px solid #e53e3e;
}

/* Panels */

.panel {
border-left: #2d3748 solid 4px;
margin: 21px 0;
}

.panel-content {
background-color: #edf2f7;
color: #718096;
padding: 16px;
}

.panel-content p {
color: #718096;
}

.panel-item {
padding: 0;
}

.panel-item p:last-of-type {
margin-bottom: 0;
padding-bottom: 0;
}

/* Utilities */

.break-all {
word-break: break-all;
}

+ 1
- 0
php-laravel/views/vendor/mail/text/button.blade.php Näytä tiedosto

@@ -0,0 +1 @@
{{ $slot }}: {{ $url }}

+ 1
- 0
php-laravel/views/vendor/mail/text/footer.blade.php Näytä tiedosto

@@ -0,0 +1 @@
{{ $slot }}

+ 1
- 0
php-laravel/views/vendor/mail/text/header.blade.php Näytä tiedosto

@@ -0,0 +1 @@
[{{ $slot }}]({{ $url }})

+ 9
- 0
php-laravel/views/vendor/mail/text/layout.blade.php Näytä tiedosto

@@ -0,0 +1,9 @@
{!! strip_tags($header) !!}

{!! strip_tags($slot) !!}
@isset($subcopy)

{!! strip_tags($subcopy) !!}
@endisset

{!! strip_tags($footer) !!}

+ 27
- 0
php-laravel/views/vendor/mail/text/message.blade.php Näytä tiedosto

@@ -0,0 +1,27 @@
@component('mail::layout')
{{-- Header --}}
@slot('header')
@component('mail::header', ['url' => config('app.url')])
{{ config('app.name') }}
@endcomponent
@endslot

{{-- Body --}}
{{ $slot }}

{{-- Subcopy --}}
@isset($subcopy)
@slot('subcopy')
@component('mail::subcopy')
{{ $subcopy }}
@endcomponent
@endslot
@endisset

{{-- Footer --}}
@slot('footer')
@component('mail::footer')
© {{ date('Y') }} {{ config('app.name') }}. @lang('All rights reserved.')
@endcomponent
@endslot
@endcomponent

+ 1
- 0
php-laravel/views/vendor/mail/text/panel.blade.php Näytä tiedosto

@@ -0,0 +1 @@
{{ $slot }}

+ 1
- 0
php-laravel/views/vendor/mail/text/subcopy.blade.php Näytä tiedosto

@@ -0,0 +1 @@
{{ $slot }}

+ 1
- 0
php-laravel/views/vendor/mail/text/table.blade.php Näytä tiedosto

@@ -0,0 +1 @@
{{ $slot }}

+ 62
- 0
php-laravel/views/vendor/notifications/email.blade.php Näytä tiedosto

@@ -0,0 +1,62 @@
@component('mail::message')
{{-- Greeting --}}
@if (! empty($greeting))
# {{ $greeting }}
@else
@if ($level === 'error')
# @lang('Whoops!')
@else
# @lang('Hello!')
@endif
@endif

{{-- Intro Lines --}}
@foreach ($introLines as $line)
{{ $line }}

@endforeach

{{-- Action Button --}}
@isset($actionText)
<?php
switch ($level) {
case 'success':
case 'error':
$color = $level;
break;
default:
$color = 'primary';
}
?>
@component('mail::button', ['url' => $actionUrl, 'color' => $color])
{{ $actionText }}
@endcomponent
@endisset

{{-- Outro Lines --}}
@foreach ($outroLines as $line)
{{ $line }}

@endforeach

{{-- Salutation --}}
@if (! empty($salutation))
{{ $salutation }}
@else
@lang('Regards'),<br>
{{ config('app.name') }}
@endif

{{-- Subcopy --}}
@isset($actionText)
@slot('subcopy')
@lang(
"If you’re having trouble clicking the \":actionText\" button, copy and paste the URL below\n".
'into your web browser:',
[
'actionText' => $actionText,
]
) <span class="break-all">[{{ $displayableActionUrl }}]({{ $actionUrl }})</span>
@endslot
@endisset
@endcomponent

+ 25
- 0
php-laravel/views/verify-email.blade.php Näytä tiedosto

@@ -0,0 +1,25 @@
@extends('master')

@section('title', 'Home')
@section('heading-style', 'info-page')

@section('head-metas')
@parent
<link rel="stylesheet" href="/css/panel.css">
@endsection

@section('content')
<main>
<div class="info-heading">
<h3>To continue, we need to make sure your email address is yours. Click the verification link we've sent to you.</h3>
<button id="resend_verification" class="brand-button">Resend</button>
</div>
</main>
@endsection

@section('scripts')
@parent
<script src="js/app.js"></script>
<script src="js/chunk-vendors.js"></script>
<!-- <script src="main.js"></script> -->
@endsection

+ 132
- 0
php-laravel/views/welcome.blade.php Näytä tiedosto

@@ -0,0 +1,132 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

<title>Laravel</title>

<!-- Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">

<!-- Styles -->
<style>
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}a{background-color:transparent}[hidden]{display:none}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}*,:after,:before{box-sizing:border-box;border:0 solid #e2e8f0}a{color:inherit;text-decoration:inherit}svg,video{display:block;vertical-align:middle}video{max-width:100%;height:auto}.bg-white{--bg-opacity:1;background-color:#fff;background-color:rgba(255,255,255,var(--bg-opacity))}.bg-gray-100{--bg-opacity:1;background-color:#f7fafc;background-color:rgba(247,250,252,var(--bg-opacity))}.border-gray-200{--border-opacity:1;border-color:#edf2f7;border-color:rgba(237,242,247,var(--border-opacity))}.border-t{border-top-width:1px}.flex{display:flex}.grid{display:grid}.hidden{display:none}.items-center{align-items:center}.justify-center{justify-content:center}.font-semibold{font-weight:600}.h-5{height:1.25rem}.h-8{height:2rem}.h-16{height:4rem}.text-sm{font-size:.875rem}.text-lg{font-size:1.125rem}.leading-7{line-height:1.75rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-1{margin-left:.25rem}.mt-2{margin-top:.5rem}.mr-2{margin-right:.5rem}.ml-2{margin-left:.5rem}.mt-4{margin-top:1rem}.ml-4{margin-left:1rem}.mt-8{margin-top:2rem}.ml-12{margin-left:3rem}.-mt-px{margin-top:-1px}.max-w-6xl{max-width:72rem}.min-h-screen{min-height:100vh}.overflow-hidden{overflow:hidden}.p-6{padding:1.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.pt-8{padding-top:2rem}.fixed{position:fixed}.relative{position:relative}.top-0{top:0}.right-0{right:0}.shadow{box-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06)}.text-center{text-align:center}.text-gray-200{--text-opacity:1;color:#edf2f7;color:rgba(237,242,247,var(--text-opacity))}.text-gray-300{--text-opacity:1;color:#e2e8f0;color:rgba(226,232,240,var(--text-opacity))}.text-gray-400{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}.text-gray-500{--text-opacity:1;color:#a0aec0;color:rgba(160,174,192,var(--text-opacity))}.text-gray-600{--text-opacity:1;color:#718096;color:rgba(113,128,150,var(--text-opacity))}.text-gray-700{--text-opacity:1;color:#4a5568;color:rgba(74,85,104,var(--text-opacity))}.text-gray-900{--text-opacity:1;color:#1a202c;color:rgba(26,32,44,var(--text-opacity))}.underline{text-decoration:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.w-5{width:1.25rem}.w-8{width:2rem}.w-auto{width:auto}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}@media (min-width:640px){.sm\:rounded-lg{border-radius:.5rem}.sm\:block{display:block}.sm\:items-center{align-items:center}.sm\:justify-start{justify-content:flex-start}.sm\:justify-between{justify-content:space-between}.sm\:h-20{height:5rem}.sm\:ml-0{margin-left:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pt-0{padding-top:0}.sm\:text-left{text-align:left}.sm\:text-right{text-align:right}}@media (min-width:768px){.md\:border-t-0{border-top-width:0}.md\:border-l{border-left-width:1px}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:1024px){.lg\:px-8{padding-left:2rem;padding-right:2rem}}@media (prefers-color-scheme:dark){.dark\:bg-gray-800{--bg-opacity:1;background-color:#2d3748;background-color:rgba(45,55,72,var(--bg-opacity))}.dark\:bg-gray-900{--bg-opacity:1;background-color:#1a202c;background-color:rgba(26,32,44,var(--bg-opacity))}.dark\:border-gray-700{--border-opacity:1;border-color:#4a5568;border-color:rgba(74,85,104,var(--border-opacity))}.dark\:text-white{--text-opacity:1;color:#fff;color:rgba(255,255,255,var(--text-opacity))}.dark\:text-gray-400{--text-opacity:1;color:#cbd5e0;color:rgba(203,213,224,var(--text-opacity))}}
</style>

<style>
body {
font-family: 'Nunito', sans-serif;
}
</style>
</head>
<body class="antialiased">
<div class="relative flex items-top justify-center min-h-screen bg-gray-100 dark:bg-gray-900 sm:items-center py-4 sm:pt-0">
@if (Route::has('login'))
<div class="hidden fixed top-0 right-0 px-6 py-4 sm:block">
@auth
<a href="{{ url('/home') }}" class="text-sm text-gray-700 underline">Home</a>
@else
<a href="{{ route('login') }}" class="text-sm text-gray-700 underline">Log in</a>

@if (Route::has('register'))
<a href="{{ route('register') }}" class="ml-4 text-sm text-gray-700 underline">Register</a>
@endif
@endauth
</div>
@endif

<div class="max-w-6xl mx-auto sm:px-6 lg:px-8">
<div class="flex justify-center pt-8 sm:justify-start sm:pt-0">
<svg viewBox="0 0 651 192" fill="none" xmlns="http://www.w3.org/2000/svg" class="h-16 w-auto text-gray-700 sm:h-20">
<g clip-path="url(#clip0)" fill="#EF3B2D">
<path d="M248.032 44.676h-16.466v100.23h47.394v-14.748h-30.928V44.676zM337.091 87.202c-2.101-3.341-5.083-5.965-8.949-7.875-3.865-1.909-7.756-2.864-11.669-2.864-5.062 0-9.69.931-13.89 2.792-4.201 1.861-7.804 4.417-10.811 7.661-3.007 3.246-5.347 6.993-7.016 11.239-1.672 4.249-2.506 8.713-2.506 13.389 0 4.774.834 9.26 2.506 13.459 1.669 4.202 4.009 7.925 7.016 11.169 3.007 3.246 6.609 5.799 10.811 7.66 4.199 1.861 8.828 2.792 13.89 2.792 3.913 0 7.804-.955 11.669-2.863 3.866-1.908 6.849-4.533 8.949-7.875v9.021h15.607V78.182h-15.607v9.02zm-1.431 32.503c-.955 2.578-2.291 4.821-4.009 6.73-1.719 1.91-3.795 3.437-6.229 4.582-2.435 1.146-5.133 1.718-8.091 1.718-2.96 0-5.633-.572-8.019-1.718-2.387-1.146-4.438-2.672-6.156-4.582-1.719-1.909-3.032-4.152-3.938-6.73-.909-2.577-1.36-5.298-1.36-8.161 0-2.864.451-5.585 1.36-8.162.905-2.577 2.219-4.819 3.938-6.729 1.718-1.908 3.77-3.437 6.156-4.582 2.386-1.146 5.059-1.718 8.019-1.718 2.958 0 5.656.572 8.091 1.718 2.434 1.146 4.51 2.674 6.229 4.582 1.718 1.91 3.054 4.152 4.009 6.729.953 2.577 1.432 5.298 1.432 8.162-.001 2.863-.479 5.584-1.432 8.161zM463.954 87.202c-2.101-3.341-5.083-5.965-8.949-7.875-3.865-1.909-7.756-2.864-11.669-2.864-5.062 0-9.69.931-13.89 2.792-4.201 1.861-7.804 4.417-10.811 7.661-3.007 3.246-5.347 6.993-7.016 11.239-1.672 4.249-2.506 8.713-2.506 13.389 0 4.774.834 9.26 2.506 13.459 1.669 4.202 4.009 7.925 7.016 11.169 3.007 3.246 6.609 5.799 10.811 7.66 4.199 1.861 8.828 2.792 13.89 2.792 3.913 0 7.804-.955 11.669-2.863 3.866-1.908 6.849-4.533 8.949-7.875v9.021h15.607V78.182h-15.607v9.02zm-1.432 32.503c-.955 2.578-2.291 4.821-4.009 6.73-1.719 1.91-3.795 3.437-6.229 4.582-2.435 1.146-5.133 1.718-8.091 1.718-2.96 0-5.633-.572-8.019-1.718-2.387-1.146-4.438-2.672-6.156-4.582-1.719-1.909-3.032-4.152-3.938-6.73-.909-2.577-1.36-5.298-1.36-8.161 0-2.864.451-5.585 1.36-8.162.905-2.577 2.219-4.819 3.938-6.729 1.718-1.908 3.77-3.437 6.156-4.582 2.386-1.146 5.059-1.718 8.019-1.718 2.958 0 5.656.572 8.091 1.718 2.434 1.146 4.51 2.674 6.229 4.582 1.718 1.91 3.054 4.152 4.009 6.729.953 2.577 1.432 5.298 1.432 8.162 0 2.863-.479 5.584-1.432 8.161zM650.772 44.676h-15.606v100.23h15.606V44.676zM365.013 144.906h15.607V93.538h26.776V78.182h-42.383v66.724zM542.133 78.182l-19.616 51.096-19.616-51.096h-15.808l25.617 66.724h19.614l25.617-66.724h-15.808zM591.98 76.466c-19.112 0-34.239 15.706-34.239 35.079 0 21.416 14.641 35.079 36.239 35.079 12.088 0 19.806-4.622 29.234-14.688l-10.544-8.158c-.006.008-7.958 10.449-19.832 10.449-13.802 0-19.612-11.127-19.612-16.884h51.777c2.72-22.043-11.772-40.877-33.023-40.877zm-18.713 29.28c.12-1.284 1.917-16.884 18.589-16.884 16.671 0 18.697 15.598 18.813 16.884h-37.402zM184.068 43.892c-.024-.088-.073-.165-.104-.25-.058-.157-.108-.316-.191-.46-.056-.097-.137-.176-.203-.265-.087-.117-.161-.242-.265-.345-.085-.086-.194-.148-.29-.223-.109-.085-.206-.182-.327-.252l-.002-.001-.002-.002-35.648-20.524a2.971 2.971 0 00-2.964 0l-35.647 20.522-.002.002-.002.001c-.121.07-.219.167-.327.252-.096.075-.205.138-.29.223-.103.103-.178.228-.265.345-.066.089-.147.169-.203.265-.083.144-.133.304-.191.46-.031.085-.08.162-.104.25-.067.249-.103.51-.103.776v38.979l-29.706 17.103V24.493a3 3 0 00-.103-.776c-.024-.088-.073-.165-.104-.25-.058-.157-.108-.316-.191-.46-.056-.097-.137-.176-.203-.265-.087-.117-.161-.242-.265-.345-.085-.086-.194-.148-.29-.223-.109-.085-.206-.182-.327-.252l-.002-.001-.002-.002L40.098 1.396a2.971 2.971 0 00-2.964 0L1.487 21.919l-.002.002-.002.001c-.121.07-.219.167-.327.252-.096.075-.205.138-.29.223-.103.103-.178.228-.265.345-.066.089-.147.169-.203.265-.083.144-.133.304-.191.46-.031.085-.08.162-.104.25-.067.249-.103.51-.103.776v122.09c0 1.063.568 2.044 1.489 2.575l71.293 41.045c.156.089.324.143.49.202.078.028.15.074.23.095a2.98 2.98 0 001.524 0c.069-.018.132-.059.2-.083.176-.061.354-.119.519-.214l71.293-41.045a2.971 2.971 0 001.489-2.575v-38.979l34.158-19.666a2.971 2.971 0 001.489-2.575V44.666a3.075 3.075 0 00-.106-.774zM74.255 143.167l-29.648-16.779 31.136-17.926.001-.001 34.164-19.669 29.674 17.084-21.772 12.428-43.555 24.863zm68.329-76.259v33.841l-12.475-7.182-17.231-9.92V49.806l12.475 7.182 17.231 9.92zm2.97-39.335l29.693 17.095-29.693 17.095-29.693-17.095 29.693-17.095zM54.06 114.089l-12.475 7.182V46.733l17.231-9.92 12.475-7.182v74.537l-17.231 9.921zM38.614 7.398l29.693 17.095-29.693 17.095L8.921 24.493 38.614 7.398zM5.938 29.632l12.475 7.182 17.231 9.92v79.676l.001.005-.001.006c0 .114.032.221.045.333.017.146.021.294.059.434l.002.007c.032.117.094.222.14.334.051.124.088.255.156.371a.036.036 0 00.004.009c.061.105.149.191.222.288.081.105.149.22.244.314l.008.01c.084.083.19.142.284.215.106.083.202.178.32.247l.013.005.011.008 34.139 19.321v34.175L5.939 144.867V29.632h-.001zm136.646 115.235l-65.352 37.625V148.31l48.399-27.628 16.953-9.677v33.862zm35.646-61.22l-29.706 17.102V66.908l17.231-9.92 12.475-7.182v33.841z"/>
</g>
</svg>
</div>

<div class="mt-8 bg-white dark:bg-gray-800 overflow-hidden shadow sm:rounded-lg">
<div class="grid grid-cols-1 md:grid-cols-2">
<div class="p-6">
<div class="flex items-center">
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" class="w-8 h-8 text-gray-500"><path d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"></path></svg>
<div class="ml-4 text-lg leading-7 font-semibold"><a href="https://laravel.com/docs" class="underline text-gray-900 dark:text-white">Documentation</a></div>
</div>

<div class="ml-12">
<div class="mt-2 text-gray-600 dark:text-gray-400 text-sm">
Laravel has wonderful, thorough documentation covering every aspect of the framework. Whether you are new to the framework or have previous experience with Laravel, we recommend reading all of the documentation from beginning to end.
</div>
</div>
</div>

<div class="p-6 border-t border-gray-200 dark:border-gray-700 md:border-t-0 md:border-l">
<div class="flex items-center">
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" class="w-8 h-8 text-gray-500"><path d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z"></path><path d="M15 13a3 3 0 11-6 0 3 3 0 016 0z"></path></svg>
<div class="ml-4 text-lg leading-7 font-semibold"><a href="https://laracasts.com" class="underline text-gray-900 dark:text-white">Laracasts</a></div>
</div>

<div class="ml-12">
<div class="mt-2 text-gray-600 dark:text-gray-400 text-sm">
Laracasts offers thousands of video tutorials on Laravel, PHP, and JavaScript development. Check them out, see for yourself, and massively level up your development skills in the process.
</div>
</div>
</div>

<div class="p-6 border-t border-gray-200 dark:border-gray-700">
<div class="flex items-center">
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" class="w-8 h-8 text-gray-500"><path d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z"></path></svg>
<div class="ml-4 text-lg leading-7 font-semibold"><a href="https://laravel-news.com/" class="underline text-gray-900 dark:text-white">Laravel News</a></div>
</div>

<div class="ml-12">
<div class="mt-2 text-gray-600 dark:text-gray-400 text-sm">
Laravel News is a community driven portal and newsletter aggregating all of the latest and most important news in the Laravel ecosystem, including new package releases and tutorials.
</div>
</div>
</div>

<div class="p-6 border-t border-gray-200 dark:border-gray-700 md:border-l">
<div class="flex items-center">
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" class="w-8 h-8 text-gray-500"><path d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
<div class="ml-4 text-lg leading-7 font-semibold text-gray-900 dark:text-white">Vibrant Ecosystem</div>
</div>

<div class="ml-12">
<div class="mt-2 text-gray-600 dark:text-gray-400 text-sm">
Laravel's robust library of first-party tools and libraries, such as <a href="https://forge.laravel.com" class="underline">Forge</a>, <a href="https://vapor.laravel.com" class="underline">Vapor</a>, <a href="https://nova.laravel.com" class="underline">Nova</a>, and <a href="https://envoyer.io" class="underline">Envoyer</a> help you take your projects to the next level. Pair them with powerful open source libraries like <a href="https://laravel.com/docs/billing" class="underline">Cashier</a>, <a href="https://laravel.com/docs/dusk" class="underline">Dusk</a>, <a href="https://laravel.com/docs/broadcasting" class="underline">Echo</a>, <a href="https://laravel.com/docs/horizon" class="underline">Horizon</a>, <a href="https://laravel.com/docs/sanctum" class="underline">Sanctum</a>, <a href="https://laravel.com/docs/telescope" class="underline">Telescope</a>, and more.
</div>
</div>
</div>
</div>
</div>

<div class="flex justify-center mt-4 sm:items-center sm:justify-between">
<div class="text-center text-sm text-gray-500 sm:text-left">
<div class="flex items-center">
<svg fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" stroke="currentColor" class="-mt-px w-5 h-5 text-gray-400">
<path d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path>
</svg>

<a href="https://laravel.bigcartel.com" class="ml-1 underline">
Shop
</a>

<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" class="ml-4 -mt-px w-5 h-5 text-gray-400">
<path d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"></path>
</svg>

<a href="https://github.com/sponsors/taylorotwell" class="ml-1 underline">
Sponsor
</a>
</div>
</div>

<div class="ml-4 text-center text-sm text-gray-500 sm:text-right sm:ml-0">
Laravel v{{ Illuminate\Foundation\Application::VERSION }} (PHP v{{ PHP_VERSION }})
</div>
</div>
</div>
</div>
</body>
</html>

main.scss → scss/main.scss Näytä tiedosto


panel.vue → vue/panel.vue Näytä tiedosto


Loading…
Peruuta
Tallenna