Compare commits

..

9 Commits

Author SHA1 Message Date
Peter Papp
73a728e606 change vuefilemanager version 2020-08-25 12:37:09 +02:00
Peter Papp
af1228e363 reading metadata in shared link RC 2020-08-25 12:36:21 +02:00
Peter Papp
f3a2758bcc reading metadata in shared link v0.1 2020-08-25 12:04:13 +02:00
Peter Papp
6f9f2f2d34 reading metadata in shared link 2020-08-25 11:03:53 +02:00
Peter Papp
9f4c21a1b1 UserSubscription page fix
PaymentMethods page fix
2020-08-24 09:30:38 +02:00
Peter Papp
88315e4a91 fixed paginated button 2020-08-24 07:20:45 +02:00
Peter Papp
25bb186c89 updated readme 2020-08-24 07:17:11 +02:00
Peter Papp
cca832a1c1 backend pagination and sorting from laravel database release 2020-08-24 06:35:42 +02:00
Peter Papp
6dd0b4f026 backend pagination and sorting from laravel database 2020-08-21 16:04:21 +02:00
103 changed files with 5033 additions and 5184 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -21,8 +21,10 @@ But, it can't be done without you, development is more and more complicated and
- [Nginx Configuration](#nginx-configuration)
- [Apache Configuration](#apache-configuration)
- [Recover Failed Installation](#installation-failed)
- [Regular Update](#regular-update)
- [Update from 1.6.x to 1.7 ](#update-from-16x-to-17)
- [Update Guide](#update-guide)
- [Instructions](#instructions)
- [Update from 1.7.x to 1.7.7](#update-from-17x-to-177)
- [Update from 1.6.x to 1.7](#update-from-16x-to-17)
- [Payments](#payments)
- [Get your active plans](#get-your-active-plans)
- [Manage Failed Payments](#manage-failed-payments)
@@ -188,17 +190,23 @@ At worst scenarios, to reset Setup Wizard, delete all tables in your previously
After these steps, installation will be reseted.
## Regular Update
- `Don't forget create backup of your database and storage before make any changes in your production application.`
- `If you serve your files in local storage driver pay attention and don't delete your /storage folder`
## Update Guide
### Instructions
`Don't forget create backup of your database and storage before make any changes in your production application.`
`If you serve your files in local storage driver pay attention and don't delete your /storage folder`
Follow this steps:
- Make a backup of the .env config file located on your server.
- Upload and replace all the files on your server with what's inside the app folder.
- Restore your `.env` config file on your server.
## Update VueFileManager from 1.6.x to 1.7
`Don't forget create backup of your database and storage before make any changes in your production application.`
## Update from 1.7.x to 1.7.7
If you are upgrading app to 1.7.7 from 1.7.x, make sure you have copied new /vendor folder or if you are using terminal or git, run `composer update` command to update your vendors.
## Update from 1.6.x to 1.7
For those, who purchase extended licence, place these lines at the end of your `/.env` file:
```
@@ -210,13 +218,7 @@ STRIPE_WEBHOOK_SECRET=
CASHIER_PAYMENT_NOTIFICATION=App\Notifications\ConfirmPayment
```
Then follow this steps:
- Make sure you have PHP >= 7.2.5 version
- Make a backup of the .env config file located on your server.
- Upload and replace all the files on your server with what's inside the app folder.
- Restore your `.env` config file on your server.
- Go to https://your-domain.com/upgrade and follow the setup wizard instructions.
Then go to https://your-domain.com/upgrade and follow the setup wizard instructions.
# Payments
VueFileManager is packed with **Stripe** payment options. To configure Stripe, you will be asked in Setup Wizard to set up. Or, if you skip this installation process, you will find stripe set up in you admin `Dashboard / Settings / Payments`.
@@ -258,6 +260,13 @@ To start server on your localhost, run command below. Then go to your generated
php artisan serve
```
After successfully installation via Setup Wizard, stop your artisan server, clear config cache and run your artisan server again:
```
php artisan config:clear
php artisan serve
```
*After any change in your .env you have to restart your artisan server to reload your config cache.*
To develop your Vue front-end, you have to install npm modules by this command:
```
npm install

File diff suppressed because it is too large Load Diff

View File

@@ -60,7 +60,7 @@ class DashboardController extends Controller
public function new_registrations()
{
return new UsersCollection(
User::take(7)->orderByDesc('created_at')->get()
User::sortable(['created_at' => 'desc'])->paginate(10)
);
}
}

View File

@@ -19,7 +19,7 @@ class PagesController extends Controller
public function index()
{
return new PageCollection(
Page::all()
Page::sortable()->paginate(10)
);
}

View File

@@ -153,7 +153,7 @@ class PlanController extends Controller
$subscribers = Subscription::where('stripe_plan', $id)->pluck('user_id');
return new UsersCollection(
User::findMany($subscribers)
User::sortable()->findMany($subscribers)
);
}
}

View File

@@ -85,8 +85,8 @@ class UserController extends Controller
{
$user = User::find($id);
if (! $user->stripeId()) {
return response('User is not stripe customer', 404);
if (! $user->stripeId() || ! $user->subscription('main')) {
return response('User doesn\'t have any subscription.', 404);
}
return new UserSubscription(
@@ -102,7 +102,7 @@ class UserController extends Controller
public function users()
{
return new UsersCollection(
User::all()
User::sortable()->paginate('20')
);
}

View File

@@ -3,12 +3,15 @@
namespace App\Http\Controllers;
use App\Content;
use App\FileManagerFile;
use App\FileManagerFolder;
use App\Http\Requests\PublicPages\SendMessageRequest;
use App\Http\Resources\PageResource;
use App\Http\Tools\Demo;
use App\Mail\SendSupportForm;
use App\Page;
use App\Setting;
use App\User;
use Artisan;
use Doctrine\DBAL\Driver\PDOException;
use Illuminate\Http\Request;
@@ -96,6 +99,68 @@ class AppFunctionsController extends Controller
->with('installation', $connection);
}
/**
* Get og site for web crawlers
*
* @param $token
*/
public function og_site($token)
{
// Get all settings
$settings = Setting::all();
// Get shared token
$shared = get_shared($token);
// Get user
$user = User::findOrFail($shared->user_id);
// Handle single file
if ($shared->type === 'file') {
// Get file record
$file = FileManagerFile::where('user_id', $shared->user_id)
->where('unique_id', $shared->item_id)
->first();
if ($file->thumbnail) {
$file->setPublicUrl($token);
}
$metadata = [
'is_protected' => $shared->protected,
'url' => url('/shared', ['token' => $token]),
'user' => $user->name,
'name' => $file->name,
'size' => $file->filesize,
'thumbnail' => $file->thumbnail ? $file->thumbnail : null,
];
}
// Handle single file
if ($shared->type === 'folder') {
// Get file record
$folder = FileManagerFolder::where('user_id', $shared->user_id)
->where('unique_id', $shared->item_id)
->first();
$metadata = [
'is_protected' => $shared->protected,
'url' => url('/shared', ['token' => $token]),
'user' => $user->name,
'name' => $folder->name,
'size' => $folder->items,
'thumbnail' => null,
];
}
// Return view
return view("og-view")
->with('settings', $settings)
->with('metadata', $metadata);
}
/**
* Check if setup wizard was passed
*

View File

@@ -149,6 +149,8 @@ class SettingController extends Controller
]);
// Clear cache
Artisan::call('cache:clear');
Artisan::call('config:clear');
Artisan::call('config:cache');
}
}

View File

@@ -32,8 +32,7 @@ class FileSharingController extends Controller
public function index($token)
{
// Get shared token
$shared = Share::where(\DB::raw('BINARY `token`'), $token)
->first();
$shared = get_shared($token);
if (! $shared) {
return view("index");

View File

@@ -244,12 +244,12 @@ function is_editor($shared)
function get_unique_id(): int
{
// Get files and folders
$folders = FileManagerFolder::withTrashed()->latest();
$files = FileManagerFile::withTrashed()->latest();
$folders = FileManagerFolder::withTrashed()->get();
$files = FileManagerFile::withTrashed()->get();
// Get last ids
$folders_unique = ! $folders->first() ? 0 : (int) $folders->first()->unique_id;
$files_unique = ! $files->first() ? 0 : (int) $files->first()->unique_id;
$folders_unique = $folders->isEmpty() ? 0 : $folders->last()->unique_id;
$files_unique = $files->isEmpty() ? 0 : $files->last()->unique_id;
// Count new unique id
$unique_id = $folders_unique > $files_unique ? $folders_unique + 1 : $files_unique + 1;

View File

@@ -23,17 +23,17 @@ class UserResource extends JsonResource
'id' => (string)$this->id,
'type' => 'user',
'attributes' => [
'storage_capacity' => $this->settings->storage_capacity,
'subscription' => $this->subscribed('main'),
'incomplete_payment' => $this->hasIncompletePayment('main') ? route('cashier.payment', $this->subscription('main')->latestPayment()->id) : null,
'stripe_customer' => is_null($this->stripe_id) ? false : true,
'name' => $this->name,
'email' => env('APP_DEMO') ? obfuscate_email($this->email) : $this->email,
'avatar' => $this->avatar,
'role' => $this->role,
'created_at_formatted' => format_date($this->created_at, '%d. %B. %Y'),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
'storage_capacity' => $this->settings->storage_capacity,
'subscription' => $this->subscribed('main'),
'incomplete_payment' => $this->hasIncompletePayment('main') ? route('cashier.payment', $this->subscription('main')->latestPayment()->id) : null,
'stripe_customer' => is_null($this->stripe_id) ? false : true,
'name' => $this->name,
'email' => env('APP_DEMO') ? obfuscate_email($this->email) : $this->email,
'avatar' => $this->avatar,
'role' => $this->role,
'created_at_formatted' => format_date($this->created_at, '%d. %B. %Y'),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
]
],
'relationships' => [

View File

@@ -3,9 +3,42 @@
namespace App;
use Illuminate\Database\Eloquent\Model;
use Kyslik\ColumnSortable\Sortable;
/**
* App\Page
*
* @property int $id
* @property int $visibility
* @property string $title
* @property string $slug
* @property string $content
* @method static \Illuminate\Database\Eloquent\Builder|Page newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Page newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Page query()
* @method static \Illuminate\Database\Eloquent\Builder|Page sortable($defaultParameters = null)
* @method static \Illuminate\Database\Eloquent\Builder|Page whereContent($value)
* @method static \Illuminate\Database\Eloquent\Builder|Page whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|Page whereSlug($value)
* @method static \Illuminate\Database\Eloquent\Builder|Page whereTitle($value)
* @method static \Illuminate\Database\Eloquent\Builder|Page whereVisibility($value)
* @mixin \Eloquent
*/
class Page extends Model
{
use Sortable;
/**
* Sortable columns
*
* @var string[]
*/
public $sortable = [
'title',
'slug',
'visibility',
];
public $timestamps = false;
protected $guarded = ['id'];

View File

@@ -380,6 +380,8 @@ class StripeService
*/
public function getInvoices()
{
return $this->stripe->invoices()->all();
return $this->stripe->invoices()->all([
'limit' => 20
]);
}
}

View File

@@ -4,6 +4,20 @@ namespace App;
use Illuminate\Database\Eloquent\Model;
/**
* App\Setting
*
* @property int $id
* @property string $name
* @property string|null $value
* @method static \Illuminate\Database\Eloquent\Builder|Setting newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Setting newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Setting query()
* @method static \Illuminate\Database\Eloquent\Builder|Setting whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|Setting whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder|Setting whereValue($value)
* @mixin \Eloquent
*/
class Setting extends Model
{
public $timestamps = false;

View File

@@ -5,14 +5,12 @@ namespace App;
use App\Notifications\ResetPassword;
use App\Notifications\ResetUserPasswordNotification;
use ByteUnits\Metric;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Storage;
use Laravel\Cashier\Billable;
use Laravel\Passport\HasApiTokens;
use Kyslik\ColumnSortable\Sortable;
use Rinvex\Subscriptions\Traits\HasSubscriptions;
/**
@@ -75,10 +73,11 @@ use Rinvex\Subscriptions\Traits\HasSubscriptions;
* @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)
* @method static \Illuminate\Database\Eloquent\Builder|User sortable($defaultParameters = null)
*/
class User extends Authenticatable
{
use HasApiTokens, Notifiable, Billable;
use HasApiTokens, Notifiable, Billable, Sortable;
protected $guarded = ['id', 'role'];
@@ -113,6 +112,19 @@ class User extends Authenticatable
'used_capacity', 'storage'
];
/**
* Sortable columns
*
* @var string[]
*/
public $sortable = [
'id',
'name',
'role',
'created_at',
'storage_capacity',
];
/**
* Get tax rate id for user
*
@@ -197,7 +209,6 @@ class User extends Authenticatable
{
// Get avatar from external storage
if ($this->attributes['avatar'] && is_storage_driver(['s3', 'spaces', 'wasabi', 'backblaze'])) {
return Storage::temporaryUrl($this->attributes['avatar'], now()->addDay());
}

View File

@@ -4,6 +4,34 @@ namespace App;
use Illuminate\Database\Eloquent\Model;
/**
* App\UserSettings
*
* @property int $id
* @property int $user_id
* @property int $storage_capacity
* @property string|null $billing_name
* @property string|null $billing_address
* @property string|null $billing_state
* @property string|null $billing_city
* @property string|null $billing_postal_code
* @property string|null $billing_country
* @property string|null $billing_phone_number
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings query()
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings whereBillingAddress($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings whereBillingCity($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings whereBillingCountry($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings whereBillingName($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings whereBillingPhoneNumber($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings whereBillingPostalCode($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings whereBillingState($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings whereStorageCapacity($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings whereUserId($value)
* @mixin \Eloquent
*/
class UserSettings extends Model
{
public $timestamps = false;

View File

@@ -15,6 +15,8 @@
"fruitcake/laravel-cors": "^1.0",
"gabrielelana/byte-units": "^0.5.0",
"intervention/image": "^2.5",
"jaybizzle/laravel-crawler-detect": "^1.2",
"kyslik/column-sortable": "^6.3",
"laravel/cashier": "^12.0",
"laravel/framework": "^7.0",
"laravel/passport": "^8.4",

173
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "c2e0a4adcc267140dcc5f4b1ee31ae2f",
"content-hash": "aadbe31a952d26aab5730369e2bf4089",
"packages": [
{
"name": "asm89/stack-cors",
@@ -1467,6 +1467,176 @@
],
"time": "2019-11-02T09:15:47+00:00"
},
{
"name": "jaybizzle/crawler-detect",
"version": "v1.2.98",
"source": {
"type": "git",
"url": "https://github.com/JayBizzle/Crawler-Detect.git",
"reference": "02b24e5d4dc347737577f48c688ee14c3b5dfd4f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/JayBizzle/Crawler-Detect/zipball/02b24e5d4dc347737577f48c688ee14c3b5dfd4f",
"reference": "02b24e5d4dc347737577f48c688ee14c3b5dfd4f",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8|^5.5|^6.5",
"satooshi/php-coveralls": "1.*"
},
"type": "library",
"autoload": {
"psr-4": {
"Jaybizzle\\CrawlerDetect\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mark Beech",
"email": "m@rkbee.ch",
"role": "Developer"
}
],
"description": "CrawlerDetect is a PHP class for detecting bots/crawlers/spiders via the user agent",
"homepage": "https://github.com/JayBizzle/Crawler-Detect/",
"keywords": [
"crawler",
"crawler detect",
"crawler detector",
"crawlerdetect",
"php crawler detect"
],
"time": "2020-08-20T18:36:15+00:00"
},
{
"name": "jaybizzle/laravel-crawler-detect",
"version": "v1.2.0",
"source": {
"type": "git",
"url": "https://github.com/JayBizzle/Laravel-Crawler-Detect.git",
"reference": "e39b536fb5e4f21a49328e83a2ab26f0b64b06f7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/JayBizzle/Laravel-Crawler-Detect/zipball/e39b536fb5e4f21a49328e83a2ab26f0b64b06f7",
"reference": "e39b536fb5e4f21a49328e83a2ab26f0b64b06f7",
"shasum": ""
},
"require": {
"jaybizzle/crawler-detect": "1.*",
"php": ">=5.4.0"
},
"require-dev": {
"orchestra/testbench": "3.1.*",
"phpunit/phpunit": "4.*"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Jaybizzle\\LaravelCrawlerDetect\\LaravelCrawlerDetectServiceProvider"
],
"aliases": {
"Crawler": "Jaybizzle\\LaravelCrawlerDetect\\Facades\\LaravelCrawlerDetect"
}
}
},
"autoload": {
"psr-4": {
"Jaybizzle\\LaravelCrawlerDetect\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Mark Beech",
"email": "mbeech@mark-beech.co.uk"
}
],
"description": "A Laravel package to detect web crawlers via the user agent",
"homepage": "http://github.com/JayBizzle/Laravel-Crawler-Detect",
"keywords": [
"bot",
"crawler",
"crawler detect",
"crawler detector",
"crawlerdetect",
"detect",
"laravel",
"php crawler detect",
"spider",
"user-agent"
],
"time": "2017-06-01T20:29:30+00:00"
},
{
"name": "kyslik/column-sortable",
"version": "6.3.0",
"source": {
"type": "git",
"url": "https://github.com/Kyslik/column-sortable.git",
"reference": "9f2ddfabe5cfd9cfd67b15805e2d0de48429c995"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Kyslik/column-sortable/zipball/9f2ddfabe5cfd9cfd67b15805e2d0de48429c995",
"reference": "9f2ddfabe5cfd9cfd67b15805e2d0de48429c995",
"shasum": ""
},
"require": {
"illuminate/database": "5.8.*|^6.0|^7.0",
"illuminate/support": "5.8.*|^6.0|^7.0",
"php": ">=7.2"
},
"require-dev": {
"orchestra/testbench": "^5.0",
"phpunit/phpunit": "^8.5"
},
"type": "package",
"extra": {
"laravel": {
"providers": [
"Kyslik\\ColumnSortable\\ColumnSortableServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Kyslik\\ColumnSortable\\": "src/ColumnSortable/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Martin Kiesel",
"email": "martin.kiesel@gmail.com",
"role": "Developer and maintainer"
}
],
"description": "Package for handling column sorting in Laravel 6.x",
"keywords": [
"column",
"laravel",
"sort",
"sortable",
"sorting"
],
"time": "2020-03-08T12:26:23+00:00"
},
{
"name": "laminas/laminas-diactoros",
"version": "2.3.1",
@@ -8501,6 +8671,7 @@
"keywords": [
"tokenizer"
],
"abandoned": true,
"time": "2019-09-17T06:23:10+00:00"
},
{

View File

@@ -231,6 +231,7 @@ return [
'View' => Illuminate\Support\Facades\View::class,
'Image' => Intervention\Image\Facades\Image::class,
'Stripe' => Cartalyst\Stripe\Laravel\Facades\Stripe::class,
'Crawler' => Jaybizzle\LaravelCrawlerDetect\Facades\LaravelCrawlerDetect::class,
],
'deploy_secret' => env('APP_DEPLOY_SECRET'),

View File

@@ -44,7 +44,7 @@ return [
],
[
'name' => 'header_description',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Trully freedom.',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Truly freedom.',
],
[
'name' => 'features_title',
@@ -52,7 +52,7 @@ return [
],
[
'name' => 'features_description',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Trully freedom.',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Truly freedom.',
],
[
'name' => 'feature_title_1',
@@ -84,7 +84,7 @@ return [
],
[
'name' => 'pricing_description',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Trully freedom.',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Truly freedom.',
],
[
'name' => 'get_started_title',
@@ -92,7 +92,7 @@ return [
],
[
'name' => 'get_started_description',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Trully freedom.',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Truly freedom.',
],
[
'name' => 'footer_content',

View File

@@ -2,7 +2,7 @@
return [
'version' => '1.7.6',
'version' => '1.7.8',
// Define size of chunk uploaded by MB. E.g. integer 128 means chunk size will be 128MB.
'chunk_size' => env('CHUNK_SIZE', '128'),

View File

@@ -35,7 +35,7 @@ class ContentSeeder extends Seeder
],
[
'name' => 'header_description',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Trully freedom.',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Truly freedom.',
],
[
'name' => 'features_title',
@@ -43,7 +43,7 @@ class ContentSeeder extends Seeder
],
[
'name' => 'features_description',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Trully freedom.',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Truly freedom.',
],
[
'name' => 'feature_title_1',
@@ -75,7 +75,7 @@ class ContentSeeder extends Seeder
],
[
'name' => 'pricing_description',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Trully freedom.',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Truly freedom.',
],
[
'name' => 'get_started_title',
@@ -83,7 +83,7 @@ class ContentSeeder extends Seeder
],
[
'name' => 'get_started_description',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Trully freedom.',
'value' => 'Your private cloud storage software build on Laravel & Vue.js. No limits & no monthly fees. Truly freedom.',
],
[
'name' => 'footer_content',

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
public/js/main.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -5,7 +5,7 @@
*/
/*!
* vue-i18n v8.21.0
* vue-i18n v8.18.2
* (c) 2020 kazuya kawaguchi
* Released under the MIT License.
*/
@@ -17,7 +17,7 @@
*/
/**
* vee-validate v3.3.9
* vee-validate v3.3.7
* (c) 2020 Abdelrahman Awad
* @license MIT
*/

View File

@@ -1,6 +1,6 @@
<template>
<WidgetWrapper :icon="icon" :title="title">
<DatatableWrapper v-if="users" :paginator="false" :columns="columns" :data="users" class="table table-users">
<DatatableWrapper @init="isLoading = false" api="/api/dashboard/new-users" :paginator="false" :columns="columns" class="table table-users">
<template slot-scope="{ row }">
<tr>
<td style="width: 300px">
@@ -65,27 +65,26 @@
data() {
return {
isLoading: false,
users: undefined,
columns: [
{
label: this.$t('admin_page_user.table.name'),
field: 'data.attributes.name',
sortable: true
field: 'name',
sortable: false
},
{
label: this.$t('admin_page_user.table.role'),
field: 'data.attributes.role',
sortable: true
field: 'role',
sortable: false
},
{
label: this.$t('admin_page_user.table.storage_used'),
field: 'relationships.storage.data.attributes.used',
sortable: true
field: 'used',
sortable: false
},
{
label: this.$t('admin_page_user.table.created_at'),
field: 'data.attributes.created_at_formatted',
sortable: true
field: 'created_at',
sortable: false
},
{
label: this.$t('admin_page_user.table.action'),
@@ -107,13 +106,6 @@
}
}
},
created() {
axios.get('/api/dashboard/new-users')
.then(response => {
this.users = response.data.data
this.isLoading = false
})
}
}
</script>

View File

@@ -1,9 +1,9 @@
<template>
<div class="page-tab">
<div id="loader" v-if="isLoading">
<div id="loader" v-show="isLoading">
<Spinner></Spinner>
</div>
<slot v-if="! isLoading"></slot>
<slot v-show="! isLoading"></slot>
</div>
</template>

View File

@@ -4,410 +4,489 @@
<thead class="table-header">
<tr>
<th
v-for="(column, index) in columns"
@click="sort(column.field, column.sortable, index)"
:key="index"
:class="{ sortable: column.sortable }"
v-if="! column.hidden"
v-for="(column, index) in columns"
@click="sort(column.field, column.sortable)"
:key="index"
:class="{ 'sortable': column.sortable }"
v-if="! column.hidden"
>
<span>{{ column.label }}</span>
<chevron-up-icon v-if="false" :class="{ 'arrow-down': filter.sort === 'ASC' }" size="14" class="filter-arrow"></chevron-up-icon>
<chevron-up-icon v-if="column.sortable" :class="{ 'arrow-down': filter.sort === 'ASC' }" size="14" class="filter-arrow"></chevron-up-icon>
</th>
</tr>
</thead>
<tbody class="table-body">
<slot v-for="row in visibleData" :row="row">
<slot v-for="row in data.data" :row="row">
<DatatableCell :data="row" :key="row.id"/>
</slot>
</tbody>
</table>
<div v-if="hasData && paginator" class="paginator-wrapper">
<ul v-if="chunks.length > 1" class="pagination">
<li class="page-item">
<a
@click="goToPage(pageIndex - 1)"
class="page-link"
:class="{ disabled: pageIndex == 0 }"
>
<slot v-if="! isLoading && ! hasData" name="empty-page"></slot>
<div v-if="paginator && hasData" class="paginator-wrapper">
<!--Show if there is only 6 pages-->
<ul v-if="data.meta.total > 20 && data.meta.last_page <= 6" class="pagination">
<!--Go previous icon-->
<li class="page-item previous">
<a @click="goToPage(pageIndex - 1)" class="page-link" :class="{ disabled: pageIndex == 0 }">
<chevron-left-icon size="14" class="icon"></chevron-left-icon>
</a>
</li>
<li
v-for="(row, index) in chunks"
:key="index"
class="page-item"
@click="goToPage(index)"
>
<a
class="page-link"
:class="{ active: pageIndex == index }">
{{ index + 1 }}
<li v-for="(page, index) in 6" :key="index" class="page-item" @click="goToPage(page)">
<a class="page-link" :class="{ active: pageIndex === page }">
{{ page }}
</a>
</li>
<li class="page-item">
<a
@click="goToPage(pageIndex + 1)"
class="page-link"
:class="{ disabled: pageIndex + 1 == chunks.length }"
>
<!--Go next icon-->
<li class="page-item next">
<a @click="goToPage(pageIndex + 1)" class="page-link" :class="{ disabled: pageIndex + 1 == data.meta.last_page }">
<chevron-right-icon size="14" class="icon"></chevron-right-icon>
</a>
</li>
</ul>
<span class="paginator-info">{{ $t('datatable.paginate_info', {visible: visibleData.length, total: data.length}) }}</span>
<!--Show if there is more than 6 pages-->
<ul v-if="data.meta.total > 20 && data.meta.last_page > 6" class="pagination">
<!--Go previous icon-->
<li class="page-item previous">
<a @click="goToPage(pageIndex - 1)" class="page-link" :class="{ disabled: pageIndex == 0 }">
<chevron-left-icon size="14" class="icon"></chevron-left-icon>
</a>
</li>
<!--Show first Page-->
<li class="page-item" v-if="pageIndex >= 5" @click="goToPage(1)">
<a class="page-link">
1
</a>
</li>
<li v-if="pageIndex < 5" v-for="(page, index) in 5" :key="index" class="page-item" @click="goToPage(page)">
<a class="page-link" :class="{ active: pageIndex === page }">
{{ page }}
</a>
</li>
<li class="page-item" v-if="pageIndex >= 5">
<a class="page-link">...</a>
</li>
<!--Floated Pages-->
<li v-if="pageIndex >= 5 && pageIndex < (data.meta.last_page - 3)" v-for="(page, index) in floatPages" :key="index" class="page-item" @click="goToPage(page)">
<a class="page-link" :class="{ active: pageIndex === page }">
{{ page }}
</a>
</li>
<li class="page-item" v-if="pageIndex < (data.meta.last_page - 3)">
<a class="page-link">...</a>
</li>
<li v-if="pageIndex > (data.meta.last_page - 4)" v-for="(page, index) in 5" :key="index" class="page-item" @click="goToPage(data.meta.last_page - (4 - index))">
<a class="page-link" :class="{ active: pageIndex === (data.meta.last_page - (4 - index)) }">
{{ data.meta.last_page - (4 - index) }}
</a>
</li>
<!--Show last page-->
<li class="page-item" v-if="pageIndex < (data.meta.last_page - 3)" @click="goToPage(data.meta.last_page)">
<a class="page-link">
{{ data.meta.last_page }}
</a>
</li>
<!--Go next icon-->
<li class="page-item next">
<a @click="goToPage(pageIndex + 1)" class="page-link" :class="{ disabled: pageIndex + 1 == data.meta.last_page }">
<chevron-right-icon size="14" class="icon"></chevron-right-icon>
</a>
</li>
</ul>
<span class="paginator-info">{{ $t('datatable.paginate_info', {visible: data.meta.per_page, total: data.meta.total}) }}</span>
</div>
</div>
</template>
<script>
import { ChevronUpIcon, ChevronLeftIcon, ChevronRightIcon } from 'vue-feather-icons'
import DatatableCell from '@/components/Others/Tables/DatatableCell'
import {chunk, sortBy} from 'lodash'
import {ChevronUpIcon, ChevronLeftIcon, ChevronRightIcon} from 'vue-feather-icons'
import DatatableCell from '@/components/Others/Tables/DatatableCell'
import {chunk, sortBy} from 'lodash'
import axios from "axios";
export default {
props: ['columns', 'data', 'scope', 'paginator'],
components: {
ChevronRightIcon,
ChevronLeftIcon,
DatatableCell,
ChevronUpIcon,
export default {
name: 'DatatableWrapper',
props: [
'columns', 'scope', 'paginator', 'api', 'tableData'
],
components: {
ChevronRightIcon,
ChevronLeftIcon,
DatatableCell,
ChevronUpIcon,
},
computed: {
hasData() {
return this.data && this.data.data && this.data.data.length > 0
},
data() {
return {
items_per_view: 20,
pageIndex: 0,
paginatorVisible: true,
chunks: [],
filter: {
sort: 'DESC',
field: undefined,
index: undefined,
}
}
},
methods: {
goToPage(index) {
if (index == this.chunks.length || index == -1) return
// Update page index
this.pageIndex = index
},
sort(field, sortable, index) {
// Prevent sortable if is disabled
if (!sortable) return
// Set filter
this.filter.field = field
this.filter.index = index
// Set sorting direction
if (this.filter.sort === 'DESC') {
this.filter.sort = 'ASC'
} else if (this.filter.sort === 'ASC') {
this.filter.sort = 'DESC'
}
}
},
computed: {
hasData() {
return this.data.length > 0 ? true : false
},
visibleData() {
// Prefent errors when data is empty
if (!this.hasData) return
// Reconfigure data
if (this.filter.field) {
// Set chunks with sorting
if (this.filter.sort === 'DESC') {
// DESC
this.chunks = chunk(sortBy(this.data, this.filter.field), this.items_per_view)
} else {
// ASC
this.chunks = chunk(sortBy(this.data, this.filter.field).reverse(), this.items_per_view)
}
} else {
// Get data to chunks
this.chunks = chunk(this.data, this.items_per_view)
}
// Return chunks
return this.chunks[this.pageIndex]
floatPages() {
return [(this.pageIndex - 1), this.pageIndex, (this.pageIndex + 1)];
}
},
data() {
return {
data: undefined,
isLoading: true,
pageIndex: 1,
filter: {
sort: 'DESC',
field: undefined,
}
}
},
methods: {
goToPage(index) {
if (index > this.data.meta.last_page || index === 0) return
this.pageIndex = index
this.getPage(index)
},
sort(field, sortable) {
// Prevent sortable if is disabled
if (!sortable) return
// Set filter
this.filter.field = field
// Set sorting direction
if (this.filter.sort === 'DESC') {
this.filter.sort = 'ASC'
} else if (this.filter.sort === 'ASC') {
this.filter.sort = 'DESC'
}
this.getPage(this.pageIndex)
},
getPage(page) {
// Get api URI
this.URI = this.api;
// Set page index
if (this.paginator)
this.URI = this.URI + '?page=' + page
// Add filder URI if is defined sorting
if (this.filter.field)
this.URI = this.URI + (this.paginator ? '&' : '?') + 'sort=' + this.filter.field + '&direction=' + this.filter.sort
this.isLoading = true
// Get data
axios.get(this.URI)
.then(response => {
this.data = response.data
this.$emit('data', response.data)
})
.catch(() => this.$isSomethingWrong())
.finally(() => {
this.$emit('init', true)
this.isLoading = false
}
)
},
},
created() {
if (this.api)
this.getPage(this.pageIndex)
if (this.tableData)
this.data = this.tableData,
this.isLoading = false
}
}
</script>
<style lang="scss" scoped>
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
.datatable {
height: 100%;
}
.datatable {
height: 100%;
}
.table-row {
@include transition;
}
.table-row {
@include transition;
}
.table-row-enter,
.table-row-leave-to {
opacity: 0;
@include transform(translateY(-100%));
}
.table-row-enter,
.table-row-leave-to {
opacity: 0;
@include transform(translateY(-100%));
}
.table-row-leave-active {
position: absolute;
}
.table-row-leave-active {
position: absolute;
}
.table {
.table {
width: 100%;
border-collapse: collapse;
overflow-x: auto;
tr {
width: 100%;
border-collapse: collapse;
overflow-x: auto;
td, th {
&:first-child {
padding-left: 15px;
}
&:last-child {
padding-right: 15px;
text-align: right;
}
}
}
.table-header {
margin-bottom: 10px;
tr {
width: 100%;
td, th {
&:first-child {
padding-left: 15px;
padding: 12px;
text-align: left;
span {
color: $theme;
font-weight: 700;
@include font-size(12);
white-space: nowrap;
}
&.sortable {
cursor: pointer;
&:hover {
.filter-arrow {
opacity: 1;
}
}
}
&:last-child {
padding-right: 15px;
text-align: right;
}
}
}
.filter-arrow {
vertical-align: middle;
margin-left: 8px;
@include transition;
opacity: 0;
path {
fill: $text-muted;
}
&.arrow-down {
@include transform(rotate(180deg));
}
}
span {
font-size: 13px;
color: $text-muted;
font-weight: bold;
}
}
.table-body {
tr {
border-radius: 8px;
//border-bottom: 1px solid #f5f5f5;
&:hover {
background: $light_background;
}
td, th {
padding: 12px;
&:last-child {
button {
margin-right: 0;
}
}
}
}
span, a.page-link {
@include font-size(15);
font-weight: 700;
padding: 10px 35px 10px 0;
display: block;
white-space: nowrap;
}
}
}
.pagination {
.page-item {
padding: 3px;
display: inline-block;
}
.page-link {
width: 30px;
height: 30px;
display: block;
color: $text;
border-radius: 6px;
text-align: center;
line-height: 2.4;
font-weight: bold;
font-size: 13px;
cursor: pointer;
@include transition(0.15s);
.icon {
vertical-align: middle;
margin-top: -2px;
}
&:hover:not(.disabled) {
background: $light_background;
color: $text;
}
&.active {
color: $text;
background: $light_background;
}
&.disabled {
background: transparent;
cursor: default;
svg path {
fill: $text-muted;
}
}
}
}
.paginator-wrapper {
margin-top: 30px;
margin-bottom: 40px;
display: flex;
justify-content: space-between;
align-items: center;
.paginator-info {
font-size: 13px;
color: $text-muted;
}
}
.user-preview {
display: flex;
align-items: center;
cursor: pointer;
img {
width: 45px;
margin-right: 22px;
}
}
@media only screen and (max-width: 690px) {
.paginator-wrapper {
display: block;
text-align: center;
.paginator-info {
margin-top: 10px;
display: block;
}
}
}
@media (prefers-color-scheme: dark) {
.table {
.table-header {
margin-bottom: 10px;
tr {
td, th {
padding: 12px;
text-align: left;
span {
color: $theme;
font-weight: 700;
@include font-size(12);
white-space: nowrap;
}
&.sortable {
cursor: pointer;
}
&:last-child {
text-align: right;
}
}
}
.filter-arrow {
vertical-align: middle;
margin-left: 8px;
@include transition;
path {
fill: $text-muted;
}
&.arrow-down {
@include transform(rotate(180deg));
}
}
span {
font-size: 13px;
color: $text-muted;
font-weight: bold;
}
}
.table-body {
tr {
border-radius: 8px;
//border-bottom: 1px solid #f5f5f5;
tr, th {
&:hover {
background: $light_background;
}
td, th {
padding: 12px;
&:last-child {
button {
margin-right: 0;
}
}
}
}
span, a.page-link {
@include font-size(15);
font-weight: 700;
padding: 10px 35px 10px 0;
display: block;
white-space: nowrap;
}
}
}
.pagination {
.page-item {
padding: 3px;
display: inline-block;
}
.page-link {
width: 30px;
height: 30px;
display: block;
color: $text;
border-radius: 6px;
text-align: center;
line-height: 2.4;
font-weight: bold;
font-size: 13px;
cursor: pointer;
@include transition(0.15s);
.icon {
vertical-align: middle;
margin-top: -2px;
}
&:hover:not(.disabled) {
background: $light_background;
color: $text;
}
&.active {
color: $text;
background: $light_background;
}
&.disabled {
background: transparent;
cursor: default;
svg path {
fill: $text-muted;
background: $dark_mode_foreground;
}
}
}
}
.paginator-wrapper {
margin-top: 30px;
margin-bottom: 40px;
display: flex;
justify-content: space-between;
align-items: center;
.paginator-info {
font-size: 13px;
color: $text-muted;
color: $dark_mode_text_secondary;
}
}
.user-preview {
display: flex;
align-items: center;
cursor: pointer;
.pagination {
img {
width: 45px;
margin-right: 22px;
}
}
.page-link {
color: $dark_mode_text_secondary;
@media only screen and (max-width: 690px) {
.paginator-wrapper {
display: block;
text-align: center;
.paginator-info {
margin-top: 10px;
display: block;
}
}
}
@media (prefers-color-scheme: dark) {
.table {
.table-header {
tr {
td, th {
span {
color: $theme;
}
}
}
svg polyline {
stroke: $dark_mode_text_primary;
}
.table-body {
tr, th {
&:hover {
background: $dark_mode_foreground;
}
}
&:hover:not(.disabled) {
color: $theme;
background: rgba($theme, 0.1);
}
}
.paginator-wrapper {
.paginator-info {
color: $dark_mode_text_secondary;
&.active {
color: $theme;
background: rgba($theme, 0.1);
}
}
.pagination {
.page-link {
color: $dark_mode_text_secondary;
&.disabled {
background: transparent;
cursor: default;
svg polyline {
stroke: $dark_mode_text_primary;
}
&:hover:not(.disabled) {
color: $theme;
background: rgba($theme, 0.1);
}
&.active {
color: $theme;
background: rgba($theme, 0.1);
}
&.disabled {
background: transparent;
cursor: default;
svg polyline {
stroke: $dark_mode_text_secondary;
}
stroke: $dark_mode_text_secondary;
}
}
}
}
}
</style>

View File

@@ -135,9 +135,7 @@
// Send request to get verify account
axios
.post('/api/settings/email', this.mail, {
_method: 'put'
})
.post('/api/settings/email', this.mail)
.then(() => {
events.$emit('toaster', {

View File

@@ -710,9 +710,7 @@
// Send request to get verify account
axios
.post('/api/settings/stripe', this.stripeCredentials, {
_method: 'put'
})
.post('/api/settings/stripe', this.stripeCredentials)
.then(() => {
// End loading

View File

@@ -2,12 +2,12 @@
<div id="single-page">
<!--Page Content-->
<div id="page-content" v-if="! isLoading && invoices.length > 0">
<div id="page-content" v-show="! isLoading && config.stripe_public_key">
<MobileHeader :title="$router.currentRoute.meta.title"/>
<PageHeader :title="$router.currentRoute.meta.title"/>
<div class="content-page">
<DatatableWrapper :paginator="true" :columns="columns" :data="invoices" class="table">
<div class="content-page" v-if="config.stripe_public_key">
<DatatableWrapper @data="invoices = $event" @init="isLoading = false" api="/api/invoices" :paginator="false" :columns="columns" class="table">
<template slot-scope="{ row }">
<tr>
<td>
@@ -126,27 +126,27 @@
{
label: this.$t('admin_page_invoices.table.number'),
field: 'data.attributes.order',
sortable: true
sortable: false
},
{
label: this.$t('admin_page_invoices.table.total'),
field: 'data.attributes.bag.amount',
sortable: true
sortable: false
},
{
label: this.$t('admin_page_invoices.table.plan'),
field: 'data.attributes.bag.amount',
sortable: true
sortable: false
},
{
label: this.$t('admin_page_invoices.table.payed'),
field: 'data.attributes.created_at',
sortable: true
sortable: false
},
{
label: this.$t('admin_page_invoices.table.user'),
field: 'relationships.user.data.attributes.name',
sortable: true
sortable: false
},
{
label: this.$t('admin_page_user.table.action'),
@@ -156,15 +156,8 @@
}
},
created() {
if (this.config.stripe_public_key) {
axios.get('/api/invoices')
.then(response => {
this.invoices = response.data.data
this.isLoading = false
})
} else {
if (! this.config.stripe_public_key)
this.isLoading = false
}
}
}
</script>

View File

@@ -1,11 +1,11 @@
<template>
<div id="single-page">
<div id="page-content" v-if="! isLoading && pages.length > 0">
<div id="page-content" v-show="! isLoading">
<MobileHeader :title="$router.currentRoute.meta.title"/>
<PageHeader :title="$router.currentRoute.meta.title"/>
<div class="content-page">
<DatatableWrapper :paginator="false" :columns="columns" :data="pages" class="table table-users">
<DatatableWrapper @init="isLoading = false" api="/api/pages" :paginator="false" :columns="columns" class="table table-users">
<template slot-scope="{ row }">
<tr>
<td class="name" style="min-width: 200px">
@@ -75,21 +75,20 @@
data() {
return {
isLoading: true,
pages: undefined,
columns: [
{
label: this.$t('admin_pages.table.page'),
field: 'data.attributes.title',
field: 'title',
sortable: true
},
{
label: this.$t('admin_pages.table.slug'),
field: 'data.attributes.slug',
field: 'slug',
sortable: true
},
{
label: this.$t('admin_pages.table.status'),
field: 'data.attributes.visibility',
field: 'visibility',
sortable: true
},
{
@@ -104,13 +103,6 @@
this.$updateText('/pages/' + slug, 'visibility', val)
}
},
created() {
axios.get('/api/pages')
.then(response => {
this.pages = response.data.data
this.isLoading = false
})
}
}
</script>

View File

@@ -1,12 +1,12 @@
<template>
<div id="single-page">
<!--Page Content-->
<div id="page-content" v-if="! isLoading && plans.length > 0">
<!--Stripe plans-->
<div id="page-content" v-show="stripeConfiguredWithPlans">
<MobileHeader :title="$router.currentRoute.meta.title"/>
<PageHeader :title="$router.currentRoute.meta.title"/>
<div class="content-page">
<div class="content-page" v-if="config.stripe_public_key">
<div class="table-tools">
<div class="buttons">
<router-link :to="{name: 'PlanCreate'}">
@@ -19,7 +19,7 @@
</div>
</div>
<DatatableWrapper :paginator="false" :columns="columns" :data="plans" class="table table-users">
<DatatableWrapper @data="plans = $event" @init="isLoading = false" api="/api/plans" :paginator="false" :columns="columns" class="table table-users">
<template slot-scope="{ row }">
<tr>
<td style="max-width: 80px">
@@ -64,9 +64,9 @@
</div>
</div>
<!--Empty plans-->
<!--Stripe configured but has empty plans-->
<EmptyPageContent
v-if="! isLoading && plans.length === 0 && config.stripe_public_key"
v-if="isEmptyPlans"
icon="file"
:title="$t('admin_page_plans.empty.title')"
:description="$t('admin_page_plans.empty.description')"
@@ -76,9 +76,9 @@
</router-link>
</EmptyPageContent>
<!--Stripe Not Configured-->
<!--Stripe is Not Configured-->
<EmptyPageContent
v-if="! config.stripe_public_key"
v-if="stripeIsNotConfigured"
icon="settings"
:title="$t('activation.stripe.title')"
:description="$t('activation.stripe.description')"
@@ -126,6 +126,18 @@
Edit2Icon,
Spinner,
},
computed: {
...mapGetters(['config']),
isEmptyPlans() {
return ! this.isLoading && this.plans.length === 0 && this.config.stripe_public_key
},
stripeIsNotConfigured() {
return ! this.config.stripe_public_key
},
stripeConfiguredWithPlans() {
return ! this.isLoading && this.config.stripe_public_key
}
},
data() {
return {
isLoading: true,
@@ -134,27 +146,27 @@
{
label: this.$t('admin_page_plans.table.status'),
field: 'data.attributes.status',
sortable: true
sortable: false
},
{
label: this.$t('admin_page_plans.table.name'),
field: 'data.attributes.name',
sortable: true
sortable: false
},
{
label: this.$t('admin_page_plans.table.subscribers'),
field: 'data.attributes.subscribers',
sortable: true
sortable: false
},
{
label: this.$t('admin_page_plans.table.price'),
field: 'data.attributes.price',
sortable: true
sortable: false
},
{
label: this.$t('admin_page_plans.table.storage_capacity'),
field: 'data.attributes.capacity',
sortable: true
sortable: false
},
{
label: this.$t('admin_page_user.table.action'),
@@ -163,24 +175,14 @@
],
}
},
computed: {
...mapGetters(['config']),
},
methods: {
changeStatus(val, id) {
this.$updateText('/plans/' + id + '/update', 'is_active', val)
}
},
created() {
if (this.config.stripe_public_key) {
axios.get('/api/plans')
.then(response => {
this.plans = response.data.data
this.isLoading = false
})
} else {
if (! this.config.stripe_public_key)
this.isLoading = false
}
}
}
</script>

View File

@@ -1,7 +1,9 @@
<template>
<PageTab :is-loading="isLoading">
<PageTabGroup v-if="subscribers && subscribers.length > 0">
<DatatableWrapper :paginator="true" :columns="columns" :data="subscribers" class="table">
<PageTabGroup>
<DatatableWrapper @init="isLoading = false" :api="'/api/plans/' + this.$route.params.id + '/subscribers'" :paginator="false" :columns="columns" :data="subscribers" class="table">
<!--Table data content-->
<template slot-scope="{ row }">
<tr>
<td>
@@ -30,11 +32,15 @@
</td>
</tr>
</template>
<!--Empty page-->
<template v-slot:empty-page>
<InfoBox>
<p>{{ $t('admin_page_plans.subscribers.empty') }}</p>
</InfoBox>
</template>
</DatatableWrapper>
</PageTabGroup>
<InfoBox v-else>
<p>{{ $t('admin_page_plans.subscribers.empty') }}</p>
</InfoBox>
</PageTab>
</template>
@@ -62,17 +68,17 @@
data() {
return {
subscribers: undefined,
isLoading: false,
isLoading: true,
columns: [
{
label: this.$t('admin_page_user.table.name'),
field: 'data.attributes.name',
field: 'name',
sortable: true
},
{
label: this.$t('admin_page_user.table.storage_used'),
field: 'data.relationships.storage.data.attributes.used',
sortable: true
field: 'used',
sortable: false
},
{
label: this.$t('admin_page_user.table.action'),
@@ -81,13 +87,6 @@
],
}
},
created() {
axios.get('/api/plans/' + this.$route.params.id + '/subscribers')
.then(response => {
this.subscribers = response.data.data
this.isLoading = false
})
}
}
</script>

View File

@@ -1,10 +1,12 @@
<template>
<div id="single-page">
<div id="page-content" v-if="! isLoading">
<div id="page-content">
<MobileHeader :title="$router.currentRoute.meta.title"/>
<PageHeader :title="$router.currentRoute.meta.title"/>
<div class="content-page">
<!--Table tools-->
<div class="table-tools">
<div class="buttons">
<router-link :to="{name: 'UserCreate'}">
@@ -13,11 +15,10 @@
</MobileActionButton>
</router-link>
</div>
<div class="searching">
</div>
</div>
<DatatableWrapper :paginator="true" :columns="columns" :data="users" class="table table-users">
<!--Datatable-->
<DatatableWrapper @init="isLoading = false" api="/api/users" :paginator="true" :columns="columns" class="table table-users">
<template slot-scope="{ row }">
<tr>
<td style="min-width: 320px">
@@ -113,7 +114,6 @@
data() {
return {
isLoading: true,
users: [],
columns: undefined,
}
},
@@ -133,34 +133,34 @@
this.columns = [
{
label: this.$t('admin_page_user.table.name'),
field: 'data.attributes.name',
field: 'name',
sortable: true
},
{
label: this.$t('admin_page_user.table.role'),
field: 'data.attributes.role',
field: 'role',
sortable: true
},
{
label: this.$t('admin_page_user.table.plan'),
field: 'data.attributes.subscription',
sortable: true,
field: 'subscription',
sortable: false,
hidden: ! this.config.isSaaS,
},
{
label: this.$t('admin_page_user.table.storage_used'),
field: 'relationships.storage.data.attributes.used',
field: 'used',
sortable: true
},
{
label: this.$t('admin_page_user.table.storage_capacity'),
field: 'relationships.storage.data.attributes.capacity',
field: 'settings.storage_capacity',
sortable: true,
hidden: ! this.config.storageLimit,
},
{
label: this.$t('admin_page_user.table.created_at'),
field: 'data.attributes.created_at_formatted',
field: 'created_at',
sortable: true
},
{
@@ -169,12 +169,6 @@
sortable: false
},
]
axios.get('/api/users')
.then(response => {
this.users = response.data.data
this.isLoading = false
})
}
}
</script>

View File

@@ -1,7 +1,15 @@
<template>
<PageTab :is-loading="isLoading" :class="{'form-fixed-width': ! isLoading && invoices.length === 0}">
<PageTabGroup v-if="invoices && invoices.length > 0">
<DatatableWrapper :paginator="true" :columns="columns" :data="invoices" class="table">
<PageTab :is-loading="isLoading">
<PageTabGroup>
<DatatableWrapper
@init="isLoading = false"
:api="'/api/users/' + this.$route.params.id + '/invoices'"
:paginator="false"
:columns="columns"
class="table"
>
<!--Table data content-->
<template slot-scope="{ row }">
<tr>
<td>
@@ -33,11 +41,15 @@
</td>
</tr>
</template>
<!--Empty page-->
<template v-slot:empty-page>
<InfoBox class="form-fixed-width">
<p>{{ $t('admin_page_user.invoices.empty') }}</p>
</InfoBox>
</template>
</DatatableWrapper>
</PageTabGroup>
<InfoBox v-else>
<p>{{ $t('admin_page_user.invoices.empty') }}</p>
</InfoBox>
</PageTab>
</template>
@@ -63,27 +75,26 @@
data() {
return {
isLoading: true,
invoices: undefined,
columns: [
{
label: this.$t('admin_page_invoices.table.number'),
field: 'data.attributes.order',
sortable: true
sortable: false
},
{
label: this.$t('admin_page_invoices.table.total'),
field: 'data.attributes.bag.amount',
sortable: true
sortable: false
},
{
label: this.$t('admin_page_invoices.table.plan'),
field: 'data.attributes.bag.amount',
sortable: true
sortable: false
},
{
label: this.$t('admin_page_invoices.table.payed'),
field: 'data.attributes.created_at',
sortable: true
sortable: false
},
{
label: this.$t('admin_page_user.table.action'),
@@ -92,13 +103,6 @@
],
}
},
created() {
axios.get('/api/users/' + this.$route.params.id + '/invoices')
.then(response => {
this.invoices = response.data.data
this.isLoading = false
})
}
}
</script>

View File

@@ -1,6 +1,6 @@
<template>
<PageTab :is-loading="isLoading" class="form-fixed-width">
<PageTabGroup v-if="subscription">
<PageTabGroup v-if="subscription && !isLoading">
<FormLabel>
{{ $t('user_subscription.title') }}
</FormLabel>
@@ -27,7 +27,7 @@
</ListInfo>
</div>
</PageTabGroup>
<PageTabGroup v-if="! subscription">
<PageTabGroup v-if="! subscription && !isLoading">
<InfoBox>
<p>{{ $t('admin_page_user.subscription.empty') }}</p>
</InfoBox>

View File

@@ -358,6 +358,7 @@
this.errorMessage = error.response.data.message
}
// Show server error
if (error.response.status === 500) {
this.isError = true
this.errorMessage = error.response.data.message
@@ -452,16 +453,22 @@
// Get setup intent for stripe
axios.get('/api/stripe/setup-intent')
.then(response => this.clientSecret = response.data.client_secret)
.then(response => {
this.clientSecret = response.data.client_secret
})
.catch(() => this.$isSomethingWrong())
axios.get('/api/user/payments')
.then(response => {
this.defaultPaymentMethod = response.data.default
this.PaymentMethods = response.data.others
this.isLoading = false
})
.catch(() => this.$isSomethingWrong())
.finally(() => {
this.isLoading = false
}
)
}
}
</script>

View File

@@ -1,8 +1,10 @@
<template>
<PageTab :is-loading="isLoading">
<PageTabGroup v-if="invoices && invoices.length > 0">
<PageTabGroup v-show="! isLoading">
<FormLabel>{{ $t('user_invoices.title') }}</FormLabel>
<DatatableWrapper :paginator="true" :columns="columns" :data="invoices" class="table">
<DatatableWrapper @init="isLoading = false" api="/api/user/invoices" :paginator="false" :columns="columns" class="table">
<!--Table data content-->
<template slot-scope="{ row }">
<tr>
<td>
@@ -34,11 +36,15 @@
</td>
</tr>
</template>
<!--Empty page-->
<template v-slot:empty-page>
<InfoBox>
<p>{{ $t('user_invoices.empty') }}</p>
</InfoBox>
</template>
</DatatableWrapper>
</PageTabGroup>
<InfoBox v-else>
<p>{{ $t('user_invoices.empty') }}</p>
</InfoBox>
</PageTab>
</template>
@@ -69,22 +75,22 @@
{
label: this.$t('rows.invoice.number'),
field: 'data.attributes.order',
sortable: true
sortable: false
},
{
label: this.$t('rows.invoice.total'),
field: 'data.attributes.bag.amount',
sortable: true
sortable: false
},
{
label: this.$t('rows.invoice.plan'),
field: 'data.attributes.bag.amount',
sortable: true
sortable: false
},
{
label: this.$t('rows.invoice.payed'),
field: 'data.attributes.created_at',
sortable: true
sortable: false
},
{
label: this.$t('admin_page_user.table.action'),
@@ -93,13 +99,6 @@
],
}
},
created() {
axios.get('/api/user/invoices')
.then(response => {
this.invoices = response.data.data
this.isLoading = false
})
}
}
</script>

View File

@@ -1,15 +1,23 @@
<template>
<PageTab :is-loading="isLoading">
<PageTabGroup v-if="PaymentMethods && PaymentMethods.length > 0">
<PageTabGroup>
<!--Page title-->
<FormLabel>{{ $t('user_payments.title') }}</FormLabel>
<div class="page-actions">
<!--Add payment method button-->
<div class="page-actions" v-if="PaymentMethods && PaymentMethods.length > 0">
<router-link :to="{name: 'CreatePaymentMethod'}">
<MobileActionButton icon="credit-card">
{{ $t('user_payments.add_card') }}
</MobileActionButton>
</router-link>
</div>
<DatatableWrapper :paginator="false" :columns="columns" :data="PaymentMethods" class="table">
<!--Payment methods table-->
<DatatableWrapper v-if="PaymentMethods" :table-data="{data: PaymentMethods}" :paginator="false" :columns="columns" class="table">
<!--Table data content-->
<template slot-scope="{ row }">
<tr :class="{'is-deleting': row.data.attributes.card_id === deletingID}">
<td style="width: 300px">
@@ -24,11 +32,6 @@
</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 }}
@@ -46,11 +49,15 @@
</td>
</tr>
</template>
<!--Empty page-->
<template v-slot:empty-page>
<InfoBox>
<p>{{ $t('user_payments.empty') }} <router-link v-if="user.data.attributes.stripe_customer" :to="{name: 'CreatePaymentMethod'}">Add new payment method.</router-link> </p>
</InfoBox>
</template>
</DatatableWrapper>
</PageTabGroup>
<InfoBox v-else>
<p>{{ $t('user_payments.empty') }} <router-link v-if="user.data.attributes.stripe_customer" :to="{name: 'CreatePaymentMethod'}">Add new payment method.</router-link> </p>
</InfoBox>
</PageTab>
</template>
@@ -92,17 +99,12 @@
{
label: this.$t('rows.card.number'),
field: 'data.attributes.total',
sortable: true
sortable: false
},
/*{
label: this.$t('rows.card.status'),
field: 'data.attributes.status',
sortable: true
},*/
{
label: this.$t('rows.card.expiration'),
field: 'data.attributes.total',
sortable: true
sortable: false
},
{
label: this.$t('admin_page_user.table.action'),
@@ -127,19 +129,6 @@
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: this.$t('popup_set_card.title'),
@@ -166,19 +155,19 @@
.then(response => {
if (response.status == 204) {
this.PaymentMethods = []
this.PaymentMethods = {}
}
if (response.status == 200) {
this.defaultPaymentCard = response.data.default
this.PaymentMethods = response.data.others.data
this.PaymentMethods.push(response.data.default)
}
}).finally(() => {
this.isLoading = false
})
}
)
}
},
created() {

View File

@@ -1,6 +1,6 @@
<template>
<PageTab :is-loading="isLoading">
<PageTabGroup v-if="subscription">
<PageTabGroup v-if="subscription && !isLoading">
<FormLabel>
{{ $t('user_subscription.title') }}
</FormLabel>
@@ -45,7 +45,7 @@
</div>
</div>
</PageTabGroup>
<InfoBox v-else>
<InfoBox v-if="! subscription && !isLoading">
<p>{{ $t('user_subscription.empty') }}</p>
</InfoBox>
</PageTab>

View File

@@ -52,4 +52,8 @@ return [
'col_amount' => 'Amount',
'total' => 'Total',
// OG Page
'user_sending' => ':name is sending you this file',
'protected_file' => 'This link is protected by password',
];

View File

@@ -52,4 +52,8 @@ return [
'col_amount' => 'Amount',
'total' => 'Total',
// OG Page
'user_sending' => ':name is sending you this file',
'protected_file' => 'This link is protected by password',
];

View File

@@ -52,4 +52,8 @@ return [
'col_amount' => 'Čiastka',
'total' => 'Spolu',
// OG Page
'user_sending' => ':name ti posiela súbor.',
'protected_file' => 'Tento link je chránený heslom',
];

View File

@@ -15,7 +15,6 @@
.error-message {
position: absolute;
left: 0;
top: 50px;
}
}
}

View File

@@ -11,6 +11,12 @@
{{--StyleSheet--}}
<link href="{{ asset('css/app.css') }}?v={{ get_version() }}" rel="stylesheet">
{{--OG items--}}
<meta property="og:url" content="{{ url('/') }}">
<meta property="og:title" content="{{ isset($settings->app_title) && $settings->app_title ? $settings->app_title : 'VueFileManager' }} | {{ isset($settings->app_description) && $settings->app_description ? $settings->app_description : __('vuefilemanager.app_description') }}">
<meta property="og:description" content="{{ isset($settings->app_description) && $settings->app_description ? $settings->app_description : __('vuefilemanager.app_description') }}">
<meta property="og:image" content="{{ asset('assets/images/vuefilemanager-og-image.jpg') }}">
{{-- Apple Mobile Web App--}}
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="white">

Some files were not shown because too many files have changed in this diff Show More