frontend & backend update

This commit is contained in:
carodej
2020-06-19 08:03:29 +02:00
parent 95bc310def
commit a2cab6198e
83 changed files with 4464 additions and 1907 deletions
+823 -475
View File
File diff suppressed because it is too large Load Diff
@@ -5,6 +5,8 @@ namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Resources\GatewayCollection; use App\Http\Resources\GatewayCollection;
use App\Http\Resources\GatewayResource; use App\Http\Resources\GatewayResource;
use App\Http\Resources\InvoiceCollection;
use App\Invoice;
use App\PaymentGateway; use App\PaymentGateway;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@@ -33,6 +35,19 @@ class GatewayController extends Controller
return new GatewayResource($gateway); return new GatewayResource($gateway);
} }
/**
* Get single payment gateway by slug
*
* @param $slug
* @return InvoiceCollection
*/
public function show_transactions($slug)
{
return new InvoiceCollection(
Invoice::where('provider', $slug)->get()
);
}
/** /**
* Update payment gateway options * Update payment gateway options
* *
+28 -30
View File
@@ -8,12 +8,21 @@ use App\Http\Resources\PlanResource;
use App\Http\Resources\UserResource; use App\Http\Resources\UserResource;
use App\Http\Resources\UsersCollection; use App\Http\Resources\UsersCollection;
use App\Plan; use App\Plan;
use App\Services\StripeService;
use App\User; use App\User;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Rinvex\Subscriptions\Models\PlanFeature; use Rinvex\Subscriptions\Models\PlanFeature;
class PlanController extends Controller class PlanController extends Controller
{ {
/**
* PlanController constructor.
*/
public function __construct(StripeService $stripe)
{
$this->stripe = $stripe;
}
/** /**
* Get all plans * Get all plans
* *
@@ -22,7 +31,7 @@ class PlanController extends Controller
public function index() public function index()
{ {
return new PlanCollection( return new PlanCollection(
app('rinvex.subscriptions.plan')->all() $this->stripe->getPlans()
); );
} }
@@ -35,7 +44,7 @@ class PlanController extends Controller
public function show($id) public function show($id)
{ {
return new PlanResource( return new PlanResource(
app('rinvex.subscriptions.plan')->find($id) $this->stripe->getPlan($id)
); );
} }
@@ -47,26 +56,9 @@ class PlanController extends Controller
*/ */
public function store(Request $request) public function store(Request $request)
{ {
$plan = app('rinvex.subscriptions.plan')->create([ return new PlanResource(
'description' => $request->input('attributes.description'), $this->stripe->createPlan($request)
'price' => $request->input('attributes.price'), );
'name' => $request->input('attributes.name'),
'currency' => 'USD',
'invoice_period' => 1,
'sort_order' => 1,
'signup_fee' => 0,
]);
// Create multiple plan features at once
$plan->features()->saveMany([
new PlanFeature([
'name' => 'Storage capacity',
'value' => $request->input('attributes.capacity'),
'sort_order' => 1
]),
]);
return new PlanResource($plan);
} }
/** /**
@@ -78,18 +70,24 @@ class PlanController extends Controller
*/ */
public function update(Request $request, $id) public function update(Request $request, $id)
{ {
// TODO: validation request $this->stripe->updatePlan($request, $id);
$plan = app('rinvex.subscriptions.plan')->find($id);
if ($request->name === 'capacity') {
$plan->getFeatureBySlug('storage-capacity')->update(['value' => $request->value]);
} else {
$plan->update(make_single_input($request));
}
return response('Saved!', 204); return response('Saved!', 204);
} }
/**
* Delete plan
*
* @param $id
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
*/
public function delete($id)
{
$this->stripe->deletePlan($id);
return response('Done!', 204);
}
/** /**
* Get subscriptions * Get subscriptions
* *
@@ -258,11 +258,6 @@ class EditItemsController extends Controller
return Demo::upload($request); return Demo::upload($request);
} }
// Check if user can upload
if (config('vuefilemanager.limit_storage_by_capacity') && user_storage_percentage() >= 100) {
abort(423, 'You exceed your storage limit!');
}
// Check permission to upload for authenticated editor // Check permission to upload for authenticated editor
if ($request->user()->tokenCan('editor')) { if ($request->user()->tokenCan('editor')) {
@@ -4,19 +4,32 @@ namespace App\Http\Controllers\General;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Resources\PricingCollection; use App\Http\Resources\PricingCollection;
use App\Services\StripeService;
use Illuminate\Http\Request; use Illuminate\Http\Request;
class PricingController extends Controller class PricingController extends Controller
{ {
/**
* PlanController constructor.
*/
public function __construct(StripeService $stripe)
{
$this->stripe = $stripe;
}
/** /**
* Get all active plans * Get all active plans
* *
* @return PricingCollection * @return PricingCollection
*/ */
public function index() { public function index()
{
return new PricingCollection( $collection = new PricingCollection(
app('rinvex.subscriptions.plan')->where('is_active', 1)->get() $this->stripe->getActivePlans()
); );
return $collection->sortBy('product.metadata.capacity')
->values()
->all();
} }
} }
@@ -0,0 +1,107 @@
<?php
namespace App\Http\Controllers\User;
use App\Http\Controllers\Controller;
use App\Http\Resources\PaymentCardCollection;
use App\Http\Resources\PaymentCardResource;
use App\Http\Resources\PaymentDefaultCardResource;
use App\Services\PaymentService;
use App\UserCard;
use Auth;
use Illuminate\Http\Request;
use Laravel\Cashier\PaymentMethod;
class PaymentCardsController extends Controller
{
/**
* @var PaymentService
*/
private $payment;
/**
* PaymentCardsController constructor.
*/
public function __construct(PaymentService $payment)
{
$this->payment = $payment;
}
/**
* Update card detail
*
* @param Request $request
* @param $id
*/
public function update(Request $request, $id)
{
$user = Auth::user();
// Update DefaultPayment Method
$user->updateDefaultPaymentMethod($id);
// Sync default payment method
$user->updateDefaultPaymentMethodFromStripe();
}
/**
* Delete user credit card
*
*/
public function delete($id)
{
$user = Auth::user();
// Get payment method
$paymentMethod = $user->findPaymentMethod($id);
// Delete payment method
$paymentMethod->delete();
// Sync default payment method
$user->updateDefaultPaymentMethodFromStripe();
return response('Done!', 204);
}
/**
* Get user payment methods sorted by default and others
*
* @return array
*/
public function payment_methods()
{
$user = Auth::user();
// Get default payment method
$defaultPaymentMethodObject = $user->defaultPaymentMethod();
$defaultPaymentMethod = $defaultPaymentMethodObject instanceof PaymentMethod
? $defaultPaymentMethodObject->asStripePaymentMethod()
: $defaultPaymentMethodObject;
// filter payment methods without default payment
$paymentMethods = $user->paymentMethods()->filter(function ($paymentMethod) use ($defaultPaymentMethod) {
return $paymentMethod->id !== $defaultPaymentMethod->id;
});
// Get payment methods
$paymentMethodsMapped = $paymentMethods->map(function ($paymentMethod) {
return $paymentMethod->asStripePaymentMethod();
})->values()->all();
if (is_null($paymentMethodsMapped) && is_null($paymentMethodsMapped)) {
return [
'default' => null,
'others' => [],
];
}
return [
'default' => $defaultPaymentMethod instanceof PaymentMethod
? new PaymentCardResource($defaultPaymentMethod)
: new PaymentDefaultCardResource($defaultPaymentMethod),
'others' => new PaymentCardCollection($paymentMethodsMapped),
];
}
}
@@ -3,53 +3,82 @@
namespace App\Http\Controllers\User; namespace App\Http\Controllers\User;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\Subscription\StoreUpgradeAccountRequest;
use App\Http\Resources\UserSubscription;
use App\Invoice; use App\Invoice;
use App\Services\PaymentService;
use App\Services\StripeService;
use Auth; use Auth;
use Cartalyst\Stripe\Exception\CardErrorException;
use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Laravel\Cashier\Exceptions\IncompletePayment;
use Symfony\Component\HttpKernel\Exception\HttpException;
class SubscriptionController extends Controller class SubscriptionController extends Controller
{ {
private $stripe;
/**
* SubscriptionController constructor.
* @param $payment
*/
public function __construct(StripeService $stripe)
{
$this->stripe = $stripe;
}
/**
* Generate setup intent
*
* @return \Stripe\SetupIntent
*/
public function stripe_setup_intent()
{
// Get user
$user = Auth::user();
// Create stripe customer if not exist
$user->createOrGetStripeCustomer();
// Return setup intent
return $user->createSetupIntent();
}
public function show()
{
return new UserSubscription(
Auth::user()
);
}
/** /**
* Upgrade account to subscription * Upgrade account to subscription
* *
* @param Request $request * @param StoreUpgradeAccountRequest $request
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response * @return ResponseFactory|\Illuminate\Http\Response
*/ */
public function upgrade(Request $request) public function upgrade(StoreUpgradeAccountRequest $request)
{ {
// TODO: validation request
// Get user // Get user
$user = Auth::user(); $user = Auth::user();
// Get requested plan // Get requested plan
$plan = app('rinvex.subscriptions.plan') $plan = $this->stripe->getPlan($request->input('plan.data.id'));
->find($request->input('plan.data.id'));
// Check if user have subscription // Set user billing
if ($user->activeSubscriptions()->count() !== 0) { $user->setBilling($request->input('billing'));
// Get old subscription // Make subscription
$subscription = $user->subscription('main'); $this->stripe->createOrReplaceSubscription($request, $user);
// Change subscription plan
$subscription->changePlan($plan);
} else {
// Create subscription
$user->newSubscription('main', $plan);
}
// Update user storage limit // Update user storage limit
$user->settings()->update([ $user->settings()->update([
'storage_capacity' => $plan->features->first()->value 'storage_capacity' => $plan['product']['metadata']['capacity']
]); ]);
// Store invoice // Store invoice
Invoice::create( Invoice::create(get_invoice_data($user, $plan, 'stripe'));
get_invoice_data($user, $plan)
);
return response('Done!', 204); return response('Done!', 204);
} }
@@ -57,15 +86,25 @@ class SubscriptionController extends Controller
/** /**
* Cancel Subscription * Cancel Subscription
* *
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response * @return ResponseFactory|\Illuminate\Http\Response
*/ */
public function cancel() { public function cancel()
{
// Get user
$user = Auth::user();
// Cancel subscription // Cancel subscription
$user->subscription('main')->cancel(); Auth::user()->subscription('main')->cancel();
return response('Done!', 204);
}
/**
* Resume Subscription
*
* @return ResponseFactory|\Illuminate\Http\Response
*/
public function resume()
{
// Resume subscription
Auth::user()->subscription('main')->resume();
return response('Done!', 204); return response('Done!', 204);
} }
@@ -27,7 +27,14 @@ function get_invoice_number()
} }
} }
function get_invoice_data($user, $plan) /**
* Get data to render in invoice tempalte
* @param $user
* @param $plan
* @param $provider
* @return array
*/
function get_invoice_data($user, $plan, $provider)
{ {
$subscription = $user->subscription('main'); $subscription = $user->subscription('main');
$order_number = get_invoice_number(); $order_number = get_invoice_number();
@@ -36,15 +43,17 @@ function get_invoice_data($user, $plan)
return [ return [
'token' => $token, 'token' => $token,
'order' => $order_number, 'order' => $order_number,
'provider' => $provider,
'user_id' => $user->id, 'user_id' => $user->id,
'plan_id' => $plan->id, 'plan_id' => $plan['plan']['id'],
'total' => $plan->price, 'total' => $plan['plan']['amount'],
'currency' => 'USD', 'currency' => 'USD',
'bag' => [ 'bag' => [
[ [
'description' => 'Subscription - ' . $plan->name, 'description' => 'Subscription - ' . $plan['product']['name'],
'date' => format_date($subscription->starts_at, '%d. %B. %Y') . ' - ' . format_date($subscription->ends_at, '%d. %B. %Y'), //'date' => format_date($subscription->starts_at, '%d. %B. %Y') . ' - ' . format_date($subscription->ends_at, '%d. %B. %Y'),
'amount' => $plan->price, 'date' => format_date(Carbon::now(),'%d. %B. %Y'),
'amount' => $plan['plan']['amount'],
] ]
], ],
'seller' => [ 'seller' => [
@@ -282,15 +291,21 @@ function get_storage_fill_percentage($used, $capacity)
} }
/** /**
* Get user capacity fill percentage * Get user capacity fill by percentage
* *
* @return string * @return string
*/ */
function user_storage_percentage() function user_storage_percentage($id, $additionals = null)
{ {
$user = Auth::user(); $user = \App\User::findOrFail($id);
return get_storage_fill_percentage($user->used_capacity, $user->settings->storage_capacity); $used = $user->used_capacity;
if ($additionals) {
$used = $user->used_capacity + $additionals;
}
return get_storage_fill_percentage($used, $user->settings->storage_capacity);
} }
/** /**
+23
View File
@@ -0,0 +1,23 @@
<?php
/**
* Check if current user subscribed plan is highest
*
* @param $id
* @param $subscribed_capacity
* @return int
*/
function is_highest_plan($plan)
{
$plans = app('rinvex.subscriptions.plan')->all();
$unsubscribed = $plans->filter(function ($item) use ($plan) {
return $item->id !== $plan->id;
});
$capacities = $unsubscribed->map(function ($item) {
return $item->features->first()->value;
});
return max(Arr::flatten($capacities)) < $plan->features->first()->value ? 1 : 0;
}
@@ -0,0 +1,56 @@
<?php
namespace App\Http\Requests\Subscription;
use Illuminate\Foundation\Http\FormRequest;
class StoreUpgradeAccountRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
// Billings
'billing' => 'required|array',
'billing.billing_address' => 'required|string',
'billing.billing_city' => 'required|string',
'billing.billing_country' => 'required|string',
'billing.billing_name' => 'required|string',
'billing.billing_phone_number' => 'required|string',
'billing.billing_postal_code' => 'required|string',
'billing.billing_state' => 'required|string',
// Payment
'payment' => 'required|array',
'payment.type' => 'required|string',
'payment.meta' => 'required|sometimes|array',
'payment.meta.pm' => 'required|sometimes|string',
// Plan
'plan.data' => 'required|array',
'plan.data.attributes' => 'required|array',
'plan.data.attributes.capacity' => 'required|digits_between:1,9',
'plan.data.attributes.capacity_formatted' => 'required|string',
'plan.data.attributes.currency' => 'required|string',
'plan.data.attributes.description' => 'required|string',
'plan.data.attributes.name' => 'required|string',
'plan.data.attributes.price' => 'required|string',
'plan.data.id' => 'required|string',
'plan.data.type' => 'required|string',
];
}
}
+10 -9
View File
@@ -19,15 +19,16 @@ class GatewayResource extends JsonResource
'id' => (string)$this->id, 'id' => (string)$this->id,
'type' => 'gateways', 'type' => 'gateways',
'attributes' => [ 'attributes' => [
'status' => $this->status, 'status' => $this->status,
'sandbox' => $this->sandbox, 'sandbox' => $this->sandbox,
'name' => $this->name, 'name' => $this->name,
'slug' => $this->slug, 'slug' => $this->slug,
'logo' => $this->logo, 'logo' => $this->logo,
'client_id' => $this->client_id, 'client_id' => $this->client_id,
'secret' => $this->secret, 'secret' => $this->secret,
'webhook' => $this->webhook, 'webhook' => $this->webhook,
'optional' => $this->optional, 'payment_processed' => $this->payment_processed,
'optional' => $this->optional,
] ]
] ]
]; ];
@@ -0,0 +1,23 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class PaymentCardCollection extends ResourceCollection
{
public $collects = PaymentCardResource::class;
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => $this->collection,
];
}
}
@@ -0,0 +1,35 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class PaymentCardResource 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' => 'payment_method',
'attributes' => [
'provider' => 'stripe',
'card_id' => $this['id'],
'brand' => strtolower($this['card']['brand']),
'last4' => $this['card']['last4'],
'exp_month' => $this['card']['exp_month'],
'exp_year' => $this['card']['exp_year'],
'created_at' => format_date($this['created_at'], '%d. %B. %Y'),
'status' => 'active',
'default' => 0,
]
]
];
}
}
@@ -0,0 +1,35 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class PaymentDefaultCardResource 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' => 'payment_method',
'attributes' => [
'provider' => 'stripe',
'card_id' => $this['id'],
'brand' => isset($this['brand']) ? strtolower($this['brand']) : strtolower($this['card']['brand']),
'last4' => isset($this['last4']) ? $this['last4'] : $this['card']['last4'],
'exp_month' => isset($this['exp_month']) ? $this['exp_month'] : $this['card']['exp_month'],
'exp_year' => isset($this['exp_year']) ? $this['exp_year'] : $this['card']['exp_year'],
'created_at' => format_date($this['created_at'], '%d. %B. %Y'),
'status' => 'active',
'default' => 0,
]
]
];
}
}
+10 -11
View File
@@ -16,19 +16,18 @@ class PlanResource extends JsonResource
{ {
return [ return [
'data' => [ 'data' => [
'id' => (string)$this->id, 'id' => $this['plan']['id'],
'type' => 'plans', 'type' => 'plans',
'attributes' => [ 'attributes' => [
'subscribers' => $this->subscriptions->count(), 'subscribers' => $this['plan']['aggregate_usage'],
'status' => $this->is_active, 'status' => $this['plan']['active'],
'name' => $this->name, 'name' => $this['product']['name'],
'description' => $this->description, 'description' => $this['product']['description'],
'price' => $this->price, 'price' => $this['plan']['amount'],
'capacity_formatted' => format_gigabytes($this->features->first()->value), 'capacity_formatted' => format_gigabytes($this['product']['metadata']['capacity']),
'capacity' => $this->features->first()->value, 'capacity' => $this['product']['metadata']['capacity'],
'created_at_formatted' => format_date($this->created_at), 'created_at_formatted' => format_date($this['plan']['created']),
'created_at' => $this->created_at, 'created_at' => $this['plan']['created'],
'updated_at' => $this->updated_at,
] ]
] ]
]; ];
+9 -8
View File
@@ -3,28 +3,29 @@
namespace App\Http\Resources; namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource; use Illuminate\Http\Resources\Json\JsonResource;
use Laravel\Cashier\Cashier;
class PricingResource extends JsonResource class PricingResource extends JsonResource
{ {
/** /**
* Transform the resource into an array. * Transform the resource into an array.
* *
* @param \Illuminate\Http\Request $request * @param \Illuminate\Http\Request $request
* @return array * @return array
*/ */
public function toArray($request) public function toArray($request)
{ {
return [ return [
'data' => [ 'data' => [
'id' => (string)$this->id, 'id' => $this['plan']['id'],
'type' => 'plans', 'type' => 'plans',
'attributes' => [ 'attributes' => [
'name' => $this->name, 'name' => $this['product']['name'],
'description' => $this->description, 'description' => $this['product']['description'],
'price' => $this->price, 'price' => Cashier::formatAmount($this['plan']['amount']),
'capacity_formatted' => format_gigabytes($this->features->first()->value), 'capacity_formatted' => format_gigabytes($this['product']['metadata']['capacity']),
'capacity' => (int) $this->features->first()->value, 'capacity' => (int) $this['product']['metadata']['capacity'],
'currency' => 'USD', 'currency' => 'USD',
] ]
] ]
]; ];
+9 -7
View File
@@ -2,6 +2,9 @@
namespace App\Http\Resources; namespace App\Http\Resources;
use App\Services\StripeService;
use App\User;
use Cartalyst\Stripe\Api\PaymentMethods;
use Faker\Factory; use Faker\Factory;
use Illuminate\Http\Resources\Json\JsonResource; use Illuminate\Http\Resources\Json\JsonResource;
@@ -27,16 +30,14 @@ class UserResource extends JsonResource
'email' => env('APP_DEMO') ? $faker->email : $this->email, 'email' => env('APP_DEMO') ? $faker->email : $this->email,
'avatar' => $this->avatar, 'avatar' => $this->avatar,
'role' => $this->role, 'role' => $this->role,
'subscription' => $this->subscribed('main'),
'created_at_formatted' => format_date($this->created_at, '%d. %B. %Y'), 'created_at_formatted' => format_date($this->created_at, '%d. %B. %Y'),
'created_at' => $this->created_at, 'created_at' => $this->created_at,
'updated_at' => $this->updated_at, 'updated_at' => $this->updated_at,
] ]
], ],
'relationships' => [ 'relationships' => [
'subscription' => $this->activeSubscriptions()->count() !== 0 'settings' => [
? new UserSubscription($this->subscription('main'))
: null,
'settings' => [
'data' => [ 'data' => [
'id' => (string)$this->settings->id, 'id' => (string)$this->settings->id,
'type' => 'settings', 'type' => 'settings',
@@ -51,14 +52,14 @@ class UserResource extends JsonResource
] ]
] ]
], ],
'storage' => [ 'storage' => [
'data' => [ 'data' => [
'id' => '1', 'id' => '1',
'type' => 'storage', 'type' => 'storage',
'attributes' => $this->storage 'attributes' => $this->storage
] ]
], ],
'favourites' => [ 'favourites' => [
'data' => [ 'data' => [
'id' => '1', 'id' => '1',
'type' => 'folders_favourite', 'type' => 'folders_favourite',
@@ -67,7 +68,7 @@ class UserResource extends JsonResource
], ],
], ],
], ],
'tree' => [ 'tree' => [
'data' => [ 'data' => [
'id' => '1', 'id' => '1',
'type' => 'folders_tree', 'type' => 'folders_tree',
@@ -76,6 +77,7 @@ class UserResource extends JsonResource
], ],
], ],
], ],
'payment_methods' => new PaymentCardCollection($this->payment_cards)
] ]
]; ];
} }
+23 -11
View File
@@ -14,21 +14,33 @@ class UserSubscription extends JsonResource
*/ */
public function toArray($request) public function toArray($request)
{ {
$stripe = resolve('App\Services\StripeService');
$active_subscription = $this->subscription('main')->asStripeSubscription();
// Get subscription details
$subscription = $stripe->getPlan($this->subscription('main')->stripe_plan);
// Retrieve the timestamp from Stripe
$current_period_end = $active_subscription["current_period_end"];
$current_period_start = $active_subscription["current_period_start"];
$canceled_at = $active_subscription["canceled_at"];
return [ return [
'data' => [ 'data' => [
'id' => $this->id, 'id' => $subscription['plan']['id'],
'type' => 'subscription', 'type' => 'subscription',
'attributes' => [ 'attributes' => [
'active' => $this->active(), /*'is_highest' => is_highest_plan($this->plan),*/
'canceled' => $this->canceled(), 'active' => $subscription['plan']['active'],
'name' => $this->plan->name, 'canceled' => $this->subscription('main')->cancelled(),
'capacity' => (int) $this->plan->features->first()->value, 'name' => $subscription['product']['name'],
'capacity_formatted' => format_gigabytes($this->plan->features->first()->value), 'capacity' => (int)$subscription['product']['metadata']['capacity'],
'slug' => $this->slug, 'capacity_formatted' => format_gigabytes($subscription['product']['metadata']['capacity']),
'canceled_at' => format_date($this->created_at, '%d. %B. %Y'), 'slug' => $subscription['plan']['id'],
'created_at' => format_date($this->created_at, '%d. %B. %Y'), 'canceled_at' => format_date($canceled_at, '%d. %B. %Y'),
'starts_at' => format_date($this->starts_at, '%d. %B. %Y'), 'created_at' => format_date($current_period_start, '%d. %B. %Y'),
'ends_at' => format_date($this->ends_at, '%d. %B. %Y'), 'ends_at' => format_date($current_period_end, '%d. %B. %Y'),
] ]
] ]
]; ];
+10 -4
View File
@@ -197,14 +197,20 @@ class Editor
*/ */
public static function upload($request, $shared = null) public static function upload($request, $shared = null)
{ {
// Get user data
$user_scope = is_null($shared) ? $request->user()->token()->scopes[0] : 'editor';
$user_id = is_null($shared) ? Auth::id() : $shared->user_id;
// Get parent_id from request // Get parent_id from request
$folder_id = $request->parent_id === 0 ? 0 : $request->parent_id; $folder_id = $request->parent_id === 0 ? 0 : $request->parent_id;
$file = $request->file('file'); $file = $request->file('file');
// Get user data
$user_scope = is_null($shared) ? $request->user()->token()->scopes[0] : 'editor';
$user_id = is_null($shared) ? Auth::id() : $shared->user_id;
$user_storage_used = user_storage_percentage($user_id, $file->getSize());
// Check if user can upload
if (config('vuefilemanager.limit_storage_by_capacity') && $user_storage_used >= 100) {
abort(423, 'You exceed your storage limit!');
}
// File // File
$filename = Str::random() . '-' . str_replace(' ', '', $file->getClientOriginalName()); $filename = Str::random() . '-' . str_replace(' ', '', $file->getClientOriginalName());
$filetype = get_file_type($file); $filetype = get_file_type($file);
+37
View File
@@ -4,6 +4,43 @@ namespace App;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
/**
* App\Invoice
*
* @property int $id
* @property string $token
* @property string $order
* @property string|null $provider
* @property string $user_id
* @property string $plan_id
* @property array $seller
* @property array $client
* @property array $bag
* @property string|null $notes
* @property string $total
* @property string $currency
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\User|null $user
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice query()
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereBag($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereClient($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereCurrency($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereNotes($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereOrder($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice wherePlanId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereProvider($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereSeller($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereToken($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereTotal($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereUserId($value)
* @mixin \Eloquent
*/
class Invoice extends Model class Invoice extends Model
{ {
protected $guarded = [ protected $guarded = [
+30
View File
@@ -4,6 +4,36 @@ namespace App;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
/**
* App\PaymentGateway
*
* @property int $id
* @property int $status
* @property int $sandbox
* @property string $name
* @property string $slug
* @property string $logo
* @property string|null $client_id
* @property string|null $secret
* @property string|null $webhook
* @property string|null $optional
* @property int|null $payment_processed
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway query()
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereClientId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereLogo($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereOptional($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway wherePaymentProcessed($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereSandbox($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereSecret($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereSlug($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereWebhook($value)
* @mixin \Eloquent
*/
class PaymentGateway extends Model class PaymentGateway extends Model
{ {
protected $guarded = ['id']; protected $guarded = ['id'];
+199
View File
@@ -0,0 +1,199 @@
<?php
namespace App\Services;
use App\PaymentGateway;
use App\User;
use App\UserCard;
use App\UserSettings;
use Cartalyst\Stripe\Exception\CardErrorException;
use Stripe;
use Symfony\Component\HttpKernel\Exception\HttpException;
class PaymentService
{
/**
* PaymentService constructor.
*/
public function __construct()
{
$this->stripe = Stripe::make(config('services.stripe.secret'), '2020-03-02');
}
/**
* Get and charge customer
*
* @param $request
* @param $plan
* @param $user
*/
public function getAndChargeCustomer($request, $plan, $user)
{
// Get Stripe Customer
$customer = $this->findOrCreateStripeCustomer($user);
$paymentIntent = $this->stripe->paymentIntents()->create([
'amount' => $plan->price,
'currency' => 'USD',
'payment_method_types' => [
'card',
],
]);
dd($paymentIntent);
// Try charge customer
try {
if ($request->has('payment.meta.card_token')) {
// Register customer credit card
$created_card = $this->registerStripeCreditCard($customer, $request->input('payment.meta.card_token'));
// Charge with created credit card
/*$this->stripe->charges()->create([
'description' => 'Subscription ' . $plan->name,
'customer' => $customer['id'],
'amount' => $plan->price,
'currency' => 'USD',
'source' => $created_card->card_id
]);*/
} else {
// Charge with customer default creadit card
$this->stripe->charges()->create([
'description' => 'Subscription ' . $plan->name,
'customer' => $customer['id'],
'amount' => $plan->price,
'currency' => 'USD',
]);
}
} catch (CardErrorException $error) {
//dd($error);
// Remove previously registered card
if (in_array($error->getErrorCode(), ['rejected_card', 'card_declined'])
&& $request->has('payment.meta.card_token')
&& isset($error->getRawOutput()['error']['charge'])) {
// Get charge
$charge = $this->stripe->charges()->find($error->getRawOutput()['error']['charge']);
// Remove card from stripe
$this->deleteStripeCard($user->settings->stripe_customer_id, $charge['payment_method']);
// Get card
$error_card = UserCard::where('card_id', $charge['payment_method'])->first();
// Delete card
$error_card->delete();
}
throw new HttpException($error->getCode(), $error->getMessage());
}
// Increase payment processed column
PaymentGateway::where('slug', 'stripe')->first()->increment('payment_processed');
}
/**
* Find or create stripe customer
*
* @param $user
* @return array
*/
private function findOrCreateStripeCustomer($user)
{
// Get existing stripe customer
if ($user->settings->stripe_customer_id) {
return $this->stripe->customers()->find($user->settings->stripe_customer_id);
}
// Create new stripe costumer
if (!$user->settings->stripe_customer_id) {
$customer = $this->stripe->customers()->create([
'email' => $user->email,
'name' => $user->name,
'address' => [
'city' => $user->settings->billing_city,
'country' => $user->settings->billing_country,
'line1' => $user->settings->billing_address,
'state' => $user->settings->billing_state,
'postal_code' => $user->settings->billing_postal_code,
]
]);
// Store user stripe_customer_id
$user->settings()->update([
'stripe_customer_id' => $customer['id']
]);
return $customer;
}
}
/**
* Register stripe credit card
*
* @param $customer
* @param $card_id
* @return object
*/
private function registerStripeCreditCard($customer, $card_id)
{
// Register user card
$card = $this->stripe->cards()->create($customer['id'], $card_id);
// Get user settings
$user_settings = UserSettings::where('stripe_customer_id', $customer['id'])->first();
// Set default status
$default_card = UserCard::where('user_id', $user_settings->user_id)->get()->count() > 0 ? 0 : 1;
// Store user card
$card = UserCard::create([
'user_id' => $user_settings->user_id,
'status' => 'active',
'default' => $default_card,
'provider' => 'stripe',
'card_id' => $card['id'],
'brand' => $card['brand'],
'last4' => $card['last4'],
'exp_month' => $card['exp_month'],
'exp_year' => $card['exp_year'],
]);
return $card;
}
/**
* Set new default source from existing credit card
*
* @param $stripe_customer_id
* @param $card_id
*/
public function setDefaultStripeCard($stripe_customer_id, $card_id)
{
$this->stripe->customers()->update($stripe_customer_id, [
'default_source' => $card_id,
]);
}
/**
* Delete customer stripe credit card
*
* @param $stripe_customer_id
* @param $card_id
*/
public function deleteStripeCard($stripe_customer_id, $card_id)
{
$this->stripe->cards()->delete($stripe_customer_id, $card_id);
}
}
+229
View File
@@ -0,0 +1,229 @@
<?php
namespace App\Services;
use Illuminate\Support\Str;
use Laravel\Cashier\Exceptions\IncompletePayment;
use Stripe;
use Symfony\Component\HttpKernel\Exception\HttpException;
class StripeService
{
/**
* Stripe Service constructor.
*/
public function __construct()
{
$this->stripe = Stripe::make(env('STRIPE_SECRET'), '2020-03-02');
}
/**
* Get default payment option or set new default payment
*
* @param $request
* @param $user
* @return mixed
*/
public function getOrSetDefaultPaymentMethod($request, $user)
{
// Check payment method
if (! $request->has('payment.meta.pm') && $user->hasDefaultPaymentMethod()) {
// Get default payment
return $user->defaultPaymentMethod()->paymentMethod;
} else if ( $request->has('payment.meta.pm') && $user->hasDefaultPaymentMethod() ) {
// Set new payment
return $user->addPaymentMethod($request->input('payment.meta.pm'))->paymentMethod;
} else if ( $request->has('payment.meta.pm') && ! $user->hasDefaultPaymentMethod() ) {
// Set new payment
return $user->updateDefaultPaymentMethod($request->input('payment.meta.pm'))->paymentMethod;
} else {
throw new HttpException(400, 'Something went wrong.');
}
}
/**
* Create new subscription or replace by new subscription
*
* @param $request
* @param $user
* @param $paymentMethod
*/
public function createOrReplaceSubscription($request, $user)
{
try {
// Get payment method
$paymentMethod = $this->getOrSetDefaultPaymentMethod($request, $user);
// Check if user have subscription
if ($user->subscribed('main')) {
// Change subscription plan
$user->subscription('main')->skipTrial()->swap($request->input('plan.data.id'));
} else {
// Create subscription
$user->newSubscription('main', $request->input('plan.data.id'))->create($paymentMethod);
}
} catch (IncompletePayment $exception) {
throw new HttpException(400, 'We can\'t charge your card');
}
}
/**
* Create plan
*
* @param $request
* @return mixed
*/
public function createPlan($request)
{
$product = $this->stripe->products()->create([
'name' => $request->input('attributes.name'),
'description' => $request->input('attributes.description'),
'metadata' => [
'capacity' => $request->input('attributes.capacity')
]
]);
$plan = $this->stripe->plans()->create([
'id' => Str::slug($request->input('attributes.name')),
'amount' => $request->input('attributes.price'),
'currency' => 'USD',
'interval' => 'month',
'product' => $product['id'],
]);
return compact('plan', 'product');
}
/**
* Update plan
*
* @param $request
* @param $id
*/
public function updatePlan($request, $id)
{
$plan_colls = ['is_active', 'price'];
$product_colls = ['name', 'description', 'capacity'];
$plan = $this->stripe->plans()->find($id);
// Update product
if (in_array($request->name, $product_colls)) {
if ($request->name === 'capacity') {
$this->stripe->products()->update($plan['product'], ['metadata' => ['capacity' => $request->value]]);
}
if ($request->name === 'name') {
$this->stripe->products()->update($plan['product'], ['name' => $request->value]);
}
if ($request->name === 'description') {
$this->stripe->products()->update($plan['product'], ['description' => $request->value]);
}
}
// Update plan
if (in_array($request->name, $plan_colls)) {
if ($request->name === 'is_active') {
$this->stripe->plans()->update($id, ['active' => $request->value]);
}
}
}
/**
* Get plan details
*
* @param $id
* @return mixed
*/
public function getPlan($id)
{
$plan = $this->stripe->plans()->find($id);
$product = $this->stripe->products()->find($plan['product']);
return compact('plan', 'product');
}
/**
* Get all plans
*
* @return mixed
*/
public function getPlans()
{
// Get stripe plans
$stripe_plans = $this->stripe->plans()->all();
// Plans container
$plans = [];
foreach ($stripe_plans['data'] as $plan) {
// Get stripe product
$product = $this->stripe->products()->find($plan['product']);
// Push data to $plan container
array_push($plans, [
'plan' => $plan,
'product' => $product,
]);
}
return $plans;
}
/**
* Get all active plans
*
* @return mixed
*/
public function getActivePlans()
{
// Get stripe plans
$stripe_plans = $this->stripe->plans()->all();
// Plans container
$plans = [];
foreach ($stripe_plans['data'] as $plan) {
if ($plan['active']) {
// Get stripe product
$product = $this->stripe->products()->find($plan['product']);
// Push data to $plan container
array_push($plans, [
'plan' => $plan,
'product' => $product,
]);
}
}
return $plans;
}
/**
* Delete plan
*
* @param $slug
*/
public function deletePlan($slug)
{
$this->stripe->plans()->delete($slug);
}
}
+69 -18
View File
@@ -10,6 +10,7 @@ use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Notification; use Illuminate\Support\Facades\Notification;
use Laravel\Cashier\Billable;
use Laravel\Passport\HasApiTokens; use Laravel\Passport\HasApiTokens;
use Rinvex\Subscriptions\Traits\HasSubscriptions; use Rinvex\Subscriptions\Traits\HasSubscriptions;
@@ -53,10 +54,31 @@ use Rinvex\Subscriptions\Traits\HasSubscriptions;
* @method static \Illuminate\Database\Eloquent\Builder|\App\User whereRememberToken($value) * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereRememberToken($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\User whereUpdatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|\App\User whereUpdatedAt($value)
* @mixin \Eloquent * @mixin \Eloquent
* @property string $role
* @property string|null $stripe_id
* @property string|null $card_brand
* @property string|null $card_last_four
* @property string|null $trial_ends_at
* @property-read \Illuminate\Database\Eloquent\Collection|\App\FileManagerFolder[] $favourite_folders
* @property-read int|null $favourite_folders_count
* @property-read mixed $folder_tree
* @property-read mixed $storage
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Invoice[] $invoices
* @property-read int|null $invoices_count
* @property-read \Illuminate\Database\Eloquent\Collection|\App\UserCard[] $payment_cards
* @property-read int|null $payment_cards_count
* @property-read \App\UserSettings|null $settings
* @property-read \Illuminate\Database\Eloquent\Collection|\Laravel\Cashier\Subscription[] $subscriptions
* @property-read int|null $subscriptions_count
* @method static \Illuminate\Database\Eloquent\Builder|\App\User whereCardBrand($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\User whereCardLastFour($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\User whereRole($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\User whereStripeId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\User whereTrialEndsAt($value)
*/ */
class User extends Authenticatable class User extends Authenticatable
{ {
use HasApiTokens, Notifiable, HasSubscriptions; use HasApiTokens, Notifiable, Billable;
/** /**
* The attributes that are mass assignable. * The attributes that are mass assignable.
@@ -95,11 +117,11 @@ class User extends Authenticatable
* *
* @return mixed * @return mixed
*/ */
public function getStorageAttribute() { public function getStorageAttribute()
{
return [ return [
'used' => (float) get_storage_fill_percentage($this->used_capacity, $this->settings->storage_capacity), 'used' => (float)get_storage_fill_percentage($this->used_capacity, $this->settings->storage_capacity),
'capacity' => $this->settings->storage_capacity, 'capacity' => $this->settings->storage_capacity,
'capacity_formatted' => format_gigabytes($this->settings->storage_capacity), 'capacity_formatted' => format_gigabytes($this->settings->storage_capacity),
]; ];
} }
@@ -109,8 +131,8 @@ class User extends Authenticatable
* *
* @return mixed * @return mixed
*/ */
public function getUsedCapacityAttribute() { public function getUsedCapacityAttribute()
{
$user_capacity = $this->files_with_trashed->map(function ($item) { $user_capacity = $this->files_with_trashed->map(function ($item) {
return $item->getRawOriginal(); return $item->getRawOriginal();
})->sum('filesize'); })->sum('filesize');
@@ -118,7 +140,8 @@ class User extends Authenticatable
return $user_capacity; return $user_capacity;
} }
public function getFolderTreeAttribute() { public function getFolderTreeAttribute()
{
return FileManagerFolder::with(['folders.shared', 'shared:token,id,item_id,permission,protected']) return FileManagerFolder::with(['folders.shared', 'shared:token,id,item_id,permission,protected'])
->where('parent_id', 0) ->where('parent_id', 0)
->where('user_id', $this->id) ->where('user_id', $this->id)
@@ -139,6 +162,24 @@ class User extends Authenticatable
return url('/assets/images/' . 'default-avatar.png'); return url('/assets/images/' . 'default-avatar.png');
} }
/**
* Set user billing info
*
* @param $billing
*/
public function setBilling($billing)
{
$this->settings()->update([
'billing_address' => $billing['billing_address'],
'billing_city' => $billing['billing_city'],
'billing_country' => $billing['billing_country'],
'billing_name' => $billing['billing_name'],
'billing_phone_number' => $billing['billing_phone_number'],
'billing_postal_code' => $billing['billing_postal_code'],
'billing_state' => $billing['billing_state'],
]);
}
/** /**
* Send the password reset notification. * Send the password reset notification.
* *
@@ -165,8 +206,8 @@ class User extends Authenticatable
* *
* @return \Illuminate\Database\Eloquent\Relations\HasMany|\Illuminate\Database\Query\Builder * @return \Illuminate\Database\Eloquent\Relations\HasMany|\Illuminate\Database\Query\Builder
*/ */
public function latest_uploads() { public function latest_uploads()
{
return $this->hasMany(FileManagerFile::class)->with(['parent'])->orderBy('created_at', 'DESC')->take(40); return $this->hasMany(FileManagerFile::class)->with(['parent'])->orderBy('created_at', 'DESC')->take(40);
} }
@@ -175,8 +216,8 @@ class User extends Authenticatable
* *
* @return \Illuminate\Database\Eloquent\Relations\HasMany * @return \Illuminate\Database\Eloquent\Relations\HasMany
*/ */
public function files() { public function files()
{
return $this->hasMany(FileManagerFile::class); return $this->hasMany(FileManagerFile::class);
} }
@@ -185,8 +226,8 @@ class User extends Authenticatable
* *
* @return \Illuminate\Database\Eloquent\Relations\HasMany * @return \Illuminate\Database\Eloquent\Relations\HasMany
*/ */
public function files_with_trashed() { public function files_with_trashed()
{
return $this->hasMany(FileManagerFile::class)->withTrashed(); return $this->hasMany(FileManagerFile::class)->withTrashed();
} }
@@ -195,8 +236,8 @@ class User extends Authenticatable
* *
* @return \Illuminate\Database\Eloquent\Relations\HasOne * @return \Illuminate\Database\Eloquent\Relations\HasOne
*/ */
public function settings() { public function settings()
{
return $this->hasOne(UserSettings::class); return $this->hasOne(UserSettings::class);
} }
@@ -205,8 +246,18 @@ class User extends Authenticatable
* *
* @return \Illuminate\Database\Eloquent\Relations\HasOne * @return \Illuminate\Database\Eloquent\Relations\HasOne
*/ */
public function invoices() { public function invoices()
{
return $this->hasMany(Invoice::class); return $this->hasMany(Invoice::class);
} }
/**
* Get user payment cards
*
* @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function payment_cards()
{
return $this->hasMany(UserCard::class);
}
} }
+48
View File
@@ -0,0 +1,48 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
/**
* App\UserCard
*
* @property int $id
* @property int $user_id
* @property string $provider
* @property int $default
* @property string $status
* @property string $card_id
* @property string $brand
* @property string $last4
* @property string $exp_month
* @property string $exp_year
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\User|null $user
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard query()
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereBrand($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereCardId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereDefault($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereExpMonth($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereExpYear($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereLast4($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereProvider($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\UserCard whereUserId($value)
* @mixin \Eloquent
*/
class UserCard extends Model
{
protected $guarded = ['id'];
public function user() {
return $this->hasOne(User::class);
}
}
+4 -2
View File
@@ -9,18 +9,19 @@
"license": "MIT", "license": "MIT",
"require": { "require": {
"php": "^7.2", "php": "^7.2",
"cartalyst/stripe-laravel": "^12.0",
"doctrine/dbal": "^2.10", "doctrine/dbal": "^2.10",
"fideloper/proxy": "^4.0", "fideloper/proxy": "^4.0",
"fruitcake/laravel-cors": "^1.0", "fruitcake/laravel-cors": "^1.0",
"gabrielelana/byte-units": "^0.5.0", "gabrielelana/byte-units": "^0.5.0",
"intervention/image": "^2.5", "intervention/image": "^2.5",
"laravel/cashier": "^12.0",
"laravel/framework": "^7.0", "laravel/framework": "^7.0",
"laravel/passport": "^8.4", "laravel/passport": "^8.4",
"laravel/scout": "^7.2", "laravel/scout": "^7.2",
"laravel/tinker": "^2.0", "laravel/tinker": "^2.0",
"league/flysystem-aws-s3-v3": "^1.0", "league/flysystem-aws-s3-v3": "^1.0",
"league/flysystem-cached-adapter": "^1.0", "league/flysystem-cached-adapter": "^1.0",
"rinvex/laravel-subscriptions": "^4.0",
"teamtnt/laravel-scout-tntsearch-driver": "^8.3" "teamtnt/laravel-scout-tntsearch-driver": "^8.3"
}, },
"require-dev": { "require-dev": {
@@ -50,7 +51,8 @@
"database/factories" "database/factories"
], ],
"files": [ "files": [
"app/Http/helpers.php" "app/Http//Helpers/helpers.php",
"app/Http//Helpers/subscription.php"
] ]
}, },
"autoload-dev": { "autoload-dev": {
Generated
+680 -12
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "32b62c653a002d0eea89b606c0f5982a", "content-hash": "e30e23b1a0df4073ff74004cde20ddb8",
"packages": [ "packages": [
{ {
"name": "asm89/stack-cors", "name": "asm89/stack-cors",
@@ -188,6 +188,122 @@
], ],
"time": "2020-04-15T15:59:35+00:00" "time": "2020-04-15T15:59:35+00:00"
}, },
{
"name": "cartalyst/stripe",
"version": "v2.4.1",
"source": {
"type": "git",
"url": "https://github.com/cartalyst/stripe.git",
"reference": "f7753a8c947aa7c58abad96db8714e1594160b0c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cartalyst/stripe/zipball/f7753a8c947aa7c58abad96db8714e1594160b0c",
"reference": "f7753a8c947aa7c58abad96db8714e1594160b0c",
"shasum": ""
},
"require": {
"guzzlehttp/guzzle": "~6.0",
"php": ">=5.5.9"
},
"require-dev": {
"mockery/mockery": "~0.9",
"phpunit/phpunit": "~4.8"
},
"type": "library",
"extra": {
"component": "package",
"branch-alias": {
"dev-master": "2.4.x-dev"
}
},
"autoload": {
"psr-4": {
"Cartalyst\\Stripe\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Cartalyst LLC",
"email": "help@cartalyst.com",
"homepage": "https://cartalyst.com"
}
],
"description": "A comprehensive Stripe API package.",
"keywords": [
"cartalyst",
"php",
"stripe"
],
"time": "2020-01-03T12:02:51+00:00"
},
{
"name": "cartalyst/stripe-laravel",
"version": "v12.0.0",
"source": {
"type": "git",
"url": "https://github.com/cartalyst/stripe-laravel.git",
"reference": "e171e6721e3491daa7eaa8d85ca6d4536086c327"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cartalyst/stripe-laravel/zipball/e171e6721e3491daa7eaa8d85ca6d4536086c327",
"reference": "e171e6721e3491daa7eaa8d85ca6d4536086c327",
"shasum": ""
},
"require": {
"cartalyst/stripe": "^2.0",
"illuminate/support": "^7.0",
"php": "^7.2.5"
},
"require-dev": {
"cartalyst/php-cs-fixer-config": "^1.0",
"phpunit/phpunit": "^8.0"
},
"type": "library",
"extra": {
"component": "implementation",
"branch-alias": {
"dev-master": "12.0.x-dev"
},
"laravel": {
"providers": [
"Cartalyst\\Stripe\\Laravel\\StripeServiceProvider"
],
"aliases": {
"Stripe": "Cartalyst\\Stripe\\Laravel\\Facades\\Stripe"
}
}
},
"autoload": {
"psr-4": {
"Cartalyst\\Stripe\\Laravel\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Cartalyst LLC",
"email": "help@cartalyst.com",
"homepage": "https://cartalyst.com"
}
],
"description": "Laravel 7 integration for the Cartalyst Stripe package.",
"keywords": [
"cartalyst",
"laravel",
"php",
"stripe"
],
"time": "2020-03-03T18:50:48+00:00"
},
{ {
"name": "defuse/php-encryption", "name": "defuse/php-encryption",
"version": "v2.2.1", "version": "v2.2.1",
@@ -675,6 +791,72 @@
], ],
"time": "2020-05-25T17:44:05+00:00" "time": "2020-05-25T17:44:05+00:00"
}, },
{
"name": "dompdf/dompdf",
"version": "v0.8.5",
"source": {
"type": "git",
"url": "https://github.com/dompdf/dompdf.git",
"reference": "6782abfc090b132134cd6cea0ec6d76f0fce2c56"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dompdf/dompdf/zipball/6782abfc090b132134cd6cea0ec6d76f0fce2c56",
"reference": "6782abfc090b132134cd6cea0ec6d76f0fce2c56",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-mbstring": "*",
"phenx/php-font-lib": "^0.5.1",
"phenx/php-svg-lib": "^0.3.3",
"php": "^7.1"
},
"require-dev": {
"phpunit/phpunit": "^7.5",
"squizlabs/php_codesniffer": "^3.5"
},
"suggest": {
"ext-gd": "Needed to process images",
"ext-gmagick": "Improves image processing performance",
"ext-imagick": "Improves image processing performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-develop": "0.7-dev"
}
},
"autoload": {
"psr-4": {
"Dompdf\\": "src/"
},
"classmap": [
"lib/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1"
],
"authors": [
{
"name": "Fabien Ménager",
"email": "fabien.menager@gmail.com"
},
{
"name": "Brian Sweeney",
"email": "eclecticgeek@gmail.com"
},
{
"name": "Gabriel Bull",
"email": "me@gabrielbull.com"
}
],
"description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
"homepage": "https://github.com/dompdf/dompdf",
"time": "2020-02-20T03:52:51+00:00"
},
{ {
"name": "dragonmantank/cron-expression", "name": "dragonmantank/cron-expression",
"version": "v2.3.0", "version": "v2.3.0",
@@ -1462,6 +1644,80 @@
], ],
"time": "2020-05-20T16:45:56+00:00" "time": "2020-05-20T16:45:56+00:00"
}, },
{
"name": "laravel/cashier",
"version": "v12.0.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/cashier.git",
"reference": "e3479f548a7d167ff1c241e718041162e66306a5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/cashier/zipball/e3479f548a7d167ff1c241e718041162e66306a5",
"reference": "e3479f548a7d167ff1c241e718041162e66306a5",
"shasum": ""
},
"require": {
"dompdf/dompdf": "^0.8.0",
"ext-json": "*",
"illuminate/contracts": "^6.0|^7.0",
"illuminate/database": "^6.0|^7.0",
"illuminate/http": "^6.0|^7.0",
"illuminate/log": "^6.0|^7.0",
"illuminate/notifications": "^6.0|^7.0",
"illuminate/routing": "^6.0|^7.0",
"illuminate/support": "^6.0|^7.0",
"illuminate/view": "^6.0|^7.0",
"moneyphp/money": "^3.2",
"nesbot/carbon": "^2.0",
"php": "^7.2",
"stripe/stripe-php": "^7.0",
"symfony/http-kernel": "^4.3|^5.0",
"symfony/intl": "^4.3|^5.0"
},
"require-dev": {
"mockery/mockery": "^1.0",
"orchestra/testbench": "^4.0|^5.0",
"phpunit/phpunit": "^8.0|^9.0"
},
"suggest": {
"ext-intl": "Allows for more locales besides the default \"en\" when formatting money values."
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "12.x-dev"
},
"laravel": {
"providers": [
"Laravel\\Cashier\\CashierServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Laravel\\Cashier\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylorotwell@gmail.com"
}
],
"description": "Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.",
"keywords": [
"billing",
"laravel",
"stripe"
],
"time": "2020-06-16T18:54:05+00:00"
},
{ {
"name": "laravel/framework", "name": "laravel/framework",
"version": "v7.14.1", "version": "v7.14.1",
@@ -2254,6 +2510,88 @@
], ],
"time": "2020-04-29T22:14:38+00:00" "time": "2020-04-29T22:14:38+00:00"
}, },
{
"name": "moneyphp/money",
"version": "v3.3.1",
"source": {
"type": "git",
"url": "https://github.com/moneyphp/money.git",
"reference": "122664c2621a95180a13c1ac81fea1d2ef20781e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/moneyphp/money/zipball/122664c2621a95180a13c1ac81fea1d2ef20781e",
"reference": "122664c2621a95180a13c1ac81fea1d2ef20781e",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": ">=5.6"
},
"require-dev": {
"cache/taggable-cache": "^0.4.0",
"doctrine/instantiator": "^1.0.5",
"ext-bcmath": "*",
"ext-gmp": "*",
"ext-intl": "*",
"florianv/exchanger": "^1.0",
"florianv/swap": "^3.0",
"friends-of-phpspec/phpspec-code-coverage": "^3.1.1 || ^4.3",
"moneyphp/iso-currencies": "^3.2.1",
"php-http/message": "^1.4",
"php-http/mock-client": "^1.0.0",
"phpspec/phpspec": "^3.4.3",
"phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.5.18 || ^8.5",
"psr/cache": "^1.0",
"symfony/phpunit-bridge": "^4"
},
"suggest": {
"ext-bcmath": "Calculate without integer limits",
"ext-gmp": "Calculate without integer limits",
"ext-intl": "Format Money objects with intl",
"florianv/exchanger": "Exchange rates library for PHP",
"florianv/swap": "Exchange rates library for PHP",
"psr/cache-implementation": "Used for Currency caching"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
}
},
"autoload": {
"psr-4": {
"Money\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mathias Verraes",
"email": "mathias@verraes.net",
"homepage": "http://verraes.net"
},
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com"
},
{
"name": "Frederik Bosch",
"email": "f.bosch@genkgo.nl"
}
],
"description": "PHP implementation of Fowler's Money pattern",
"homepage": "http://moneyphp.org",
"keywords": [
"Value Object",
"money",
"vo"
],
"time": "2020-03-18T17:49:59+00:00"
},
{ {
"name": "monolog/monolog", "name": "monolog/monolog",
"version": "2.1.0", "version": "2.1.0",
@@ -2686,6 +3024,83 @@
], ],
"time": "2018-07-02T15:55:56+00:00" "time": "2018-07-02T15:55:56+00:00"
}, },
{
"name": "phenx/php-font-lib",
"version": "0.5.2",
"source": {
"type": "git",
"url": "https://github.com/PhenX/php-font-lib.git",
"reference": "ca6ad461f032145fff5971b5985e5af9e7fa88d8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PhenX/php-font-lib/zipball/ca6ad461f032145fff5971b5985e5af9e7fa88d8",
"reference": "ca6ad461f032145fff5971b5985e5af9e7fa88d8",
"shasum": ""
},
"require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5 || ^6 || ^7"
},
"type": "library",
"autoload": {
"psr-4": {
"FontLib\\": "src/FontLib"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0"
],
"authors": [
{
"name": "Fabien Ménager",
"email": "fabien.menager@gmail.com"
}
],
"description": "A library to read, parse, export and make subsets of different types of font files.",
"homepage": "https://github.com/PhenX/php-font-lib",
"time": "2020-03-08T15:31:32+00:00"
},
{
"name": "phenx/php-svg-lib",
"version": "v0.3.3",
"source": {
"type": "git",
"url": "https://github.com/PhenX/php-svg-lib.git",
"reference": "5fa61b65e612ce1ae15f69b3d223cb14ecc60e32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PhenX/php-svg-lib/zipball/5fa61b65e612ce1ae15f69b3d223cb14ecc60e32",
"reference": "5fa61b65e612ce1ae15f69b3d223cb14ecc60e32",
"shasum": ""
},
"require": {
"sabberworm/php-css-parser": "^8.3"
},
"require-dev": {
"phpunit/phpunit": "^5.5|^6.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Svg\\": "src/Svg"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0"
],
"authors": [
{
"name": "Fabien Ménager",
"email": "fabien.menager@gmail.com"
}
],
"description": "A library to read, parse and export to PDF SVG files.",
"homepage": "https://github.com/PhenX/php-svg-lib",
"time": "2019-09-11T20:02:13+00:00"
},
{ {
"name": "php-http/message-factory", "name": "php-http/message-factory",
"version": "v1.0.2", "version": "v1.0.2",
@@ -3546,16 +3961,16 @@
}, },
{ {
"name": "rinvex/laravel-subscriptions", "name": "rinvex/laravel-subscriptions",
"version": "v4.0.6", "version": "dev-subscription-query-fix",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/rinvex/laravel-subscriptions.git", "url": "https://github.com/MakingCG/laravel-subscriptions.git",
"reference": "f54539dfc98dbfa54d59e28c563e981fd28ad1f0" "reference": "0ed71077f34b9ca21c3755fcf09463b37802c0e8"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/rinvex/laravel-subscriptions/zipball/f54539dfc98dbfa54d59e28c563e981fd28ad1f0", "url": "https://api.github.com/repos/MakingCG/laravel-subscriptions/zipball/0ed71077f34b9ca21c3755fcf09463b37802c0e8",
"reference": "f54539dfc98dbfa54d59e28c563e981fd28ad1f0", "reference": "0ed71077f34b9ca21c3755fcf09463b37802c0e8",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -3590,20 +4005,29 @@
"Rinvex\\Subscriptions\\": "src" "Rinvex\\Subscriptions\\": "src"
} }
}, },
"notification-url": "https://packagist.org/downloads/", "autoload-dev": {
"psr-4": {
"Rinvex\\Subscriptions\\Tests\\": "tests"
}
},
"scripts": {
"test": [
"vendor/bin/phpunit"
]
},
"license": [ "license": [
"MIT" "MIT"
], ],
"authors": [ "authors": [
{ {
"name": "Rinvex LLC", "name": "Rinvex LLC",
"email": "help@rinvex.com", "homepage": "https://rinvex.com",
"homepage": "https://rinvex.com" "email": "help@rinvex.com"
}, },
{ {
"name": "Abdelrahman Omran", "name": "Abdelrahman Omran",
"email": "me@omranic.com",
"homepage": "https://omranic.com", "homepage": "https://omranic.com",
"email": "me@omranic.com",
"role": "Project Lead" "role": "Project Lead"
}, },
{ {
@@ -3622,7 +4046,13 @@
"subscription", "subscription",
"value" "value"
], ],
"time": "2020-05-30T02:21:52+00:00" "support": {
"email": "help@rinvex.com",
"issues": "https://github.com/rinvex/laravel-subscriptions/issues",
"source": "https://github.com/rinvex/laravel-subscriptions",
"docs": "https://github.com/rinvex/laravel-subscriptions/blob/master/README.md"
},
"time": "2020-06-15T13:17:55+00:00"
}, },
{ {
"name": "rinvex/laravel-support", "name": "rinvex/laravel-support",
@@ -3707,6 +4137,51 @@
], ],
"time": "2020-05-30T02:22:01+00:00" "time": "2020-05-30T02:22:01+00:00"
}, },
{
"name": "sabberworm/php-css-parser",
"version": "8.3.1",
"source": {
"type": "git",
"url": "https://github.com/sabberworm/PHP-CSS-Parser.git",
"reference": "d217848e1396ef962fb1997cf3e2421acba7f796"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/d217848e1396ef962fb1997cf3e2421acba7f796",
"reference": "d217848e1396ef962fb1997cf3e2421acba7f796",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
},
"require-dev": {
"codacy/coverage": "^1.4",
"phpunit/phpunit": "~4.8"
},
"type": "library",
"autoload": {
"psr-0": {
"Sabberworm\\CSS": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Raphael Schweikert"
}
],
"description": "Parser for CSS Files written in PHP",
"homepage": "http://www.sabberworm.com/blog/2010/6/10/php-css-parser",
"keywords": [
"css",
"parser",
"stylesheet"
],
"time": "2020-06-01T09:10:00+00:00"
},
{ {
"name": "spatie/eloquent-sortable", "name": "spatie/eloquent-sortable",
"version": "3.8.0", "version": "3.8.0",
@@ -3934,6 +4409,63 @@
], ],
"time": "2020-04-30T10:01:26+00:00" "time": "2020-04-30T10:01:26+00:00"
}, },
{
"name": "stripe/stripe-php",
"version": "v7.37.1",
"source": {
"type": "git",
"url": "https://github.com/stripe/stripe-php.git",
"reference": "e3e29131a131785c3d2f85556b0154e8170e9bba"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/stripe/stripe-php/zipball/e3e29131a131785c3d2f85556b0154e8170e9bba",
"reference": "e3e29131a131785c3d2f85556b0154e8170e9bba",
"shasum": ""
},
"require": {
"ext-curl": "*",
"ext-json": "*",
"ext-mbstring": "*",
"php": ">=5.6.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "2.16.1",
"php-coveralls/php-coveralls": "^2.1",
"phpunit/phpunit": "^5.7",
"squizlabs/php_codesniffer": "^3.3",
"symfony/process": "~3.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
}
},
"autoload": {
"psr-4": {
"Stripe\\": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Stripe and contributors",
"homepage": "https://github.com/stripe/stripe-php/contributors"
}
],
"description": "Stripe PHP Library",
"homepage": "https://stripe.com/",
"keywords": [
"api",
"payment processing",
"stripe"
],
"time": "2020-06-11T16:27:35+00:00"
},
{ {
"name": "swiftmailer/swiftmailer", "name": "swiftmailer/swiftmailer",
"version": "v6.2.3", "version": "v6.2.3",
@@ -4570,6 +5102,82 @@
"homepage": "https://symfony.com", "homepage": "https://symfony.com",
"time": "2020-05-31T06:14:18+00:00" "time": "2020-05-31T06:14:18+00:00"
}, },
{
"name": "symfony/intl",
"version": "v5.1.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/intl.git",
"reference": "b21d69ebb33adfcb7e6d8b0a0a8799db7090705b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/intl/zipball/b21d69ebb33adfcb7e6d8b0a0a8799db7090705b",
"reference": "b21d69ebb33adfcb7e6d8b0a0a8799db7090705b",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"symfony/polyfill-intl-icu": "~1.0",
"symfony/polyfill-php80": "^1.15"
},
"require-dev": {
"symfony/filesystem": "^4.4|^5.0"
},
"suggest": {
"ext-intl": "to use the component with locales other than \"en\""
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.1-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Component\\Intl\\": ""
},
"classmap": [
"Resources/stubs"
],
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Bernhard Schussek",
"email": "bschussek@gmail.com"
},
{
"name": "Eriksen Costa",
"email": "eriksen.costa@infranology.com.br"
},
{
"name": "Igor Wiedler",
"email": "igor@wiedler.ch"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "A PHP replacement layer for the C intl extension that includes additional data from the ICU library.",
"homepage": "https://symfony.com",
"keywords": [
"i18n",
"icu",
"internationalization",
"intl",
"l10n",
"localization"
],
"time": "2020-05-30T20:35:19+00:00"
},
{ {
"name": "symfony/mime", "name": "symfony/mime",
"version": "v5.1.0", "version": "v5.1.0",
@@ -4810,6 +5418,64 @@
], ],
"time": "2020-05-12T16:47:27+00:00" "time": "2020-05-12T16:47:27+00:00"
}, },
{
"name": "symfony/polyfill-intl-icu",
"version": "v1.17.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-icu.git",
"reference": "4ef3923e4a86e1b6ef72d42be59dbf7d33a685e3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/4ef3923e4a86e1b6ef72d42be59dbf7d33a685e3",
"reference": "4ef3923e4a86e1b6ef72d42be59dbf7d33a685e3",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"symfony/intl": "~2.3|~3.0|~4.0|~5.0"
},
"suggest": {
"ext-intl": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.17-dev"
}
},
"autoload": {
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for intl's ICU-related data and classes",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"icu",
"intl",
"polyfill",
"portable",
"shim"
],
"time": "2020-05-12T16:14:59+00:00"
},
{ {
"name": "symfony/polyfill-intl-idn", "name": "symfony/polyfill-intl-idn",
"version": "v1.17.0", "version": "v1.17.0",
@@ -8661,7 +9327,9 @@
], ],
"aliases": [], "aliases": [],
"minimum-stability": "dev", "minimum-stability": "dev",
"stability-flags": [], "stability-flags": {
"rinvex/laravel-subscriptions": 20
},
"prefer-stable": true, "prefer-stable": true,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
+38 -39
View File
@@ -193,51 +193,50 @@ return [
'aliases' => [ 'aliases' => [
'App' => Illuminate\Support\Facades\App::class, 'App' => Illuminate\Support\Facades\App::class,
'Arr' => Illuminate\Support\Arr::class, 'Arr' => Illuminate\Support\Arr::class,
'Artisan' => Illuminate\Support\Facades\Artisan::class, 'Artisan' => Illuminate\Support\Facades\Artisan::class,
'Auth' => Illuminate\Support\Facades\Auth::class, 'Auth' => Illuminate\Support\Facades\Auth::class,
'Blade' => Illuminate\Support\Facades\Blade::class, 'Blade' => Illuminate\Support\Facades\Blade::class,
'Broadcast' => Illuminate\Support\Facades\Broadcast::class, 'Broadcast' => Illuminate\Support\Facades\Broadcast::class,
'Bus' => Illuminate\Support\Facades\Bus::class, 'Bus' => Illuminate\Support\Facades\Bus::class,
'Cache' => Illuminate\Support\Facades\Cache::class, 'Cache' => Illuminate\Support\Facades\Cache::class,
'Config' => Illuminate\Support\Facades\Config::class, 'Config' => Illuminate\Support\Facades\Config::class,
'Cookie' => Illuminate\Support\Facades\Cookie::class, 'Cookie' => Illuminate\Support\Facades\Cookie::class,
'Crypt' => Illuminate\Support\Facades\Crypt::class, 'Crypt' => Illuminate\Support\Facades\Crypt::class,
'DB' => Illuminate\Support\Facades\DB::class, 'DB' => Illuminate\Support\Facades\DB::class,
'Eloquent' => Illuminate\Database\Eloquent\Model::class, 'Eloquent' => Illuminate\Database\Eloquent\Model::class,
'Event' => Illuminate\Support\Facades\Event::class, 'Event' => Illuminate\Support\Facades\Event::class,
'File' => Illuminate\Support\Facades\File::class, 'File' => Illuminate\Support\Facades\File::class,
'Gate' => Illuminate\Support\Facades\Gate::class, 'Gate' => Illuminate\Support\Facades\Gate::class,
'Hash' => Illuminate\Support\Facades\Hash::class, 'Hash' => Illuminate\Support\Facades\Hash::class,
'Lang' => Illuminate\Support\Facades\Lang::class, 'Lang' => Illuminate\Support\Facades\Lang::class,
'Log' => Illuminate\Support\Facades\Log::class, 'Log' => Illuminate\Support\Facades\Log::class,
'Mail' => Illuminate\Support\Facades\Mail::class, 'Mail' => Illuminate\Support\Facades\Mail::class,
'Notification' => Illuminate\Support\Facades\Notification::class, 'Notification' => Illuminate\Support\Facades\Notification::class,
'Password' => Illuminate\Support\Facades\Password::class, 'Password' => Illuminate\Support\Facades\Password::class,
'Queue' => Illuminate\Support\Facades\Queue::class, 'Queue' => Illuminate\Support\Facades\Queue::class,
'Redirect' => Illuminate\Support\Facades\Redirect::class, 'Redirect' => Illuminate\Support\Facades\Redirect::class,
'Redis' => Illuminate\Support\Facades\Redis::class, 'Redis' => Illuminate\Support\Facades\Redis::class,
'Request' => Illuminate\Support\Facades\Request::class, 'Request' => Illuminate\Support\Facades\Request::class,
'Response' => Illuminate\Support\Facades\Response::class, 'Response' => Illuminate\Support\Facades\Response::class,
'Route' => Illuminate\Support\Facades\Route::class, 'Route' => Illuminate\Support\Facades\Route::class,
'Schema' => Illuminate\Support\Facades\Schema::class, 'Schema' => Illuminate\Support\Facades\Schema::class,
'Session' => Illuminate\Support\Facades\Session::class, 'Session' => Illuminate\Support\Facades\Session::class,
'Storage' => Illuminate\Support\Facades\Storage::class, 'Storage' => Illuminate\Support\Facades\Storage::class,
'Str' => Illuminate\Support\Str::class, 'Str' => Illuminate\Support\Str::class,
'URL' => Illuminate\Support\Facades\URL::class, 'URL' => Illuminate\Support\Facades\URL::class,
'Validator' => Illuminate\Support\Facades\Validator::class, 'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class, 'View' => Illuminate\Support\Facades\View::class,
'Image' => Intervention\Image\Facades\Image::class,
'Image' => Intervention\Image\Facades\Image::class 'Stripe' => Cartalyst\Stripe\Laravel\Facades\Stripe::class,
], ],
'deploy_secret' => env('APP_DEPLOY_SECRET'), 'deploy_secret' => env('APP_DEPLOY_SECRET'),
'deploy_branch' => env('APP_DEPLOY_BRANCH'), 'deploy_branch' => env('APP_DEPLOY_BRANCH'),
'debug_blacklist' => [ 'debug_blacklist' => [
'_ENV' => [ '_ENV' => [
'APP_KEY', 'APP_KEY',
'DB_PASSWORD', 'DB_PASSWORD',
'REDIS_PASSWORD', 'REDIS_PASSWORD',
@@ -262,7 +261,7 @@ return [
'AWS_ACCESS_KEY_ID', 'AWS_ACCESS_KEY_ID',
'AWS_SECRET_ACCESS_KEY', 'AWS_SECRET_ACCESS_KEY',
], ],
'_POST' => [ '_POST' => [
'password', 'password',
], ],
], ],
-1
View File
@@ -34,5 +34,4 @@ return [
'client_id' => env('PASSPORT_CLIENT_ID'), 'client_id' => env('PASSPORT_CLIENT_ID'),
'client_secret' => env('PASSPORT_CLIENT_SECRET'), 'client_secret' => env('PASSPORT_CLIENT_SECRET'),
], ],
]; ];
+12
View File
@@ -0,0 +1,12 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use App\UserCard;
use Faker\Generator as Faker;
$factory->define(UserCard::class, function (Faker $faker) {
return [
//
];
});
@@ -26,7 +26,7 @@ class CreateFileManagerFiles extends Migration
$table->text('mimetype')->nullable(); $table->text('mimetype')->nullable();
$table->text('filesize')->nullable(); $table->text('filesize')->nullable();
$table->enum('type', ['image', 'file'])->nullable(); $table->text('type')->nullable();
$table->softDeletes(); $table->softDeletes();
$table->timestamps(); $table->timestamps();
@@ -1,32 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class ChangeTypeAttributeInFileManagerFilesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('file_manager_files', function (Blueprint $table) {
DB::statement('ALTER TABLE file_manager_files MODIFY type TEXT;');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('file_manager_files', function (Blueprint $table) {
//
});
}
}
@@ -23,6 +23,7 @@ class CreatePaymentGatewaysTable extends Migration
$table->text('client_id')->nullable(); $table->text('client_id')->nullable();
$table->text('secret')->nullable(); $table->text('secret')->nullable();
$table->text('webhook')->nullable(); $table->text('webhook')->nullable();
$table->bigInteger('payment_processed')->default(0);
$table->longText('optional')->nullable(); $table->longText('optional')->nullable();
}); });
} }
@@ -17,6 +17,7 @@ class CreateInvoicesTable extends Migration
$table->bigIncrements('id'); $table->bigIncrements('id');
$table->text('token'); $table->text('token');
$table->text('order'); $table->text('order');
$table->text('provider');
$table->text('user_id'); $table->text('user_id');
$table->text('plan_id'); $table->text('plan_id');
$table->longText('seller'); $table->longText('seller');
@@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUserCardsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('user_cards', function (Blueprint $table) {
$table->id();
$table->bigInteger('user_id');
$table->text('provider');
$table->text('card_id');
$table->text('brand');
$table->text('last4');
$table->text('exp_month');
$table->text('exp_year');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('user_cards');
}
}
@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddStatusToUserCardsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('user_cards', function (Blueprint $table) {
$table->text('status')->after('provider');
$table->boolean('default')->default(0)->after('provider');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('user_cards', function (Blueprint $table) {
$table->removeColumn('status');
$table->removeColumn('default');
});
}
}
@@ -1,69 +0,0 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreatePlansTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::create(config('rinvex.subscriptions.tables.plans'), function (Blueprint $table) {
// Columns
$table->increments('id');
$table->string('slug');
$table->{$this->jsonable()}('name');
$table->{$this->jsonable()}('description')->nullable();
$table->boolean('is_active')->default(true);
$table->decimal('price')->default('0.00');
$table->decimal('signup_fee')->default('0.00');
$table->string('currency', 3);
$table->smallInteger('trial_period')->unsigned()->default(0);
$table->string('trial_interval')->default('day');
$table->smallInteger('invoice_period')->unsigned()->default(0);
$table->string('invoice_interval')->default('month');
$table->smallInteger('grace_period')->unsigned()->default(0);
$table->string('grace_interval')->default('day');
$table->tinyInteger('prorate_day')->unsigned()->nullable();
$table->tinyInteger('prorate_period')->unsigned()->nullable();
$table->tinyInteger('prorate_extend_due')->unsigned()->nullable();
$table->smallInteger('active_subscribers_limit')->unsigned()->nullable();
$table->mediumInteger('sort_order')->unsigned()->default(0);
$table->timestamps();
$table->softDeletes();
// Indexes
$table->unique('slug');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down(): void
{
Schema::dropIfExists(config('rinvex.subscriptions.tables.plans'));
}
/**
* Get jsonable column data type.
*
* @return string
*/
protected function jsonable(): string
{
$driverName = DB::connection()->getPdo()->getAttribute(PDO::ATTR_DRIVER_NAME);
$dbVersion = DB::connection()->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION);
$isOldVersion = version_compare($dbVersion, '5.7.8', 'lt');
return $driverName === 'mysql' && $isOldVersion ? 'text' : 'json';
}
}
@@ -1,61 +0,0 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreatePlanFeaturesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::create(config('rinvex.subscriptions.tables.plan_features'), function (Blueprint $table) {
// Columns
$table->increments('id');
$table->integer('plan_id')->unsigned();
$table->string('slug');
$table->{$this->jsonable()}('name');
$table->{$this->jsonable()}('description')->nullable();
$table->string('value');
$table->smallInteger('resettable_period')->unsigned()->default(0);
$table->string('resettable_interval')->default('month');
$table->mediumInteger('sort_order')->unsigned()->default(0);
$table->timestamps();
$table->softDeletes();
// Indexes
$table->unique(['plan_id', 'slug']);
$table->foreign('plan_id')->references('id')->on(config('rinvex.subscriptions.tables.plans'))
->onDelete('cascade')->onUpdate('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down(): void
{
Schema::dropIfExists(config('rinvex.subscriptions.tables.plan_features'));
}
/**
* Get jsonable column data type.
*
* @return string
*/
protected function jsonable(): string
{
$driverName = DB::connection()->getPdo()->getAttribute(PDO::ATTR_DRIVER_NAME);
$dbVersion = DB::connection()->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION);
$isOldVersion = version_compare($dbVersion, '5.7.8', 'lt');
return $driverName === 'mysql' && $isOldVersion ? 'text' : 'json';
}
}
@@ -1,63 +0,0 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreatePlanSubscriptionsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::create(config('rinvex.subscriptions.tables.plan_subscriptions'), function (Blueprint $table) {
$table->increments('id');
$table->morphs('user');
$table->integer('plan_id')->unsigned();
$table->string('slug');
$table->{$this->jsonable()}('name');
$table->{$this->jsonable()}('description')->nullable();
$table->dateTime('trial_ends_at')->nullable();
$table->dateTime('starts_at')->nullable();
$table->dateTime('ends_at')->nullable();
$table->dateTime('cancels_at')->nullable();
$table->dateTime('canceled_at')->nullable();
$table->string('timezone')->nullable();
$table->timestamps();
$table->softDeletes();
// Indexes
$table->unique('slug');
$table->foreign('plan_id')->references('id')->on(config('rinvex.subscriptions.tables.plans'))
->onDelete('cascade')->onUpdate('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down(): void
{
Schema::dropIfExists(config('rinvex.subscriptions.tables.plan_subscriptions'));
}
/**
* Get jsonable column data type.
*
* @return string
*/
protected function jsonable(): string
{
$driverName = DB::connection()->getPdo()->getAttribute(PDO::ATTR_DRIVER_NAME);
$dbVersion = DB::connection()->getPdo()->getAttribute(PDO::ATTR_SERVER_VERSION);
$isOldVersion = version_compare($dbVersion, '5.7.8', 'lt');
return $driverName === 'mysql' && $isOldVersion ? 'text' : 'json';
}
}
@@ -1,44 +0,0 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreatePlanSubscriptionUsageTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::create(config('rinvex.subscriptions.tables.plan_subscription_usage'), function (Blueprint $table) {
$table->increments('id');
$table->integer('subscription_id')->unsigned();
$table->integer('feature_id')->unsigned();
$table->smallInteger('used')->unsigned();
$table->dateTime('valid_until')->nullable();
$table->string('timezone')->nullable();
$table->timestamps();
$table->softDeletes();
$table->unique(['subscription_id', 'feature_id']);
$table->foreign('subscription_id')->references('id')->on(config('rinvex.subscriptions.tables.plan_subscriptions'))
->onDelete('cascade')->onUpdate('cascade');
$table->foreign('feature_id')->references('id')->on(config('rinvex.subscriptions.tables.plan_features'))
->onDelete('cascade')->onUpdate('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down(): void
{
Schema::dropIfExists(config('rinvex.subscriptions.tables.plan_subscription_usage'));
}
}
+1
View File
@@ -0,0 +1 @@
<svg width="780" height="500" enable-background="new 0 0 780 500" version="1.1" viewBox="0 0 780 500" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M40,0h700c22.092,0,40,17.909,40,40v420c0,22.092-17.908,40-40,40H40c-22.091,0-40-17.908-40-40V40 C0,17.909,17.909,0,40,0z" fill="#fff"/><path d="m114.26 257.95c-4.8 0.601-13.203 2.4-18.004 6.601-14.403 12.599-6.001 34.798 23.406 34.798 16.803 0 33.607-10.8 46.81-27.6-19.204-9-34.808-15.6-52.212-13.799z" fill="#2BA6DF"/><path d="m221.83 273.4c27.006 9 33.007 9.6 33.007 9.6v-95.998c0-16.199-13.203-29.398-30.006-29.398h-126.03c-16.204 0-30.007 13.2-30.007 29.4v126c0 16.199 13.203 29.398 30.007 29.398h126.03c16.203 0 30.006-13.2 30.006-29.4v-1.199s-48.01-19.799-72.616-31.799c-16.203 19.799-37.208 32.399-59.413 32.399-37.208 0-49.811-32.399-31.807-53.399 3.6-4.8 10.202-9 20.404-11.399 15.604-3.601 40.81 2.399 64.215 10.2 4.2-7.801 7.801-16.2 10.202-25.2h-72.616v-7.2h37.208v-14.399h-45.61v-7.2h45.61v-18.599s0-3 3-3h18.005v22.2h45.01v6.601h-45.01v13.199h36.608c-3.6 14.399-9.002 27.599-15.604 38.998 12.003 4.2 22.205 7.801 29.407 10.199z" fill="#2BA6DF"/><path d="m340.49 201.22l39.608 85.797h-21.005l-10.802-22.801h-32.408l-10.802 22.801h-13.803l40.209-83.398s1.199-2.4 3.6-2.4h5.402v1e-3zm-21.006 55.197h25.206l-12.603-27-12.603 27zm87.448 31.282h-19.204v-81.598c0-2.399 1.2-3.601 4.2-3.601h15.004v85.199zm36.007-0.558h-19.203v-61.798c0-2.4 1.199-3.601 4.199-3.601h15.004v65.399zm23.483-64.116h12.604v6c2.4-1.8 4.801-3.601 7.801-4.2 3.002-1.199 6.002-1.8 10.203-1.8 4.199 0 7.801 0.601 10.803 2.399 3.6 1.801 6 3.601 8.4 6.601s4.201 6 5.402 9.601c1.199 3.6 1.799 7.199 1.799 11.398 0 5.4-0.6 10.2-2.398 14.4-1.801 4.199-3.602 8.399-6.602 11.399-3.002 3-6.002 5.999-10.203 7.8-4.201 1.799-8.402 2.399-13.203 2.399h-6.602c-1.799 0-3.6-0.601-5.4-1.2v21.6h-19.206v-78.598c-0.599-7.799 2.401-7.799 6.602-7.799zm12.603 46.798c0 1.801 0 3 0.6 4.2s1.201 2.399 2.4 3.601c1.199 1.199 2.4 1.799 3.602 2.399 1.199 0.601 3 0.601 4.801 0.601 1.799 0 3-0.601 4.801-1.801 1.199-1.199 3-2.4 3.602-4.8 0.6-2.399 1.799-4.2 2.398-7.2 0.602-3 1.201-5.4 1.201-9 0-4.199 0-7.799-0.6-10.799-0.602-3-1.201-5.4-2.4-7.8-1.201-1.8-2.4-3.601-3.6-4.8-1.201-1.2-3.002-1.801-4.803-1.801-1.199 0-2.4 0-3.6 0.601-1.201 0.601-2.4 1.2-3.002 1.8-1.199 0.6-1.799 1.801-3 2.4-1.201 1.199-1.801 1.8-2.4 3v29.4-1e-3zm81.028-46.798c4.801 0 8.402 0.601 12.002 1.2 3 0.6 6.002 1.8 8.402 3.6 2.4 1.801 3.6 3.6 4.801 5.4 1.199 2.4 1.801 4.8 1.801 7.199v47.398h-19.205v-4.8c-1.199 1.2-2.398 1.8-3 2.399l-3.602 1.801c-1.199 0.6-2.4 1.199-4.199 1.199-1.801 0.601-3.602 0.601-5.402 0.601-3 0-5.4-0.601-7.803-1.2-2.398-0.6-4.199-1.8-6-3.6-1.801-1.2-3.002-3-3.602-5.4-0.6-1.8-1.199-4.199-1.199-6.6s0.6-4.801 1.199-6.6c0.6-1.801 2.4-3.601 3.602-5.4 1.199-1.801 3.6-3 5.4-4.2 2.4-1.2 4.201-2.399 7.203-3.601 2.398-1.198 4.799-1.799 7.801-2.998 2.4-0.601 5.4-1.801 7.801-2.4l2.4-0.6v-6.601c0-1.8 0-3.6-0.6-5.399l-1.801-3.601c-0.6-0.6-1.799-1.8-2.398-1.8-1.201-0.6-2.4-0.6-3.602-0.6 0 0-10.203 0-19.205 7.8l-4.201-6.601c-1.798 1.203 9.004-6.596 23.407-6.596zm6.599 31.799c-1.799 0.601-3.6 1.8-5.398 3l-5.402 3.601c-1.801 1.199-3 2.399-3.6 4.199-1.201 1.2-1.201 3-1.201 4.2s0 2.4 0.6 3.601c0.6 1.199 0.6 1.8 1.199 3 0.602 1.199 1.201 1.199 1.801 1.8 0.6 0.6 1.201 0.6 2.4 0.6 1.801 0 3.002-0.6 4.801-1.2l5.402-3.6v-19.2h-0.602zm46.555 53.998h-12.604l-0.6-11.399s15.604 0.6 20.404-5.4c1.801-2.4 3-8.999 3-8.999l-31.205-59.998h21.004l19.805 38.999 17.404-39h13.803l-31.807 70.799c-1.2 2.4-6.001 14.999-19.204 14.998z" fill="#3F3A39"/><ellipse cx="433.4" cy="204.1" rx="9.602" ry="8.5" fill="#2BA6DF"/><path d="m683.68 172.91h-6.602v-1.201h14.402v1.201h-6.6v18.599h-1.801v-18.599h0.601zm10.716-1.201h2.4l4.201 11.4 1.801 4.2c0.6-1.2 1.199-3 1.799-4.2l4.201-11.4h2.4v20.4h-2.398v-18l-1.8 4.801-4.201 12h-1.199l-4.201-12-1.801-4.801v18h-1.801v-20.4h0.599z" fill="#3F3A39"/></svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

+1
View File
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 4.9 KiB

+1
View File
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

+1
View File
@@ -0,0 +1 @@
<svg width="780" height="500" enable-background="new 0 0 780 500" version="1.1" viewBox="0 0 780 500" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M40,0h700c22.092,0,40,17.909,40,40v420c0,22.092-17.908,40-40,40H40c-22.091,0-40-17.908-40-40V40 C0,17.909,17.909,0,40,0z" fill="#0079BE"/><path d="m599.93 251.45c0-99.415-82.98-168.13-173.9-168.1h-78.242c-92.003-0.033-167.73 68.705-167.73 168.1 0 90.93 75.727 165.64 167.73 165.2h78.242c90.914 0.436 173.9-74.294 173.9-165.2z" fill="#fff"/><path d="m348.28 97.43c-84.07 0.027-152.19 68.308-152.21 152.58 0.02 84.258 68.144 152.53 152.21 152.56 84.09-0.027 152.23-68.303 152.24-152.56-0.011-84.272-68.149-152.55-152.24-152.58z" fill="#0079BE"/><path d="m252.07 249.6c0.08-41.181 25.746-76.297 61.94-90.25v180.48c-36.194-13.948-61.861-49.045-61.94-90.23zm131 90.274v-180.53c36.207 13.92 61.914 49.057 61.979 90.257-0.065 41.212-25.772 76.322-61.979 90.269z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 945 B

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg width="780" height="500" viewBox="0 0 780 500" xmlns="http://www.w3.org/2000/svg"><g fill-rule="evenodd"><path d="M54.992 0C24.627 0 0 24.63 0 55.004v390.992C0 476.376 24.619 501 54.992 501h670.016C755.373 501 780 476.37 780 445.996V55.004C780 24.624 755.381 0 725.008 0H54.992z" fill="#4D4D4D"/><path d="M327.152 161.893c8.837 0 16.248 1.784 25.268 6.09v22.751c-8.544-7.863-15.955-11.154-25.756-11.154-19.264 0-34.414 15.015-34.414 34.05 0 20.075 14.681 34.196 35.37 34.196 9.312 0 16.586-3.12 24.8-10.857v22.763c-9.341 4.14-16.911 5.776-25.756 5.776-31.278 0-55.582-22.596-55.582-51.737 0-28.826 24.951-51.878 56.07-51.878zm-97.113.627c11.546 0 22.11 3.72 30.943 10.994l-10.748 13.248c-5.35-5.646-10.41-8.028-16.564-8.028-8.853 0-15.3 4.745-15.3 10.989 0 5.354 3.619 8.188 15.944 12.482 23.365 8.044 30.29 15.176 30.29 30.926 0 19.193-14.976 32.553-36.32 32.553-15.63 0-26.994-5.795-36.458-18.872l13.268-12.03c4.73 8.61 12.622 13.222 22.42 13.222 9.163 0 15.947-5.952 15.947-13.984 0-4.164-2.055-7.734-6.158-10.258-2.066-1.195-6.158-2.977-14.2-5.647-19.291-6.538-25.91-13.527-25.91-27.185 0-16.225 14.214-28.41 32.846-28.41zm234.723 1.728h22.437l28.084 66.592 28.446-66.592h22.267l-45.494 101.686h-11.053l-44.687-101.686zm-397.348.152h30.15c33.312 0 56.534 20.382 56.534 49.641 0 14.59-7.104 28.696-19.118 38.057-10.108 7.901-21.626 11.445-37.574 11.445H67.414V164.4zm96.135 0h20.54v99.143h-20.54V164.4zm411.734 0h58.252v16.8H595.81v22.005h36.336v16.791h-36.336v26.762h37.726v16.785h-58.252V164.4zm71.858 0h30.455c23.69 0 37.265 10.71 37.265 29.272 0 15.18-8.514 25.14-23.986 28.105l33.148 41.766h-25.26l-28.429-39.828h-2.678v39.828h-20.515V164.4zm20.515 15.616v30.025h6.002c13.117 0 20.069-5.362 20.069-15.328 0-9.648-6.954-14.697-19.745-14.697h-6.326zM87.94 181.199v65.559h5.512c13.273 0 21.656-2.394 28.11-7.88 7.103-5.955 11.376-15.465 11.376-24.98 0-9.499-4.273-18.725-11.376-24.681-6.785-5.78-14.837-8.018-28.11-8.018H87.94z" fill="#FFF"/><path d="m415.13 161.21c30.941 0 56.022 23.58 56.022 52.709v0.033c0 29.13-25.081 52.742-56.021 52.742s-56.022-23.613-56.022-52.742v-0.033c0-29.13 25.082-52.71 56.022-52.71zm364.85 127.15c-26.05 18.33-221.08 149.34-558.75 212.62h503.76c30.365 0 54.992-24.63 54.992-55.004v-157.62z" fill="#F47216"/></g></svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

+1
View File
@@ -0,0 +1 @@
<svg width="780" height="500" enable-background="new 0 0 780 500" version="1.1" viewBox="0 0 780 500" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M41.68,0h698.14c23.027,0,41.68,18.983,41.68,42.42v414.66c0,23.437-18.652,42.42-41.68,42.42H41.68 C18.652,499.5,0,480.517,0,457.08V42.42C0,18.983,18.652,0,41.68,0z"/><path d="m167.25 181.4c6.8-2.3 14.1-3.5 21.7-3.5 33.2 0 60.9 23.601 67.2 54.9l47-9.6c-10.8-53.2-57.8-93.301-114.2-93.301-12.9 0-25.3 2.101-36.9 6l15.2 45.501z" fill="#FFF100"/><path d="m111.75 333.8l31.8-36c-14.2-12.6-23.1-30.9-23.1-51.4 0-20.399 8.9-38.8 23.1-51.3l-31.8-35.899c-24.1 21.399-39.3 52.5-39.3 87.3 0 34.699 15.2 65.898 39.3 87.299z" fill="#00A3DF"/><path d="m256.15 260.2c-6.4 31.3-34 54.8-67.2 54.8-7.6 0-14.9-1.2-21.8-3.5l-15.2 45.5c11.6 3.899 24.1 6 37 6 56.4 0 103.4-40 114.2-93.2l-47-9.6z" fill="#EE4023"/><path d="m459.75 292.4c-7.8 7.601-18.3 12.2-29.9 12-8-0.1-15.399-2.5-21.6-6.5l-15.601 24.801c10.7 6.699 23.2 10.699 36.801 10.899 19.699 0.3 37.699-7.5 50.8-20.2l-20.5-21zm-28.2-101.1c-39.2-0.6-71.6 30.8-72.2 70-0.2 14.7 4 28.5 11.5 39.9l128.8-55.101c-7.2-30.899-34.8-54.2-68.1-54.799m-42.7 75.599c-0.2-1.6-0.3-3.3-0.3-5 0.4-23.1 19.4-41.6 42.5-41.199 12.6 0.199 23.8 5.899 31.3 14.899l-73.5 31.3zm151.3-107.6v137.3l23.801 9.9-11.301 27.1-23.6-9.8c-5.3-2.3-8.9-5.8-11.6-9.8-2.601-4-4.601-9.601-4.601-17v-137.7h27.301zm85.901 63.5c4.2-1.4 8.6-2.1 13.3-2.1 20.3 0 37.101 14.399 41 33.5l28.7-5.9c-6.6-32.5-35.3-56.9-69.7-56.9-7.899 0-15.5 1.301-22.5 3.601l9.2 27.799zm-33.901 92.9l19.4-21.9c-8.7-7.7-14.1-18.9-14.1-31.4s5.5-23.699 14.1-31.3l-19.4-21.899c-14.699 13-24 32.1-24 53.3s9.301 40.199 24 53.199zm88.201-44.801c-3.899 19.101-20.8 33.5-41 33.5-4.6 0-9.1-0.8-13.3-2.199l-9.3 27.8c7.1 2.399 14.7 3.7 22.6 3.7 34.4 0 63.101-24.4 69.7-56.9l-28.7-5.901z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+1
View File
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, 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 780 500" style="enable-background:new 0 0 780 500;" xml:space="preserve">
<style type="text/css">
.st0{fill:#F37421;}
.st1{fill:#FFFFFF;}
.st2{fill-rule:evenodd;clip-rule:evenodd;fill:#FFE700;}
.st3{fill-rule:evenodd;clip-rule:evenodd;fill:#FFFFFF;}
</style>
<path class="st0" d="M724.3,0H55.7C25.4,0,0.8,24.6,0.8,54.9v390.2c0,30.3,24.6,54.9,54.9,54.9h668.7c30.3,0,54.9-24.6,54.9-54.9
V54.9C779.2,24.6,754.6,0,724.3,0z"/>
<g>
<polygon class="st1" points="100,139.9 100,314.9 139.7,314.9 139.7,241.7 204.9,241.7 204.9,314.9 244.4,314.9 244.4,139.9
204.9,139.9 204.9,207.2 139.7,207.2 139.7,139.9 100,139.9 "/>
<path class="st1" d="M586.2,264c0.5-2.9,1.3-8.8,1.3-15.6c0-31.4-15.6-63.4-56.6-63.4c-44.1,0-64.1,35.6-64.1,67.8
c0,39.7,24.7,64.7,67.8,64.7c17.1,0,33-2.6,46-7.8l-5.2-26.7c-10.6,3.4-21.6,5.2-35.1,5.2c-18.4,0-34.5-7.8-35.8-24.1H586.2
L586.2,264z M504.1,237c1-10.6,7.8-25.7,24.4-25.7c18.2,0,22.3,16.1,22.3,25.7H504.1L504.1,237z"/>
<path class="st1" d="M600.1,314.9h39.5v-64.4c0-3.1,0.3-6.2,0.8-8.8c2.6-12.2,12.5-20,27-20c4.4,0,7.8,0.5,10.6,1v-37.1
c-2.9-0.5-4.7-0.5-8.3-0.5c-12.2,0-27.8,7.8-34.3,26.2h-1l-1.3-23.4h-34c0.5,10.9,1,23.1,1,41.8V314.9L600.1,314.9z"/>
<path class="st2" d="M284.5,132.9c11.2,0,20.3,9.1,20.3,20.3c0,11.2-9.1,20.3-20.3,20.3c-11.2,0-20.3-9.1-20.3-20.3
C264.2,142,273.3,132.9,284.5,132.9L284.5,132.9z"/>
<path class="st3" d="M367.9,285.3h19.8c19.9,0,28.9-12.7,28.9-26c0-13.3-1-43.3-25.5-43.3c-28.2,0-23.7,37.3-23.5,56.8
C367.5,277,367.8,281.2,367.9,285.3L367.9,285.3z M264.2,187.8h40.6v71.6c0,13.3,7.4,26,23.7,26c0.1-32.2,0-65.4-1.1-97.6h33.9
c0.7,6.2,1.4,12.4,2,18.6c16-32,66.5-25,83.5,2.1c17.5,27.9,23.5,107.9-59.1,107.9h-19.2c0.2,16.2,0.2,32.5,0.2,48.8h-40.6
c0-15.6,0.1-32,0.2-48.8c-43.9-0.2-64.1-28-64.1-57V187.8L264.2,187.8z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

+1
View File
@@ -0,0 +1 @@
<svg width="780" height="500" enable-background="new 0 0 780 500" version="1.1" viewBox="0 0 780 500" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M40,0h700c22.092,0,40,17.909,40,40v420c0,22.092-17.908,40-40,40H40c-22.091,0-40-17.908-40-40V40 C0,17.909,17.909,0,40,0z" fill="#0E4C96"/><path d="m632.24 361.27c0 41.615-33.729 75.36-75.36 75.36h-409.13v-297.88c0-41.626 33.733-75.37 75.365-75.37l409.12-1e-3 1e-3 297.89z" fill="#fff"/><linearGradient id="c" x1="83.977" x2="84.977" y1="645.5" y2="645.5" gradientTransform="matrix(132.87 0 0 -323.02 -10686 2.0876e5)" gradientUnits="userSpaceOnUse"><stop stop-color="#007B40" offset="0"/><stop stop-color="#55B330" offset="1"/></linearGradient><path d="m498.86 256.54c11.684 0.253 23.437-0.516 35.076 0.4 11.787 2.199 14.629 20.043 4.156 25.888-7.141 3.851-15.633 1.433-23.379 2.113h-15.852l-1e-3 -28.401zm41.833-32.145c2.596 9.164-6.238 17.392-15.066 16.13h-26.767c0.185-8.642-0.368-18.021 0.271-26.208 10.725 0.301 21.549-0.616 32.21 0.479 4.581 1.151 8.414 4.917 9.352 9.599zm64.428-135.9c0.498 17.501 0.071 35.927 0.214 53.783-0.035 72.596 0.072 145.19-0.055 217.79-0.47 27.207-24.582 50.844-51.601 51.387-27.046 0.111-54.095 0.016-81.142 0.047v-109.75c29.47-0.154 58.959 0.307 88.417-0.232 13.667-0.859 28.632-9.875 29.27-24.914 1.61-15.103-12.632-25.551-26.152-27.201-5.198-0.135-5.044-1.516 0-2.117 12.892-2.787 23.02-16.133 19.226-29.499-3.236-14.058-18.772-19.499-31.697-19.472-26.351-0.18-52.709-0.026-79.062-0.077 0.172-20.489-0.354-41 0.286-61.474 2.087-26.716 26.806-48.747 53.447-48.27h78.849v-1e-3z" fill="url(#c)"/><linearGradient id="b" x1="83.984" x2="84.979" y1="645.5" y2="645.5" gradientTransform="matrix(133.43 0 0 -323.02 -11031 2.0876e5)" gradientUnits="userSpaceOnUse"><stop stop-color="#1D2970" offset="0"/><stop stop-color="#006DBA" offset="1"/></linearGradient><path d="m174.74 139.54c0.674-27.163 24.889-50.611 51.875-51.007 26.944-0.083 53.891-0.012 80.837-0.036-0.074 90.885 0.148 181.78-0.112 272.66-1.038 26.835-24.99 49.835-51.679 50.308-26.996 0.099-53.995 0.014-80.992 0.042v-113.45c26.223 6.194 53.722 8.832 80.473 4.721 15.993-2.574 33.488-10.424 38.902-27.014 3.986-14.191 1.742-29.126 2.334-43.691v-33.824h-46.297c-0.208 22.369 0.426 44.779-0.335 67.125-1.248 13.734-14.846 22.46-27.8 21.994-16.066 0.17-47.898-11.639-47.898-11.639-0.08-41.918 0.466-94.409 0.692-136.18z" fill="url(#b)"/><linearGradient id="a" x1="83.978" x2="84.977" y1="645.5" y2="645.5" gradientTransform="matrix(132.96 0 0 -323.03 -10842 2.0877e5)" gradientUnits="userSpaceOnUse"><stop stop-color="#6E2B2F" offset="0"/><stop stop-color="#E30138" offset="1"/></linearGradient><path d="m324.72 211.89c-2.434 0.517-0.489-8.301-1.113-11.646 0.165-21.15-0.347-42.323 0.283-63.458 2.083-26.829 26.991-48.916 53.739-48.288h78.766c-0.073 90.884 0.147 181.78-0.111 272.66-1.039 26.834-24.992 49.833-51.681 50.308-26.997 0.1-53.997 0.015-80.997 0.043v-124.3c18.44 15.128 43.5 17.483 66.473 17.524 17.316-6e-3 34.534-2.674 51.35-6.67v-22.772c-18.953 9.446-41.232 15.446-62.243 10.019-14.655-3.65-25.294-17.812-25.056-32.937-1.699-15.728 7.524-32.335 22.981-37.011 19.189-6.008 40.107-1.413 58.096 6.397 3.854 2.019 7.765 4.521 6.222-1.921v-17.9c-30.084-7.156-62.101-9.792-92.329-2.004-8.749 2.469-17.271 6.212-24.38 11.958z" fill="url(#a)"/></svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

+1
View File
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.7 KiB

+32
View File
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, 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 780 500" style="enable-background:new 0 0 780 500;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{fill:#37A72E;}
.st2{fill:url(#Combined-Shape_4_);}
</style>
<path class="st0" d="M40,0h700c22.1,0,40,17.9,40,40v420c0,22.1-17.9,40-40,40H40c-22.1,0-40-17.9-40-40V40C0,17.9,17.9,0,40,0z"/>
<g id="Page-1">
<g id="Artboard" transform="translate(-91.000000, -154.000000)">
<g id="Group" transform="translate(91.000000, 154.000000)">
<path id="Combined-Shape" class="st1" d="M544.1,240.5v108h60v-64h68c28.6-0.2,52.9-18.5,62.1-44H544.1z"/>
<linearGradient id="Combined-Shape_4_" gradientUnits="userSpaceOnUse" x1="362.4047" y1="275.4307" x2="363.4047" y2="275.4307" gradientTransform="matrix(201.7633 0 0 -79 -72583.8438 21950.0254)">
<stop offset="0" style="stop-color:#00A0E5"/>
<stop offset="1" style="stop-color:#0077C3"/>
</linearGradient>
<path id="Combined-Shape_1_" class="st2" d="M536.1,151.5c3.5,44.1,45.3,79,96.3,79c0.2,0,104.3,0,104.3,0
c0.8-4,1.2-8.2,1.2-12.5c0-36.6-29.5-66.2-66-66.5L536.1,151.5z"/>
<path id="Combined-Shape_2_" class="st1" d="M447.3,229.4l0-0.1L447.3,229.4c0.7-1.2,1.8-1.9,3.2-1.9c2,0,3.5,1.6,3.6,3.5l0,0
v116.5h60v-196h-60c-7.6,0.3-16.2,5.8-19.4,12.7L387,266.6c-0.1,0.4-0.3,0.8-0.5,1.2l0,0l0,0c-0.7,1-1.9,1.7-3.3,1.7
c-2.2,0-4-1.8-4-4v-114h-60v196h60v0c7.5-0.4,15.9-5.9,19.1-12.7l49-105.1C447.2,229.6,447.3,229.5,447.3,229.4L447.3,229.4z"/>
<path id="Combined-Shape_3_" class="st1" d="M223.3,232.8l-35.1,114.7H145L110,232.7c-0.3-1.8-1.9-3.2-3.9-3.2
c-2.2,0-3.9,1.8-3.9,3.9c0,0,0,0,0,0l0,114h-60v-196h51.5H109c11,0,22.6,8.6,25.8,19.1l29.2,95.5c1.5,4.8,3.8,4.7,5.3,0
l29.2-95.5c3.2-10.6,14.8-19.1,25.8-19.1h15.3h51.5v196h-60v-114c0,0,0,0,0-0.1c0-2.2-1.8-3.9-3.9-3.9
C225.2,229.5,223.6,230.9,223.3,232.8L223.3,232.8z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

+1
View File
@@ -0,0 +1 @@
<svg width="780" height="500" enable-background="new 0 0 780 500" version="1.1" viewBox="0 0 780 500" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M725,0H55C24.673,0,0,24.673,0,55v391c0,30.327,24.673,55,55,55h670c30.325,0,55-24.673,55-55V55 C780,24.673,755.325,0,725,0z" fill="#FFF"/><path d="m168.38 169.85c-8.399-5.774-19.359-8.668-32.88-8.668h-52.346c-4.145 0-6.435 2.073-6.87 6.214l-21.265 133.48c-0.221 1.311 0.107 2.51 0.981 3.6 0.869 1.093 1.962 1.636 3.271 1.636h24.864c4.361 0 6.758-2.068 7.198-6.216l5.888-35.985c0.215-1.744 0.982-3.162 2.291-4.254 1.308-1.09 2.944-1.804 4.907-2.13 1.963-0.324 3.814-0.487 5.562-0.487 1.743 0 3.814 0.11 6.217 0.327 2.397 0.218 3.925 0.324 4.58 0.324 18.756 0 33.478-5.285 44.167-15.866 10.684-10.577 16.032-25.244 16.032-44.004 0-12.868-4.202-22.192-12.597-27.975zm-26.99 40.08c-1.094 7.635-3.926 12.649-8.506 15.049-4.581 2.403-11.124 3.597-19.629 3.597l-10.797 0.328 5.563-35.007c0.434-2.397 1.851-3.597 4.252-3.597h6.218c8.72 0 15.049 1.257 18.975 3.761 3.924 2.51 5.233 7.802 3.924 15.869z" fill="#003087"/><path d="m720.79 161.18h-24.208c-2.405 0-3.821 1.2-4.253 3.599l-21.267 136.1-0.328 0.654c0 1.096 0.437 2.127 1.311 3.109 0.868 0.979 1.963 1.471 3.271 1.471h21.595c4.138 0 6.429-2.068 6.871-6.215l21.265-133.81v-0.325c-2e-3 -3.053-1.424-4.58-4.257-4.58z" fill="#009CDE"/><path d="m428.31 213.86c0-1.088-0.438-2.126-1.306-3.106-0.875-0.981-1.857-1.474-2.945-1.474h-25.191c-2.404 0-4.366 1.096-5.89 3.271l-34.679 51.04-14.394-49.075c-1.096-3.488-3.493-5.236-7.198-5.236h-24.54c-1.093 0-2.075 0.492-2.942 1.474-0.875 0.98-1.309 2.019-1.309 3.106 0 0.44 2.127 6.871 6.379 19.303 4.252 12.434 8.833 25.848 13.741 40.244 4.908 14.394 7.468 22.031 7.688 22.898-17.886 24.43-26.826 37.518-26.826 39.26 0 2.838 1.417 4.254 4.253 4.254h25.191c2.399 0 4.361-1.088 5.89-3.271l83.427-120.4c0.433-0.433 0.651-1.193 0.651-2.289z" fill="#003087"/><path d="m662.89 209.28h-24.865c-3.056 0-4.904 3.599-5.559 10.797-5.677-8.72-16.031-13.088-31.083-13.088-15.704 0-29.065 5.89-40.077 17.668-11.016 11.779-16.521 25.631-16.521 41.551 0 12.871 3.761 23.121 11.285 30.752 7.524 7.639 17.611 11.451 30.266 11.451 6.323 0 12.757-1.311 19.3-3.926 6.544-2.617 11.665-6.105 15.379-10.469 0 0.219-0.222 1.198-0.654 2.942-0.44 1.748-0.655 3.06-0.655 3.926 0 3.494 1.414 5.234 4.254 5.234h22.576c4.138 0 6.541-2.068 7.193-6.216l13.415-85.389c0.215-1.309-0.111-2.507-0.981-3.599-0.876-1.087-1.964-1.634-3.273-1.634zm-42.694 64.452c-5.562 5.453-12.269 8.179-20.12 8.179-6.328 0-11.449-1.742-15.377-5.234-3.928-3.483-5.891-8.282-5.891-14.396 0-8.064 2.727-14.884 8.181-20.446 5.446-5.562 12.214-8.343 20.284-8.343 6.102 0 11.174 1.8 15.212 5.397 4.032 3.599 6.055 8.563 6.055 14.888-1e-3 7.851-2.783 14.505-8.344 19.955z" fill="#009CDE"/><path d="m291.23 209.28h-24.864c-3.058 0-4.908 3.599-5.563 10.797-5.889-8.72-16.25-13.088-31.081-13.088-15.704 0-29.065 5.89-40.078 17.668-11.016 11.779-16.521 25.631-16.521 41.551 0 12.871 3.763 23.121 11.288 30.752 7.525 7.639 17.61 11.451 30.262 11.451 6.104 0 12.433-1.311 18.975-3.926 6.543-2.617 11.778-6.105 15.704-10.469-0.875 2.616-1.309 4.907-1.309 6.868 0 3.494 1.417 5.234 4.253 5.234h22.574c4.141 0 6.543-2.068 7.198-6.216l13.413-85.389c0.215-1.309-0.112-2.507-0.981-3.599-0.873-1.087-1.962-1.634-3.27-1.634zm-42.695 64.614c-5.563 5.351-12.382 8.017-20.447 8.017-6.329 0-11.4-1.742-15.214-5.234-3.819-3.483-5.726-8.282-5.726-14.396 0-8.064 2.725-14.884 8.18-20.446 5.449-5.562 12.211-8.343 20.284-8.343 6.104 0 11.175 1.8 15.214 5.398 4.032 3.599 6.052 8.563 6.052 14.888 0 8.069-2.781 14.778-8.343 20.116z" fill="#003087"/><path d="m540.04 169.85c-8.398-5.774-19.356-8.668-32.879-8.668h-52.02c-4.364 0-6.765 2.073-7.197 6.214l-21.266 133.48c-0.221 1.312 0.106 2.511 0.981 3.601 0.865 1.092 1.962 1.635 3.271 1.635h26.826c2.617 0 4.361-1.416 5.235-4.252l5.89-37.949c0.216-1.744 0.98-3.162 2.29-4.254 1.309-1.09 2.943-1.803 4.908-2.13 1.962-0.324 3.812-0.487 5.562-0.487 1.743 0 3.814 0.11 6.214 0.327 2.399 0.218 3.931 0.324 4.58 0.324 18.76 0 33.479-5.285 44.168-15.866 10.688-10.577 16.031-25.244 16.031-44.004 2e-3 -12.867-4.199-22.191-12.594-27.974zm-33.534 53.82c-4.799 3.271-11.997 4.906-21.592 4.906l-10.47 0.328 5.562-35.007c0.432-2.397 1.849-3.597 4.252-3.597h5.887c4.798 0 8.614 0.218 11.454 0.653 2.831 0.44 5.562 1.799 8.179 4.089 2.618 2.291 3.926 5.618 3.926 9.98 0 9.16-2.402 15.375-7.198 18.648z" fill="#009CDE"/></svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

+1
View File
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.5 KiB

+1
View File
@@ -0,0 +1 @@
<svg width="780" height="500" enable-background="new 0 0 780 500" version="1.1" viewBox="0 0 780 500" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><path d="M40,0h700c22.092,0,40,17.909,40,40v420c0,22.092-17.908,40-40,40H40c-22.091,0-40-17.908-40-40V40 C0,17.909,17.909,0,40,0z" fill="#0E4595"/><path d="m293.2 348.73l33.361-195.76h53.36l-33.385 195.76h-53.336zm246.11-191.54c-10.57-3.966-27.137-8.222-47.822-8.222-52.725 0-89.865 26.55-90.18 64.603-0.299 28.13 26.514 43.822 46.752 53.186 20.771 9.595 27.752 15.714 27.654 24.283-0.131 13.121-16.586 19.116-31.922 19.116-21.357 0-32.703-2.967-50.227-10.276l-6.876-3.11-7.489 43.823c12.463 5.464 35.51 10.198 59.438 10.443 56.09 0 92.5-26.246 92.916-66.882 0.199-22.269-14.016-39.216-44.801-53.188-18.65-9.055-30.072-15.099-29.951-24.268 0-8.137 9.668-16.839 30.557-16.839 17.449-0.27 30.09 3.535 39.938 7.5l4.781 2.26 7.232-42.429m137.31-4.223h-41.232c-12.773 0-22.332 3.487-27.941 16.234l-79.244 179.4h56.031s9.16-24.123 11.232-29.418c6.125 0 60.555 0.084 68.338 0.084 1.596 6.853 6.49 29.334 6.49 29.334h49.514l-43.188-195.64zm-65.418 126.41c4.412-11.279 21.26-54.723 21.26-54.723-0.316 0.522 4.379-11.334 7.074-18.684l3.605 16.879s10.219 46.729 12.354 56.528h-44.293zm-363.3-126.41l-52.24 133.5-5.567-27.13c-9.725-31.273-40.025-65.155-73.898-82.118l47.766 171.2 56.456-0.064 84.004-195.39h-56.521" fill="#fff"/><path d="m146.92 152.96h-86.041l-0.681 4.073c66.938 16.204 111.23 55.363 129.62 102.41l-18.71-89.96c-3.23-12.395-12.597-16.094-24.186-16.527" fill="#F2AE14"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+283 -249
View File
@@ -1,253 +1,287 @@
{ {
"/js/main.js": "/js/main.js", "/js/main.js": "/js/main.js",
"/css/app.css": "/css/app.css", "/css/app.css": "/css/app.css",
"/js/main.db73e300d42a147d92c9.hot-update.js": "/js/main.db73e300d42a147d92c9.hot-update.js", "/js/main.c592c1b513edd7534891.hot-update.js": "/js/main.c592c1b513edd7534891.hot-update.js",
"/js/main.0ea5df0b535e6454abdc.hot-update.js": "/js/main.0ea5df0b535e6454abdc.hot-update.js", "/js/main.80ad9909faceb74207f1.hot-update.js": "/js/main.80ad9909faceb74207f1.hot-update.js",
"/js/main.2cda68eaebe1626133c7.hot-update.js": "/js/main.2cda68eaebe1626133c7.hot-update.js", "/js/main.4b91997785e85d7d6e32.hot-update.js": "/js/main.4b91997785e85d7d6e32.hot-update.js",
"/js/main.88f0e74490296b17f12b.hot-update.js": "/js/main.88f0e74490296b17f12b.hot-update.js", "/js/main.e787780098edc245ae3c.hot-update.js": "/js/main.e787780098edc245ae3c.hot-update.js",
"/js/main.8ba41725bdb9b08ea41b.hot-update.js": "/js/main.8ba41725bdb9b08ea41b.hot-update.js", "/js/main.e55b17a6a5a315b93bef.hot-update.js": "/js/main.e55b17a6a5a315b93bef.hot-update.js",
"/js/main.7b5e66db24a0d8fffc66.hot-update.js": "/js/main.7b5e66db24a0d8fffc66.hot-update.js", "/js/main.28fdd591cc524a88c846.hot-update.js": "/js/main.28fdd591cc524a88c846.hot-update.js",
"/js/main.e44f7ecf17c7e0268ae4.hot-update.js": "/js/main.e44f7ecf17c7e0268ae4.hot-update.js", "/js/main.2b812942c78e860c03db.hot-update.js": "/js/main.2b812942c78e860c03db.hot-update.js",
"/js/main.6749d2651a3d98182751.hot-update.js": "/js/main.6749d2651a3d98182751.hot-update.js", "/js/main.211081d76c54b5482de5.hot-update.js": "/js/main.211081d76c54b5482de5.hot-update.js",
"/js/main.5c564e162157c5e6fb80.hot-update.js": "/js/main.5c564e162157c5e6fb80.hot-update.js", "/js/main.fd6434aee58e09a56ff4.hot-update.js": "/js/main.fd6434aee58e09a56ff4.hot-update.js",
"/js/main.c6a1574884392a89cbaa.hot-update.js": "/js/main.c6a1574884392a89cbaa.hot-update.js", "/js/main.4c1d8cba619a7927638c.hot-update.js": "/js/main.4c1d8cba619a7927638c.hot-update.js",
"/js/main.cdebd19025c701560b92.hot-update.js": "/js/main.cdebd19025c701560b92.hot-update.js", "/js/main.aff41805a3b84c8ebae5.hot-update.js": "/js/main.aff41805a3b84c8ebae5.hot-update.js",
"/js/main.f896b7d13aad9732ad0e.hot-update.js": "/js/main.f896b7d13aad9732ad0e.hot-update.js", "/js/main.6f72ba9701b9e19c9be0.hot-update.js": "/js/main.6f72ba9701b9e19c9be0.hot-update.js",
"/js/main.14e6ae82553043793d06.hot-update.js": "/js/main.14e6ae82553043793d06.hot-update.js", "/js/main.0e4c6671c2367b10695d.hot-update.js": "/js/main.0e4c6671c2367b10695d.hot-update.js",
"/js/main.e1d8968eaca9c7a94e14.hot-update.js": "/js/main.e1d8968eaca9c7a94e14.hot-update.js", "/js/main.1985d2920179f9a6f78f.hot-update.js": "/js/main.1985d2920179f9a6f78f.hot-update.js",
"/js/main.ee5e426715877748a226.hot-update.js": "/js/main.ee5e426715877748a226.hot-update.js", "/js/main.9f9975790f0a229f2cb2.hot-update.js": "/js/main.9f9975790f0a229f2cb2.hot-update.js",
"/js/main.54454754153a4b810c68.hot-update.js": "/js/main.54454754153a4b810c68.hot-update.js", "/js/main.0fba79cfcfa3990cf559.hot-update.js": "/js/main.0fba79cfcfa3990cf559.hot-update.js",
"/js/main.73250572619a4dca0d35.hot-update.js": "/js/main.73250572619a4dca0d35.hot-update.js", "/js/main.03715cbb9f7cdc53e0ff.hot-update.js": "/js/main.03715cbb9f7cdc53e0ff.hot-update.js",
"/js/main.4dbe97b31e06a3c1bebf.hot-update.js": "/js/main.4dbe97b31e06a3c1bebf.hot-update.js", "/js/main.b6aaa5a10b3ddcee7f9d.hot-update.js": "/js/main.b6aaa5a10b3ddcee7f9d.hot-update.js",
"/js/main.809268a1e86deba22298.hot-update.js": "/js/main.809268a1e86deba22298.hot-update.js", "/js/main.d7271130fb59906d5182.hot-update.js": "/js/main.d7271130fb59906d5182.hot-update.js",
"/js/main.4490e500053d12748641.hot-update.js": "/js/main.4490e500053d12748641.hot-update.js", "/js/main.957b7763ea3c9accc8cc.hot-update.js": "/js/main.957b7763ea3c9accc8cc.hot-update.js",
"/js/main.3953c82fcb9e8aa236f4.hot-update.js": "/js/main.3953c82fcb9e8aa236f4.hot-update.js", "/js/main.e23eea5ec4310f745400.hot-update.js": "/js/main.e23eea5ec4310f745400.hot-update.js",
"/js/main.90a12056f475838d806c.hot-update.js": "/js/main.90a12056f475838d806c.hot-update.js", "/js/main.6e342ea527f2c3f9b013.hot-update.js": "/js/main.6e342ea527f2c3f9b013.hot-update.js",
"/js/main.fba13eb0d8bdc2de63f9.hot-update.js": "/js/main.fba13eb0d8bdc2de63f9.hot-update.js", "/js/main.612f96382ad8ba3d109c.hot-update.js": "/js/main.612f96382ad8ba3d109c.hot-update.js",
"/js/main.2d77e5427d4b8b55419b.hot-update.js": "/js/main.2d77e5427d4b8b55419b.hot-update.js", "/js/main.aba02ea167c96d2b7634.hot-update.js": "/js/main.aba02ea167c96d2b7634.hot-update.js",
"/js/main.966dba3cdb08000411ae.hot-update.js": "/js/main.966dba3cdb08000411ae.hot-update.js", "/js/main.9d67410798c6c29f0842.hot-update.js": "/js/main.9d67410798c6c29f0842.hot-update.js",
"/js/main.8a320a785898586b8d10.hot-update.js": "/js/main.8a320a785898586b8d10.hot-update.js", "/js/main.e4515959db9d0f8747f2.hot-update.js": "/js/main.e4515959db9d0f8747f2.hot-update.js",
"/js/main.28edbc28a0031b3f4913.hot-update.js": "/js/main.28edbc28a0031b3f4913.hot-update.js", "/js/main.cb089a2ecaf1fd0be078.hot-update.js": "/js/main.cb089a2ecaf1fd0be078.hot-update.js",
"/js/main.79eb3750c72852c42485.hot-update.js": "/js/main.79eb3750c72852c42485.hot-update.js", "/js/main.5ba809ade4770806836a.hot-update.js": "/js/main.5ba809ade4770806836a.hot-update.js",
"/js/main.9703ef1a8f855482838f.hot-update.js": "/js/main.9703ef1a8f855482838f.hot-update.js", "/js/main.990c352366666e2ca2f2.hot-update.js": "/js/main.990c352366666e2ca2f2.hot-update.js",
"/js/main.1cbfcf8465e5d6aa0096.hot-update.js": "/js/main.1cbfcf8465e5d6aa0096.hot-update.js", "/js/main.4c12e8bb7d017220e3fa.hot-update.js": "/js/main.4c12e8bb7d017220e3fa.hot-update.js",
"/js/main.f1e7b129d14fc15d72e8.hot-update.js": "/js/main.f1e7b129d14fc15d72e8.hot-update.js", "/js/main.0bd1b62237741b506f57.hot-update.js": "/js/main.0bd1b62237741b506f57.hot-update.js",
"/js/main.58f0f9d441ec01c7d553.hot-update.js": "/js/main.58f0f9d441ec01c7d553.hot-update.js", "/js/main.90a24d55baa0b9e45607.hot-update.js": "/js/main.90a24d55baa0b9e45607.hot-update.js",
"/js/main.45858a0dfab5dd3a911a.hot-update.js": "/js/main.45858a0dfab5dd3a911a.hot-update.js", "/js/main.f0152d5ede68576fab2f.hot-update.js": "/js/main.f0152d5ede68576fab2f.hot-update.js",
"/js/main.ade5cf3ef31b2e506425.hot-update.js": "/js/main.ade5cf3ef31b2e506425.hot-update.js", "/js/main.f87dba2f8ad4b673eef3.hot-update.js": "/js/main.f87dba2f8ad4b673eef3.hot-update.js",
"/js/main.c4304b2de45e8f06f7b5.hot-update.js": "/js/main.c4304b2de45e8f06f7b5.hot-update.js", "/js/main.7738f2b3cc86dadbfcf9.hot-update.js": "/js/main.7738f2b3cc86dadbfcf9.hot-update.js",
"/js/main.0961cf18a9605cb65e3f.hot-update.js": "/js/main.0961cf18a9605cb65e3f.hot-update.js", "/js/main.a6bc9844316ff7a73a3a.hot-update.js": "/js/main.a6bc9844316ff7a73a3a.hot-update.js",
"/js/main.5a371d0e098a933c4640.hot-update.js": "/js/main.5a371d0e098a933c4640.hot-update.js", "/js/main.30f1c08c6c5c716bc99b.hot-update.js": "/js/main.30f1c08c6c5c716bc99b.hot-update.js",
"/js/main.d3c2f6bd4ce092746115.hot-update.js": "/js/main.d3c2f6bd4ce092746115.hot-update.js", "/js/main.52f9059179cdfa541c49.hot-update.js": "/js/main.52f9059179cdfa541c49.hot-update.js",
"/js/main.e191340cc52008653ed0.hot-update.js": "/js/main.e191340cc52008653ed0.hot-update.js", "/js/main.bd918a21f99459ac0c5a.hot-update.js": "/js/main.bd918a21f99459ac0c5a.hot-update.js",
"/js/main.3cb43f27a625a07fcc0b.hot-update.js": "/js/main.3cb43f27a625a07fcc0b.hot-update.js", "/js/main.cd35d127280aa3ed72bf.hot-update.js": "/js/main.cd35d127280aa3ed72bf.hot-update.js",
"/js/main.1410e64aed21b84592e7.hot-update.js": "/js/main.1410e64aed21b84592e7.hot-update.js", "/js/main.fad01e38ba0a025fff8e.hot-update.js": "/js/main.fad01e38ba0a025fff8e.hot-update.js",
"/js/main.08a6a51c0acea91879f7.hot-update.js": "/js/main.08a6a51c0acea91879f7.hot-update.js", "/js/main.5f401e8ed55572e305cb.hot-update.js": "/js/main.5f401e8ed55572e305cb.hot-update.js",
"/js/main.f3a5ecf31e3df2d23957.hot-update.js": "/js/main.f3a5ecf31e3df2d23957.hot-update.js", "/js/main.0781bb19d4803f1c18cd.hot-update.js": "/js/main.0781bb19d4803f1c18cd.hot-update.js",
"/js/main.7c67297040c94f2a585e.hot-update.js": "/js/main.7c67297040c94f2a585e.hot-update.js", "/js/main.4647329b218e437cd36f.hot-update.js": "/js/main.4647329b218e437cd36f.hot-update.js",
"/js/main.d7c58200175c3d4ab962.hot-update.js": "/js/main.d7c58200175c3d4ab962.hot-update.js", "/js/main.cbadabd16ec66f6c56d6.hot-update.js": "/js/main.cbadabd16ec66f6c56d6.hot-update.js",
"/js/main.58979cee95bba3ec04c5.hot-update.js": "/js/main.58979cee95bba3ec04c5.hot-update.js", "/js/main.2b9b5409dee799203a6c.hot-update.js": "/js/main.2b9b5409dee799203a6c.hot-update.js",
"/js/main.94f329f8b9d0aa2d4182.hot-update.js": "/js/main.94f329f8b9d0aa2d4182.hot-update.js", "/js/main.21dd08ba11f996085687.hot-update.js": "/js/main.21dd08ba11f996085687.hot-update.js",
"/js/main.23a1f3a1d28f2b39e1b7.hot-update.js": "/js/main.23a1f3a1d28f2b39e1b7.hot-update.js", "/js/main.afe5395def9a1c6bfba3.hot-update.js": "/js/main.afe5395def9a1c6bfba3.hot-update.js",
"/js/main.a7df7455225c5d05227f.hot-update.js": "/js/main.a7df7455225c5d05227f.hot-update.js", "/js/main.bce3823ed3f28958fa5f.hot-update.js": "/js/main.bce3823ed3f28958fa5f.hot-update.js",
"/js/main.7b16550bbd8be6cc9b8d.hot-update.js": "/js/main.7b16550bbd8be6cc9b8d.hot-update.js", "/js/main.6d0abf817c91fcbc246d.hot-update.js": "/js/main.6d0abf817c91fcbc246d.hot-update.js",
"/js/main.780968811eb17fef25a7.hot-update.js": "/js/main.780968811eb17fef25a7.hot-update.js", "/js/main.d20577f503f0d098d7d3.hot-update.js": "/js/main.d20577f503f0d098d7d3.hot-update.js",
"/js/main.aad31e3890b61350c4fa.hot-update.js": "/js/main.aad31e3890b61350c4fa.hot-update.js", "/js/main.609a2280828ed517bdc1.hot-update.js": "/js/main.609a2280828ed517bdc1.hot-update.js",
"/js/main.41b4b985c13b8890717d.hot-update.js": "/js/main.41b4b985c13b8890717d.hot-update.js", "/js/main.d7ad5e9d5b97bbdc37db.hot-update.js": "/js/main.d7ad5e9d5b97bbdc37db.hot-update.js",
"/js/main.e025100d60c74d0392d1.hot-update.js": "/js/main.e025100d60c74d0392d1.hot-update.js", "/js/main.66dc93a5bf413b7786be.hot-update.js": "/js/main.66dc93a5bf413b7786be.hot-update.js",
"/js/main.67661103ba5d1dd3cac6.hot-update.js": "/js/main.67661103ba5d1dd3cac6.hot-update.js", "/js/main.e616a0e15889e0865a7b.hot-update.js": "/js/main.e616a0e15889e0865a7b.hot-update.js",
"/js/main.4859f6f2e961eba13edf.hot-update.js": "/js/main.4859f6f2e961eba13edf.hot-update.js", "/js/main.58f7b99d0ee0f875e7dc.hot-update.js": "/js/main.58f7b99d0ee0f875e7dc.hot-update.js",
"/js/main.2412ccd7b9afcb3dd059.hot-update.js": "/js/main.2412ccd7b9afcb3dd059.hot-update.js", "/js/main.e79958d0a6039bd1c735.hot-update.js": "/js/main.e79958d0a6039bd1c735.hot-update.js",
"/js/main.0a9300e8875685257a5a.hot-update.js": "/js/main.0a9300e8875685257a5a.hot-update.js", "/js/main.f88db9bb087b351dc1a4.hot-update.js": "/js/main.f88db9bb087b351dc1a4.hot-update.js",
"/js/main.6e32369655d811852893.hot-update.js": "/js/main.6e32369655d811852893.hot-update.js", "/js/main.8a872d714e919c2818ad.hot-update.js": "/js/main.8a872d714e919c2818ad.hot-update.js",
"/js/main.07e4dcb8b14f80943019.hot-update.js": "/js/main.07e4dcb8b14f80943019.hot-update.js", "/js/main.8a719961322acc70e9ef.hot-update.js": "/js/main.8a719961322acc70e9ef.hot-update.js",
"/js/main.a970e8749724b2c1488f.hot-update.js": "/js/main.a970e8749724b2c1488f.hot-update.js", "/js/main.a46e2e1d1032e2bb09ac.hot-update.js": "/js/main.a46e2e1d1032e2bb09ac.hot-update.js",
"/js/main.26c44e4e22a245d1e8a9.hot-update.js": "/js/main.26c44e4e22a245d1e8a9.hot-update.js", "/js/main.f4e3d15c80b88b4936be.hot-update.js": "/js/main.f4e3d15c80b88b4936be.hot-update.js",
"/js/main.de6ddacb5c245c079fab.hot-update.js": "/js/main.de6ddacb5c245c079fab.hot-update.js", "/js/main.afca50dd54922bf43026.hot-update.js": "/js/main.afca50dd54922bf43026.hot-update.js",
"/js/main.564a42509ff2e25ba549.hot-update.js": "/js/main.564a42509ff2e25ba549.hot-update.js", "/js/main.22a33372319fe8c233e6.hot-update.js": "/js/main.22a33372319fe8c233e6.hot-update.js",
"/js/main.c63aad49b7cd674b91a7.hot-update.js": "/js/main.c63aad49b7cd674b91a7.hot-update.js", "/js/main.066da2d957bdd49143d0.hot-update.js": "/js/main.066da2d957bdd49143d0.hot-update.js",
"/js/main.81931bb64710a2f54368.hot-update.js": "/js/main.81931bb64710a2f54368.hot-update.js", "/js/main.1b2ebbdc84449535f397.hot-update.js": "/js/main.1b2ebbdc84449535f397.hot-update.js",
"/js/main.34efd0ebe70125b4d9cd.hot-update.js": "/js/main.34efd0ebe70125b4d9cd.hot-update.js", "/js/main.4ac37a1720ab1578cec3.hot-update.js": "/js/main.4ac37a1720ab1578cec3.hot-update.js",
"/js/main.3de56aa8af78d2912a79.hot-update.js": "/js/main.3de56aa8af78d2912a79.hot-update.js", "/js/main.623f3b31b7a87a8668b4.hot-update.js": "/js/main.623f3b31b7a87a8668b4.hot-update.js",
"/js/main.3492d8df8ba23f79e39f.hot-update.js": "/js/main.3492d8df8ba23f79e39f.hot-update.js", "/js/main.7632e72183adc326096a.hot-update.js": "/js/main.7632e72183adc326096a.hot-update.js",
"/js/main.1a1f505e34d61bf5c741.hot-update.js": "/js/main.1a1f505e34d61bf5c741.hot-update.js", "/js/main.6f007dc67a704a23e916.hot-update.js": "/js/main.6f007dc67a704a23e916.hot-update.js",
"/js/main.7d6fa20346e95f934020.hot-update.js": "/js/main.7d6fa20346e95f934020.hot-update.js", "/js/main.e553af47e4049586ac44.hot-update.js": "/js/main.e553af47e4049586ac44.hot-update.js",
"/js/main.e5fa1224b06e6203aaa4.hot-update.js": "/js/main.e5fa1224b06e6203aaa4.hot-update.js", "/js/main.92b31d53366c3ad8622b.hot-update.js": "/js/main.92b31d53366c3ad8622b.hot-update.js",
"/js/main.a2e4f55324730e0a1137.hot-update.js": "/js/main.a2e4f55324730e0a1137.hot-update.js", "/js/main.e09827aea6d23ff07c1e.hot-update.js": "/js/main.e09827aea6d23ff07c1e.hot-update.js",
"/js/main.82081e3a97b9075cbf38.hot-update.js": "/js/main.82081e3a97b9075cbf38.hot-update.js", "/js/main.04281820cee21428693e.hot-update.js": "/js/main.04281820cee21428693e.hot-update.js",
"/js/main.ab06dcdaa2fc897e4576.hot-update.js": "/js/main.ab06dcdaa2fc897e4576.hot-update.js", "/js/main.f4067ec10e4cf8bd8a64.hot-update.js": "/js/main.f4067ec10e4cf8bd8a64.hot-update.js",
"/js/main.8aee157f69acbf01d692.hot-update.js": "/js/main.8aee157f69acbf01d692.hot-update.js", "/js/main.430e98b75c1019f0292d.hot-update.js": "/js/main.430e98b75c1019f0292d.hot-update.js",
"/js/main.82087e18f1767795f3cc.hot-update.js": "/js/main.82087e18f1767795f3cc.hot-update.js", "/js/main.578582fa836e0594b831.hot-update.js": "/js/main.578582fa836e0594b831.hot-update.js",
"/js/main.f2c483d7d1261df861b3.hot-update.js": "/js/main.f2c483d7d1261df861b3.hot-update.js", "/js/main.0513b4f06058ecad6d0f.hot-update.js": "/js/main.0513b4f06058ecad6d0f.hot-update.js",
"/js/main.ef75264f1a02af5ca3cd.hot-update.js": "/js/main.ef75264f1a02af5ca3cd.hot-update.js", "/js/main.311234d50f9c449dc26b.hot-update.js": "/js/main.311234d50f9c449dc26b.hot-update.js",
"/js/main.cd61e67651fe4e61f358.hot-update.js": "/js/main.cd61e67651fe4e61f358.hot-update.js", "/js/main.aced0a5d0980fe602161.hot-update.js": "/js/main.aced0a5d0980fe602161.hot-update.js",
"/js/main.97ba89c984ffe538a333.hot-update.js": "/js/main.97ba89c984ffe538a333.hot-update.js", "/js/main.ad0cab16daa356d285bc.hot-update.js": "/js/main.ad0cab16daa356d285bc.hot-update.js",
"/js/main.a23de6f4cea6b80806b4.hot-update.js": "/js/main.a23de6f4cea6b80806b4.hot-update.js", "/js/main.40423454c85a1c529658.hot-update.js": "/js/main.40423454c85a1c529658.hot-update.js",
"/js/main.90440654186ae45a6f63.hot-update.js": "/js/main.90440654186ae45a6f63.hot-update.js", "/js/main.5f56f327d2ee898c55f5.hot-update.js": "/js/main.5f56f327d2ee898c55f5.hot-update.js",
"/js/main.483297e74b80dfdc50c3.hot-update.js": "/js/main.483297e74b80dfdc50c3.hot-update.js", "/js/main.d4206606dda50af813c6.hot-update.js": "/js/main.d4206606dda50af813c6.hot-update.js",
"/js/main.e2831389d5cdf78e4bd3.hot-update.js": "/js/main.e2831389d5cdf78e4bd3.hot-update.js", "/js/main.b2fbfa55f560e77648b4.hot-update.js": "/js/main.b2fbfa55f560e77648b4.hot-update.js",
"/js/main.3af60e2033e8130d75f6.hot-update.js": "/js/main.3af60e2033e8130d75f6.hot-update.js", "/js/main.e05f4750489ca893d94d.hot-update.js": "/js/main.e05f4750489ca893d94d.hot-update.js",
"/js/main.1b1ac80d4947b920ea2f.hot-update.js": "/js/main.1b1ac80d4947b920ea2f.hot-update.js", "/js/main.af394e802253a7191c04.hot-update.js": "/js/main.af394e802253a7191c04.hot-update.js",
"/js/main.8a52e7d2eff934f9de4d.hot-update.js": "/js/main.8a52e7d2eff934f9de4d.hot-update.js", "/js/main.ce416ea8ea898d2b842c.hot-update.js": "/js/main.ce416ea8ea898d2b842c.hot-update.js",
"/js/main.b67e5558feff737183b6.hot-update.js": "/js/main.b67e5558feff737183b6.hot-update.js", "/js/main.c1b17e139a6bffd834f5.hot-update.js": "/js/main.c1b17e139a6bffd834f5.hot-update.js",
"/js/main.2202ff72b0fe9ff3f0c2.hot-update.js": "/js/main.2202ff72b0fe9ff3f0c2.hot-update.js", "/js/main.46df392fd59c5498018f.hot-update.js": "/js/main.46df392fd59c5498018f.hot-update.js",
"/js/main.d9cb8d8e4ca7d410b22f.hot-update.js": "/js/main.d9cb8d8e4ca7d410b22f.hot-update.js", "/js/main.5b083225fa62793303a5.hot-update.js": "/js/main.5b083225fa62793303a5.hot-update.js",
"/js/main.a1d688c433c9fcf14bab.hot-update.js": "/js/main.a1d688c433c9fcf14bab.hot-update.js", "/js/main.ff1635f87e05dae40c64.hot-update.js": "/js/main.ff1635f87e05dae40c64.hot-update.js",
"/js/main.b2cf91ae6a0fbc682e64.hot-update.js": "/js/main.b2cf91ae6a0fbc682e64.hot-update.js", "/js/main.181ca1486b6c63df043a.hot-update.js": "/js/main.181ca1486b6c63df043a.hot-update.js",
"/js/main.63e1d96c978ec4ca4e3a.hot-update.js": "/js/main.63e1d96c978ec4ca4e3a.hot-update.js", "/js/main.aed85b7900a1ffb09898.hot-update.js": "/js/main.aed85b7900a1ffb09898.hot-update.js",
"/js/main.6e476cf8eabaf496f17a.hot-update.js": "/js/main.6e476cf8eabaf496f17a.hot-update.js", "/js/main.a96f14da0f826e0f73fe.hot-update.js": "/js/main.a96f14da0f826e0f73fe.hot-update.js",
"/js/main.fa9bfaf5cf5b454739e7.hot-update.js": "/js/main.fa9bfaf5cf5b454739e7.hot-update.js", "/js/main.19bc16cb52d3dc751604.hot-update.js": "/js/main.19bc16cb52d3dc751604.hot-update.js",
"/js/main.b246393843e1e04d1472.hot-update.js": "/js/main.b246393843e1e04d1472.hot-update.js", "/js/main.13103769e6134409fa2a.hot-update.js": "/js/main.13103769e6134409fa2a.hot-update.js",
"/js/main.cfeac3c0594ca2e18cbc.hot-update.js": "/js/main.cfeac3c0594ca2e18cbc.hot-update.js", "/js/main.a8ddc0df618dca5b1160.hot-update.js": "/js/main.a8ddc0df618dca5b1160.hot-update.js",
"/js/main.ffaf8712fd1cd702ea8a.hot-update.js": "/js/main.ffaf8712fd1cd702ea8a.hot-update.js", "/js/main.98abd09248c9893c4a30.hot-update.js": "/js/main.98abd09248c9893c4a30.hot-update.js",
"/js/main.1a9b77d1d99fa0cbe049.hot-update.js": "/js/main.1a9b77d1d99fa0cbe049.hot-update.js", "/js/main.4b42b13afb3e6e18a916.hot-update.js": "/js/main.4b42b13afb3e6e18a916.hot-update.js",
"/js/main.35f70fbeab261f617c64.hot-update.js": "/js/main.35f70fbeab261f617c64.hot-update.js", "/js/main.da8ab6e69627bbcff9a1.hot-update.js": "/js/main.da8ab6e69627bbcff9a1.hot-update.js",
"/js/main.2adec493e4879764b552.hot-update.js": "/js/main.2adec493e4879764b552.hot-update.js", "/js/main.fb5fe1c0ead4cb6c3647.hot-update.js": "/js/main.fb5fe1c0ead4cb6c3647.hot-update.js",
"/js/main.84e27e090764f99a5f8d.hot-update.js": "/js/main.84e27e090764f99a5f8d.hot-update.js", "/js/main.ecbde6764e0032ecaaff.hot-update.js": "/js/main.ecbde6764e0032ecaaff.hot-update.js",
"/js/main.d5fad1aae15440174c3b.hot-update.js": "/js/main.d5fad1aae15440174c3b.hot-update.js", "/js/main.45cdddc1e11584321f8f.hot-update.js": "/js/main.45cdddc1e11584321f8f.hot-update.js",
"/js/main.f082a5aa1a7d811653b0.hot-update.js": "/js/main.f082a5aa1a7d811653b0.hot-update.js", "/js/main.5e716c4399d6c4e7c60c.hot-update.js": "/js/main.5e716c4399d6c4e7c60c.hot-update.js",
"/js/main.cd15a772e4d701a910b1.hot-update.js": "/js/main.cd15a772e4d701a910b1.hot-update.js", "/js/main.85b156a9d0ebbe510f72.hot-update.js": "/js/main.85b156a9d0ebbe510f72.hot-update.js",
"/js/main.1e62178bd569cacdace7.hot-update.js": "/js/main.1e62178bd569cacdace7.hot-update.js", "/js/main.5de3358896db4e70d82d.hot-update.js": "/js/main.5de3358896db4e70d82d.hot-update.js",
"/js/main.19bca5f612f96fe6d127.hot-update.js": "/js/main.19bca5f612f96fe6d127.hot-update.js", "/js/main.ede66032dd422936528c.hot-update.js": "/js/main.ede66032dd422936528c.hot-update.js",
"/js/main.c775d381b77c164f7fc4.hot-update.js": "/js/main.c775d381b77c164f7fc4.hot-update.js", "/js/main.5465d00f698d022075f8.hot-update.js": "/js/main.5465d00f698d022075f8.hot-update.js",
"/js/main.f8c6cffd1559d9578b83.hot-update.js": "/js/main.f8c6cffd1559d9578b83.hot-update.js", "/js/main.aa025d5f6feec8c0dab2.hot-update.js": "/js/main.aa025d5f6feec8c0dab2.hot-update.js",
"/js/main.c7ff369b0854108f96c7.hot-update.js": "/js/main.c7ff369b0854108f96c7.hot-update.js", "/js/main.820910b8cdce9f354543.hot-update.js": "/js/main.820910b8cdce9f354543.hot-update.js",
"/js/main.062ff67378e519023a1e.hot-update.js": "/js/main.062ff67378e519023a1e.hot-update.js", "/js/main.d14ee47779985cb32ec0.hot-update.js": "/js/main.d14ee47779985cb32ec0.hot-update.js",
"/js/main.6e66fc4d78272a71315e.hot-update.js": "/js/main.6e66fc4d78272a71315e.hot-update.js", "/js/main.8d266a1d740fa389c2bf.hot-update.js": "/js/main.8d266a1d740fa389c2bf.hot-update.js",
"/js/main.a8e90242995935949f17.hot-update.js": "/js/main.a8e90242995935949f17.hot-update.js", "/js/main.99143ecd6bfafab9b677.hot-update.js": "/js/main.99143ecd6bfafab9b677.hot-update.js",
"/js/main.4e224f3aad42c7f5ed0b.hot-update.js": "/js/main.4e224f3aad42c7f5ed0b.hot-update.js", "/js/main.bbb2be0d78aaec9dfda1.hot-update.js": "/js/main.bbb2be0d78aaec9dfda1.hot-update.js",
"/js/main.385eb38db8eb0702b502.hot-update.js": "/js/main.385eb38db8eb0702b502.hot-update.js", "/js/main.76904835fe400dd9848f.hot-update.js": "/js/main.76904835fe400dd9848f.hot-update.js",
"/js/main.996d84214f93de3c1c0b.hot-update.js": "/js/main.996d84214f93de3c1c0b.hot-update.js", "/js/main.cafd83e6c2744baad34a.hot-update.js": "/js/main.cafd83e6c2744baad34a.hot-update.js",
"/js/main.38d70851a7c72784d66d.hot-update.js": "/js/main.38d70851a7c72784d66d.hot-update.js", "/js/main.2002a76681e8d0433941.hot-update.js": "/js/main.2002a76681e8d0433941.hot-update.js",
"/js/main.dbbfab82de065b7b60cd.hot-update.js": "/js/main.dbbfab82de065b7b60cd.hot-update.js", "/js/main.2fd100a299a791164337.hot-update.js": "/js/main.2fd100a299a791164337.hot-update.js",
"/js/main.574aea784108ee1eac07.hot-update.js": "/js/main.574aea784108ee1eac07.hot-update.js", "/js/main.3abd3c2a19f02f47bebc.hot-update.js": "/js/main.3abd3c2a19f02f47bebc.hot-update.js",
"/js/main.9de756c799555e75db7d.hot-update.js": "/js/main.9de756c799555e75db7d.hot-update.js", "/js/main.2bc80b9424efd6eadbed.hot-update.js": "/js/main.2bc80b9424efd6eadbed.hot-update.js",
"/js/main.83ab95777680fb06e0aa.hot-update.js": "/js/main.83ab95777680fb06e0aa.hot-update.js", "/js/main.48a8f2b34315a51837ed.hot-update.js": "/js/main.48a8f2b34315a51837ed.hot-update.js",
"/js/main.e666c42f96b6bdb22969.hot-update.js": "/js/main.e666c42f96b6bdb22969.hot-update.js", "/js/main.bb7c8231d66a0cb23884.hot-update.js": "/js/main.bb7c8231d66a0cb23884.hot-update.js",
"/js/main.03428097b17bfe163a16.hot-update.js": "/js/main.03428097b17bfe163a16.hot-update.js", "/js/main.f3c27d8639d237140393.hot-update.js": "/js/main.f3c27d8639d237140393.hot-update.js",
"/js/main.05d6a5f7f8c614a903e8.hot-update.js": "/js/main.05d6a5f7f8c614a903e8.hot-update.js", "/js/main.aa88aa732da3530ef6fc.hot-update.js": "/js/main.aa88aa732da3530ef6fc.hot-update.js",
"/js/main.7d8ff39260c5f281d28f.hot-update.js": "/js/main.7d8ff39260c5f281d28f.hot-update.js", "/js/main.2f10611b4cb0849baf9a.hot-update.js": "/js/main.2f10611b4cb0849baf9a.hot-update.js",
"/js/main.ad2349f007227d33ace3.hot-update.js": "/js/main.ad2349f007227d33ace3.hot-update.js", "/js/main.cdb3c9099ecd3aef1c6d.hot-update.js": "/js/main.cdb3c9099ecd3aef1c6d.hot-update.js",
"/js/main.61787afe25ca75dad5ec.hot-update.js": "/js/main.61787afe25ca75dad5ec.hot-update.js", "/js/main.4f6cbd01f21463974339.hot-update.js": "/js/main.4f6cbd01f21463974339.hot-update.js",
"/js/main.f130f7974630d2357291.hot-update.js": "/js/main.f130f7974630d2357291.hot-update.js", "/js/main.75e920f256de199321da.hot-update.js": "/js/main.75e920f256de199321da.hot-update.js",
"/js/main.194f15b84f0ba10c3558.hot-update.js": "/js/main.194f15b84f0ba10c3558.hot-update.js", "/js/main.3aa153417b65e9dd8e66.hot-update.js": "/js/main.3aa153417b65e9dd8e66.hot-update.js",
"/js/main.c9d4446b200a7919a284.hot-update.js": "/js/main.c9d4446b200a7919a284.hot-update.js", "/js/main.412dd1fb982791c51e22.hot-update.js": "/js/main.412dd1fb982791c51e22.hot-update.js",
"/js/main.a8a11f9957cb0472fcb1.hot-update.js": "/js/main.a8a11f9957cb0472fcb1.hot-update.js", "/js/main.f7dace814dd7cf6cd2d2.hot-update.js": "/js/main.f7dace814dd7cf6cd2d2.hot-update.js",
"/js/main.15201b3e0fd22133a26a.hot-update.js": "/js/main.15201b3e0fd22133a26a.hot-update.js", "/js/main.107c49051b193edea885.hot-update.js": "/js/main.107c49051b193edea885.hot-update.js",
"/js/main.24c3c7e84b88e524c006.hot-update.js": "/js/main.24c3c7e84b88e524c006.hot-update.js", "/js/main.b31481977f618806d60a.hot-update.js": "/js/main.b31481977f618806d60a.hot-update.js",
"/js/main.4a9d275860a5b9a8d2d2.hot-update.js": "/js/main.4a9d275860a5b9a8d2d2.hot-update.js", "/js/main.44dcaa7eb8423dcba8c0.hot-update.js": "/js/main.44dcaa7eb8423dcba8c0.hot-update.js",
"/js/main.1a78e45577ac640daa18.hot-update.js": "/js/main.1a78e45577ac640daa18.hot-update.js", "/js/main.ff751f6b0426846af09f.hot-update.js": "/js/main.ff751f6b0426846af09f.hot-update.js",
"/js/main.91f804d23d8cd6a0562e.hot-update.js": "/js/main.91f804d23d8cd6a0562e.hot-update.js", "/js/main.2b668837a746a960d470.hot-update.js": "/js/main.2b668837a746a960d470.hot-update.js",
"/js/main.37f4690a9d8970b2a997.hot-update.js": "/js/main.37f4690a9d8970b2a997.hot-update.js", "/js/main.c26ae7faad32e3f752f9.hot-update.js": "/js/main.c26ae7faad32e3f752f9.hot-update.js",
"/js/main.39cc460540d3e54e861d.hot-update.js": "/js/main.39cc460540d3e54e861d.hot-update.js", "/js/main.71819bd2c52cfd992c49.hot-update.js": "/js/main.71819bd2c52cfd992c49.hot-update.js",
"/js/main.97e2ae5d288287b185a4.hot-update.js": "/js/main.97e2ae5d288287b185a4.hot-update.js", "/js/main.b2a492cb77c7a3dbc8b8.hot-update.js": "/js/main.b2a492cb77c7a3dbc8b8.hot-update.js",
"/js/main.bcaa46e78128bc1ce527.hot-update.js": "/js/main.bcaa46e78128bc1ce527.hot-update.js", "/js/main.755b3b6f634d7ea41269.hot-update.js": "/js/main.755b3b6f634d7ea41269.hot-update.js",
"/js/main.f9b275607e4c5781a474.hot-update.js": "/js/main.f9b275607e4c5781a474.hot-update.js", "/js/main.63a686c9457eecec0aed.hot-update.js": "/js/main.63a686c9457eecec0aed.hot-update.js",
"/js/main.3625f6f0997dd72db8fb.hot-update.js": "/js/main.3625f6f0997dd72db8fb.hot-update.js", "/js/main.bc7270bc738d06bf003c.hot-update.js": "/js/main.bc7270bc738d06bf003c.hot-update.js",
"/js/main.8aac94e8b65eb3822505.hot-update.js": "/js/main.8aac94e8b65eb3822505.hot-update.js", "/js/main.6a812e6a9a8fdec9e0f9.hot-update.js": "/js/main.6a812e6a9a8fdec9e0f9.hot-update.js",
"/js/main.962286776eaf79087cff.hot-update.js": "/js/main.962286776eaf79087cff.hot-update.js", "/js/main.605b505026463b82cf92.hot-update.js": "/js/main.605b505026463b82cf92.hot-update.js",
"/js/main.a66ab1c4d2fa23744e67.hot-update.js": "/js/main.a66ab1c4d2fa23744e67.hot-update.js", "/js/main.67ec3e67dc7fe249a511.hot-update.js": "/js/main.67ec3e67dc7fe249a511.hot-update.js",
"/js/main.9a1ab04495b8495aa7a3.hot-update.js": "/js/main.9a1ab04495b8495aa7a3.hot-update.js", "/js/main.60f7f14041f1b1c43ba7.hot-update.js": "/js/main.60f7f14041f1b1c43ba7.hot-update.js",
"/js/main.394df0e7d4886f791e9a.hot-update.js": "/js/main.394df0e7d4886f791e9a.hot-update.js", "/js/main.378fefdc90168f530ced.hot-update.js": "/js/main.378fefdc90168f530ced.hot-update.js",
"/js/main.bbd2e296d4d14fafbc5c.hot-update.js": "/js/main.bbd2e296d4d14fafbc5c.hot-update.js", "/js/main.50bcdbbb6689ab1f3ff8.hot-update.js": "/js/main.50bcdbbb6689ab1f3ff8.hot-update.js",
"/js/main.c9330023b054be206d4d.hot-update.js": "/js/main.c9330023b054be206d4d.hot-update.js", "/js/main.bad32b198b2701141385.hot-update.js": "/js/main.bad32b198b2701141385.hot-update.js",
"/js/main.5d81a2f9c58ce58a43cd.hot-update.js": "/js/main.5d81a2f9c58ce58a43cd.hot-update.js", "/js/main.054d269f4a98578503c1.hot-update.js": "/js/main.054d269f4a98578503c1.hot-update.js",
"/js/main.86fa688fb982fbd12aca.hot-update.js": "/js/main.86fa688fb982fbd12aca.hot-update.js", "/js/main.41b69188f146b379f87b.hot-update.js": "/js/main.41b69188f146b379f87b.hot-update.js",
"/js/main.645f6368417b7c4fd590.hot-update.js": "/js/main.645f6368417b7c4fd590.hot-update.js", "/js/main.3e018a3648dfc61d3df3.hot-update.js": "/js/main.3e018a3648dfc61d3df3.hot-update.js",
"/js/main.6edba403d405425e87cf.hot-update.js": "/js/main.6edba403d405425e87cf.hot-update.js", "/js/main.2936652ac5660f94a796.hot-update.js": "/js/main.2936652ac5660f94a796.hot-update.js",
"/js/main.ca0cd44c7d8d17cc9991.hot-update.js": "/js/main.ca0cd44c7d8d17cc9991.hot-update.js", "/js/main.fcc623fc42f160e06cfe.hot-update.js": "/js/main.fcc623fc42f160e06cfe.hot-update.js",
"/js/main.b831183422933c78119c.hot-update.js": "/js/main.b831183422933c78119c.hot-update.js", "/js/main.93d580e0883dc95e1116.hot-update.js": "/js/main.93d580e0883dc95e1116.hot-update.js",
"/js/main.80db7eb954bc974b52ec.hot-update.js": "/js/main.80db7eb954bc974b52ec.hot-update.js", "/js/main.28d29c71ea1ed0a0b0b4.hot-update.js": "/js/main.28d29c71ea1ed0a0b0b4.hot-update.js",
"/js/main.8bee745b28ee104901f8.hot-update.js": "/js/main.8bee745b28ee104901f8.hot-update.js", "/js/main.2a76ff437524f4ee71c0.hot-update.js": "/js/main.2a76ff437524f4ee71c0.hot-update.js",
"/js/main.e0e4ec61d482c4f3dbe8.hot-update.js": "/js/main.e0e4ec61d482c4f3dbe8.hot-update.js", "/js/main.cff525ce21d6a17180c1.hot-update.js": "/js/main.cff525ce21d6a17180c1.hot-update.js",
"/js/main.bb3ed5bb83568a1abbda.hot-update.js": "/js/main.bb3ed5bb83568a1abbda.hot-update.js", "/js/main.160476920d84b7b64f03.hot-update.js": "/js/main.160476920d84b7b64f03.hot-update.js",
"/js/main.0fca3fcc3088a5f8b7e0.hot-update.js": "/js/main.0fca3fcc3088a5f8b7e0.hot-update.js", "/js/main.30dda3674a213af39664.hot-update.js": "/js/main.30dda3674a213af39664.hot-update.js",
"/js/main.fb7b96cb40ab22050c79.hot-update.js": "/js/main.fb7b96cb40ab22050c79.hot-update.js", "/js/main.c011a2db86676be81958.hot-update.js": "/js/main.c011a2db86676be81958.hot-update.js",
"/js/main.9348fa55f71efc5060b8.hot-update.js": "/js/main.9348fa55f71efc5060b8.hot-update.js", "/js/main.3b1eb2be045aeb2794d7.hot-update.js": "/js/main.3b1eb2be045aeb2794d7.hot-update.js",
"/js/main.0122b6629f01a705476d.hot-update.js": "/js/main.0122b6629f01a705476d.hot-update.js", "/js/main.06a1a0a0d13e736f4b21.hot-update.js": "/js/main.06a1a0a0d13e736f4b21.hot-update.js",
"/js/main.ffc029e04518fabf33cd.hot-update.js": "/js/main.ffc029e04518fabf33cd.hot-update.js", "/js/main.e01d5781e9a5c7916665.hot-update.js": "/js/main.e01d5781e9a5c7916665.hot-update.js",
"/js/main.0666ac365d039c279130.hot-update.js": "/js/main.0666ac365d039c279130.hot-update.js", "/js/main.a8be6a86ef7ea1fa09aa.hot-update.js": "/js/main.a8be6a86ef7ea1fa09aa.hot-update.js",
"/js/main.844c2113c0c54845083c.hot-update.js": "/js/main.844c2113c0c54845083c.hot-update.js", "/js/main.10ef93f11f9971bffcd0.hot-update.js": "/js/main.10ef93f11f9971bffcd0.hot-update.js",
"/js/main.04c649da59688caed7f4.hot-update.js": "/js/main.04c649da59688caed7f4.hot-update.js", "/js/main.8272060c881465e5c1b8.hot-update.js": "/js/main.8272060c881465e5c1b8.hot-update.js",
"/js/main.df8852739b6eb0d8ddd8.hot-update.js": "/js/main.df8852739b6eb0d8ddd8.hot-update.js", "/js/main.3c4afd1bbc5dbddbc2f5.hot-update.js": "/js/main.3c4afd1bbc5dbddbc2f5.hot-update.js",
"/js/main.2bb035b765159291e6d4.hot-update.js": "/js/main.2bb035b765159291e6d4.hot-update.js", "/js/main.5095c746248692e2fab9.hot-update.js": "/js/main.5095c746248692e2fab9.hot-update.js",
"/js/main.6eb45c6e294e847c45ea.hot-update.js": "/js/main.6eb45c6e294e847c45ea.hot-update.js", "/js/main.b7e058950b6450f5be86.hot-update.js": "/js/main.b7e058950b6450f5be86.hot-update.js",
"/js/main.c6070c2ada89c43fa419.hot-update.js": "/js/main.c6070c2ada89c43fa419.hot-update.js", "/js/main.0cd416bcfc7be3e736b4.hot-update.js": "/js/main.0cd416bcfc7be3e736b4.hot-update.js",
"/js/main.f828168b89124130bc68.hot-update.js": "/js/main.f828168b89124130bc68.hot-update.js", "/js/main.074d97f29afb98992021.hot-update.js": "/js/main.074d97f29afb98992021.hot-update.js",
"/js/main.e5eb5a13e710543f7bd9.hot-update.js": "/js/main.e5eb5a13e710543f7bd9.hot-update.js", "/js/main.b832de402f7a0bb84f16.hot-update.js": "/js/main.b832de402f7a0bb84f16.hot-update.js",
"/js/main.63b69c5bbda8f437d459.hot-update.js": "/js/main.63b69c5bbda8f437d459.hot-update.js", "/js/main.e2acc484ee30d5bc511b.hot-update.js": "/js/main.e2acc484ee30d5bc511b.hot-update.js",
"/js/main.a72c8891596b66b212b6.hot-update.js": "/js/main.a72c8891596b66b212b6.hot-update.js", "/js/main.6062f643e9e4b8ca338f.hot-update.js": "/js/main.6062f643e9e4b8ca338f.hot-update.js",
"/js/main.19e0710bc40f03b262e6.hot-update.js": "/js/main.19e0710bc40f03b262e6.hot-update.js", "/js/main.2e9db006c2bd6ed06276.hot-update.js": "/js/main.2e9db006c2bd6ed06276.hot-update.js",
"/js/main.0134afb94f91a4a36def.hot-update.js": "/js/main.0134afb94f91a4a36def.hot-update.js", "/js/main.c5440e1711c2366531e8.hot-update.js": "/js/main.c5440e1711c2366531e8.hot-update.js",
"/js/main.9a24933aefa3e49663f7.hot-update.js": "/js/main.9a24933aefa3e49663f7.hot-update.js", "/js/main.9621c2052cd66aab368f.hot-update.js": "/js/main.9621c2052cd66aab368f.hot-update.js",
"/js/main.cc27c9bc9c27f1b845bb.hot-update.js": "/js/main.cc27c9bc9c27f1b845bb.hot-update.js", "/js/main.0fe5939166b86f760041.hot-update.js": "/js/main.0fe5939166b86f760041.hot-update.js",
"/js/main.b93834f4ca3df91cd540.hot-update.js": "/js/main.b93834f4ca3df91cd540.hot-update.js", "/js/main.9a08a8a2109d7993ea97.hot-update.js": "/js/main.9a08a8a2109d7993ea97.hot-update.js",
"/js/main.5937cb7e39356fd7d4c4.hot-update.js": "/js/main.5937cb7e39356fd7d4c4.hot-update.js", "/js/main.bf10e2245b146a2cf6c0.hot-update.js": "/js/main.bf10e2245b146a2cf6c0.hot-update.js",
"/js/main.cea013f04296667aee3a.hot-update.js": "/js/main.cea013f04296667aee3a.hot-update.js", "/js/main.9c0628dcaf454d8ef45e.hot-update.js": "/js/main.9c0628dcaf454d8ef45e.hot-update.js",
"/js/main.bff0613b9adee03ca219.hot-update.js": "/js/main.bff0613b9adee03ca219.hot-update.js", "/js/main.93b39c613c426756176e.hot-update.js": "/js/main.93b39c613c426756176e.hot-update.js",
"/js/main.a43ba373208207664417.hot-update.js": "/js/main.a43ba373208207664417.hot-update.js", "/js/main.cddfc9fafcd6885f21e7.hot-update.js": "/js/main.cddfc9fafcd6885f21e7.hot-update.js",
"/js/main.4ff290116a377da3d41f.hot-update.js": "/js/main.4ff290116a377da3d41f.hot-update.js", "/js/main.cca75105094aa748a70d.hot-update.js": "/js/main.cca75105094aa748a70d.hot-update.js",
"/js/main.96a5134be5a0a98693e4.hot-update.js": "/js/main.96a5134be5a0a98693e4.hot-update.js", "/js/main.c8c27998c2b3cf45a66b.hot-update.js": "/js/main.c8c27998c2b3cf45a66b.hot-update.js",
"/js/main.e0432d1e9c7975c2ab9d.hot-update.js": "/js/main.e0432d1e9c7975c2ab9d.hot-update.js", "/js/main.329fc90f8bb0ac52ca61.hot-update.js": "/js/main.329fc90f8bb0ac52ca61.hot-update.js",
"/js/main.d62433ce757652be6f54.hot-update.js": "/js/main.d62433ce757652be6f54.hot-update.js", "/js/main.6f31b38bb7c9668fbbce.hot-update.js": "/js/main.6f31b38bb7c9668fbbce.hot-update.js",
"/js/main.fa91e0076416ce9b9eac.hot-update.js": "/js/main.fa91e0076416ce9b9eac.hot-update.js", "/js/main.1fd945801f700932880d.hot-update.js": "/js/main.1fd945801f700932880d.hot-update.js",
"/js/main.6791a0db3468e2abbb01.hot-update.js": "/js/main.6791a0db3468e2abbb01.hot-update.js", "/js/main.d4f2e81b634d134be133.hot-update.js": "/js/main.d4f2e81b634d134be133.hot-update.js",
"/js/main.f34c60f7b8d27d185789.hot-update.js": "/js/main.f34c60f7b8d27d185789.hot-update.js", "/js/main.b80e30c83de500fc196d.hot-update.js": "/js/main.b80e30c83de500fc196d.hot-update.js",
"/js/main.e0ac013a5685366884b0.hot-update.js": "/js/main.e0ac013a5685366884b0.hot-update.js", "/js/main.8d8063569f01acb69bc7.hot-update.js": "/js/main.8d8063569f01acb69bc7.hot-update.js",
"/js/main.45f929f9501bca0a3a2b.hot-update.js": "/js/main.45f929f9501bca0a3a2b.hot-update.js", "/js/main.d4d7a8a2373540a95962.hot-update.js": "/js/main.d4d7a8a2373540a95962.hot-update.js",
"/js/main.c17c98fa0aac463d62ec.hot-update.js": "/js/main.c17c98fa0aac463d62ec.hot-update.js", "/js/main.4b0768ce7a19f5093747.hot-update.js": "/js/main.4b0768ce7a19f5093747.hot-update.js",
"/js/main.0e03c020348420598462.hot-update.js": "/js/main.0e03c020348420598462.hot-update.js", "/js/main.34079aa9b29123a2083c.hot-update.js": "/js/main.34079aa9b29123a2083c.hot-update.js",
"/js/main.0a76373d676f6bc3f8b7.hot-update.js": "/js/main.0a76373d676f6bc3f8b7.hot-update.js", "/js/main.ec6c429db76fb2dd0212.hot-update.js": "/js/main.ec6c429db76fb2dd0212.hot-update.js",
"/js/main.7d0b5e6140d5a76d8a6e.hot-update.js": "/js/main.7d0b5e6140d5a76d8a6e.hot-update.js", "/js/main.2ab0c9706b0b5f27748f.hot-update.js": "/js/main.2ab0c9706b0b5f27748f.hot-update.js",
"/js/main.5faa42142a298e5ab92b.hot-update.js": "/js/main.5faa42142a298e5ab92b.hot-update.js", "/js/main.01b547932ef0a411f639.hot-update.js": "/js/main.01b547932ef0a411f639.hot-update.js",
"/js/main.3eaf20665f37c24473fd.hot-update.js": "/js/main.3eaf20665f37c24473fd.hot-update.js", "/js/main.f9d0e38a0c8267727c41.hot-update.js": "/js/main.f9d0e38a0c8267727c41.hot-update.js",
"/js/main.6d0cf380525ddd49bb4b.hot-update.js": "/js/main.6d0cf380525ddd49bb4b.hot-update.js", "/js/main.47a584d9a6bfa1cbaca6.hot-update.js": "/js/main.47a584d9a6bfa1cbaca6.hot-update.js",
"/js/main.96ceb1a99c9ccca2fbbe.hot-update.js": "/js/main.96ceb1a99c9ccca2fbbe.hot-update.js", "/js/main.a891f426506bbc45d3f6.hot-update.js": "/js/main.a891f426506bbc45d3f6.hot-update.js",
"/js/main.3557a28505eb1edad3c6.hot-update.js": "/js/main.3557a28505eb1edad3c6.hot-update.js", "/js/main.0ab4b48148074ad23aec.hot-update.js": "/js/main.0ab4b48148074ad23aec.hot-update.js",
"/js/main.f85533ba77bc9daa2b28.hot-update.js": "/js/main.f85533ba77bc9daa2b28.hot-update.js", "/js/main.d851fe54d559c0c439d4.hot-update.js": "/js/main.d851fe54d559c0c439d4.hot-update.js",
"/js/main.844ced51412415958d01.hot-update.js": "/js/main.844ced51412415958d01.hot-update.js", "/js/main.965fc731cb2a00fcde50.hot-update.js": "/js/main.965fc731cb2a00fcde50.hot-update.js",
"/js/main.4aef58634b3a86124e7f.hot-update.js": "/js/main.4aef58634b3a86124e7f.hot-update.js", "/js/main.7efc63569bd16d43be96.hot-update.js": "/js/main.7efc63569bd16d43be96.hot-update.js",
"/js/main.6de5f0faf8a05f2fc5a7.hot-update.js": "/js/main.6de5f0faf8a05f2fc5a7.hot-update.js", "/js/main.b56f81dd7b7cf2b4a9b2.hot-update.js": "/js/main.b56f81dd7b7cf2b4a9b2.hot-update.js",
"/js/main.c62abbf47cf5f738c9b5.hot-update.js": "/js/main.c62abbf47cf5f738c9b5.hot-update.js", "/js/main.6b538f47d7084fc5c056.hot-update.js": "/js/main.6b538f47d7084fc5c056.hot-update.js",
"/js/main.87c321a637ae5ab917ce.hot-update.js": "/js/main.87c321a637ae5ab917ce.hot-update.js", "/js/main.0847c2f8c76446ffc1e1.hot-update.js": "/js/main.0847c2f8c76446ffc1e1.hot-update.js",
"/js/main.90ad40b9a9f360d249f9.hot-update.js": "/js/main.90ad40b9a9f360d249f9.hot-update.js", "/js/main.fa9ded0194e51af751b4.hot-update.js": "/js/main.fa9ded0194e51af751b4.hot-update.js",
"/js/main.91b09e11f41e643af61b.hot-update.js": "/js/main.91b09e11f41e643af61b.hot-update.js", "/js/main.8e479c4cc567a7f86fbf.hot-update.js": "/js/main.8e479c4cc567a7f86fbf.hot-update.js",
"/js/main.6e993020f81c4506f7f1.hot-update.js": "/js/main.6e993020f81c4506f7f1.hot-update.js", "/js/main.c2131ca9020d74887a86.hot-update.js": "/js/main.c2131ca9020d74887a86.hot-update.js",
"/js/main.eb69554875d7f6f0af13.hot-update.js": "/js/main.eb69554875d7f6f0af13.hot-update.js", "/js/main.ae7a03f5a01e1b1fb79f.hot-update.js": "/js/main.ae7a03f5a01e1b1fb79f.hot-update.js",
"/js/main.b00263659caf576e365f.hot-update.js": "/js/main.b00263659caf576e365f.hot-update.js", "/js/main.4719a377f8009f031b35.hot-update.js": "/js/main.4719a377f8009f031b35.hot-update.js",
"/js/main.3731a12d7df03dd0b1ac.hot-update.js": "/js/main.3731a12d7df03dd0b1ac.hot-update.js", "/js/main.c87c48fa6a02b2403911.hot-update.js": "/js/main.c87c48fa6a02b2403911.hot-update.js",
"/js/main.14283c0c8928f2bac642.hot-update.js": "/js/main.14283c0c8928f2bac642.hot-update.js", "/js/main.47109b08d1688186ca0f.hot-update.js": "/js/main.47109b08d1688186ca0f.hot-update.js",
"/js/main.5d22154675bb7301f3c3.hot-update.js": "/js/main.5d22154675bb7301f3c3.hot-update.js", "/js/main.74b35f583dd235319010.hot-update.js": "/js/main.74b35f583dd235319010.hot-update.js",
"/js/main.68f901614ec25bb9be2e.hot-update.js": "/js/main.68f901614ec25bb9be2e.hot-update.js", "/js/main.8ad646c7fdf758fc3077.hot-update.js": "/js/main.8ad646c7fdf758fc3077.hot-update.js",
"/js/main.94b2828776f652bbecd1.hot-update.js": "/js/main.94b2828776f652bbecd1.hot-update.js", "/js/main.f43a2d4521fab4b9a94e.hot-update.js": "/js/main.f43a2d4521fab4b9a94e.hot-update.js",
"/js/main.a5193c2c4b1f57e843ac.hot-update.js": "/js/main.a5193c2c4b1f57e843ac.hot-update.js", "/js/main.3435832fdb3175879cf7.hot-update.js": "/js/main.3435832fdb3175879cf7.hot-update.js",
"/js/main.50b2e366bd6b72990a03.hot-update.js": "/js/main.50b2e366bd6b72990a03.hot-update.js", "/js/main.c58b3c90adbd372bdd97.hot-update.js": "/js/main.c58b3c90adbd372bdd97.hot-update.js",
"/js/main.f7a87e8ee0dc7a8c3d35.hot-update.js": "/js/main.f7a87e8ee0dc7a8c3d35.hot-update.js", "/js/main.c08fa8f3335ed51d3f75.hot-update.js": "/js/main.c08fa8f3335ed51d3f75.hot-update.js",
"/js/main.460fb4a8eadec50eafc0.hot-update.js": "/js/main.460fb4a8eadec50eafc0.hot-update.js", "/js/main.4c82e15c2d67b287e8f8.hot-update.js": "/js/main.4c82e15c2d67b287e8f8.hot-update.js",
"/js/main.b56efb8b6dda94e0e837.hot-update.js": "/js/main.b56efb8b6dda94e0e837.hot-update.js", "/js/main.508fada0c3ecbb174a11.hot-update.js": "/js/main.508fada0c3ecbb174a11.hot-update.js",
"/js/main.7696e7ea10b668afeccc.hot-update.js": "/js/main.7696e7ea10b668afeccc.hot-update.js", "/js/main.61da2361919afdd32838.hot-update.js": "/js/main.61da2361919afdd32838.hot-update.js",
"/js/main.7d32beb20b5719f11726.hot-update.js": "/js/main.7d32beb20b5719f11726.hot-update.js", "/js/main.602057bf75bb7533497a.hot-update.js": "/js/main.602057bf75bb7533497a.hot-update.js",
"/js/main.6325449e7341db20a9ce.hot-update.js": "/js/main.6325449e7341db20a9ce.hot-update.js", "/js/main.08f17d3db95a580ebcff.hot-update.js": "/js/main.08f17d3db95a580ebcff.hot-update.js",
"/js/main.2cc0ebd3eba768b8429c.hot-update.js": "/js/main.2cc0ebd3eba768b8429c.hot-update.js", "/js/main.fde740604ac11217991e.hot-update.js": "/js/main.fde740604ac11217991e.hot-update.js",
"/js/main.22edc66a07d0d7fc8d83.hot-update.js": "/js/main.22edc66a07d0d7fc8d83.hot-update.js", "/js/main.470a4e47222c5cd0ca6c.hot-update.js": "/js/main.470a4e47222c5cd0ca6c.hot-update.js",
"/js/main.60bed97815a1ea0a09d6.hot-update.js": "/js/main.60bed97815a1ea0a09d6.hot-update.js", "/js/main.78a6535c7174d7490de2.hot-update.js": "/js/main.78a6535c7174d7490de2.hot-update.js",
"/js/main.f6a63bab2aa5511566e7.hot-update.js": "/js/main.f6a63bab2aa5511566e7.hot-update.js", "/js/main.c8a4f8f465ce6970b2ea.hot-update.js": "/js/main.c8a4f8f465ce6970b2ea.hot-update.js",
"/js/main.121e2dd11bc35045482b.hot-update.js": "/js/main.121e2dd11bc35045482b.hot-update.js", "/js/main.d8e90a128296036c0c15.hot-update.js": "/js/main.d8e90a128296036c0c15.hot-update.js",
"/js/main.541def265ec163ed2d5f.hot-update.js": "/js/main.541def265ec163ed2d5f.hot-update.js", "/js/main.4f84cab5a93c6fca138b.hot-update.js": "/js/main.4f84cab5a93c6fca138b.hot-update.js",
"/js/main.c9814fe8d127f6265fdf.hot-update.js": "/js/main.c9814fe8d127f6265fdf.hot-update.js", "/js/main.ff08fabb70c77845dacf.hot-update.js": "/js/main.ff08fabb70c77845dacf.hot-update.js",
"/js/main.0c31610719dd6fbfd7ab.hot-update.js": "/js/main.0c31610719dd6fbfd7ab.hot-update.js", "/js/main.73b58a7269460f4044f6.hot-update.js": "/js/main.73b58a7269460f4044f6.hot-update.js",
"/js/main.73d20180f790009a0536.hot-update.js": "/js/main.73d20180f790009a0536.hot-update.js", "/js/main.bffdfdca5226e1971a1f.hot-update.js": "/js/main.bffdfdca5226e1971a1f.hot-update.js",
"/js/main.bd8cd2b0d65c5499a5fa.hot-update.js": "/js/main.bd8cd2b0d65c5499a5fa.hot-update.js", "/js/main.5d24f494cb656c4aea06.hot-update.js": "/js/main.5d24f494cb656c4aea06.hot-update.js",
"/js/main.4b941ee3158e8f359e0b.hot-update.js": "/js/main.4b941ee3158e8f359e0b.hot-update.js", "/js/main.284dea61b9e166ccf9f0.hot-update.js": "/js/main.284dea61b9e166ccf9f0.hot-update.js",
"/js/main.4eed502c18c3384e017c.hot-update.js": "/js/main.4eed502c18c3384e017c.hot-update.js", "/js/main.0865fc8f9bcc9f534bd5.hot-update.js": "/js/main.0865fc8f9bcc9f534bd5.hot-update.js",
"/js/main.0ff9c65b54e9f2fd3f09.hot-update.js": "/js/main.0ff9c65b54e9f2fd3f09.hot-update.js", "/js/main.6d388afa93af449e99fd.hot-update.js": "/js/main.6d388afa93af449e99fd.hot-update.js",
"/js/main.0c5bcf3373897713488f.hot-update.js": "/js/main.0c5bcf3373897713488f.hot-update.js", "/js/main.108d51ae66a3b2e72dbc.hot-update.js": "/js/main.108d51ae66a3b2e72dbc.hot-update.js",
"/js/main.5d00a679a40bb2c37b11.hot-update.js": "/js/main.5d00a679a40bb2c37b11.hot-update.js", "/js/main.f78ad0c36590167303c7.hot-update.js": "/js/main.f78ad0c36590167303c7.hot-update.js",
"/js/main.2559125fe1eef3ee7dd3.hot-update.js": "/js/main.2559125fe1eef3ee7dd3.hot-update.js", "/js/main.607a892f908136301269.hot-update.js": "/js/main.607a892f908136301269.hot-update.js",
"/js/main.55d5f2f55034cb7a4da6.hot-update.js": "/js/main.55d5f2f55034cb7a4da6.hot-update.js", "/js/main.85d4f345841dfbb40e07.hot-update.js": "/js/main.85d4f345841dfbb40e07.hot-update.js",
"/js/main.890897a7ce8479317a97.hot-update.js": "/js/main.890897a7ce8479317a97.hot-update.js", "/js/main.945083b1b0445126d561.hot-update.js": "/js/main.945083b1b0445126d561.hot-update.js",
"/js/main.988a45b98fc8b8c5e41c.hot-update.js": "/js/main.988a45b98fc8b8c5e41c.hot-update.js", "/js/main.b09684c47f389bcc887e.hot-update.js": "/js/main.b09684c47f389bcc887e.hot-update.js",
"/js/main.33577380f3a58be208ee.hot-update.js": "/js/main.33577380f3a58be208ee.hot-update.js", "/js/main.19d37869465800d743bb.hot-update.js": "/js/main.19d37869465800d743bb.hot-update.js",
"/js/main.ada254f372b54b162118.hot-update.js": "/js/main.ada254f372b54b162118.hot-update.js", "/js/main.c54cd5dfd83b055f78af.hot-update.js": "/js/main.c54cd5dfd83b055f78af.hot-update.js",
"/js/main.afa97b2d282c673884a9.hot-update.js": "/js/main.afa97b2d282c673884a9.hot-update.js" "/js/main.672249e68aa26cdc3467.hot-update.js": "/js/main.672249e68aa26cdc3467.hot-update.js",
"/js/main.784cd47e7b5a14f729ac.hot-update.js": "/js/main.784cd47e7b5a14f729ac.hot-update.js",
"/js/main.6bb6b81e129e23fb017c.hot-update.js": "/js/main.6bb6b81e129e23fb017c.hot-update.js",
"/js/main.db67acf9cd2003665508.hot-update.js": "/js/main.db67acf9cd2003665508.hot-update.js",
"/js/main.42bbb785d8bc3374175e.hot-update.js": "/js/main.42bbb785d8bc3374175e.hot-update.js",
"/js/main.0985aa644d053b57a808.hot-update.js": "/js/main.0985aa644d053b57a808.hot-update.js",
"/js/main.49338628cefc585d505e.hot-update.js": "/js/main.49338628cefc585d505e.hot-update.js",
"/js/main.efce52309183dd157590.hot-update.js": "/js/main.efce52309183dd157590.hot-update.js",
"/js/main.35aeb37a9609024aeac9.hot-update.js": "/js/main.35aeb37a9609024aeac9.hot-update.js",
"/js/main.53a1fde399bfc98f701c.hot-update.js": "/js/main.53a1fde399bfc98f701c.hot-update.js",
"/js/main.f822941031e6e082f07c.hot-update.js": "/js/main.f822941031e6e082f07c.hot-update.js",
"/js/main.ca79ce64147b6e0e55cb.hot-update.js": "/js/main.ca79ce64147b6e0e55cb.hot-update.js",
"/js/main.937e0ee30a05573041b3.hot-update.js": "/js/main.937e0ee30a05573041b3.hot-update.js",
"/js/main.e7d9b3bc53ac4b3053bc.hot-update.js": "/js/main.e7d9b3bc53ac4b3053bc.hot-update.js",
"/js/main.96bcd5ef0412bc46d1ed.hot-update.js": "/js/main.96bcd5ef0412bc46d1ed.hot-update.js",
"/js/main.b07bbcaf5c0f638640a0.hot-update.js": "/js/main.b07bbcaf5c0f638640a0.hot-update.js",
"/js/main.25e093e405cc4a690242.hot-update.js": "/js/main.25e093e405cc4a690242.hot-update.js",
"/js/main.6e0fa93bb2ee5213af77.hot-update.js": "/js/main.6e0fa93bb2ee5213af77.hot-update.js",
"/js/main.ef2172323fe57a1a3c55.hot-update.js": "/js/main.ef2172323fe57a1a3c55.hot-update.js",
"/js/main.91c8655cf52d84f6a284.hot-update.js": "/js/main.91c8655cf52d84f6a284.hot-update.js",
"/js/main.8864391e1b0b56720f7c.hot-update.js": "/js/main.8864391e1b0b56720f7c.hot-update.js",
"/js/main.bdb661bd0667afb07d42.hot-update.js": "/js/main.bdb661bd0667afb07d42.hot-update.js",
"/js/main.a907b2a57b6a2917f3da.hot-update.js": "/js/main.a907b2a57b6a2917f3da.hot-update.js",
"/js/main.8bd891d1410908be3f0b.hot-update.js": "/js/main.8bd891d1410908be3f0b.hot-update.js",
"/js/main.70b0ed0c55368738458d.hot-update.js": "/js/main.70b0ed0c55368738458d.hot-update.js",
"/js/main.0c542c7f0c6e83b4fa18.hot-update.js": "/js/main.0c542c7f0c6e83b4fa18.hot-update.js",
"/js/main.69f8ec453d307c1f58d5.hot-update.js": "/js/main.69f8ec453d307c1f58d5.hot-update.js",
"/js/main.3b0d20392d7731b198b2.hot-update.js": "/js/main.3b0d20392d7731b198b2.hot-update.js",
"/js/main.9755e76f416f1eb7566c.hot-update.js": "/js/main.9755e76f416f1eb7566c.hot-update.js",
"/js/main.8fd8b140290b7b62e8bf.hot-update.js": "/js/main.8fd8b140290b7b62e8bf.hot-update.js",
"/js/main.a61786216f1a7f65d8c4.hot-update.js": "/js/main.a61786216f1a7f65d8c4.hot-update.js",
"/js/main.ab0a2ac4fee47195d276.hot-update.js": "/js/main.ab0a2ac4fee47195d276.hot-update.js",
"/js/main.ea5986d9d962e977f388.hot-update.js": "/js/main.ea5986d9d962e977f388.hot-update.js",
"/js/main.e9ecf936319bd5e5a9ba.hot-update.js": "/js/main.e9ecf936319bd5e5a9ba.hot-update.js",
"/js/main.83747ba398461b3dae0c.hot-update.js": "/js/main.83747ba398461b3dae0c.hot-update.js"
} }
+5
View File
@@ -8,6 +8,9 @@
<MobileNavigation /> <MobileNavigation />
<!--Confirm Popup-->
<Confirm />
<!--Share Item setup--> <!--Share Item setup-->
<ShareCreate/> <ShareCreate/>
<ShareEdit/> <ShareEdit/>
@@ -42,6 +45,7 @@
import MobileNavigation from '@/components/Others/MobileNavigation' import MobileNavigation from '@/components/Others/MobileNavigation'
import MobileMenu from '@/components/FilesView/MobileMenu' import MobileMenu from '@/components/FilesView/MobileMenu'
import ShareCreate from '@/components/Others/ShareCreate' import ShareCreate from '@/components/Others/ShareCreate'
import Confirm from '@/components/Others/Popup/Confirm'
import ShareEdit from '@/components/Others/ShareEdit' import ShareEdit from '@/components/Others/ShareEdit'
import MoveItem from '@/components/Others/MoveItem' import MoveItem from '@/components/Others/MoveItem'
import Vignette from '@/components/Others/Vignette' import Vignette from '@/components/Others/Vignette'
@@ -61,6 +65,7 @@
ShareEdit, ShareEdit,
MoveItem, MoveItem,
Vignette, Vignette,
Confirm,
MenuBar, MenuBar,
Alert, Alert,
}, },
@@ -136,6 +136,7 @@
} }
.popup-content { .popup-content {
.title { .title {
@include font-size(22); @include font-size(22);
text-transform: uppercase; text-transform: uppercase;
@@ -110,7 +110,8 @@
'homeDirectory', 'homeDirectory',
]), ]),
hasCapacity() { hasCapacity() {
return this.$store.getters.user.relationships.storage.data.attributes.used <= 100 //return this.$store.getters.user.relationships.storage.data.attributes.used <= 100
return true
}, },
directoryName() { directoryName() {
return this.currentFolder ? this.currentFolder.name : this.homeDirectory.name return this.currentFolder ? this.currentFolder.name : this.homeDirectory.name
@@ -37,6 +37,11 @@
color: $theme; color: $theme;
background: rgba($theme, 0.1); background: rgba($theme, 0.1);
} }
&.red {
color: $danger;
background: rgba($danger, 0.1);
}
} }
@media only screen and (max-width: 1024px) { @media only screen and (max-width: 1024px) {
@@ -1,12 +1,21 @@
<template> <template>
<div class="page-tab"> <div class="page-tab">
<slot></slot> <div id="loader" v-if="isLoading">
<Spinner></Spinner>
</div>
<slot v-if="! isLoading"></slot>
</div> </div>
</template> </template>
<script> <script>
import Spinner from '@/components/FilesView/Spinner'
export default { export default {
name: 'PageTab', name: 'PageTab',
props: ['isLoading'],
components: {
Spinner,
},
} }
</script> </script>
@@ -15,7 +15,7 @@
</section> </section>
<footer class="plan-footer"> <footer class="plan-footer">
<b class="price"> <b class="price">
{{ plan.data.attributes.price }} USD/Mo. {{ plan.data.attributes.price }}/Mo.
</b> </b>
<ButtonBase @click.native="selectPlan(plan)" type="submit" button-style="secondary" class="sign-in-button"> <ButtonBase @click.native="selectPlan(plan)" type="submit" button-style="secondary" class="sign-in-button">
Sign Up Sign Up
@@ -52,12 +52,7 @@
axios.get('/api/public/pricing') axios.get('/api/public/pricing')
.then(response => { .then(response => {
this.plans = response.data.data.filter(plan => { this.plans = response.data
if (this.$store.getters.user.relationships.subscription)
return plan.data.attributes.capacity > this.$store.getters.user.relationships.subscription.data.attributes.capacity
return true
})
this.$emit('load', false) this.$emit('load', false)
}) })
} }
@@ -72,6 +67,7 @@
text-align: center; text-align: center;
flex: 0 0 33%; flex: 0 0 33%;
padding: 0 25px; padding: 0 25px;
margin-bottom: 45px;
.plan-wrapper { .plan-wrapper {
box-shadow: 0 7px 20px 5px hsla(220, 36%, 16%, 0.03); box-shadow: 0 7px 20px 5px hsla(220, 36%, 16%, 0.03);
@@ -0,0 +1,133 @@
<template>
<PopupWrapper>
<div class="popup-image">
<span class="emoji">{{ emoji }}</span>
</div>
<PopupContent class="content">
<h1 v-if="title" class="title">{{ title }}</h1>
<p v-if="message" class="message">{{ message }}</p>
</PopupContent>
<PopupActions>
<ButtonBase
@click.native="closePopup"
button-style="secondary"
class="popup-button"
>Cancel
</ButtonBase>
<ButtonBase
@click.native="confirm"
:button-style="buttonColor"
class="popup-button"
>Yes, I'm sure
</ButtonBase>
</PopupActions>
</PopupWrapper>
</template>
<script>
import PopupWrapper from '@/components/Others/Popup/PopupWrapper'
import PopupActions from '@/components/Others/Popup/PopupActions'
import PopupContent from '@/components/Others/Popup/PopupContent'
import ButtonBase from '@/components/FilesView/ButtonBase'
import {events} from '@/bus'
export default {
name: 'ConfirmPopup',
components: {
PopupWrapper,
PopupActions,
PopupContent,
ButtonBase,
},
data() {
return {
confirmationData: [],
message: undefined,
title: undefined,
emoji: undefined,
buttonColor: undefined,
}
},
methods: {
closePopup() {
events.$emit('popup:close')
},
confirm() {
// Close popup
events.$emit('popup:close')
// Confirmation popup
events.$emit('action:confirmed', this.confirmationData)
// Clear confirmation data
this.confirmationData = []
}
},
mounted() {
// Show confirm
events.$on('confirm:open', args => {
this.title = args.title
this.message = args.message
this.emoji = '🤔'
this.confirmationData = args.action
this.buttonColor = 'danger-solid'
if (args.buttonColor) {
this.buttonColor = args.buttonColor
}
})
}
}
</script>
<style scoped lang="scss">
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
.popup-image {
padding-top: 20px;
text-align: center;
margin-bottom: 20px;
.emoji {
@include font-size(56);
line-height: 1;
}
}
.content {
text-align: center;
padding-bottom: 10px;
padding-left: 20px;
padding-right: 20px;
.title {
@include font-size(22);
text-transform: uppercase;
font-weight: 800;
color: $text;
}
.message {
@include font-size(16);
color: #333;
margin-top: 5px;
}
}
@media (prefers-color-scheme: dark) {
.content {
.title {
color: $dark_mode_text_primary;
}
.message {
color: $dark_mode_text_secondary;
}
}
}
</style>
@@ -35,6 +35,13 @@
this.isVisibleWrapper = true this.isVisibleWrapper = true
}) })
// Open called popup
events.$on('confirm:open', ({name}) => {
if (this.name === name)
this.isVisibleWrapper = true
})
// Close popup // Close popup
events.$on('popup:close', () => { events.$on('popup:close', () => {
@@ -29,6 +29,7 @@
events.$on('popup:open', () => this.isVisibleVignette = true) events.$on('popup:open', () => this.isVisibleVignette = true)
events.$on('alert:open', () => this.isVisibleVignette = true) events.$on('alert:open', () => this.isVisibleVignette = true)
events.$on('success:open', () => this.isVisibleVignette = true) events.$on('success:open', () => this.isVisibleVignette = true)
events.$on('confirm:open', () => this.isVisibleVignette = true)
} }
} }
</script> </script>
+278 -306
View File
@@ -5,313 +5,285 @@ import axios from 'axios'
import router from '@/router' import router from '@/router'
const Helpers = { const Helpers = {
install(Vue) { install(Vue) {
Vue.prototype.$updateText = debounce(function (route, name, value) { Vue.prototype.$updateText = debounce(function (route, name, value) {
if (value === '') return if (value === '') return
axios.patch(this.$store.getters.api + route, {name, value}) axios.patch(this.$store.getters.api + route, {name, value})
.catch(error => { .catch(error => {
events.$emit('alert:open', { events.$emit('alert:open', {
title: this.$t('popup_error.title'), title: this.$t('popup_error.title'),
message: this.$t('popup_error.message'), message: this.$t('popup_error.message'),
}) })
}) })
}, 300) }, 300)
Vue.prototype.$updateImage = function (route, name, image) { Vue.prototype.$getCreditCardBrand = function (brand) {
return `/assets/icons/${brand}.svg`
// Create form }
let formData = new FormData()
Vue.prototype.$updateImage = function (route, name, image) {
// Add image to form
formData.append(name, image) // Create form
formData.append('_method', 'PATCH') let formData = new FormData()
axios.post(this.$store.getters.api + route, formData, { // Add image to form
headers: { formData.append(name, image)
'Content-Type': 'multipart/form-data', formData.append('_method', 'PATCH')
}
}) axios.post(this.$store.getters.api + route, formData, {
.catch(error => { headers: {
events.$emit('alert:open', { 'Content-Type': 'multipart/form-data',
title: this.$t('popup_error.title'), }
message: this.$t('popup_error.message'), })
}) .catch(error => {
}) events.$emit('alert:open', {
} title: this.$t('popup_error.title'),
message: this.$t('popup_error.message'),
Vue.prototype.$openImageOnNewTab = function(source) { })
let win = window.open(source, '_blank') })
}
win.focus()
} Vue.prototype.$openImageOnNewTab = function (source) {
let win = window.open(source, '_blank')
Vue.prototype.$createFolder = function(folderName) {
this.$store.dispatch('createFolder', folderName) win.focus()
} }
Vue.prototype.$uploadFiles = async function(files) { Vue.prototype.$createFolder = function (folderName) {
// Prevent submit empty files this.$store.dispatch('createFolder', folderName)
if (files && files.length == 0) return }
// Check storage size Vue.prototype.$uploadFiles = async function (files) {
if (! this.$isThisLocation(['public']) && this.$store.getters.app.storage.percentage >= 100) { // Prevent submit empty files
events.$emit('alert:open', { if (files && files.length == 0) return
emoji: '😬😬😬',
title: this.$t('popup_exceed_limit.title'), let fileCount = files ? files.length : 0
message: this.$t('popup_exceed_limit.message') let fileCountSucceed = 1
})
store.commit('UPDATE_FILE_COUNT_PROGRESS', {
return current: fileCountSucceed,
} total: fileCount
})
let fileCount = files ? files.length : 0
let fileCountSucceed = 1 // Get parent id
const rootFolder = this.$store.getters.currentFolder
store.commit('UPDATE_FILE_COUNT_PROGRESS', { ? this.$store.getters.currentFolder.unique_id
current: fileCountSucceed, : 0
total: fileCount
}) for (var i = files.length - 1; i >= 0; i--) {
let formData = new FormData()
// Get parent id
const rootFolder = this.$store.getters.currentFolder // Append data
? this.$store.getters.currentFolder.unique_id formData.append('file', files[i])
: 0
// Append form data
for (var i = files.length - 1; i >= 0; i--) { formData.append('parent_id', rootFolder)
let formData = new FormData()
// Upload data
// Append data await store.dispatch('uploadFiles', formData)
formData.append('file', files[i]) .then(() => {
// Progress file log
// Append form data store.commit('UPDATE_FILE_COUNT_PROGRESS', {
formData.append('parent_id', rootFolder) current: fileCountSucceed,
total: fileCount
console.log(i); })
console.log(files[i]);
console.log(formData); // Uploading finished
if (fileCount === fileCountSucceed) {
// Upload data store.commit('UPDATE_FILE_COUNT_PROGRESS', undefined)
await store.dispatch('uploadFiles', formData) } else {
.then(() => { // Add uploaded file
// Progress file log fileCountSucceed++
store.commit('UPDATE_FILE_COUNT_PROGRESS', { }
current: fileCountSucceed, }).catch(error => {
total: fileCount switch (error.response.status) {
}) case 423:
events.$emit('alert:open', {
// Uploading finished emoji: '😬',
if (fileCount === fileCountSucceed) { title: this.$t('popup_exceed_limit.title'),
store.commit('UPDATE_FILE_COUNT_PROGRESS', undefined) message: this.$t('popup_exceed_limit.message')
} else { })
// Add uploaded file break;
fileCountSucceed++ case 413:
} events.$emit('alert:open', {
}).catch(error => { emoji: '😟',
title: this.$t('popup_paylod_error.title'),
if (error.response.status === 423) { message: this.$t('popup_paylod_error.message')
})
events.$emit('alert:open', { break;
emoji: '😬😬😬', default:
title: this.$t('popup_exceed_limit.title'), events.$emit('alert:open', {
message: this.$t('popup_exceed_limit.message') title: this.$t('popup_error.title'),
}) message: this.$t('popup_error.message'),
})
} else if (error.response.status === 413) { break;
}
events.$emit('alert:open', { })
emoji: '😟', }
title: this.$t('popup_paylod_error.title'), }
message: this.$t('popup_paylod_error.message')
}) Vue.prototype.$uploadExternalFiles = async function (event, parent_id) {
} else { // Prevent submit empty files
if (event.dataTransfer.items.length == 0) return
// Show error message
events.$emit('alert:open', { // Get files
title: this.$t('popup_error.title'), const files = [...event.dataTransfer.items].map(item => item.getAsFile());
message: this.$t('popup_error.message'),
}) let fileCountSucceed = 1
}
}) store.commit('UPDATE_FILE_COUNT_PROGRESS', {
} current: fileCountSucceed,
} total: files.length
})
Vue.prototype.$uploadExternalFiles = async function(event, parent_id) {
for (var i = files.length - 1; i >= 0; i--) {
// Prevent submit empty files
if (event.dataTransfer.items.length == 0) return let formData = new FormData()
// Get files // Append data
const files = [...event.dataTransfer.items].map(item => item.getAsFile()); formData.append('file', files[i])
// Check storage size // Append form data
if (! this.$isThisLocation(['public']) && this.$store.getters.app.storage.percentage >= 100) { formData.append('parent_id', parent_id)
events.$emit('alert:open', {
emoji: '😬😬😬', // Upload data
title: this.$t('popup_exceed_limit.title'), await store.dispatch('uploadFiles', formData).then(() => {
message: this.$t('popup_exceed_limit.message') // Progress file log
}) store.commit('UPDATE_FILE_COUNT_PROGRESS', {
current: fileCountSucceed,
return total: files.length
} })
// Progress file log
let fileCountSucceed = 1 store.commit('INCREASE_FOLDER_ITEM', parent_id)
store.commit('UPDATE_FILE_COUNT_PROGRESS', { // Uploading finished
current: fileCountSucceed, if (files.length === fileCountSucceed) {
total: files.length store.commit('UPDATE_FILE_COUNT_PROGRESS', undefined)
}) } else {
// Add uploaded file
fileCountSucceed++
}
}).catch(error => {
switch (error.response.status) {
case 423:
events.$emit('alert:open', {
emoji: '😬',
title: this.$t('popup_exceed_limit.title'),
message: this.$t('popup_exceed_limit.message')
})
break;
case 413:
events.$emit('alert:open', {
emoji: '😟',
title: this.$t('popup_paylod_error.title'),
message: this.$t('popup_paylod_error.message')
})
break;
default:
events.$emit('alert:open', {
title: this.$t('popup_error.title'),
message: this.$t('popup_error.message'),
})
break;
}
})
}
}
Vue.prototype.$downloadFile = function (url, filename) {
var anchor = document.createElement('a')
anchor.href = url
anchor.download = filename
document.body.appendChild(anchor)
anchor.click()
}
Vue.prototype.$closePopup = function () {
events.$emit('popup:close')
}
Vue.prototype.$isThisRoute = function (route, locations) {
return includes(locations, route.name)
}
Vue.prototype.$isThisLocation = function (location) {
// Get current location
let currentLocation = store.getters.currentFolder && store.getters.currentFolder.location ? store.getters.currentFolder.location : undefined
// Check if type is object
if (typeof location === 'Object' || location instanceof Object) {
return includes(location, currentLocation)
} else {
return currentLocation === location
}
}
Vue.prototype.$checkPermission = function (type) {
let currentPermission = store.getters.permission
// Check if type is object
if (typeof type === 'Object' || type instanceof Object) {
return includes(type, currentPermission)
} else {
return currentPermission === type
}
}
Vue.prototype.$isMobile = function () {
const toMatch = [
/Android/i,
/webOS/i,
/iPhone/i,
/iPad/i,
/iPod/i,
/BlackBerry/i,
/Windows Phone/i
]
return toMatch.some(toMatchItem => {
return navigator.userAgent.match(toMatchItem)
})
}
Vue.prototype.$isMinimalScale = function () {
let sizeType = store.getters.filesViewWidth
return sizeType === 'minimal-scale'
}
Vue.prototype.$isCompactScale = function () {
let sizeType = store.getters.filesViewWidth
return sizeType === 'compact-scale'
}
Vue.prototype.$isFullScale = function () {
let sizeType = store.getters.filesViewWidth
return sizeType === 'full-scale'
}
Vue.prototype.$isSomethingWrong = function () {
for (var i = files.length - 1; i >= 0; i--) { events.$emit('alert:open', {
title: this.$t('popup_error.title'),
let formData = new FormData() message: this.$t('popup_error.message'),
})
// Append data }
formData.append('file', files[i]) }
// Append form data
formData.append('parent_id', parent_id)
// Upload data
await store.dispatch('uploadFiles', formData).then(() => {
// Progress file log
store.commit('UPDATE_FILE_COUNT_PROGRESS', {
current: fileCountSucceed,
total: files.length
})
// Progress file log
store.commit('INCREASE_FOLDER_ITEM', parent_id)
// Uploading finished
if (files.length === fileCountSucceed) {
store.commit('UPDATE_FILE_COUNT_PROGRESS', undefined)
} else {
// Add uploaded file
fileCountSucceed++
}
}).catch(error => {
if (error.response.status == 423) {
events.$emit('alert:open', {
emoji: '😬😬😬',
title: this.$t('popup_exceed_limit.title'),
message: this.$t('popup_exceed_limit.message')
})
} else if (error.response.status === 413) {
events.$emit('alert:open', {
emoji: '😟',
title: this.$t('popup_paylod_error.title'),
message: this.$t('popup_paylod_error.message')
})
} else {
// Show error message
events.$emit('alert:open', {
title: this.$t('popup_error.title'),
message: this.$t('popup_error.message'),
})
}
})
}
}
Vue.prototype.$downloadFile = function(url, filename) {
var anchor = document.createElement('a')
anchor.href = url
anchor.download = filename
document.body.appendChild(anchor)
anchor.click()
}
Vue.prototype.$closePopup = function() {
events.$emit('popup:close')
}
Vue.prototype.$isThisRoute = function(route, locations) {
return includes(locations, route.name)
}
Vue.prototype.$isThisLocation = function(location) {
// Get current location
let currentLocation = store.getters.currentFolder && store.getters.currentFolder.location ? store.getters.currentFolder.location : undefined
// Check if type is object
if (typeof location === 'Object' || location instanceof Object) {
return includes(location, currentLocation)
} else {
return currentLocation === location
}
}
Vue.prototype.$checkPermission = function(type) {
let currentPermission = store.getters.permission
// Check if type is object
if (typeof type === 'Object' || type instanceof Object) {
return includes(type, currentPermission)
} else {
return currentPermission === type
}
}
Vue.prototype.$isMobile = function() {
const toMatch = [
/Android/i,
/webOS/i,
/iPhone/i,
/iPad/i,
/iPod/i,
/BlackBerry/i,
/Windows Phone/i
]
return toMatch.some(toMatchItem => {
return navigator.userAgent.match(toMatchItem)
})
}
Vue.prototype.$isMinimalScale = function() {
let sizeType = store.getters.filesViewWidth
return sizeType === 'minimal-scale'
}
Vue.prototype.$isCompactScale = function() {
let sizeType = store.getters.filesViewWidth
return sizeType === 'compact-scale'
}
Vue.prototype.$isFullScale = function() {
let sizeType = store.getters.filesViewWidth
return sizeType === 'full-scale'
}
Vue.prototype.$isSomethingWrong = function() {
events.$emit('alert:open', {
title: this.$t('popup_error.title'),
message: this.$t('popup_error.message'),
})
}
}
} }
export default Helpers export default Helpers
+1 -1
View File
@@ -211,7 +211,7 @@
"message": "Something went wrong and we can't continue. Please contact us." "message": "Something went wrong and we can't continue. Please contact us."
}, },
"popup_exceed_limit": { "popup_exceed_limit": {
"title": "Whooops, you exceed your storage limit :(", "title": "Whooops, you exceed storage limit :(",
"message": "Please contact your administrator to change your limit." "message": "Please contact your administrator to change your limit."
}, },
"popup_share_create": { "popup_share_create": {
+10
View File
@@ -15,6 +15,7 @@ import Profile from './views/User/Settings'
import Invoice from './views/User/Invoices' import Invoice from './views/User/Invoices'
import Password from './views/User/Password' import Password from './views/User/Password'
import Subscription from './views/User/Subscription' import Subscription from './views/User/Subscription'
import PaymentCards from './views/User/PaymentCards'
import Trash from './views/FilePages/Trash' import Trash from './views/FilePages/Trash'
import Files from './views/FilePages/Files' import Files from './views/FilePages/Files'
@@ -399,6 +400,15 @@ const routesUser = [
title: 'Subscription' title: 'Subscription'
}, },
}, },
{
name: 'PaymentCards',
path: '/settings/payment-cards',
component: PaymentCards,
meta: {
requiresAuth: true,
title: 'Payment Cards'
},
},
] ]
}, },
{ {
+15 -11
View File
@@ -11,22 +11,26 @@ const defaultState = {
const actions = { const actions = {
getAppData: ({commit, getters}) => { getAppData: ({commit, getters}) => {
return new Promise((resolve, reject) => {
axios
.get(getters.api + '/user')
.then((response) => {
resolve(response)
axios commit('RETRIEVE_USER', response.data)
.get(getters.api + '/user')
.then((response) => {
commit('RETRIEVE_USER', response.data)
}).catch((error) => { }).catch((error) => {
reject(error)
// Redirect if unauthenticated // Redirect if unauthenticated
if ([401, 403].includes(error.response.status)) { if ([401, 403].includes(error.response.status)) {
commit('SET_AUTHORIZED', false) commit('SET_AUTHORIZED', false)
router.push({name: 'SignIn'}) router.push({name: 'SignIn'})
}
} }
} )
) })
}, },
logOut: ({getters, commit}) => { logOut: ({getters, commit}) => {
axios axios
+2 -12
View File
@@ -27,12 +27,7 @@
</td> </td>
<td> <td>
<span class="cell-item"> <span class="cell-item">
{{ row.data.attributes.payments_processed }} {{ row.data.attributes.payment_processed }}
</span>
</td>
<td>
<span class="cell-item">
{{ row.data.attributes.active_subscribers }}
</span> </span>
</td> </td>
<td> <td>
@@ -100,12 +95,7 @@
}, },
{ {
label: 'Payments Processed', label: 'Payments Processed',
field: 'data.attributes.payments_processed', field: 'data.attributes.payment_processed',
sortable: true
},
{
label: 'Active Subscribers',
field: 'data.attributes.active_subscribers',
sortable: true sortable: true
}, },
{ {
@@ -1,47 +1,68 @@
<template> <template>
<PageTab> <PageTab>
<PageTabGroup> <PageTabGroup v-if="transactions.length > 0">
<DatatableWrapper :paginator="true" :columns="columns" :data="invoices" class="table"> <DatatableWrapper :paginator="true" :columns="columns" :data="transactions" class="table">
<template scope="{ row }"> <template scope="{ row }">
<tr> <tr>
<td> <td>
<span class="cell-item"> <a :href="'/invoice/' + row.data.attributes.token" target="_blank" class="cell-item">
${{ row.attributes.total }} {{ row.data.attributes.order }}
</span> </a>
</td> </td>
<td> <td>
<span class="cell-item"> <span class="cell-item">
{{ row.attributes.plan }} ${{ row.data.attributes.total }}
</span> </span>
</td> </td>
<td> <td>
<span class="cell-item"> <span class="cell-item">
{{ row.attributes.created_at_formatted }} {{ row.data.attributes.bag[0].description }}
</span> </span>
</td>
<td>
<span class="cell-item">
{{ row.data.attributes.created_at_formatted }}
</span>
</td>
<td>
<router-link :to="{name: 'UserInvoices', params: {id: row.relationships.user.data.id}}">
<DatatableCellImage
image-size="small"
:image="row.relationships.user.data.attributes.avatar"
:title="row.relationships.user.data.attributes.name"
/>
</router-link>
</td> </td>
<td> <td>
<div class="action-icons"> <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> </div>
</td> </td>
</tr> </tr>
</template> </template>
</DatatableWrapper> </DatatableWrapper>
</PageTabGroup> </PageTabGroup>
<PageTabGroup v-else>
You don't have any transactions yet.
</PageTabGroup>
</PageTab> </PageTab>
</template> </template>
<script> <script>
import DatatableCellImage from '@/components/Others/Tables/DatatableCellImage'
import DatatableWrapper from '@/components/Others/Tables/DatatableWrapper' import DatatableWrapper from '@/components/Others/Tables/DatatableWrapper'
import PageTabGroup from '@/components/Others/Layout/PageTabGroup' import PageTabGroup from '@/components/Others/Layout/PageTabGroup'
import PageTab from '@/components/Others/Layout/PageTab' import PageTab from '@/components/Others/Layout/PageTab'
import {DownloadCloudIcon} from "vue-feather-icons"; import {ExternalLinkIcon} from "vue-feather-icons";
import axios from 'axios' import axios from 'axios'
export default { export default {
name: 'GatewayTransactions', name: 'GatewayTransactions',
components: { components: {
DownloadCloudIcon, DatatableCellImage,
ExternalLinkIcon,
DatatableWrapper, DatatableWrapper,
PageTabGroup, PageTabGroup,
PageTab, PageTab,
@@ -49,97 +70,13 @@
data() { data() {
return { return {
isLoading: false, isLoading: false,
invoices: [ transactions: [],
{
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/',
},
},
],
columns: [ columns: [
{
label: 'Invoice Number',
field: 'attributes.total',
sortable: true
},
{ {
label: 'Total', label: 'Total',
field: 'attributes.total', field: 'attributes.total',
@@ -155,6 +92,11 @@
field: 'attributes.created_at', field: 'attributes.created_at',
sortable: true sortable: true
}, },
{
label: 'User',
field: 'relationships.user.data.id',
sortable: true
},
{ {
label: this.$t('admin_page_user.table.action'), label: this.$t('admin_page_user.table.action'),
field: 'data.action', field: 'data.action',
@@ -164,11 +106,11 @@
} }
}, },
created() { created() {
/*axios.get('/api/users/' + this.$route.params.id + '/storage') axios.get('/api/gateways/' + this.$route.params.slug + '/transactions')
.then(response => { .then(response => {
this.storage = response.data.data this.transactions = response.data.data
this.isLoading = false this.isLoading = false
})*/ })
} }
} }
</script> </script>
@@ -34,15 +34,6 @@
</ValidationProvider> </ValidationProvider>
</div> </div>
<!--Price-->
<div class="block-wrapper">
<label>Price:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="Plan price" rules="required" v-slot="{ errors }">
<input @input="$updateText('/plans/' + $route.params.id + '/update', 'price', plan.attributes.price)" v-model="plan.attributes.price" placeholder="Plan price" type="number" step="0.01" min="1" max="99999" :class="{'is-error': errors[0]}"/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<!--Storage Capacity--> <!--Storage Capacity-->
<div class="block-wrapper"> <div class="block-wrapper">
<label>Storage Capacity:</label> <label>Storage Capacity:</label>
+42 -40
View File
@@ -35,8 +35,8 @@
</ColorLabel> </ColorLabel>
</td> </td>
<td v-if="config.isSaaS"> <td v-if="config.isSaaS">
<span class="cell-item" v-if="row.relationships.subscription"> <span class="cell-item" v-if="row.data.attributes.subscription">
{{ row.relationships.subscription.data.attributes.name }} Premium
</span> </span>
<span class="cell-item" v-else> <span class="cell-item" v-else>
Free Free
@@ -114,44 +114,7 @@
return { return {
isLoading: true, isLoading: true,
users: [], users: [],
columns: [ columns: undefined,
{
label: this.$t('admin_page_user.table.name'),
field: 'data.attributes.name',
sortable: true
},
{
label: this.$t('admin_page_user.table.role'),
field: 'data.attributes.role',
sortable: true
},
{
label: 'Subscription Plan',
field: 'data.attributes.role',
sortable: true,
hidden: true,
},
{
label: this.$t('admin_page_user.table.storage_used'),
field: 'data.attributes.storage.used',
sortable: true
},
{
label: this.$t('admin_page_user.table.storage_capacity'),
field: 'data.attributes.storage.capacity',
sortable: true
},
{
label: this.$t('admin_page_user.table.created_at'),
field: 'data.attributes.created_at_formatted',
sortable: true
},
{
label: this.$t('admin_page_user.table.action'),
field: 'data.action',
sortable: false
},
],
} }
}, },
methods: { methods: {
@@ -167,6 +130,45 @@
} }
}, },
created() { created() {
this.columns = [
{
label: this.$t('admin_page_user.table.name'),
field: 'data.attributes.name',
sortable: true
},
{
label: this.$t('admin_page_user.table.role'),
field: 'data.attributes.role',
sortable: true
},
{
label: 'Subscription Plan',
field: 'data.attributes.role',
sortable: true,
hidden: ! this.config.isSaaS,
},
{
label: this.$t('admin_page_user.table.storage_used'),
field: 'data.attributes.storage.used',
sortable: true
},
{
label: this.$t('admin_page_user.table.storage_capacity'),
field: 'data.attributes.storage.capacity',
sortable: true
},
{
label: this.$t('admin_page_user.table.created_at'),
field: 'data.attributes.created_at_formatted',
sortable: true
},
{
label: this.$t('admin_page_user.table.action'),
field: 'data.action',
sortable: false
},
]
axios.get('/api/users') axios.get('/api/users')
.then(response => { .then(response => {
this.users = response.data.data this.users = response.data.data
+15 -4
View File
@@ -26,7 +26,7 @@
</div> </div>
</div> </div>
<div v-if="config.isSaaS" class="headline-actions"> <div v-if="config.isSaaS" class="headline-actions">
<router-link :to="{name: 'UpgradePlan'}"> <router-link :to="{name: 'UpgradePlan'}" v-if="! user.relationships.subscription || (user.relationships.subscription && ! user.relationships.subscription.data.attributes.is_highest)">
<ButtonBase button-style="secondary" type="button"> <ButtonBase button-style="secondary" type="button">
Upgrade Plan Upgrade Plan
</ButtonBase> </ButtonBase>
@@ -56,13 +56,22 @@
<router-link v-if="config.isSaaS" replace :to="{name: 'Subscription'}" class="menu-list-item link"> <router-link v-if="config.isSaaS" replace :to="{name: 'Subscription'}" class="menu-list-item link">
<div class="icon"> <div class="icon">
<credit-card-icon size="17"></credit-card-icon> <cloud-icon size="17"></cloud-icon>
</div> </div>
<div class="label"> <div class="label">
Subscription Subscription
</div> </div>
</router-link> </router-link>
<router-link v-if="config.isSaaS" replace :to="{name: 'PaymentCards'}" class="menu-list-item link">
<div class="icon">
<credit-card-icon size="17"></credit-card-icon>
</div>
<div class="label">
Payment Cards
</div>
</router-link>
<router-link v-if="config.isSaaS" replace :to="{name: 'Invoice'}" class="menu-list-item link"> <router-link v-if="config.isSaaS" replace :to="{name: 'Invoice'}" class="menu-list-item link">
<div class="icon"> <div class="icon">
<file-text-icon size="17"></file-text-icon> <file-text-icon size="17"></file-text-icon>
@@ -105,6 +114,7 @@
CreditCardIcon, CreditCardIcon,
HardDriveIcon, HardDriveIcon,
FileTextIcon, FileTextIcon,
CloudIcon,
UserIcon, UserIcon,
LockIcon, LockIcon,
} from 'vue-feather-icons' } from 'vue-feather-icons'
@@ -112,6 +122,7 @@
export default { export default {
name: 'Settings', name: 'Settings',
components: { components: {
CloudIcon,
ButtonBase, ButtonBase,
CreditCardIcon, CreditCardIcon,
UserImageInput, UserImageInput,
@@ -127,10 +138,10 @@
computed: { computed: {
...mapGetters(['user', 'config']), ...mapGetters(['user', 'config']),
subscriptionStatus() { subscriptionStatus() {
return this.user.relationships.subscription ? 'Premium' : 'Free' return this.user.data.attributes.subscription ? 'Premium' : 'Free'
}, },
subscriptionColor() { subscriptionColor() {
return this.user.relationships.subscription ? 'green' : 'purple' return this.user.data.attributes.subscription ? 'green' : 'purple'
}, },
}, },
data() { data() {
+424 -138
View File
@@ -13,118 +13,186 @@
</div> </div>
<div class="order"> <div class="order">
<div class="billing" v-if="billing">
<b class="form-group-label">Billing Information:</b>
<ValidationObserver ref="order" v-slot="{ invalid }" tag="form" class="form block-form">
<div class="form block-form">
<div class="block-wrapper"> <div class="steps">
<label>Name:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" rules="required"
name="billing_name" v-slot="{ errors }">
<input v-model="billing.billing_name"
placeholder="Type your billing name"
type="text"
:class="{'is-error': errors[0]}"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper"> <div class="payment-card">
<label>Address:</label> <b class="form-group-label">Payment Card:</b>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" rules="required"
name="billing_address" v-slot="{ errors }">
<input v-model="billing.billing_address"
placeholder="Type your billing address"
type="text"
:class="{'is-error': errors[0]}"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper"> <!-- Pay by new credit card -->
<label>State:</label> <div class="register-card" v-show="! defaultPaymentCard || payByNewCard">
<ValidationProvider tag="div" mode="passive" class="input-wrapper" rules="required" <p class="payment-demo-disclaimer">
name="billing_state" v-slot="{ errors }"> For test your payment please use <b>4242 4242 4242 4242</b> as a card number, <b>11/22</b>
<input v-model="billing.billing_state" as the expiration date and <b>123</b> as CVC number and ZIP <b>12345</b>.
placeholder="Type your billing state" </p>
type="text"
:class="{'is-error': errors[0]}"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="wrapper-inline"> <div ref="stripeCard" class="stripe-card" :class="{'is-error': isError }"></div>
<div class="block-wrapper">
<label>City:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper"
rules="required" name="billing_city" v-slot="{ errors }">
<input v-model="billing.billing_city"
placeholder="Type your billing city"
type="text"
:class="{'is-error': errors[0]}"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper"> <div class="card-error-message" v-if="isError">
<label>Postal Code:</label> <span>{{ errorMessage }}</span>
<ValidationProvider tag="div" mode="passive" class="input-wrapper"
rules="required" name="billing_postal_code"
v-slot="{ errors }">
<input v-model="billing.billing_postal_code"
placeholder="Type your billing postal code"
type="text"
:class="{'is-error': errors[0]}"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
</div>
<div class="block-wrapper">
<label>Country:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" rules="required"
name="billing_country" v-slot="{ errors }">
<input v-model="billing.billing_country"
placeholder="Type your billing country"
type="text"
:class="{'is-error': errors[0]}"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper">
<label>Phone Number:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" rules="required"
name="billing_phone_number" v-slot="{ errors }">
<input v-model="billing.billing_phone_number"
placeholder="Type your billing phone number"
type="text"
:class="{'is-error': errors[0]}"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div> </div>
</div> </div>
</ValidationObserver>
<!--User registered payment card-->
<div class="registered-cards" v-if="defaultPaymentCard && ! payByNewCard">
<div class="credit-card" :class="{'is-error': isError}">
<div class="card-number">
<img class="credit-card-icon"
:src="$getCreditCardBrand(defaultPaymentCard.data.attributes.brand)"
:alt="defaultPaymentCard.data.attributes.brand">
<div class="credit-card-numbers">
{{ defaultPaymentCard.data.attributes.last4 }}
</div>
<ColorLabel color="purple">Default</ColorLabel>
</div>
<div class="expiration-date">
<span>{{ defaultPaymentCard.data.attributes.exp_month }} / {{ defaultPaymentCard.data.attributes.exp_year }}</span>
</div>
</div>
<!--Change payment-->
<div class="change-payment" v-if="! isError">
<span>Also you can</span>
<router-link v-if="paymentCards.data.length > 0" :to="{name: 'PaymentCards'}">change your
default payment method
</router-link>
<span v-if="paymentCards.data.length > 0">or</span>
<a @click="payByNewCardForm">pay by new credit card.</a>
</div>
<!--Card error-->
<div class="card-error-message" v-if="isError">
<span>{{ errorMessage }}</span>
<span @click="payByNewCardForm"
class="link">Please pay by another payment card</span>
<span> or </span>
<router-link :to="{name: 'PaymentCards'}" class="link">Change your default payment
method
</router-link>
</div>
</div>
</div>
<div class="billing" v-if="billing">
<b class="form-group-label">Billing Information:</b>
<ValidationObserver ref="order" v-slot="{ invalid }" tag="form" class="form block-form">
<div class="form block-form">
<div class="block-wrapper">
<label>Name:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper"
rules="required"
name="billing_name" v-slot="{ errors }">
<input v-model="billing.billing_name"
placeholder="Type your billing name"
type="text"
:class="{'is-error': errors[0]}"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper">
<label>Address:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper"
rules="required"
name="billing_address" v-slot="{ errors }">
<input v-model="billing.billing_address"
placeholder="Type your billing address"
type="text"
:class="{'is-error': errors[0]}"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper">
<label>State:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper"
rules="required"
name="billing_state" v-slot="{ errors }">
<input v-model="billing.billing_state"
placeholder="Type your billing state"
type="text"
:class="{'is-error': errors[0]}"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="wrapper-inline">
<div class="block-wrapper">
<label>City:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper"
rules="required" name="billing_city"
v-slot="{ errors }">
<input v-model="billing.billing_city"
placeholder="Type your billing city"
type="text"
:class="{'is-error': errors[0]}"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper">
<label>Postal Code:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper"
rules="required" name="billing_postal_code"
v-slot="{ errors }">
<input v-model="billing.billing_postal_code"
placeholder="Type your billing postal code"
type="text"
:class="{'is-error': errors[0]}"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
</div>
<div class="block-wrapper">
<label>Country:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper"
rules="required"
name="billing_country" v-slot="{ errors }">
<input v-model="billing.billing_country"
placeholder="Type your billing country"
type="text"
:class="{'is-error': errors[0]}"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper">
<label>Phone Number:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper"
rules="required"
name="billing_phone_number" v-slot="{ errors }">
<input v-model="billing.billing_phone_number"
placeholder="Type your billing phone number"
type="text"
:class="{'is-error': errors[0]}"
/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
</div>
</ValidationObserver>
</div>
</div> </div>
<div class="summary"> <div class="summary">
<b class="form-group-label">Order Summary:</b> <b class="form-group-label">Order Summary:</b>
<div class="summary-list" :class="{'is-error': isError}" v-if="requestedPlan">
<div class="summary-list" v-if="requestedPlan">
<div class="row"> <div class="row">
<div class="cell"> <div class="cell">
<b>{{ requestedPlan.data.attributes.name }}</b> <b>{{ requestedPlan.data.attributes.name }}</b>
<small>Billed monthly</small> <small>Billed monthly</small>
</div> </div>
<div class="cell"> <div class="cell">
<b>{{ requestedPlan.data.attributes.price }} USD</b> <b>{{ requestedPlan.data.attributes.price }}</b>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
@@ -132,15 +200,14 @@
<b>Total</b> <b>Total</b>
</div> </div>
<div class="cell"> <div class="cell">
<b>{{ requestedPlan.data.attributes.price }} USD</b> <b>{{ requestedPlan.data.attributes.price }}</b>
</div> </div>
</div> </div>
<ButtonBase :disabled="isSubmitted" :loading="isSubmitted" @click.native="submitOrder" <ButtonBase :disabled="isSubmitted" :loading="isSubmitted" @click.native="submitOrder"
type="submit" button-style="theme-solid" class="next-submit"> type="submit" button-style="theme-solid" class="next-submit">
Pay Order Pay with credit card
</ButtonBase> </ButtonBase>
<p class="error-message" v-if="isError">{{ errorMessage }}</p>
<small class="disclaimer"> <small class="disclaimer">
By submit form, you agree to save the payment method and billing information in your By submit form, you agree to save the payment method and billing information in your
VueFileManager account. VueFileManager account.
@@ -148,7 +215,6 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div id="loader" v-if="isLoading"> <div id="loader" v-if="isLoading">
@@ -162,14 +228,19 @@
import PlanPricingTables from '@/components/Others/PlanPricingTables' import PlanPricingTables from '@/components/Others/PlanPricingTables'
import MobileHeader from '@/components/Mobile/MobileHeader' import MobileHeader from '@/components/Mobile/MobileHeader'
import ButtonBase from '@/components/FilesView/ButtonBase' import ButtonBase from '@/components/FilesView/ButtonBase'
import ColorLabel from '@/components/Others/ColorLabel'
import PageHeader from '@/components/Others/PageHeader' import PageHeader from '@/components/Others/PageHeader'
import Spinner from '@/components/FilesView/Spinner' import Spinner from '@/components/FilesView/Spinner'
import {CreditCardIcon} from 'vue-feather-icons' import {CreditCardIcon} from 'vue-feather-icons'
import {required} from 'vee-validate/dist/rules' import {required} from 'vee-validate/dist/rules'
import { mapGetters } from 'vuex' import {mapGetters} from 'vuex'
import {events} from "@/bus" import {events} from "@/bus"
import axios from 'axios' import axios from 'axios'
let stripe = Stripe(`pk_test_51GsACaCBETHMUxzVsYkeApHtqb85paMuye7G77PDDQ28kXqDJ5HTmqLi13aM6xee81OQK1fhkTZ7vmDiWLStU9160061Yb2MtL`),
elements = stripe.elements(),
card = undefined;
export default { export default {
name: 'UpgradePlan', name: 'UpgradePlan',
components: { components: {
@@ -180,20 +251,66 @@
MobileHeader, MobileHeader,
ButtonBase, ButtonBase,
PageHeader, PageHeader,
ColorLabel,
required, required,
Spinner, Spinner,
}, },
computed: { computed: {
...mapGetters(['requestedPlan']), ...mapGetters(['requestedPlan']),
billing() {
return this.$store.getters.user.relationships.settings.data.attributes
}
}, },
data() { data() {
return { return {
complete: false,
stripeOptions: {
hidePostalCode: false
},
isLoading: true, isLoading: true,
isSubmitted: false, isSubmitted: false,
billing: undefined, paymentCards: undefined,
defaultPaymentCard: undefined,
errorMessage: undefined,
isError: false,
payByNewCard: false,
clientSecret: undefined
} }
}, },
methods: { methods: {
payByNewCardForm() {
this.payByNewCard = true
this.isError = false
},
successOrder() {
// Update user data
//this.$store.dispatch('getAppData')
// End loading
this.isSubmitted = false
// Show toaster
events.$emit('toaster', {
type: 'success',
message: 'Your account was successfully upgraded.',
})
// Go to User page
//this.$router.push({name: 'Subscription'})
},
errorOrder(error) {
if (error.response.status = 402) {
this.isError = true
this.errorMessage = error.response.data.message
}
// End loading
this.isSubmitted = false
},
async submitOrder() { async submitOrder() {
// Validate fields // Validate fields
@@ -201,56 +318,89 @@
if (!isValid) return; if (!isValid) return;
// Remove error
this.isError = false
// Start loading // Start loading
this.isSubmitted = true this.isSubmitted = true
// Send order request // If user don't have credit card, register new
axios if (!this.defaultPaymentCard || this.payByNewCard) {
.post('/api/subscription/upgrade', {
billing: this.billing, console.log('Payment by new card');
plan: this.requestedPlan,
const {setupIntent, error} = await stripe.confirmCardSetup(this.clientSecret, {
payment_method: {
card: card,
}
}) })
.then(() => {
// Update user data if (error) {
this.$store.dispatch('getAppData')
// End loading // Set error on
this.isError = true
// End button spinner
this.isSubmitted = false this.isSubmitted = false
// Show toaster // Show error message
events.$emit('toaster', { this.errorMessage = error.message
type: 'success',
message: 'Your account was successfully upgraded.', } else {
axios
.post('/api/subscription/upgrade', {
billing: this.billing,
plan: this.requestedPlan,
payment: {
type: 'stripe',
meta: {
pm: setupIntent.payment_method,
}
}
})
.then(() => this.successOrder())
.catch((error) => this.errorOrder(error))
}
}
// if user has credit card
if (this.defaultPaymentCard && !this.payByNewCard) {
console.log('Payment by default card');
axios
.post('/api/subscription/upgrade', {
billing: this.billing,
plan: this.requestedPlan,
payment: {
type: 'stripe',
}
}) })
.then(() => this.successOrder())
// Go to User page .catch((error) => this.errorOrder(error))
this.$router.push({name: 'Storage'}) }
}) },
.catch(error => { },
mounted: function () {
// End loading card = elements.create('card');
this.isSubmitted = false card.mount(this.$refs.stripeCard);
})
}
}, },
created() { created() {
axios.get('/api/user')
// Get setup intent for stripe
axios.get('/api/stripe/setup-intent')
.then(response => this.clientSecret = response.data.client_secret)
axios.get('/api/user/payments')
.then(response => { .then(response => {
if (! this.requestedPlan) { if (!this.requestedPlan) {
this.$router.push({name: 'UpgradePlan'}) this.$router.push({name: 'UpgradePlan'})
} }
this.billing = { this.defaultPaymentCard = response.data.default
billing_name: response.data.relationships.settings.data.attributes.billing_name, this.paymentCards = response.data.others
billing_address: response.data.relationships.settings.data.attributes.billing_address,
billing_state: response.data.relationships.settings.data.attributes.billing_state,
billing_city: response.data.relationships.settings.data.attributes.billing_city,
billing_postal_code: response.data.relationships.settings.data.attributes.billing_postal_code,
billing_country: response.data.relationships.settings.data.attributes.billing_country,
billing_phone_number: response.data.relationships.settings.data.attributes.billing_phone_number,
}
this.isLoading = false this.isLoading = false
}) })
@@ -263,6 +413,133 @@
@import '@assets/vue-file-manager/_mixins'; @import '@assets/vue-file-manager/_mixins';
@import '@assets/vue-file-manager/_forms'; @import '@assets/vue-file-manager/_forms';
.change-payment {
padding-top: 10px;
span {
font-weight: 600;
}
a {
cursor: pointer;
font-weight: 700;
&:hover {
text-decoration: underline;
}
}
span, a {
color: $text-muted;
@include font-size(14);
}
}
.card-error-message {
padding-top: 10px;
span, a {
@include font-size(14);
font-weight: 600;
color: $danger;
}
.link, a {
text-decoration: underline;
cursor: pointer;
&:hover {
text-decoration: none;
}
}
}
.registered-cards {
margin-bottom: 50px;
}
.register-card {
margin-bottom: 55px;
}
.credit-card {
display: flex;
align-items: center;
justify-content: space-between;
padding: 15px;
background: $light_background;
border-radius: 8px;
margin-top: 20px;
&.is-error {
box-shadow: 0 0 7px rgba($danger, 0.3);
border: 2px solid $danger;
border-radius: 4px;
}
span {
font-weight: 700;
}
.card-number {
display: flex;
}
.credit-card-numbers {
vertical-align: middle;
margin-right: 10px;
}
.credit-card-icon {
vertical-align: middle;
max-height: 20px;
margin-right: 8px;
}
}
.payment-demo-disclaimer {
padding: 15px;
background: $light_background;
border-radius: 8px;
margin-bottom: 20px;
line-height: 1.6;
b {
color: $danger;
}
}
.stripe-card {
box-sizing: border-box;
padding: 13px 20px;
border: 1px solid transparent;
border-radius: 4px;
background-color: white;
box-shadow: 0 1px 3px 0 #e6ebf1;
-webkit-transition: box-shadow 150ms ease;
transition: box-shadow 150ms ease;
&.is-error {
box-shadow: 0 0 7px rgba($danger, 0.3);
border: 2px solid $danger;
border-radius: 4px;
}
&.StripeElement--focus {
box-shadow: 0 1px 3px 0 #cfd7df;
}
&.StripeElement--invalid {
border-color: #fa755a;
}
&.StripeElement--webkit-autofill {
background-color: #fefde5 !important;
}
}
.summary-list { .summary-list {
box-shadow: 0 7px 20px 5px hsla(220, 36%, 16%, 0.06); box-shadow: 0 7px 20px 5px hsla(220, 36%, 16%, 0.06);
border-radius: 8px; border-radius: 8px;
@@ -270,6 +547,15 @@
padding: 25px; padding: 25px;
top: 30px; top: 30px;
&.is-error {
border: 2px solid $danger;
box-shadow: 0 7px 20px 5px rgba($danger, 0.06);
}
.error-message {
font-weight: 600;
}
.next-submit { .next-submit {
width: 100%; width: 100%;
margin-top: 20px; margin-top: 20px;
@@ -317,7 +603,7 @@
.order { .order {
display: flex; display: flex;
.billing { .steps {
flex: 0 0 65%; flex: 0 0 65%;
padding-right: 30px; padding-right: 30px;
@@ -49,6 +49,12 @@
this.$store.commit('STORE_REQUESTED_PLAN', plan) this.$store.commit('STORE_REQUESTED_PLAN', plan)
} }
}, },
/*beforeMount() {
let StripeElementsScript = document.createElement('script')
StripeElementsScript.setAttribute('src', 'https://js.stripe.com/v3/')
document.head.appendChild(StripeElementsScript)
},*/
} }
</script> </script>
+244
View File
@@ -0,0 +1,244 @@
<template>
<PageTab :is-loading="isLoading">
<PageTabGroup v-if="paymentCards && paymentCards.length > 0">
<DatatableWrapper :paginator="true" :columns="columns" :data="paymentCards" class="table">
<template scope="{ row }">
<tr :class="{'is-deleting': row.data.attributes.card_id === deletingID}">
<td style="width: 300px">
<span class="cell-item">
<div class="credit-card">
<img class="credit-card-icon" :src="$getCreditCardBrand(row.data.attributes.brand)"
:alt="row.data.attributes.brand">
<div class="credit-card-numbers">
{{ row.data.attributes.last4 }}
</div>
<ColorLabel v-if="row.data.id === defaultPaymentCard.data.id" color="purple">Default</ColorLabel>
</div>
</span>
</td>
<td>
<span class="cell-item">
<ColorLabel :color="getCardStatusColor(row.data.attributes.status)">{{ getCardStatus(row.data.attributes.status) }}</ColorLabel>
</span>
</td>
<td>
<span class="cell-item">
{{ row.data.attributes.exp_month }} / {{ row.data.attributes.exp_year }}
</span>
</td>
<td>
<div class="action-icons">
<credit-card-icon size="15" class="icon icon-card" title="Set as default card" @click="setDefaultCard(row.data.attributes)" v-if="row.data.id !== defaultPaymentCard.data.id"></credit-card-icon>
<trash2-icon size="15" class="icon icon-trash" title="Delete card" @click="deleteCard(row.data.attributes)"></trash2-icon>
</div>
</td>
</tr>
</template>
</DatatableWrapper>
</PageTabGroup>
<PageTabGroup v-else>
You don't have any payment cards yet.
</PageTabGroup>
</PageTab>
</template>
<script>
import DatatableWrapper from '@/components/Others/Tables/DatatableWrapper'
import PageTabGroup from '@/components/Others/Layout/PageTabGroup'
import PageTab from '@/components/Others/Layout/PageTab'
import ColorLabel from '@/components/Others/ColorLabel'
import {CreditCardIcon, Trash2Icon} from "vue-feather-icons"
import {events} from "@/bus"
import axios from 'axios'
export default {
name: 'UserPaymentCards',
components: {
DatatableWrapper,
PageTabGroup,
Trash2Icon,
ColorLabel,
CreditCardIcon,
PageTab,
},
data() {
return {
defaultPaymentCard: undefined,
paymentCards: undefined,
deletingID: undefined,
columns: [
{
label: 'Card Number',
field: 'data.attributes.total',
sortable: true
},
{
label: 'Status',
field: 'data.attributes.status',
sortable: true
},
{
label: 'Expiration Date',
field: 'data.attributes.total',
sortable: true
},
{
label: this.$t('admin_page_user.table.action'),
field: 'data.action',
sortable: false
},
],
isLoading: true,
}
},
methods: {
getCardStatusColor(status) {
switch (status) {
case 'active':
return 'green'
break
case 'card_declined':
return 'yellow'
break
case 'expired':
return 'red'
break
}
},
getCardStatus(status) {
switch (status) {
case 'active':
return 'Active'
break
case 'card_declined':
return 'Rejected'
break
case 'expired':
return 'Expired'
break
}
},
setDefaultCard(card) {
events.$emit('confirm:open', {
title: 'Set as default card?',
message: 'Your card will be set as default and will be always charged for the next billings.',
buttonColor: 'theme-solid',
action: {
id: card.card_id,
operation: 'set-as-default-credit-card'
}
})
},
deleteCard(card) {
events.$emit('confirm:open', {
title: 'Are you sure?',
message: 'This event is irreversible and your payment card will be delete forever',
action: {
id: card.card_id,
operation: 'delete-credit-card'
}
})
},
fetchPaymentCards() {
axios.get('/api/user/payments')
.then(response => {
this.defaultPaymentCard = response.data.default
this.paymentCards = response.data.others.data
this.paymentCards.push(response.data.default)
this.isLoading = false
})
}
},
created() {
// Get payments card
this.fetchPaymentCards()
// Delete credit card
events.$on('action:confirmed', data => {
if (data.operation === 'delete-credit-card') {
this.deletingID = data.id
axios.delete('/api/user/payment-cards/' + data.id)
.then(() => {
// Get payments card
this.fetchPaymentCards()
// Show toaster
events.$emit('toaster', {
type: 'success',
message: 'Your card was successfully deleted.',
})
})
.catch(error => {
console.error(error);
})
}
if (data.operation === 'set-as-default-credit-card') {
axios.patch('/api/user/payment-cards/' + data.id, {
default: 1
})
.then(() => {
// Get payments card
this.fetchPaymentCards()
// Show toaster
events.$emit('toaster', {
type: 'success',
message: 'Your card was successfully set as default.',
})
})
.catch(error => {
console.error(error);
})
}
})
},
destroyed() {
events.$off('action:confirmed')
}
}
</script>
<style lang="scss" scoped>
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
@import '@assets/vue-file-manager/_forms';
.is-deleting {
opacity: 0.35;
}
.credit-card {
display: flex;
align-items: center;
.credit-card-numbers {
margin-right: 10px;
}
.credit-card-icon {
max-height: 20px;
margin-right: 8px;
}
}
@media only screen and (max-width: 960px) {
}
@media (prefers-color-scheme: dark) {
}
</style>
+90 -20
View File
@@ -1,34 +1,44 @@
<template> <template>
<PageTab> <PageTab :is-loading="isLoading">
<PageTabGroup v-if="subscription"> <PageTabGroup v-if="subscription">
<!--Info about active subscription--> <!--Info about active subscription-->
<div v-if="! subscription.canceled" class="state active"> <div v-if="! subscription.data.attributes.canceled" class="state active">
<ListInfo class="list-info"> <ListInfo class="list-info">
<ListInfoItem class="list-item" title="Plan" :content="subscription.name + ' - ' + subscription.capacity_formatted"/> <ListInfoItem class="list-item" title="Plan" :content="subscription.data.attributes.name + ' - ' + subscription.data.attributes.capacity_formatted"/>
<ListInfoItem class="list-item" title="Billed" content="Monthly"/> <ListInfoItem class="list-item" title="Billed" content="Monthly"/>
<ListInfoItem class="list-item" title="Status" :content="status"/> <ListInfoItem class="list-item" title="Status" :content="status"/>
<ListInfoItem class="list-item" title="Created At" :content="subscription.created_at"/> <ListInfoItem class="list-item" title="Created At" :content="subscription.data.attributes.created_at"/>
<ListInfoItem class="list-item" title="Renews At" :content="subscription.ends_at"/> <ListInfoItem class="list-item" title="Renews At" :content="subscription.data.attributes.ends_at"/>
</ListInfo> </ListInfo>
<div class="cancel-plan"> <div class="plan-action">
<ButtonBase <ButtonBase
:disabled="isDeleting"
@click.native="cancelSubscription" @click.native="cancelSubscription"
:button-style="cancelButtonStyle" :button-style="cancelButtonStyle"
class="cancel-button"> class="confirm-button">
{{ cancelButtonText }} {{ cancelButtonText }}
</ButtonBase> </ButtonBase>
</div> </div>
</div> </div>
<!--Info about canceled subscription--> <!--Info about canceled subscription-->
<div v-if="subscription.canceled" class="state canceled"> <div v-if="subscription.data.attributes.canceled" class="state canceled">
<ListInfo class="list-info"> <ListInfo class="list-info">
<ListInfoItem class="list-item" title="Plan" :content="subscription.name"/> <ListInfoItem class="list-item" title="Plan" :content="subscription.data.attributes.name"/>
<ListInfoItem class="list-item" title="Status" :content="status"/> <ListInfoItem class="list-item" title="Status" :content="status"/>
<ListInfoItem class="list-item" title="Canceled At" :content="subscription.canceled_at"/> <ListInfoItem class="list-item" title="Canceled At" :content="subscription.data.attributes.canceled_at"/>
<ListInfoItem class="list-item" title="Ends At" :content="subscription.ends_at"/> <ListInfoItem class="list-item" title="Ends At" :content="subscription.data.attributes.ends_at"/>
</ListInfo> </ListInfo>
<div class="plan-action">
<ButtonBase
:disabled="isResuming"
@click.native="resumeSubscription"
:button-style="resumeButtonStyle"
class="confirm-button">
{{ resumeButtonText }}
</ButtonBase>
</div>
</div> </div>
</PageTabGroup> </PageTabGroup>
<PageTabGroup v-else> <PageTabGroup v-else>
@@ -65,26 +75,31 @@
return this.isConfirmedCancel ? this.$t('popup_share_edit.confirm') : 'Cancel Plan' return this.isConfirmedCancel ? this.$t('popup_share_edit.confirm') : 'Cancel Plan'
}, },
cancelButtonStyle() { cancelButtonStyle() {
return this.isConfirmedCancel ? 'danger-solid' : 'danger' return this.isConfirmedCancel ? 'danger-solid' : 'secondary'
}, },
subscription() { resumeButtonText() {
return this.$store.getters.user.relationships.subscription return this.isConfirmedResume ? this.$t('popup_share_edit.confirm') : 'Resume Plan'
? this.$store.getters.user.relationships.subscription.data.attributes },
: undefined resumeButtonStyle() {
return this.isConfirmedResume ? 'theme-solid' : 'secondary'
}, },
status() { status() {
if (this.subscription.canceled) { if (this.subscription.data.attributes.canceled) {
return 'Canceled' return 'Canceled'
} }
if (this.subscription.active) { if (this.subscription.data.attributes.active) {
return 'Active' return 'Active'
} }
} }
}, },
data() { data() {
return { return {
subscription: undefined,
isConfirmedCancel: false, isConfirmedCancel: false,
isConfirmedResume: false,
isDeleting: false, isDeleting: false,
isResuming: false,
isLoading: true,
} }
}, },
methods: { methods: {
@@ -98,6 +113,7 @@
// Start deleting spinner button // Start deleting spinner button
this.isDeleting = true this.isDeleting = true
this.isLoading = true
// Send delete request // Send delete request
axios axios
@@ -105,7 +121,9 @@
.then(() => { .then(() => {
// Update user data // Update user data
this.$store.dispatch('getAppData') this.$store.dispatch('getAppData').then(() => {
this.fetchSubscriptionDetail()
})
// End deleting spinner button // End deleting spinner button
this.isDeleting = false this.isDeleting = false
@@ -122,9 +140,61 @@
// End deleting spinner button // End deleting spinner button
this.isDeleting = false this.isDeleting = false
this.isLoading = false
}) })
} }
},
resumeSubscription() {
// Set confirm button
if (! this.isConfirmedResume) {
this.isConfirmedResume = true
} else {
// Start deleting spinner button
this.isResuming = true
this.isLoading = true
// Send delete request
axios
.post('/api/subscription/resume')
.then(() => {
// Update user data
this.$store.dispatch('getAppData').then(() => {
this.fetchSubscriptionDetail()
})
// End deleting spinner button
this.isResuming = false
events.$emit('alert:open', {
emoji: '👍',
title: 'Subscription Was Resumed',
message: 'Your subscription was re-activated, and they will be billed on the original billing cycle.',
buttonStyle: 'theme',
button: 'That\'s awesome!'
})
})
.catch(() => {
// End deleting spinner button
this.isResuming = false
this.isLoading = false
})
}
},
fetchSubscriptionDetail() {
axios.get('/api/user/subscription')
.then(response => {
this.subscription = response.data
this.isLoading = false
})
} }
},
created() {
this.fetchSubscriptionDetail()
} }
} }
</script> </script>
@@ -133,7 +203,7 @@
@import '@assets/vue-file-manager/_variables'; @import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins'; @import '@assets/vue-file-manager/_mixins';
.cancel-plan { .plan-action {
margin-top: 10px; margin-top: 10px;
} }
+2 -2
View File
@@ -50,7 +50,7 @@
&.center-page { &.center-page {
height: 100%; height: 100%;
padding-top: 20px; padding-top: 20px;
padding-bottom: 0; margin-bottom: 50px;
display: grid; display: grid;
width: 100%; width: 100%;
@@ -242,7 +242,7 @@
.action-icons { .action-icons {
white-space: nowrap; white-space: nowrap;
a { a, .icon {
display: inline-block; display: inline-block;
margin-left: 10px; margin-left: 10px;
+2
View File
@@ -25,6 +25,7 @@
<div id="app"></div> <div id="app"></div>
<script src="https://js.stripe.com/v3/"></script>
<script> <script>
let config = { let config = {
locale: '{{ \Illuminate\Support\Facades\App::getLocale() }}', locale: '{{ \Illuminate\Support\Facades\App::getLocale() }}',
@@ -35,6 +36,7 @@
userRegistration: {{ config('vuefilemanager.registration') ? 1 : 0 }}, userRegistration: {{ config('vuefilemanager.registration') ? 1 : 0 }},
storageLimit: {{ config('vuefilemanager.limit_storage_by_capacity') ? 1 : 0 }}, storageLimit: {{ config('vuefilemanager.limit_storage_by_capacity') ? 1 : 0 }},
isSaaS: 1, isSaaS: 1,
isDemo: {{ env('APP_DEMO') ? 1 : 0 }},
} }
</script> </script>
+9
View File
@@ -56,13 +56,21 @@ Route::group(['middleware' => ['auth:api', 'auth.master', 'scope:master']], func
Route::patch('/user/relationships/settings', 'User\AccountController@update_user_settings'); Route::patch('/user/relationships/settings', 'User\AccountController@update_user_settings');
Route::post('/user/password', 'User\AccountController@change_password'); Route::post('/user/password', 'User\AccountController@change_password');
Route::patch('/user/profile', 'User\AccountController@update_profile'); Route::patch('/user/profile', 'User\AccountController@update_profile');
Route::get('/user/subscription', 'User\SubscriptionController@show');
Route::get('/user/invoices', 'User\AccountController@invoices'); Route::get('/user/invoices', 'User\AccountController@invoices');
Route::get('/user/storage', 'User\AccountController@storage'); Route::get('/user/storage', 'User\AccountController@storage');
Route::get('/user', 'User\AccountController@user'); Route::get('/user', 'User\AccountController@user');
// Payment cards
Route::delete('/user/payment-cards/{id}', 'User\PaymentCardsController@delete');
Route::patch('/user/payment-cards/{id}', 'User\PaymentCardsController@update');
Route::get('/user/payments', 'User\PaymentCardsController@payment_methods');
// Subscription // Subscription
Route::get('/stripe/setup-intent', 'User\SubscriptionController@stripe_setup_intent');
Route::post('/subscription/upgrade', 'User\SubscriptionController@upgrade'); Route::post('/subscription/upgrade', 'User\SubscriptionController@upgrade');
Route::post('/subscription/cancel', 'User\SubscriptionController@cancel'); Route::post('/subscription/cancel', 'User\SubscriptionController@cancel');
Route::post('/subscription/resume', 'User\SubscriptionController@resume');
// Browse // Browse
Route::get('/participant-uploads', 'FileBrowser\BrowseController@participant_uploads'); Route::get('/participant-uploads', 'FileBrowser\BrowseController@participant_uploads');
@@ -112,6 +120,7 @@ Route::group(['middleware' => ['auth:api', 'auth.master', 'auth.admin', 'scope:m
Route::get('/gateways', 'Admin\GatewayController@index'); Route::get('/gateways', 'Admin\GatewayController@index');
Route::get('/gateways/{type}', 'Admin\GatewayController@show'); Route::get('/gateways/{type}', 'Admin\GatewayController@show');
Route::patch('/gateways/{type}', 'Admin\GatewayController@update'); Route::patch('/gateways/{type}', 'Admin\GatewayController@update');
Route::get('/gateways/{type}/transactions', 'Admin\GatewayController@show_transactions');
// Plans // Plans
Route::get('/plans', 'Admin\PlanController@index'); Route::get('/plans', 'Admin\PlanController@index');
+2 -52
View File
@@ -16,59 +16,9 @@ use Rinvex\Subscriptions\Models\PlanFeature;
Route::get('/debug', function () { Route::get('/debug', function () {
$user = User::find(1);
/* return $user->subscription('main')->asStripeSubscription();
* 1. Create plan
*/
/* $plan = app('rinvex.subscriptions.plan')->create([
'name' => 'Starter Pack',
'description' => 'The best for start with',
'price' => 9.99,
'signup_fee' => 0,
'invoice_period' => 1,
'invoice_interval' => 'month',
'trial_period' => 7,
'trial_interval' => 'day',
'sort_order' => 1,
'currency' => 'USD',
]);
// Create multiple plan features at once
$plan->features()->saveMany([
new PlanFeature(['name' => 'Storage capacity', 'value' => 200, 'sort_order' => 1]),
]);
return $plan;
*/
/*
* 2. Get plan
*/
/* $plan = app('rinvex.subscriptions.plan')->find(6);
return $plan;
//return $plan->subscriptions;
$space = $plan->getFeatureBySlug('storage-capacity')->value;*/
//return $space;
/*
* 3. Create subscription
*/
$user = Auth::user();
//$plan = app('rinvex.subscriptions.plan')->find(6);
//return $user->activeSubscriptions();
return $user->subscription('Starter Pack')->cancel();
//$user->newSubscription('Starter Pack', $plan);
//return $plan->subscriptions;
//return $user->subscribedTo(5);
}); });
// Deployment Webhook URL // Deployment Webhook URL