frontend update

This commit is contained in:
carodej
2020-06-03 10:58:44 +02:00
parent 331ee52ea3
commit ca14838212
60 changed files with 1871 additions and 710 deletions

View File

@@ -66,6 +66,7 @@ class SetupDevEnvironment extends Command
public function migrateDatabase()
{
$this->call('migrate:fresh');
$this->call('db:seed');
}
/**

View File

@@ -63,6 +63,9 @@ class SetupProductionEnvironment extends Command
public function migrateDatabase()
{
$this->call('migrate:fresh');
$this->call('db:seed', [
'--class' => 'PaymentGatewaysSeeder'
]);
}
/**

View File

@@ -42,6 +42,11 @@ class UpgradeApp extends Command
$this->info('Upgrading your application to version ' . $this->argument('version'));
$this->call('down');
// Version 1.7
if ($this->argument('version') === 'v1.7') {
$this->version_1_7();
}
// Version 1.6
if ($this->argument('version') === 'v1.6') {
$this->version_1_6();
@@ -51,6 +56,19 @@ class UpgradeApp extends Command
$this->info('Your application was upgraded! 🥳🥳🥳');
}
/**
* Upgrade script to version 1.7
*/
public function version_1_7() {
// Migrate new tables and changes
$this->call('migrate');
$this->call('db:seed', [
'--class' => 'PaymentGatewaysSeeder'
]);
}
/**
* Upgrade script to version 1.6
*/

View File

@@ -3,12 +3,52 @@
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Resources\GatewayCollection;
use App\Http\Resources\GatewayResource;
use App\PaymentGateway;
use Illuminate\Http\Request;
class GatewayController extends Controller
{
public function update(Request $request, $gateway)
/**
* Get all payment gateways
*
* @return GatewayCollection
*/
public function index()
{
return $request->all();
return new GatewayCollection(PaymentGateway::all());
}
/**
* Get single payment gateway by slug
*
* @param $slug
* @return GatewayResource
*/
public function show($slug)
{
$gateway = PaymentGateway::where('slug', $slug)->firstOrFail();
return new GatewayResource($gateway);
}
/**
* Update payment gateway options
*
* @param Request $request
* @param $slug
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
*/
public function update(Request $request, $slug)
{
// TODO: validation request
$gateway = PaymentGateway::where('slug', $slug)->first();
// Update text data
$gateway->update(make_single_input($request));
return response('Saved!', 204);
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Resources\InvoiceCollection;
use App\Http\Resources\InvoiceResource;
use App\Invoice;
use Illuminate\Http\Request;
class InvoiceController extends Controller
{
/**
* Get all invoices
*
* @return InvoiceCollection
*/
public function index()
{
return new InvoiceCollection(
Invoice::all()
);
}
/**
* Get single invoice by $token
* @param $token
* @return InvoiceResource
*/
public function show($token)
{
$invoice = Invoice::where('token', $token)->firstOrFail();
return view('vuefilemanager.invoice')
->with('invoice', $invoice);
}
}

View File

@@ -3,17 +3,66 @@
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Resources\PlanCollection;
use App\Http\Resources\PlanResource;
use App\Plan;
use Illuminate\Http\Request;
class PlanController extends Controller
{
public function create(Request $request)
{
return $request->all();
/**
* Get all plans
*
* @return PlanCollection
*/
public function index() {
return new PlanCollection(Plan::all());
}
public function update(Request $request)
/**
* Get plan record
*
* @param $id
* @return PlanResource
*/
public function show($id)
{
return $request->all();
$plan = Plan::findOrFail($id);
return new PlanResource($plan);
}
/**
* Create new plan
*
* @param Request $request
* @return PlanResource
*/
public function store(Request $request)
{
// TODO: validation request
$plan = Plan::create($request->input('attributes'));
return new PlanResource($plan);
}
/**
* Update plan attribute
*
* @param Request $request
* @param $id
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
// TODO: validation request
$plan = Plan::findOrFail($id);
// Update text data
$plan->update(make_single_input($request));
return response('Saved!', 204);
}
}

View File

@@ -9,6 +9,7 @@ use App\Http\Requests\Admin\ChangeRoleRequest;
use App\Http\Requests\Admin\ChangeStorageCapacityRequest;
use App\Http\Requests\Admin\CreateUserByAdmin;
use App\Http\Requests\Admin\DeleteUserRequest;
use App\Http\Resources\InvoiceCollection;
use App\Http\Resources\UsersCollection;
use App\Http\Resources\UserResource;
use App\Http\Resources\UserStorageResource;
@@ -35,7 +36,9 @@ class UserController extends Controller
*/
public function details($id)
{
return new UserResource(User::findOrFail($id));
return new UserResource(
User::findOrFail($id)
);
}
/**
@@ -46,7 +49,22 @@ class UserController extends Controller
*/
public function storage($id)
{
return new UserStorageResource(User::findOrFail($id));
return new UserStorageResource(
User::findOrFail($id)
);
}
/**
* Get user storage details
*
* @param $id
* @return InvoiceCollection
*/
public function invoices($id)
{
return new InvoiceCollection(
User::findOrFail($id)->invoices
);
}
/**
@@ -56,7 +74,9 @@ class UserController extends Controller
*/
public function users()
{
return new UsersCollection(User::all());
return new UsersCollection(
User::all()
);
}
/**

View File

@@ -4,7 +4,9 @@ namespace App\Http\Controllers\User;
use App\FileManagerFile;
use App\FileManagerFolder;
use App\Http\Resources\InvoiceCollection;
use App\Http\Resources\StorageDetailResource;
use App\Http\Resources\UserResource;
use App\Http\Resources\UserStorageResource;
use App\Http\Tools\Demo;
use Illuminate\Contracts\Routing\ResponseFactory;
@@ -26,7 +28,7 @@ class AccountController extends Controller
public function user()
{
// Get User
$user = User::with(['favourites', 'latest_uploads'])
$user = User::with(['favourites'])
->where('id', Auth::id())
->first();
@@ -48,6 +50,18 @@ class AccountController extends Controller
];
}
/**
* Get me
*
* @return UserResource
*/
public function me()
{
return new UserResource(
Auth::user()
);
}
/**
* Get storage details
*
@@ -55,7 +69,15 @@ class AccountController extends Controller
*/
public function storage()
{
return new UserStorageResource(Auth::user());
return new UserStorageResource(
Auth::user()
);
}
public function invoices() {
return new InvoiceCollection(
Auth::user()->invoices
);
}
/**
@@ -105,6 +127,29 @@ class AccountController extends Controller
return response('Saved!', 204);
}
/**
* Update user settings relationship
*
* @param Request $request
* @return ResponseFactory|\Illuminate\Http\Response
*/
public function update_user_settings(Request $request)
{
// TODO: validation
// Get user
$user = Auth::user();
// Check if is demo
if (is_demo($user->id)) {
return Demo::response_204();
}
// Update text data
$user->settings->update(make_single_input($request));
return response('Saved!', 204);
}
/**
* Change user password
*

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class GatewayCollection extends ResourceCollection
{
public $collects = GatewayResource::class;
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => $this->collection,
];
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class GatewayResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => [
'id' => (string)$this->id,
'type' => 'gateways',
'attributes' => [
'status' => $this->status,
'sandbox' => $this->sandbox,
'name' => $this->name,
'slug' => $this->slug,
'logo' => $this->logo,
'client_id' => $this->client_id,
'secret' => $this->secret,
'webhook' => $this->webhook,
'optional' => $this->optional,
]
]
];
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class InvoiceCollection extends ResourceCollection
{
public $collects = InvoiceResource::class;
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => $this->collection,
];
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class InvoiceResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => [
'id' => (string)$this->id,
'type' => 'invoices',
'attributes' => [
'token' => $this->token,
'order' => $this->order,
'user_id' => $this->user_id,
'plan_id' => $this->plan_id,
'notes' => $this->notes,
'total' => $this->total,
'currency' => $this->currency,
'seller' => $this->seller,
'client' => $this->client,
'bag' => $this->bag,
'created_at_formatted' => format_date($this->created_at),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
]
],
'relationships' => [
'user' => [
'data' => [
'id' => (string)$this->user->id,
'type' => 'user',
'attributes' => [
'name' => $this->user->name,
'avatar' => $this->user->avatar,
]
]
]
]
];
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class PlanCollection extends ResourceCollection
{
public $collects = PlanResource::class;
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => $this->collection,
];
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class PlanResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => [
'id' => (string)$this->id,
'type' => 'plans',
'attributes' => [
'status' => $this->status,
'name' => $this->name,
'description' => $this->description,
'price' => $this->price,
'capacity_formatted' => format_gigabytes($this->capacity),
'capacity' => $this->capacity,
'created_at_formatted' => format_date($this->created_at),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
]
]
];
}
}

View File

@@ -19,7 +19,7 @@ class UserResource extends JsonResource
$faker = Factory::create();
return [
'data' => [
'data' => [
'id' => (string)$this->id,
'type' => 'user',
'attributes' => [
@@ -27,11 +27,34 @@ class UserResource extends JsonResource
'email' => env('APP_DEMO') ? $faker->email : $this->email,
'avatar' => $this->avatar,
'role' => $this->role,
'storage' => $this->storage,
'created_at_formatted' => format_date($this->created_at, '%d. %B. %Y'),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
]
],
'relationships' => [
'settings' => [
'data' => [
'id' => (string)$this->settings->id,
'type' => 'settings',
'attributes' => [
'billing_name' => $this->settings->billing_name,
'billing_address' => $this->settings->billing_address,
'billing_state' => $this->settings->billing_state,
'billing_city' => $this->settings->billing_city,
'billing_postal_code' => $this->settings->billing_postal_code,
'billing_country' => $this->settings->billing_country,
'billing_phone_number' => $this->settings->billing_phone_number,
]
]
],
'storage' => [
'data' => [
'id' => '1',
'type' => 'storage',
'attributes' => $this->storage
]
],
]
];
}

View File

@@ -55,7 +55,7 @@ class UserStorageResource extends JsonResource
return [
'data' => [
'id' => (string)$this->id,
'type' => 'user-storage',
'type' => 'storage',
'attributes' => [
'used' => Metric::bytes($this->used_capacity)->format(),
'capacity' => format_gigabytes($this->settings->storage_capacity),

18
app/Invoice.php Normal file
View File

@@ -0,0 +1,18 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Invoice extends Model
{
protected $casts = [
'seller' => 'array',
'client' => 'array',
'bag' => 'array',
];
public function user() {
return $this->hasOne(User::class, 'id', 'user_id');
}
}

12
app/PaymentGateway.php Normal file
View File

@@ -0,0 +1,12 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class PaymentGateway extends Model
{
protected $guarded = ['id'];
public $timestamps = false;
}

10
app/Plan.php Normal file
View File

@@ -0,0 +1,10 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Plan extends Model
{
protected $guarded = ['id'];
}

View File

@@ -191,4 +191,14 @@ class User extends Authenticatable
return $this->hasOne(UserSettings::class);
}
/**
* Get user invoices
*
* @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function invoices() {
return $this->hasMany(Invoice::class);
}
}

View File

@@ -13,6 +13,7 @@
"fideloper/proxy": "^4.0",
"fruitcake/laravel-cors": "^1.0",
"gabrielelana/byte-units": "^0.5.0",
"h4cc/wkhtmltopdf-amd64": "^0.12.4",
"intervention/image": "^2.5",
"laravel/framework": "^6.2",
"laravel/passport": "^8.4",

View File

@@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePlansTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('plans', function (Blueprint $table) {
$table->bigIncrements('id');
$table->boolean('status')->default(1);
$table->text('name');
$table->text('price');
$table->integer('capacity');
$table->longText('description')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('plans');
}
}

View File

@@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePaymentGatewaysTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('payment_gateways', function (Blueprint $table) {
$table->bigIncrements('id');
$table->boolean('status')->default(0);
$table->boolean('sandbox')->default(0);
$table->text('name');
$table->text('slug');
$table->text('logo');
$table->text('client_id')->nullable();
$table->text('secret')->nullable();
$table->text('webhook')->nullable();
$table->longText('optional')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('payment_gateways');
}
}

View File

@@ -0,0 +1,44 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddAttributesToUserSettingsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('user_settings', function (Blueprint $table) {
$table->text('billing_name')->nullable();
$table->text('billing_address')->nullable();
$table->text('billing_state')->nullable();
$table->text('billing_city')->nullable();
$table->text('billing_postal_code')->nullable();
$table->text('billing_country')->nullable();
$table->text('billing_phone_number')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('user_settings', function (Blueprint $table) {
$table->dropColumn('billing_name');
$table->dropColumn('billing_address');
$table->dropColumn('billing_state');
$table->dropColumn('billing_city');
$table->dropColumn('billing_postal_code');
$table->dropColumn('billing_country');
$table->dropColumn('billing_phone_number');
});
}
}

View File

@@ -0,0 +1,42 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateInvoicesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('invoices', function (Blueprint $table) {
$table->bigIncrements('id');
$table->text('token');
$table->text('order');
$table->text('user_id');
$table->text('plan_id');
$table->longText('seller');
$table->longText('client');
$table->longText('bag');
$table->longText('notes')->nullable();
$table->text('total');
$table->text('currency');
$table->text('path');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('invoices');
}
}

View File

@@ -11,6 +11,8 @@ class DatabaseSeeder extends Seeder
*/
public function run()
{
// $this->call(UsersTableSeeder::class);
$this->call(PaymentGatewaysSeeder::class);
$this->call(PlansSeeder::class);
$this->call(InvoicesSeeder::class);
}
}

View File

@@ -0,0 +1,65 @@
<?php
use Illuminate\Database\Seeder;
class InvoicesSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$user = \App\User::find(1);
$seller = [
'settings' => [
'billing_name' => 'VueFileManager',
'billing_address' => 'Somewhere 32',
'billing_state' => 'Washington',
'billing_city' => 'Manchester',
'billing_postal_code' => '04001',
'billing_country' => 'The USA',
'billing_phone_number' => '490321-6354774',
'billing_vat_number' => '7354724626246',
]
];
$invoice = \App\Invoice::create([
'token' => \Illuminate\Support\Str::random(),
'order' => '200001',
'user_id' => 1,
'plan_id' => 1,
'seller' => [
'billing_name' => $seller['settings']['billing_name'],
'billing_address' => $seller['settings']['billing_address'],
'billing_state' => $seller['settings']['billing_state'],
'billing_city' => $seller['settings']['billing_city'],
'billing_postal_code' => $seller['settings']['billing_postal_code'],
'billing_country' => $seller['settings']['billing_country'],
'billing_phone_number' => $seller['settings']['billing_phone_number'],
'billing_vat_number' => $seller['settings']['billing_vat_number'],
],
'client' => [
'billing_name' => $user->settings->billing_name,
'billing_address' => $user->settings->billing_address,
'billing_state' => $user->settings->billing_state,
'billing_city' => $user->settings->billing_city,
'billing_postal_code' => $user->settings->billing_postal_code,
'billing_country' => $user->settings->billing_country,
'billing_phone_number' => $user->settings->billing_phone_number,
],
'bag' => [
[
'description' => 'Subscription - Starter Pack',
'date' => '01-05-2020 01-06-2020',
'amount' => 2.99,
]
],
'notes' => '',
'total' => 2.99,
'currency' => 'USD',
'path' => '/invoices/200001-8HUrJkNdLKilNMeF.pdf',
]);
}
}

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Seeder;
class PaymentGatewaysSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
// Create Stripe default record
DB::table('payment_gateways')->insert([
'logo' => '/assets/images/stripe-logo-thumbnail.png',
'name' => 'Stripe',
'slug' => 'stripe',
]);
// Create PayPal default record
DB::table('payment_gateways')->insert([
'logo' => '/assets/images/paypal-logo-thumbnail.png',
'name' => 'Paypal',
'slug' => 'paypal',
]);
}
}

View File

@@ -0,0 +1,42 @@
<?php
use Carbon\Carbon;
use Illuminate\Database\Seeder;
class PlansSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
DB::table('Plans')->insert([
'name' => 'Starter Pack',
'description' => 'Faucibus massa amet fermentum sodales natoque mauris',
'price' => '9.90',
'capacity' => '200',
'created_at' => Carbon::now(),
'updated_at' => Carbon::now(),
]);
DB::table('Plans')->insert([
'name' => 'Professional Pack',
'description' => 'Fusce morbi a massa ullamcorper inceptos fermentum',
'price' => '19.90',
'capacity' => '500',
'created_at' => Carbon::now(),
'updated_at' => Carbon::now(),
]);
DB::table('Plans')->insert([
'name' => 'Business Pack',
'description' => 'Taciti metus proin sociis aenean facilisis eu',
'price' => '44.90',
'capacity' => '1000',
'created_at' => Carbon::now(),
'updated_at' => Carbon::now(),
]);
}
}

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-printer">
<polyline points="6 9 6 2 18 2 18 9"></polyline>
<path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"></path>
<rect x="6" y="14" width="12" height="8"></rect>
</svg>

After

Width:  |  Height:  |  Size: 428 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 23 KiB

2
public/css/app.css vendored

File diff suppressed because one or more lines are too long

1
public/css/invoice.css vendored Normal file
View File

@@ -0,0 +1 @@
*{outline:0;margin:0;padding:0;font-family:Nunito,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;box-sizing:border-box;-webkit-tap-highlight-color:rgba(0,0,0,0);font-size:16px;text-decoration:none}body{background:#f6f6f6}#toolbar-wrapper{max-width:800px;margin:30px auto}#toolbar-wrapper .button{background:#fff;margin-right:15px;border-radius:8px;padding:7px 10px;cursor:pointer;border:none;transition:all .15s ease}#toolbar-wrapper .button .icon{height:14px;vertical-align:middle}#toolbar-wrapper .button .icon circle,#toolbar-wrapper .button .icon line,#toolbar-wrapper .button .icon path,#toolbar-wrapper .button .icon polyline,#toolbar-wrapper .button .icon rect{transition:all .15s ease}#toolbar-wrapper .button .label{transition:all .15s ease;font-size:.875em;font-weight:700;color:#1c1d1f}#toolbar-wrapper .button:active{transform:scale(.95)}#toolbar-wrapper .button:hover{background:rgba(0,188,126,.1)}#toolbar-wrapper .button:hover .icon circle,#toolbar-wrapper .button:hover .icon line,#toolbar-wrapper .button:hover .icon path,#toolbar-wrapper .button:hover .icon polyline,#toolbar-wrapper .button:hover .icon rect{stroke:#00bc7e}#toolbar-wrapper .button:hover .label{color:#00bc7e}#invoice-wrapper{max-width:800px;padding:50px;margin:0 auto;background:#fff;border-radius:8px}.table{width:100%}.table .table-header{border-bottom:1px solid #f8f8f8}.table .table-header td{padding-bottom:10px;font-size:.75em;font-weight:700;color:#00bc7e}.table .table-header td:last-child{text-align:right}.table .table-body td{font-size:1em;font-weight:700}.table .table-body td:last-child{text-align:right}.list{margin-bottom:20px}.list .list-item{display:block;margin-bottom:5px}.list .list-item b{font-size:1em;font-weight:700}.list .list-item span{font-size:1em;font-weight:500}.invoice-header{display:flex;justify-content:space-between;align-items:center}.invoice-header .logo img{height:40px;width:auto}.invoice-header .title h1{font-size:1.875em;font-weight:300;color:#00bc7e}.invoice-subject{margin-top:40px;background:#fafafa;padding:15px 20px;border-radius:10px}.invoice-subject .list{margin-bottom:0}.invoice-partners{display:flex;justify-content:space-between;margin-top:40px}.invoice-partners .partner:last-child{min-width:250px}.invoice-partners .partner-title{color:#00bc7e;font-size:1.375em;font-weight:300;margin-bottom:10px}.invoice-order{margin-top:40px}.invoice-summary{margin-top:60px;text-align:right}.invoice-summary b{font-weight:700;font-size:1.375em}@media print{#toolbar-wrapper{display:none}#invoice-wrapper{padding:0;margin:0}.invoice-subject{padding:0}@page{margin:0}body{margin:1.6cm}}

2
public/js/main.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -1,47 +1,10 @@
{
"/js/main.js": "/js/main.js",
"/css/app.css": "/css/app.css",
"/js/main.a501882e1824ddb9d44c.hot-update.js": "/js/main.a501882e1824ddb9d44c.hot-update.js",
"/js/main.23d5523fe23375a88651.hot-update.js": "/js/main.23d5523fe23375a88651.hot-update.js",
"/js/main.00c3f4d09159cfd1a99e.hot-update.js": "/js/main.00c3f4d09159cfd1a99e.hot-update.js",
"/js/main.cc7a72c7a3ebde670abe.hot-update.js": "/js/main.cc7a72c7a3ebde670abe.hot-update.js",
"/js/main.112e117babc9a8bd4993.hot-update.js": "/js/main.112e117babc9a8bd4993.hot-update.js",
"/js/main.e786ee001157ac675460.hot-update.js": "/js/main.e786ee001157ac675460.hot-update.js",
"/js/main.6dff06d6eb3e7c04b692.hot-update.js": "/js/main.6dff06d6eb3e7c04b692.hot-update.js",
"/js/main.5258b1b99b6a9e67182d.hot-update.js": "/js/main.5258b1b99b6a9e67182d.hot-update.js",
"/js/main.5cd86eeb5dfa909343a3.hot-update.js": "/js/main.5cd86eeb5dfa909343a3.hot-update.js",
"/js/main.7f2dd3aad59432926740.hot-update.js": "/js/main.7f2dd3aad59432926740.hot-update.js",
"/js/main.4192c6d91f1c3993c8f1.hot-update.js": "/js/main.4192c6d91f1c3993c8f1.hot-update.js",
"/js/main.fbd9f9e95eae83a1f42e.hot-update.js": "/js/main.fbd9f9e95eae83a1f42e.hot-update.js",
"/js/main.88e77f7efb28af45a444.hot-update.js": "/js/main.88e77f7efb28af45a444.hot-update.js",
"/js/main.c4a876e3474204aa1718.hot-update.js": "/js/main.c4a876e3474204aa1718.hot-update.js",
"/js/main.6e9ce229ff233a01ca26.hot-update.js": "/js/main.6e9ce229ff233a01ca26.hot-update.js",
"/js/main.0723f3fb2dd2f5541629.hot-update.js": "/js/main.0723f3fb2dd2f5541629.hot-update.js",
"/js/main.af706670bbde4b96968d.hot-update.js": "/js/main.af706670bbde4b96968d.hot-update.js",
"/js/main.efdabbf9dec9f8faa560.hot-update.js": "/js/main.efdabbf9dec9f8faa560.hot-update.js",
"/js/main.9102b6202dd1560ad523.hot-update.js": "/js/main.9102b6202dd1560ad523.hot-update.js",
"/js/main.45200406e1e76097118c.hot-update.js": "/js/main.45200406e1e76097118c.hot-update.js",
"/js/main.048512ed38cfb395c00f.hot-update.js": "/js/main.048512ed38cfb395c00f.hot-update.js",
"/js/main.cb4787a2577affeea53b.hot-update.js": "/js/main.cb4787a2577affeea53b.hot-update.js",
"/js/main.7db0eb421c5a4f520278.hot-update.js": "/js/main.7db0eb421c5a4f520278.hot-update.js",
"/js/main.633408d2cac028d44bf9.hot-update.js": "/js/main.633408d2cac028d44bf9.hot-update.js",
"/js/main.fd6456b8cc0995ffcfd0.hot-update.js": "/js/main.fd6456b8cc0995ffcfd0.hot-update.js",
"/js/main.c95f3222787de9f3fc40.hot-update.js": "/js/main.c95f3222787de9f3fc40.hot-update.js",
"/js/main.1fc1222d368a08d29f72.hot-update.js": "/js/main.1fc1222d368a08d29f72.hot-update.js",
"/js/main.f82e1b2a21f5952f6094.hot-update.js": "/js/main.f82e1b2a21f5952f6094.hot-update.js",
"/js/main.c8f3130cd90d5f861582.hot-update.js": "/js/main.c8f3130cd90d5f861582.hot-update.js",
"/js/main.a678e1ea71adf201cc34.hot-update.js": "/js/main.a678e1ea71adf201cc34.hot-update.js",
"/js/main.1d9c19ea30c8579c656e.hot-update.js": "/js/main.1d9c19ea30c8579c656e.hot-update.js",
"/js/main.aaec1e4a4093928ec963.hot-update.js": "/js/main.aaec1e4a4093928ec963.hot-update.js",
"/js/main.63f3de6f8a15e43d13e3.hot-update.js": "/js/main.63f3de6f8a15e43d13e3.hot-update.js",
"/js/main.9af22655a917576ba617.hot-update.js": "/js/main.9af22655a917576ba617.hot-update.js",
"/js/main.4693e155bfb0b0cd5b0b.hot-update.js": "/js/main.4693e155bfb0b0cd5b0b.hot-update.js",
"/js/main.b193112a7666b6d18c52.hot-update.js": "/js/main.b193112a7666b6d18c52.hot-update.js",
"/js/main.ce3a02f5cca00363fce4.hot-update.js": "/js/main.ce3a02f5cca00363fce4.hot-update.js",
"/js/main.2523317251b311d6636d.hot-update.js": "/js/main.2523317251b311d6636d.hot-update.js",
"/js/main.5c951cd3429cbcdde665.hot-update.js": "/js/main.5c951cd3429cbcdde665.hot-update.js",
"/js/main.a1786d42feb161e19678.hot-update.js": "/js/main.a1786d42feb161e19678.hot-update.js",
"/js/main.267e7c60f17176c91054.hot-update.js": "/js/main.267e7c60f17176c91054.hot-update.js",
"/js/main.3c8086334fd08b0c9984.hot-update.js": "/js/main.3c8086334fd08b0c9984.hot-update.js",
"/js/main.53660e63381af24743c1.hot-update.js": "/js/main.53660e63381af24743c1.hot-update.js"
"/js/main.a3a51ffe63e5ab8ad17b.hot-update.js": "/js/main.a3a51ffe63e5ab8ad17b.hot-update.js",
"/js/main.255954174c5d7e9a08b3.hot-update.js": "/js/main.255954174c5d7e9a08b3.hot-update.js",
"/js/main.99a451b0fd3dc3ce2f33.hot-update.js": "/js/main.99a451b0fd3dc3ce2f33.hot-update.js",
"/js/main.53d3f8b1c2dd0e07b5b8.hot-update.js": "/js/main.53d3f8b1c2dd0e07b5b8.hot-update.js",
"/js/main.e3be347086dcc417ba57.hot-update.js": "/js/main.e3be347086dcc417ba57.hot-update.js",
"/js/main.85f332c95c7b4be8cd10.hot-update.js": "/js/main.85f332c95c7b4be8cd10.hot-update.js"
}

230
public/sass/invoice.scss vendored Normal file
View File

@@ -0,0 +1,230 @@
// Colors
$text: #1c1d1f;
$text-muted: rgba($text, 0.7);
$light_background: #f6f6f6;
$light_mode_border: #F8F8F8;
$theme: #00BC7E;
@mixin font-size($size) {
font-size:($size/16) + 0em;
}
@mixin transition($time: 0.3s) {
transition: $time all ease;
}
@mixin transform($effect) {
-webkit-transform: $effect;
-moz-transform: $effect;
transform: $effect;
}
* {
outline: 0;
margin: 0;
padding: 0;
font-family: 'Nunito', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
box-sizing: border-box;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
font-size: 16px;
text-decoration: none;
}
body {
background: $light_background;
}
#toolbar-wrapper {
max-width: 800px;
margin: 30px auto;
.button {
background: white;
margin-right: 15px;
border-radius: 8px;
padding: 7px 10px;
cursor: pointer;
border: none;
@include transition(150ms);
.icon {
height: 14px;
vertical-align: middle;
path, line, polyline, rect, circle {
@include transition(150ms);
}
}
.label {
@include transition(150ms);
@include font-size(14);
font-weight: 700;
color: $text;
}
&:active {
@include transform(scale(0.95));
}
&:hover {
background: rgba($theme, 0.1);
.icon {
path, line, polyline, rect, circle {
stroke: $theme;
}
}
.label {
color: $theme;
}
}
}
}
#invoice-wrapper {
max-width: 800px;
padding: 50px;
margin: 0 auto;
background: white;
border-radius: 8px;
}
.table {
width: 100%;
.table-header {
border-bottom: 1px solid $light_mode_border;
td {
padding-bottom: 10px;
@include font-size(12);
font-weight: 700;
color: $theme;
&:last-child {
text-align: right;
}
}
}
.table-body {
td {
@include font-size(16);
font-weight: 700;
&:last-child {
text-align: right;
}
}
}
}
.list {
margin-bottom: 20px;
.list-item {
display: block;
margin-bottom: 5px;
b {
@include font-size(16);
font-weight: 700;
}
span {
@include font-size(16);
font-weight: 500;
}
}
}
.invoice-header {
display: flex;
justify-content: space-between;
align-items: center;
.logo {
img {
height: 40px;
width: auto;
}
}
.title {
h1 {
@include font-size(30);
font-weight: 300;
color: $theme;
}
}
}
.invoice-subject {
margin-top: 40px;
background: hsla(0, 0%, 98%, 1);
padding: 15px 20px;
border-radius: 10px;
.list {
margin-bottom: 0;
}
}
.invoice-partners {
display: flex;
justify-content: space-between;
margin-top: 40px;
.partner:last-child {
min-width: 250px;
}
.partner-title {
color: $theme;
@include font-size(22);
font-weight: 300;
margin-bottom: 10px;
}
}
.invoice-order {
margin-top: 40px;
}
.invoice-summary {
margin-top: 60px;
text-align: right;
b {
font-weight: 700;
@include font-size(22);
}
}
@media print {
#toolbar-wrapper {
display: none;
}
#invoice-wrapper {
padding: 0;
margin: 0;
}
.invoice-subject {
padding: 0;
}
@page {
margin: 0;
}
body {
margin: 1.6cm;
}
}

View File

@@ -9,9 +9,10 @@ import NotFoundShared from './views/Shared/NotFoundShared'
import ForgottenPassword from './views/Auth/ForgottenPassword'
import CreateNewPassword from './views/Auth/CreateNewPassword'
import Settings from './views/Settings'
import Profile from './views/User/Profile'
import Settings from './views/Profile'
import Profile from './views/User/Settings'
import Storage from './views/User/Storage'
import Invoice from './views/User/Invoices'
import Trash from './views/FilePages/Trash'
import Files from './views/FilePages/Files'
import Password from './views/User/Password'
@@ -176,7 +177,7 @@ const routesAdmin = [
},
{
name: 'Gateway',
path: '/admin/payment-method/:name',
path: '/admin/payment-method/:slug',
component: Gateway,
meta: {
requiresAuth: true,
@@ -185,7 +186,7 @@ const routesAdmin = [
children: [
{
name: 'GatewayTransactions',
path: '/admin/payment-methods/:name/transactions',
path: '/admin/payment-methods/:slug/transactions',
component: GatewayTransactions,
meta: {
requiresAuth: true,
@@ -194,7 +195,7 @@ const routesAdmin = [
},
{
name: 'GatewaySettings',
path: '/admin/payment-methods/:name/settings',
path: '/admin/payment-methods/:slug/settings',
component: GatewaySettings,
meta: {
requiresAuth: true,
@@ -349,6 +350,15 @@ const routesUser = [
title: i18n.t('routes_title.settings_storage')
},
},
{
name: 'Invoice',
path: '/settings/invoices',
component: Invoice,
meta: {
requiresAuth: true,
title: 'Invoices'
},
},
]
},
{

View File

@@ -2,29 +2,7 @@
<section id="viewport">
<ContentSidebar>
<!--Locations-->
<ContentGroup :title="$t('admin_menu.admin_label')" class="navigator">
<div class="menu-list-wrapper vertical">
<router-link :to="{name: 'Users'}" class="menu-list-item link">
<div class="icon">
<users-icon size="17"></users-icon>
</div>
<div class="label">
{{ $t('admin_menu.users') }}
</div>
</router-link>
<router-link :to="{name: 'User'}" class="menu-list-item link">
<div class="icon">
<settings-icon size="17"></settings-icon>
</div>
<div class="label">
Settings
</div>
</router-link>
</div>
</ContentGroup>
<!--SaaS-->
<ContentGroup title="SaaS" class="navigator">
<div class="menu-list-wrapper vertical">
<router-link :to="{name: 'Plans'}" class="menu-list-item link">
@@ -53,6 +31,28 @@
</router-link>
</div>
</ContentGroup>
<!--Admin-->
<ContentGroup :title="$t('admin_menu.admin_label')" class="navigator">
<div class="menu-list-wrapper vertical">
<router-link :to="{name: 'Users'}" class="menu-list-item link">
<div class="icon">
<users-icon size="17"></users-icon>
</div>
<div class="label">
{{ $t('admin_menu.users') }}
</div>
</router-link>
<router-link :to="{name: 'User'}" class="menu-list-item link">
<div class="icon">
<settings-icon size="17"></settings-icon>
</div>
<div class="label">
Settings
</div>
</router-link>
</div>
</ContentGroup>
</ContentSidebar>
<router-view/>

View File

@@ -5,39 +5,39 @@
<PageHeader :title="$router.currentRoute.meta.title"/>
<div class="content-page">
<DatatableWrapper :paginator="false" :columns="columns" :data="plans" class="table table-users">
<DatatableWrapper :paginator="false" :columns="columns" :data="gateways" class="table table-users">
<template scope="{ row }">
<tr>
<td style="min-width: 200px;">
<router-link :to="{name: 'GatewaySettings', params: {name: row.attributes.type}}">
<router-link :to="{name: 'GatewaySettings', params: {slug: row.data.attributes.slug}}">
<DatatableCellImage
:image="row.attributes.avatar"
:title="row.attributes.gateway"
:image="row.data.attributes.logo"
:title="row.data.attributes.name"
/>
</router-link>
</td>
<td>
<span class="cell-item">
<SwitchInput
@input="changeStatus($event, row.attributes.type)"
:state="row.attributes.status"
@input="changeStatus($event, row.data.attributes.slug)"
:state="row.data.attributes.status"
class="switch"
/>
</span>
</td>
<td>
<span class="cell-item">
{{ row.attributes.payments_processed }}
{{ row.data.attributes.payments_processed }}
</span>
</td>
<td>
<span class="cell-item">
{{ row.attributes.active_subscribers }}
{{ row.data.attributes.active_subscribers }}
</span>
</td>
<td>
<div class="action-icons">
<router-link :to="{name: 'GatewaySettings', params: {name: row.attributes.type}}">
<router-link :to="{name: 'GatewaySettings', params: {slug: row.data.attributes.slug}}">
<edit-2-icon size="15" class="icon icon-edit"></edit-2-icon>
</router-link>
</div>
@@ -85,52 +85,27 @@
},
data() {
return {
isLoading: false,
plans: [
{
id: '2',
type: 'payment_method',
attributes: {
type: 'paypal',
gateway: 'PayPal',
avatar: '/assets/images/paypal-logo-thumbnail.png',
status: 0,
payments_processed: 234,
active_subscribers: 2920,
}
},
{
id: '1',
type: 'payment_method',
attributes: {
type: 'stripe',
gateway: 'Stripe',
avatar: '/assets/images/stripe-logo-thumbnail.png',
status: 1,
payments_processed: 798,
active_subscribers: 3587,
}
},
],
isLoading: true,
gateways: undefined,
columns: [
{
label: 'Payment Gateway',
field: 'attributes.gateway',
field: 'data.attributes.gateway',
sortable: true
},
{
label: 'Status',
field: 'attributes.status',
field: 'data.attributes.status',
sortable: true
},
{
label: 'Payments Processed',
field: 'attributes.payments_processed',
field: 'data.attributes.payments_processed',
sortable: true
},
{
label: 'Active Subscribers',
field: 'attributes.active_subscribers',
field: 'data.attributes.active_subscribers',
sortable: true
},
{
@@ -143,15 +118,15 @@
},
methods: {
changeStatus(val, type) {
this.$updateText('/gateways/' + type, 'active', val)
this.$updateText('/gateways/' + type, 'status', val)
}
},
created() {
/*axios.get('/api/plans')
axios.get('/api/gateways')
.then(response => {
this.plans = response.data.data
this.gateways = response.data.data
this.isLoading = false
})*/
})
}
}
</script>

View File

@@ -4,22 +4,22 @@
<MobileHeader :title="$router.currentRoute.meta.title"/>
<PageHeader :can-back="true" :title="$router.currentRoute.meta.title"/>
<div class="content-page">
<div class="content-page" v-if="gateway">
<!--User thumbnail-->
<div class="user-thumbnail">
<div class="avatar">
<img :src="gateway.attributes.avatar" :alt="gateway.attributes.gateway">
<img :src="gateway.attributes.logo" :alt="gateway.attributes.name">
</div>
<div class="info">
<b class="name">{{ gateway.attributes.gateway }}</b>
<b class="name">{{ gateway.attributes.name }}</b>
<span class="email">Payment Gateway</span>
</div>
</div>
<!--Page Tab links-->
<div class="menu-list-wrapper horizontal">
<router-link replace :to="{name: 'GatewaySettings', params: {name: gateway.attributes.type}}" class="menu-list-item link">
<router-link replace :to="{name: 'GatewaySettings', params: {name: gateway.attributes.slug}}" class="menu-list-item link">
<div class="icon">
<settings-icon size="17"></settings-icon>
</div>
@@ -28,7 +28,7 @@
</div>
</router-link>
<router-link replace :to="{name: 'GatewayTransactions', params: {name: gateway.attributes.type}}"
<router-link replace :to="{name: 'GatewayTransactions', params: {name: gateway.attributes.slug}}"
class="menu-list-item link">
<div class="icon">
<credit-card-icon size="17"></credit-card-icon>
@@ -75,40 +75,11 @@
}
},
created() {
if (this.$route.params.name === 'paypal') {
this.gateway = {
id: '2',
type: 'payment_method',
attributes: {
type: 'paypal',
gateway: 'PayPal',
avatar: '/assets/images/paypal-logo-thumbnail.png',
status: 0,
payments_processed: 234,
active_subscribers: 2920,
}
}
} else {
this.gateway = {
id: '1',
type: 'payment_method',
attributes: {
type: 'stripe',
gateway: 'Stripe',
avatar: '/assets/images/stripe-logo-thumbnail.png',
status: 1,
payments_processed: 798,
active_subscribers: 3587,
}
}
}
/*axios.get('/api/gateway/' + this.$route.params.name)
axios.get('/api/gateways/' + this.$route.params.slug)
.then(response => {
this.user = response.data.data
this.gateway = response.data.data
this.isLoading = false
})*/
})
}
}
</script>

View File

@@ -1,13 +1,14 @@
<template>
<PageTab>
<ValidationObserver v-if="gateway.attributes.type === 'paypal'" ref="personalInformation" v-slot="{ invalid }" tag="form" class="form block-form">
<ValidationObserver v-if="gateway.attributes.slug === 'paypal'" ref="personalInformation" v-slot="{ invalid }" tag="form" class="form block-form">
<PageTabGroup>
<b class="form-group-label">Settings</b>
<div class="block-wrapper">
<div class="input-wrapper">
<div class="inline-wrapper">
<label class="input-label">Status:</label>
<SwitchInput @input="$updateText('/gateways/paypal', 'status', paypal.status)" v-model="paypal.status" class="switch" :state="paypal.status"/>
<SwitchInput @input="$updateText('/gateways/paypal', 'status', paymentGateway.attributes.status)" v-model="paymentGateway.attributes.status" class="switch" :state="paymentGateway.attributes.status"/>
</div>
<small class="input-help">Status of your payment gateway on website.</small>
</div>
@@ -16,7 +17,7 @@
<div class="input-wrapper">
<div class="inline-wrapper">
<label class="input-label">Sandbox Mode:</label>
<SwitchInput @input="$updateText('/gateways/paypal', 'sandbox', paypal.sandbox)" v-model="paypal.sandbox" class="switch" :state="paypal.sandbox"/>
<SwitchInput @input="$updateText('/gateways/paypal', 'sandbox', paymentGateway.attributes.sandbox)" v-model="paymentGateway.attributes.sandbox" class="switch" :state="paymentGateway.attributes.sandbox"/>
</div>
<small class="input-help">With sandbox mode on, you can test your payment process on you website.</small>
</div>
@@ -30,7 +31,7 @@
<label>Paypal Client ID</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="ClientID" rules="required"
v-slot="{ errors }">
<input @keyup="$updateText('/gateways/paypal', 'client_id', paypal.client_id)" v-model="paypal.client_id" placeholder="Paste PayPal client id here" type="text" />
<input @keyup="$updateText('/gateways/paypal', 'client_id', paymentGateway.attributes.client_id)" v-model="paymentGateway.attributes.client_id" placeholder="Paste PayPal client id here" type="text" />
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
@@ -40,7 +41,7 @@
<label>Paypal Secret</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="PayPalSecret" rules="required"
v-slot="{ errors }">
<input @keyup="$updateText('/gateways/paypal', 'secret', paypal.secret)" v-model="paypal.secret" placeholder="Paste PayPal secret here" type="text" />
<input @keyup="$updateText('/gateways/paypal', 'secret', paymentGateway.attributes.secret)" v-model="paymentGateway.attributes.secret" placeholder="Paste PayPal secret here" type="text" />
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
@@ -50,21 +51,21 @@
<label>Paypal Webhook ID</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="PayPalSecret" rules="required"
v-slot="{ errors }">
<input @keyup="$updateText('/gateways/paypal', 'webhook', paypal.webhook)" v-model="paypal.webhook" placeholder="Paste PayPal webhook here" type="text" />
<input @keyup="$updateText('/gateways/paypal', 'webhook', paymentGateway.attributes.webhook)" v-model="paymentGateway.attributes.webhook" placeholder="Paste PayPal webhook here" type="text" />
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
</PageTabGroup>
</ValidationObserver>
<ValidationObserver v-if="gateway.attributes.type === 'stripe'" ref="personalInformation" v-slot="{ invalid }" tag="form" class="form block-form">
<ValidationObserver v-if="gateway.attributes.slug === 'stripe'" ref="personalInformation" v-slot="{ invalid }" tag="form" class="form block-form">
<PageTabGroup>
<b class="form-group-label">Settings</b>
<div class="block-wrapper">
<div class="input-wrapper">
<div class="inline-wrapper">
<label class="input-label">Status:</label>
<SwitchInput @input="$updateText('/gateways/stripe', 'status', stripe.status)" v-model="stripe.status" class="switch" :state="stripe.status"/>
<SwitchInput @input="$updateText('/gateways/stripe', 'status', paymentGateway.attributes.status)" v-model="paymentGateway.attributes.status" class="switch" :state="paymentGateway.attributes.status"/>
</div>
<small class="input-help">Status of your payment gateway on website.</small>
</div>
@@ -73,7 +74,7 @@
<div class="input-wrapper">
<div class="inline-wrapper">
<label class="input-label">Sandbox Mode:</label>
<SwitchInput @input="$updateText('/gateways/stripe', 'sandbox', stripe.sandbox)" v-model="stripe.sandbox" class="switch" :state="stripe.sandbox"/>
<SwitchInput @input="$updateText('/gateways/stripe', 'sandbox', paymentGateway.attributes.sandbox)" v-model="paymentGateway.attributes.sandbox" class="switch" :state="paymentGateway.attributes.sandbox"/>
</div>
<small class="input-help">With sandbox mode on, you can test your payment process on you website.</small>
</div>
@@ -86,7 +87,7 @@
<div class="block-wrapper">
<label>Stripe Client ID</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="ClientID" rules="required" v-slot="{ errors }">
<input @keyup="$updateText('/gateways/stripe', 'client_id', stripe.client_id)" v-model="stripe.client_id" placeholder="Paste stripe client id here" type="text" />
<input @keyup="$updateText('/gateways/stripe', 'client_id', paymentGateway.attributes.client_id)" v-model="paymentGateway.attributes.client_id" placeholder="Paste stripe client id here" type="text" />
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
@@ -96,7 +97,7 @@
<label>Stripe Secret</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="StripeSecret" rules="required"
v-slot="{ errors }">
<input @keyup="$updateText('/gateways/stripe', 'secret', stripe.secret)" v-model="stripe.secret" placeholder="Paste stripe secret here" type="text" />
<input @keyup="$updateText('/gateways/stripe', 'secret', paymentGateway.attributes.secret)" v-model="paymentGateway.attributes.secret" placeholder="Paste stripe secret here" type="text" />
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
@@ -137,21 +138,12 @@
return {
isLoading: false,
isSendingRequest: false,
paypal: {
status: 1,
sandbox: 0,
client_id: '',
secret: '',
webhook: '',
},
stripe: {
status: 0,
sandbox: 0,
client_id: '',
secret: '',
}
paymentGateway: undefined,
}
},
created() {
this.paymentGateway = this.gateway
}
}
</script>

View File

@@ -8,19 +8,24 @@
<DatatableWrapper :paginator="true" :columns="columns" :data="invoices" class="table">
<template scope="{ row }">
<tr>
<td>
<a :href="'/invoice/' + row.data.attributes.token" target="_blank" class="cell-item">
{{ row.data.attributes.order }}
</a>
</td>
<td>
<span class="cell-item">
${{ row.attributes.total }}
${{ row.data.attributes.total }}
</span>
</td>
<td>
<span class="cell-item">
{{ row.attributes.plan }}
{{ row.data.attributes.bag[0].description }}
</span>
</td>
<td>
<span class="cell-item">
{{ row.attributes.created_at_formatted }}
{{ row.data.attributes.created_at_formatted }}
</span>
</td>
<td>
@@ -34,9 +39,9 @@
</td>
<td>
<div class="action-icons">
<router-link :to="{name: 'UserDelete', params: {id: row.relationships.user.data.id}}">
<download-cloud-icon size="15" class="icon"></download-cloud-icon>
</router-link>
<a :href="'/invoice/' + row.data.attributes.token" target="_blank">
<external-link-icon size="15" class="icon"></external-link-icon>
</a>
</div>
</td>
</tr>
@@ -58,16 +63,16 @@
import MobileHeader from '@/components/Mobile/MobileHeader'
import SectionTitle from '@/components/Others/SectionTitle'
import ButtonBase from '@/components/FilesView/ButtonBase'
import {DownloadCloudIcon} from "vue-feather-icons";
import {ExternalLinkIcon} from "vue-feather-icons";
import PageHeader from '@/components/Others/PageHeader'
import ColorLabel from '@/components/Others/ColorLabel'
import Spinner from '@/components/FilesView/Spinner'
import axios from 'axios'
export default {
name: 'Plans',
name: 'Invoices',
components: {
DownloadCloudIcon,
ExternalLinkIcon,
DatatableCellImage,
MobileActionButton,
DatatableWrapper,
@@ -81,202 +86,14 @@
},
data() {
return {
isLoading: false,
invoices: [
{
id: '1',
type: 'invoices',
attributes: {
total: 9.99,
plan: 'Starter Plan',
created_at: '30. April. 2020',
created_at_formatted: '30. April. 2020',
download: 'https://vuefilemanager.com/',
},
relationships: {
user: {
data: {
id: '1',
type: 'users',
attributes: {
avatar: '/avatars/6osmoXJo-avatar-01.png',
name: 'Jane Doe',
email: 'howdy@hi5ve.digital',
}
}
}
}
},
{
id: '2',
type: 'invoices',
attributes: {
total: 9.99,
plan: 'Starter Plan',
created_at: '30. April. 2020',
created_at_formatted: '30. April. 2020',
download: 'https://vuefilemanager.com/',
},
relationships: {
user: {
data: {
id: '1',
type: 'users',
attributes: {
avatar: '/avatars/dSMRCbwF-69299654_2418248648259454_4545563304688353280_o.jpg',
name: 'Peter Papp',
email: 'peterpapp@makingcg.com',
}
}
}
}
},
{
id: '3',
type: 'invoices',
attributes: {
total: 49.99,
plan: 'Business Plan',
created_at: '31. April. 2020',
created_at_formatted: '31. April. 2020',
download: 'https://vuefilemanager.com/',
},
relationships: {
user: {
data: {
id: '1',
type: 'users',
attributes: {
avatar: '/assets/images/default-avatar.png',
name: 'Pavel Svintsitskiy',
email: 'pashaUSA@gmail.com',
}
}
}
}
},
{
id: '4',
type: 'invoices',
attributes: {
total: 29.99,
plan: 'Professional Plan',
created_at: '31. April. 2020',
created_at_formatted: '31. April. 2020',
download: 'https://vuefilemanager.com/',
},
relationships: {
user: {
data: {
id: '1',
type: 'users',
attributes: {
avatar: '/avatars/lTksMdJM-6D3529EF-5D8C-4959-BEC2-4BDE80A051C2.jpeg',
name: 'Torsten',
email: 'torsten.hoegel@go-on-net.de',
}
}
}
}
},
{
id: '5',
type: 'invoices',
attributes: {
total: 9.99,
plan: 'Starter Plan',
created_at: '30. April. 2020',
created_at_formatted: '30. April. 2020',
download: 'https://vuefilemanager.com/',
},
relationships: {
user: {
data: {
id: '1',
type: 'users',
attributes: {
avatar: '/avatars/6osmoXJo-avatar-01.png',
name: 'Jane Doe',
email: 'howdy@hi5ve.digital',
}
}
}
}
},
{
id: '6',
type: 'invoices',
attributes: {
total: 9.99,
plan: 'Starter Plan',
created_at: '30. April. 2020',
created_at_formatted: '30. April. 2020',
download: 'https://vuefilemanager.com/',
},
relationships: {
user: {
data: {
id: '1',
type: 'users',
attributes: {
avatar: '/avatars/dSMRCbwF-69299654_2418248648259454_4545563304688353280_o.jpg',
name: 'Peter Papp',
email: 'peterpapp@makingcg.com',
}
}
}
}
},
{
id: '7',
type: 'invoices',
attributes: {
total: 49.99,
plan: 'Business Plan',
created_at: '31. April. 2020',
created_at_formatted: '31. April. 2020',
download: 'https://vuefilemanager.com/',
},
relationships: {
user: {
data: {
id: '1',
type: 'users',
attributes: {
avatar: '/assets/images/default-avatar.png',
name: 'Pavel Svintsitskiy',
email: 'pashaUSA@gmail.com',
}
}
}
}
},
{
id: '8',
type: 'invoices',
attributes: {
total: 29.99,
plan: 'Professional Plan',
created_at: '31. April. 2020',
created_at_formatted: '31. April. 2020',
download: 'https://vuefilemanager.com/',
},
relationships: {
user: {
data: {
id: '1',
type: 'users',
attributes: {
avatar: '/avatars/lTksMdJM-6D3529EF-5D8C-4959-BEC2-4BDE80A051C2.jpeg',
name: 'Torsten',
email: 'torsten.hoegel@go-on-net.de',
}
}
}
}
},
],
isLoading: true,
invoices: undefined,
columns: [
{
label: 'Invoice Number',
field: 'attributes.total',
sortable: true
},
{
label: 'Total',
field: 'attributes.total',
@@ -306,11 +123,11 @@
}
},
created() {
/*axios.get('/api/invoices')
axios.get('/api/invoices')
.then(response => {
this.invoices = response.data.data
this.isLoading = false
})*/
})
}
}
</script>

View File

@@ -21,36 +21,36 @@
<template scope="{ row }">
<tr>
<td class="name" style="min-width: 200px">
<router-link :to="{name: 'PlanSettings', params: {id: row.id}}" class="cell-item" tag="div">
<span>{{ row.attributes.name }}</span>
<router-link :to="{name: 'PlanSettings', params: {id: row.data.id}}" class="cell-item" tag="div">
<span>{{ row.data.attributes.name }}</span>
</router-link>
</td>
<td>
<span class="cell-item">
<SwitchInput @input="changeStatus($event, row.id)" class="switch" :state="row.attributes.status"/>
<SwitchInput @input="changeStatus($event, row.data.id)" class="switch" :state="row.data.attributes.status"/>
</span>
</td>
<td>
<span class="cell-item">
${{ row.attributes.price }}
${{ row.data.attributes.price }}
</span>
</td>
<td>
<span class="cell-item">
{{ row.attributes.capacity }}
{{ row.data.attributes.capacity_formatted }}
</span>
</td>
<td>
<span class="cell-item">
{{ row.attributes.subscribers }}
{{ row.data.attributes.subscribers }}
</span>
</td>
<td>
<div class="action-icons">
<router-link :to="{name: 'PlanSettings', params: {id: row.id}}">
<router-link :to="{name: 'PlanSettings', params: {id: row.data.id}}">
<edit-2-icon size="15" class="icon icon-edit"></edit-2-icon>
</router-link>
<router-link :to="{name: 'PlanDelete', params: {id: row.id}}">
<router-link :to="{name: 'PlanDelete', params: {id: row.data.id}}">
<trash2-icon size="15" class="icon icon-trash"></trash2-icon>
</router-link>
</div>
@@ -96,42 +96,8 @@
},
data() {
return {
isLoading: false,
plans: [
{
id: '1',
type: 'plans',
attributes: {
name: 'Starter Plan',
status: 1,
price: 9.99,
capacity: '200GB',
subscribers: 172,
}
},
{
id: '2',
type: 'plans',
attributes: {
name: 'Professional Plan',
status: 0,
price: 19.99,
capacity: '500GB',
subscribers: 1929,
}
},
{
id: '3',
type: 'plans',
attributes: {
name: 'Business Plan',
status: 1,
price: 44.99,
capacity: '1TB',
subscribers: 389,
}
},
],
isLoading: true,
plans: undefined,
columns: [
{
label: 'Plan',
@@ -172,11 +138,11 @@
}
},
created() {
/*axios.get('/api/plans')
axios.get('/api/plans')
.then(response => {
this.plans = response.data.data
this.isLoading = false
})*/
})
}
}
</script>

View File

@@ -71,30 +71,16 @@
},
data() {
return {
isLoading: false,
isLoading: true,
plan: undefined,
}
},
created() {
this.plan = {
id: '1',
type: 'plans',
attributes: {
name: 'Starter Plan',
description: 'This plan fits for every storage starter.',
status: 1,
price: 9.99,
capacity: 200,
subscribers: 172,
}
}
/*axios.get('/api/gateway/' + this.$route.params.name)
axios.get('/api/plans/' + this.$route.params.id)
.then(response => {
this.user = response.data.data
this.plan = response.data.data
this.isLoading = false
})*/
})
}
}
</script>

View File

@@ -111,7 +111,9 @@
// Send request to get user token
axios
.post('/api/plans/create', this.plan)
.post('/api/plans/store', {
attributes: this.plan
})
.then(response => {
// End loading
@@ -124,7 +126,7 @@
})
// Go to User page
this.$router.push({name: 'UserDetail', params: {id: response.data.data.id}})
this.$router.push({name: 'PlanSettings', params: {id: response.data.data.id}})
})
.catch(error => {

View File

@@ -91,7 +91,7 @@
},
methods: {
changeStatus(val) {
this.$updateText('/plans/' + this.$route.params.id + '/update', 'state', val)
this.$updateText('/plans/' + this.$route.params.id + '/update', 'status', val)
}
}
}

View File

@@ -36,12 +36,12 @@
</td>
<td>
<span class="cell-item">
{{ row.data.attributes.storage.used }}%
{{ row.relationships.storage.data.attributes.used }}%
</span>
</td>
<td>
<span class="cell-item">
{{ row.data.attributes.storage.capacity_formatted }}
{{ row.relationships.storage.data.attributes.capacity_formatted }}
</span>
</td>
<td>

View File

@@ -9,16 +9,16 @@
<!--User thumbnail-->
<div class="user-thumbnail">
<div class="avatar">
<img :src="user.attributes.avatar" :alt="user.attributes.name">
<img :src="user.data.attributes.avatar" :alt="user.data.attributes.name">
</div>
<div class="info">
<b class="name">
{{ user.attributes.name }}
{{ user.data.attributes.name }}
<ColorLabel color="purple">
{{ user.attributes.role }}
{{ user.data.attributes.role }}
</ColorLabel>
</b>
<span class="email">{{ user.attributes.email }}</span>
<span class="email">{{ user.data.attributes.email }}</span>
</div>
</div>
@@ -60,7 +60,7 @@
</div>
</router-link>
<router-link replace :to="{name: 'UserDelete'}" v-if="user.attributes.name !== app.user.name"
<router-link replace :to="{name: 'UserDelete'}" v-if="user.data.attributes.name !== app.user.name"
class="menu-list-item link">
<div class="icon">
<trash2-icon size="17"></trash2-icon>
@@ -120,7 +120,7 @@
fetchUser() {
axios.get('/api/users/' + this.$route.params.id + '/detail')
.then(response => {
this.user = response.data.data
this.user = response.data
this.isLoading = false
})
}

View File

@@ -8,12 +8,16 @@
:title="$t('user_box_role.title')"
:description="$t('user_box_role.description')"
>
<ValidationObserver ref="changeRole" @submit.prevent="changeRole" v-slot="{ invalid }" tag="form" class="form block-form">
<ValidationProvider tag="div" class="block-wrapper" v-slot="{ errors }" mode="passive" name="Role" rules="required">
<ValidationObserver ref="changeRole" @submit.prevent="changeRole" v-slot="{ invalid }" tag="form"
class="form block-form">
<ValidationProvider tag="div" class="block-wrapper" v-slot="{ errors }" mode="passive" name="Role"
rules="required">
<label>{{ $t('admin_page_user.select_role') }}:</label>
<div class="single-line-form">
<SelectInput v-model="userRole" :options="roles" :placeholder="$t('admin_page_user.select_role')" :isError="errors[0]"/>
<ButtonBase :loading="isSendingRequest" :disabled="isSendingRequest" type="submit" button-style="theme" class="submit-button">
<SelectInput v-model="userRole" :options="roles"
:placeholder="$t('admin_page_user.select_role')" :isError="errors[0]"/>
<ButtonBase :loading="isSendingRequest" :disabled="isSendingRequest" type="submit"
button-style="theme" class="submit-button">
{{ $t('admin_page_user.save_role') }}
</ButtonBase>
</div>
@@ -25,8 +29,7 @@
<!--Personal Information-->
<PageTabGroup>
<ValidationObserver ref="personalInformation" v-slot="{ invalid }" tag="form" class="form block-form">
<div class="form block-form">
<b class="form-group-label">{{ $t('admin_page_user.label_person_info') }}</b>
<div class="wrapper-inline">
@@ -34,25 +37,99 @@
<div class="block-wrapper">
<label>{{ $t('page_registration.label_email') }}</label>
<div class="input-wrapper">
<input :value="user.attributes.email" :placeholder="$t('page_registration.placeholder_email')" type="email" disabled />
<input :value="user.data.attributes.email"
:placeholder="$t('page_registration.placeholder_email')"
type="email"
disabled
/>
</div>
</div>
<!--Name-->
<div class="block-wrapper">
<label>{{ $t('page_registration.label_name') }}</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="Full Name" rules="required"
v-slot="{ errors }">
<input :value="user.attributes.name"
<div class="input-wrapper">
<input :value="user.data.attributes.name"
:placeholder="$t('page_registration.placeholder_name')"
type="text"
disabled
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
</div>
</div>
</ValidationObserver>
</div>
</PageTabGroup>
<!--Billing Information-->
<PageTabGroup>
<div class="form block-form">
<b class="form-group-label">Billing Information</b>
<div class="block-wrapper">
<label>Name:</label>
<div class="input-wrapper">
<input :value="user.relationships.settings.data.attributes.billing_name"
type="text"
disabled
/>
</div>
</div>
<div class="block-wrapper">
<label>Address:</label>
<div class="input-wrapper">
<input :value="user.relationships.settings.data.attributes.billing_address"
type="text"
disabled
/>
</div>
</div>
<div class="wrapper-inline">
<div class="block-wrapper">
<label>State:</label>
<div class="input-wrapper">
<input :value="user.relationships.settings.data.attributes.billing_state"
type="text"
disabled
/>
</div>
</div>
<div class="block-wrapper">
<label>City:</label>
<div class="input-wrapper">
<input :value="user.relationships.settings.data.attributes.billing_city"
type="text"
disabled
/>
</div>
</div>
<div class="block-wrapper">
<label>Postal Code:</label>
<div class="input-wrapper">
<input :value="user.relationships.settings.data.attributes.billing_postal_code"
type="text"
disabled
/>
</div>
</div>
</div>
<div class="block-wrapper">
<label>Country:</label>
<div class="input-wrapper">
<input :value="user.relationships.settings.data.attributes.billing_country"
type="text"
disabled
/>
</div>
</div>
<div class="block-wrapper">
<label>Phone Number:</label>
<div class="input-wrapper">
<input :value="user.relationships.settings.data.attributes.billing_phone_number"
type="text"
disabled
/>
</div>
</div>
</div>
</PageTabGroup>
</PageTab>
</template>
@@ -66,7 +143,7 @@
import ButtonBase from '@/components/FilesView/ButtonBase'
import SetupBox from '@/components/Others/Forms/SetupBox'
import {required} from 'vee-validate/dist/rules'
import { mapGetters } from 'vuex'
import {mapGetters} from 'vuex'
import {events} from "@/bus"
import axios from 'axios'
@@ -150,7 +227,6 @@
max-width: 100%;
}
@media only screen and (max-width: 960px) {
}

View File

@@ -1,27 +1,34 @@
<template>
<PageTab>
<PageTab v-if="invoices">
<PageTabGroup>
<DatatableWrapper :paginator="true" :columns="columns" :data="invoices" class="table">
<template scope="{ row }">
<tr>
<td>
<a :href="'/invoice/' + row.data.attributes.token" target="_blank" class="cell-item">
{{ row.data.attributes.order }}
</a>
</td>
<td>
<span class="cell-item">
${{ row.attributes.total }}
${{ row.data.attributes.total }}
</span>
</td>
<td>
<span class="cell-item">
{{ row.attributes.plan }}
{{ row.data.attributes.bag[0].description }}
</span>
</td>
<td>
<span class="cell-item">
{{ row.attributes.created_at_formatted }}
{{ row.data.attributes.created_at_formatted }}
</span>
</td>
<td>
<div class="action-icons">
<download-cloud-icon size="15" class="icon"></download-cloud-icon>
<a :href="'/invoice/' + row.data.attributes.token" target="_blank">
<external-link-icon size="15" class="icon"></external-link-icon>
</a>
</div>
</td>
</tr>
@@ -35,124 +42,40 @@
import PageTabGroup from '@/components/Others/Layout/PageTabGroup'
import PageTab from '@/components/Others/Layout/PageTab'
import DatatableWrapper from '@/components/Others/Tables/DatatableWrapper'
import {DownloadCloudIcon} from "vue-feather-icons";
import {ExternalLinkIcon} from "vue-feather-icons";
import axios from 'axios'
export default {
name: 'UserStorage',
name: 'UserInvoices',
components: {
PageTabGroup,
PageTab,
DatatableWrapper,
DownloadCloudIcon,
ExternalLinkIcon,
},
data() {
return {
isLoading: false,
invoices: [
{
id: '1',
type: 'invoices',
attributes: {
total: 9.99,
plan: 'Starter Plan',
created_at: '30. April. 2020',
created_at_formatted: '30. April. 2020',
download: 'https://vuefilemanager.com/',
},
},
{
id: '2',
type: 'invoices',
attributes: {
total: 9.99,
plan: 'Starter Plan',
created_at: '30. April. 2020',
created_at_formatted: '30. April. 2020',
download: 'https://vuefilemanager.com/',
},
},
{
id: '3',
type: 'invoices',
attributes: {
total: 49.99,
plan: 'Business Plan',
created_at: '31. April. 2020',
created_at_formatted: '31. April. 2020',
download: 'https://vuefilemanager.com/',
},
},
{
id: '4',
type: 'invoices',
attributes: {
total: 29.99,
plan: 'Professional Plan',
created_at: '31. April. 2020',
created_at_formatted: '31. April. 2020',
download: 'https://vuefilemanager.com/',
},
},
{
id: '5',
type: 'invoices',
attributes: {
total: 9.99,
plan: 'Starter Plan',
created_at: '30. April. 2020',
created_at_formatted: '30. April. 2020',
download: 'https://vuefilemanager.com/',
},
},
{
id: '6',
type: 'invoices',
attributes: {
total: 9.99,
plan: 'Starter Plan',
created_at: '30. April. 2020',
created_at_formatted: '30. April. 2020',
download: 'https://vuefilemanager.com/',
},
},
{
id: '7',
type: 'invoices',
attributes: {
total: 49.99,
plan: 'Business Plan',
created_at: '31. April. 2020',
created_at_formatted: '31. April. 2020',
download: 'https://vuefilemanager.com/',
},
},
{
id: '8',
type: 'invoices',
attributes: {
total: 29.99,
plan: 'Professional Plan',
created_at: '31. April. 2020',
created_at_formatted: '31. April. 2020',
download: 'https://vuefilemanager.com/',
},
},
],
isLoading: true,
invoices: undefined,
columns: [
{
label: 'Invoice Number',
field: 'data.attributes.total',
sortable: true
},
{
label: 'Total',
field: 'attributes.total',
field: 'data.attributes.total',
sortable: true
},
{
label: 'Plan',
field: 'attributes.plan',
field: 'data.attributes.plan',
sortable: true
},
{
label: 'Payed',
field: 'attributes.created_at',
field: 'data.attributes.created_at',
sortable: true
},
{
@@ -164,11 +87,11 @@
}
},
created() {
/*axios.get('/api/users/' + this.$route.params.id + '/storage')
axios.get('/api/users/' + this.$route.params.id + '/invoices')
.then(response => {
this.storage = response.data.data
this.invoices = response.data.data
this.isLoading = false
})*/
})
}
}
</script>

View File

@@ -9,13 +9,13 @@
<div class="content-page">
<nav class="mobile-navigation">
<!--Admin menu-->
<b class="mobile-menu-label">Admin</b>
<MenuItemList :navigation="AdminNavigation" />
<!--SaaS menu-->
<b class="mobile-menu-label">SaaS</b>
<MenuItemList :navigation="SassNavigation" />
<!--Admin menu-->
<b class="mobile-menu-label">Admin</b>
<MenuItemList :navigation="AdminNavigation" />
</nav>
</div>
</div>

View File

@@ -1,5 +1,5 @@
<template>
<div id="single-page" v-if="app">
<div id="single-page">
<div id="page-content" class="medium-width" v-if="! isLoading">
<MobileHeader :title="$router.currentRoute.meta.title"/>
<PageHeader :title="$router.currentRoute.meta.title"/>
@@ -11,12 +11,12 @@
<div class="avatar">
<UserImageInput
v-model="avatar"
:avatar="app.user.avatar"
:avatar="profile.data.attributes.avatar"
/>
</div>
<div class="info">
<b class="name">{{ app.user.name }}</b>
<span class="email">{{ app.user.email }}</span>
<b class="name">{{ profile.data.attributes.name }}</b>
<span class="email">{{ profile.data.attributes.email }}</span>
</div>
</div>
@@ -40,6 +40,15 @@
</div>
</router-link>
<router-link replace :to="{name: 'Invoice'}" class="menu-list-item link">
<div class="icon">
<file-text-icon size="17"></file-text-icon>
</div>
<div class="label">
Invoices
</div>
</router-link>
<router-link replace :to="{name: 'Password'}" class="menu-list-item link">
<div class="icon">
<lock-icon size="17"></lock-icon>
@@ -60,7 +69,7 @@
</div>
<!--Router Content-->
<router-view :user="app.user" />
<router-view :user="profile" />
</div>
</div>
<div id="loader" v-if="isLoading">
@@ -74,10 +83,11 @@
import MobileHeader from '@/components/Mobile/MobileHeader'
import PageHeader from '@/components/Others/PageHeader'
import Spinner from '@/components/FilesView/Spinner'
import axios from 'axios'
import { mapGetters } from 'vuex'
import {
HardDriveIcon,
FileTextIcon,
UserIcon,
LockIcon,
} from 'vue-feather-icons'
@@ -86,6 +96,7 @@
name: 'Settings',
components: {
UserImageInput,
FileTextIcon,
MobileHeader,
PageHeader,
Spinner,
@@ -93,17 +104,20 @@
UserIcon,
LockIcon,
},
computed: {
...mapGetters([
'config', 'app'
]),
},
data() {
return {
avatar: undefined,
isLoading: false,
profile: undefined,
isLoading: true,
}
},
created() {
axios.get('/api/profile')
.then(response => {
this.profile = response.data
this.isLoading = false
})
}
}
</script>

View File

@@ -0,0 +1,117 @@
<template>
<PageTab v-if="invoices">
<PageTabGroup>
<DatatableWrapper :paginator="true" :columns="columns" :data="invoices" class="table">
<template scope="{ row }">
<tr>
<td>
<a :href="'/invoice/' + row.data.attributes.token" target="_blank" class="cell-item">
{{ row.data.attributes.order }}
</a>
</td>
<td>
<span class="cell-item">
${{ row.data.attributes.total }}
</span>
</td>
<td>
<span class="cell-item">
{{ row.data.attributes.bag[0].description }}
</span>
</td>
<td>
<span class="cell-item">
{{ row.data.attributes.created_at_formatted }}
</span>
</td>
<td>
<div class="action-icons">
<a :href="'/invoice/' + row.data.attributes.token" target="_blank">
<external-link-icon size="15" class="icon"></external-link-icon>
</a>
</div>
</td>
</tr>
</template>
</DatatableWrapper>
</PageTabGroup>
</PageTab>
</template>
<script>
import PageTabGroup from '@/components/Others/Layout/PageTabGroup'
import PageTab from '@/components/Others/Layout/PageTab'
import DatatableWrapper from '@/components/Others/Tables/DatatableWrapper'
import {ExternalLinkIcon} from "vue-feather-icons";
import axios from 'axios'
export default {
name: 'UserInvoices',
components: {
PageTabGroup,
PageTab,
DatatableWrapper,
ExternalLinkIcon,
},
data() {
return {
isLoading: true,
invoices: undefined,
columns: [
{
label: 'Invoice Number',
field: 'data.attributes.total',
sortable: true
},
{
label: 'Total',
field: 'data.attributes.total',
sortable: true
},
{
label: 'Plan',
field: 'data.attributes.plan',
sortable: true
},
{
label: 'Payed',
field: 'data.attributes.created_at',
sortable: true
},
{
label: this.$t('admin_page_user.table.action'),
field: 'data.action',
sortable: false
},
],
}
},
created() {
axios.get('/api/user/invoices')
.then(response => {
this.invoices = response.data.data
this.isLoading = false
})
}
}
</script>
<style lang="scss" scoped>
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
@import '@assets/vue-file-manager/_forms';
.block-form {
max-width: 100%;
}
@media only screen and (max-width: 960px) {
}
@media (prefers-color-scheme: dark) {
}
</style>

View File

@@ -1,93 +0,0 @@
<template>
<div class="page-tab">
<div class="page-tab-group">
<ValidationObserver ref="account" v-slot="{ invalid }" tag="form" class="form block-form">
<div class="block-wrapper">
<label>{{ $t('page_registration.label_email') }}</label>
<div class="input-wrapper">
<input :value="app.user.email" :placeholder="$t('page_registration.placeholder_email')"
type="email" disabled/>
</div>
</div>
<div class="block-wrapper">
<label>{{ $t('page_registration.label_name') }}</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="Full Name" rules="required"
v-slot="{ errors }">
<input @keyup="$updateText('/user/profile', 'name', name)" v-model="name"
:placeholder="$t('page_registration.placeholder_name')" type="text"
:class="{'is-error': errors[0]}"/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
</ValidationObserver>
</div>
</div>
</template>
<script>
import {ValidationProvider, ValidationObserver} from 'vee-validate/dist/vee-validate.full'
import MobileHeader from '@/components/Mobile/MobileHeader'
import ButtonBase from '@/components/FilesView/ButtonBase'
import PageHeader from '@/components/Others/PageHeader'
import ThemeLabel from '@/components/Others/ThemeLabel'
import {required} from 'vee-validate/dist/rules'
import {mapGetters} from 'vuex'
import {debounce} from 'lodash'
export default {
name: 'Profile',
components: {
ValidationProvider,
ValidationObserver,
MobileHeader,
PageHeader,
ButtonBase,
ThemeLabel,
required,
},
computed: {
...mapGetters(['app']),
},
watch: {
name: debounce(function (val) {
if (val === '') return
this.$store.commit('UPDATE_NAME', val)
}, 300),
},
data() {
return {
name: '',
isLoading: false,
}
},
created() {
if (this.app) {
this.name = this.app.user.name
this.avatar = this.app.user.avatar
}
}
}
</script>
<style lang="scss" scoped>
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
@import '@assets/vue-file-manager/_forms';
@media only screen and (max-width: 960px) {
.form {
.button-base {
width: 100%;
margin-top: 0;
text-align: center;
}
}
}
@media (prefers-color-scheme: dark) {
}
</style>

View File

@@ -0,0 +1,195 @@
<template>
<PageTab>
<PageTabGroup v-if="userInfo">
<div class="form block-form">
<b class="form-group-label">{{ $t('admin_page_user.label_person_info') }}</b>
<div class="wrapper-inline">
<div class="block-wrapper">
<label>{{ $t('page_registration.label_email') }}</label>
<div class="input-wrapper">
<input :value="userInfo.email"
:placeholder="$t('page_registration.placeholder_email')"
type="email"
disabled
/>
</div>
</div>
<div class="block-wrapper">
<label>{{ $t('page_registration.label_name') }}</label>
<div class="input-wrapper">
<input @keyup="changeUserName"
v-model="userInfo.name"
:placeholder="$t('page_registration.placeholder_name')"
type="text"
/>
</div>
</div>
</div>
</div>
</PageTabGroup>
<PageTabGroup v-if="billingInfo">
<div class="form block-form">
<b class="form-group-label">Billing Information</b>
<div class="block-wrapper">
<label>Name:</label>
<div class="input-wrapper">
<input @keyup="$updateText('/user/relationships/settings', 'billing_name', billingInfo.billing_name)"
v-model="billingInfo.billing_name"
placeholder="Type your billing name"
type="text"
/>
</div>
</div>
<div class="block-wrapper">
<label>Address:</label>
<div class="input-wrapper">
<input @keyup="$updateText('/user/relationships/settings', 'billing_address', billingInfo.billing_address)"
v-model="billingInfo.billing_address"
placeholder="Type your billing address"
type="text"
/>
</div>
</div>
<div class="wrapper-inline">
<div class="block-wrapper">
<label>State:</label>
<div class="input-wrapper">
<input @keyup="$updateText('/user/relationships/settings', 'billing_state', billingInfo.billing_state)"
v-model="billingInfo.billing_state"
placeholder="Type your billing state"
type="text"
/>
</div>
</div>
<div class="block-wrapper">
<label>City:</label>
<div class="input-wrapper">
<input @keyup="$updateText('/user/relationships/settings', 'billing_city', billingInfo.billing_city)"
v-model="billingInfo.billing_city"
placeholder="Type your billing city"
type="text"
/>
</div>
</div>
<div class="block-wrapper">
<label>Postal Code:</label>
<div class="input-wrapper">
<input @keyup="$updateText('/user/relationships/settings', 'billing_postal_code', billingInfo.billing_postal_code)"
v-model="billingInfo.billing_postal_code"
placeholder="Type your billing postal code"
type="text"
/>
</div>
</div>
</div>
<div class="block-wrapper">
<label>Country:</label>
<div class="input-wrapper">
<input @keyup="$updateText('/user/relationships/settings', 'billing_country', billingInfo.billing_country)"
v-model="billingInfo.billing_country"
placeholder="Type your billing country"
type="text"
/>
</div>
</div>
<div class="block-wrapper">
<label>Phone Number:</label>
<div class="input-wrapper">
<input @keyup="$updateText('/user/relationships/settings', 'billing_phone_number', billingInfo.billing_phone_number)"
v-model="billingInfo.billing_phone_number"
placeholder="Type your billing phone number"
type="text"
/>
</div>
</div>
</div>
</PageTabGroup>
</PageTab>
</template>
<script>
import {ValidationProvider, ValidationObserver} from 'vee-validate/dist/vee-validate.full'
import PageTabGroup from '@/components/Others/Layout/PageTabGroup'
import PageTab from '@/components/Others/Layout/PageTab'
import MobileHeader from '@/components/Mobile/MobileHeader'
import ButtonBase from '@/components/FilesView/ButtonBase'
import PageHeader from '@/components/Others/PageHeader'
import ThemeLabel from '@/components/Others/ThemeLabel'
import {required} from 'vee-validate/dist/rules'
import {mapGetters} from 'vuex'
import {debounce} from 'lodash'
export default {
name: 'Settings',
props: [
'user'
],
components: {
PageTabGroup,
PageTab,
ValidationProvider,
ValidationObserver,
MobileHeader,
PageHeader,
ButtonBase,
ThemeLabel,
required,
},
/* watch: {
'user.name': debounce(function (val) {
if (val === '') return
this.$store.commit('UPDATE_NAME', val)
}, 300),
},*/
data() {
return {
userInfo: undefined,
billingInfo: undefined,
isLoading: false,
}
},
methods: {
changeUserName() {
this.$store.commit('UPDATE_NAME', this.userInfo.name)
this.$updateText('/user/profile', 'name', this.userInfo.name)
}
},
created() {
this.userInfo = {
name: this.user.data.attributes.name,
email: this.user.data.attributes.email
}
this.billingInfo = {
billing_name: this.user.relationships.settings.data.attributes.billing_name,
billing_address: this.user.relationships.settings.data.attributes.billing_address,
billing_state: this.user.relationships.settings.data.attributes.billing_state,
billing_city: this.user.relationships.settings.data.attributes.billing_city,
billing_postal_code: this.user.relationships.settings.data.attributes.billing_postal_code,
billing_country: this.user.relationships.settings.data.attributes.billing_country,
billing_phone_number: this.user.relationships.settings.data.attributes.billing_phone_number,
}
}
}
</script>
<style lang="scss" scoped>
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
@import '@assets/vue-file-manager/_forms';
.block-form {
max-width: 100%;
}
@media only screen and (max-width: 960px) {
}
@media (prefers-color-scheme: dark) {
}
</style>

View File

@@ -71,10 +71,9 @@
margin-bottom: 30px;
overflow-x: auto;
z-index: 9;
position: sticky;
top: 30px;
background: white;
top: 40px;
&::-webkit-scrollbar {
display: none;
@@ -249,6 +248,7 @@
.cell-item {
white-space: nowrap;
color: $text;
}
}
@@ -288,6 +288,12 @@
padding-left: 30px;
padding-right: 30px;
}
.menu-list-wrapper {
&.horizontal {
top: 30px;
}
}
}
@media only screen and (max-width: 690px) {

View File

@@ -0,0 +1,193 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<title>{{ config('vuefilemanager.app_name') }}</title>
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@200;300;400;600;700;900&display=swap"
rel="stylesheet">
<link href="{{ env('APP_ENV') !== 'local' ? asset('css/invoice.css') : mix('css/invoice.css') }}?v={{ get_version() }}" rel="stylesheet">
</head>
<body>
<div id="toolbar-wrapper">
<button class="button" onclick="window.print();">
<svg class="icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-printer">
<polyline points="6 9 6 2 18 2 18 9"></polyline>
<path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"></path>
<rect x="6" y="14" width="12" height="8"></rect>
</svg>
<span class="label">Print Document</span>
</button>
</div>
<div id="invoice-wrapper">
<header class="invoice-header">
<div class="logo">
<img src="/assets/images/vuefilemanager-horizontal-logo.svg" alt="VueFileManager">
</div>
<div class="title">
<h1>Invoice</h1>
</div>
</header>
<section class="invoice-subject">
<ul class="list">
<li class="list-item">
<b>Date:</b>
<span>{{ $invoice->created_at }}</span>
</li>
<li class="list-item">
<b>Product:</b>
<span>Subscription</span>
</li>
<li class="list-item">
<b>Invoice Number:</b>
<span>{{ $invoice->order }}</span>
</li>
</ul>
</section>
<div class="invoice-partners">
<div class="partner">
<h2 class="partner-title">Seller:</h2>
<ul class="list">
@isset($invoice->seller['billing_vat_number'])
<li class="list-item">
<b>VAT number:</b>
<span>{{ $invoice->seller['billing_vat_number'] }}</span>
</li>
@endisset
@isset($invoice->seller['billing_name'])
<li class="list-item">
<b>Name:</b>
<span>{{ $invoice->seller['billing_name'] }}</span>
</li>
@endisset
@isset($invoice->seller['billing_phone_number'])
<li class="list-item">
<b>Phone:</b>
<span>{{ $invoice->seller['billing_phone_number'] }}</span>
</li>
@endisset
</ul>
<ul class="list">
@isset($invoice->seller['billing_address'])
<li class="list-item">
<b>Address:</b>
<span>{{ $invoice->seller['billing_address'] }}</span>
</li>
@endisset
@isset($invoice->seller['billing_city'])
<li class="list-item">
<b>City:</b>
<span>{{ $invoice->seller['billing_city'] }}</span>
</li>
@endisset
@isset($invoice->seller['billing_state'])
<li class="list-item">
<b>State:</b>
<span>{{ $invoice->seller['billing_state'] }}</span>
</li>
@endisset
@isset($invoice->seller['billing_postal_code'])
<li class="list-item">
<b>Postal code:</b>
<span>{{ $invoice->seller['billing_postal_code'] }}</span>
</li>
@endisset
@isset($invoice->seller['billing_country'])
<li class="list-item">
<b>Country:</b>
<span>{{ $invoice->seller['billing_country'] }}</span>
</li>
@endisset
</ul>
</div>
<div class="partner">
<h2 class="partner-title">Client:</h2>
<ul class="list">
@isset($invoice->client['billing_name'])
<li class="list-item">
<b>Name:</b>
<span>{{ $invoice->client['billing_name'] }}</span>
</li>
@endisset
@isset($invoice->client['billing_phone_number'])
<li class="list-item">
<b>Phone:</b>
<span>{{ $invoice->client['billing_phone_number'] }}</span>
</li>
@endisset
</ul>
<ul class="list">
@isset($invoice->client['billing_address'])
<li class="list-item">
<b>Address:</b>
<span>{{ $invoice->client['billing_address'] }}</span>
</li>
@endisset
@isset($invoice->client['billing_city'])
<li class="list-item">
<b>City:</b>
<span>{{ $invoice->client['billing_city'] }}</span>
</li>
@endisset
@isset($invoice->client['billing_state'])
<li class="list-item">
<b>State:</b>
<span>{{ $invoice->client['billing_state'] }}</span>
</li>
@endisset
@isset($invoice->client['billing_postal_code'])
<li class="list-item">
<b>Postal code:</b>
<span>{{ $invoice->client['billing_postal_code'] }}</span>
</li>
@endisset
@isset($invoice->client['billing_country'])
<li class="list-item">
<b>Country:</b>
<span>{{ $invoice->client['billing_country'] }}</span>
</li>
@endisset
</ul>
</div>
</div>
<div class="invoice-order">
<table class="table">
<thead class="table-header">
<tr>
<td>Description</td>
<td>Date</td>
<td>Amount</td>
</tr>
</thead>
<tbody class="table-body">
@foreach($invoice->bag as $item)
<tr>
<td>{{ $item['description'] }} (1)</td>
<td>{{ $item['date'] }}</td>
<td>{{ $item['amount'] }} {{ $invoice->currency }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<div class="invoice-summary">
<b>Total {{ $invoice->total }} {{ $invoice->currency }}</b>
</div>
</div>
</body>
</html>

View File

@@ -48,10 +48,13 @@ Route::group(['middleware' => ['api']], function () {
Route::group(['middleware' => ['auth:api', 'auth.master', 'scope:master']], function () {
// User
Route::patch('/user/relationships/settings', 'User\AccountController@update_user_settings');
Route::post('/user/password', 'User\AccountController@change_password');
Route::patch('/user/profile', 'User\AccountController@update_profile');
Route::get('/user/invoices', 'User\AccountController@invoices');
Route::get('/user/storage', 'User\AccountController@storage');
Route::get('/user', 'User\AccountController@user');
Route::get('/profile', 'User\AccountController@me');
// Browse
Route::get('/participant-uploads', 'FileBrowser\BrowseController@participant_uploads');
@@ -90,17 +93,26 @@ Route::group(['middleware' => ['auth:api', 'auth.master', 'auth.admin', 'scope:m
// Edit users
Route::post('/users/create', 'Admin\UserController@create_user');
Route::get('/users/{id}/invoices', 'Admin\UserController@invoices');
Route::patch('/users/{id}/role', 'Admin\UserController@change_role');
Route::delete('/users/{id}/delete', 'Admin\UserController@delete_user');
Route::patch('/users/{id}/capacity', 'Admin\UserController@change_storage_capacity');
Route::post('/users/{id}/send-password-email', 'Admin\UserController@send_password_reset_email');
// Gateways
Route::get('/gateways', 'Admin\GatewayController@index');
Route::get('/gateways/{type}', 'Admin\GatewayController@show');
Route::patch('/gateways/{type}', 'Admin\GatewayController@update');
// Plans
Route::post('/plans/create', 'Admin\PlanController@create');
Route::get('/plans', 'Admin\PlanController@index');
Route::get('/plans/{id}', 'Admin\PlanController@show');
Route::post('/plans/store', 'Admin\PlanController@store');
Route::patch('/plans/{id}/update', 'Admin\PlanController@update');
// Invoices
Route::get('/invoices', 'Admin\InvoiceController@index');
Route::get('/invoices/{token}', 'Admin\InvoiceController@show');
});

View File

@@ -25,6 +25,10 @@ Route::group(['middleware' => ['auth:api', 'auth.shared', 'auth.master', 'scope:
Route::get('/file/{name}', 'FileAccessController@get_file')->name('file');
});
Route::group(['middleware' => ['auth:api', 'auth.master', 'scope:master']], function () {
Route::get('/invoice/{token}', 'Admin\InvoiceController@show');
});
// Pages
Route::get('/shared/{token}', 'Sharing\FileSharingController@index');
Route::get('/{any?}', 'AppFunctionsController@index')->where('any', '.*');

6
webpack.mix.js vendored
View File

@@ -12,7 +12,7 @@ const mix = require('laravel-mix');
*/
mix.js('resources/js/main.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css', {
.sass('resources/sass/app.scss', 'public/css/app.css', {
implementation: require('node-sass')
})
.webpackConfig({
@@ -23,10 +23,10 @@ mix.js('resources/js/main.js', 'public/js')
}
},
})
.options({
/* .options({
hmrOptions: {
host: '172.20.10.5',
port: '8080'
},
})
})*/
.disableNotifications();