mirror of
https://github.com/crater-invoice/crater.git
synced 2025-10-27 11:41:09 -04:00
Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3158955d00 | |||
| 00a218fe87 | |||
| 4a92469465 | |||
| cd67ebd487 | |||
| 6853d0bb57 | |||
| 9afc19268a | |||
| f6faf10d43 | |||
| b061102c23 | |||
| e8b8cd9f04 | |||
| 9f4b5e02e8 | |||
| 1c84c1f843 | |||
| 23beb5cac7 | |||
| 2fb2f1fcc0 | |||
| 7c1529f890 | |||
| 8b54b48805 | |||
| 07601a7130 | |||
| 7fd1224502 | |||
| 1d56bd9f36 | |||
| a8783cc929 | |||
| 841ff06005 | |||
| 3a54167ec7 | |||
| bc18f9b198 | |||
| 6741f9849d | |||
| 14c0f7f486 | |||
| 095f3c0b31 | |||
| f2ad8286d7 | |||
| 18ad27d513 | |||
| 06807275b9 | |||
| 2ad39bfbd6 | |||
| 9f38947fe7 | |||
| 81c1e78bed | |||
| dd0817709b | |||
| 66a1156d5c | |||
| 1be2c7122e | |||
| e9218e4d21 | |||
| 812afc2dab | |||
| 4ec34550ed | |||
| ca6ab997de | |||
| 8b05909b74 | |||
| 1e6372cbb1 | |||
| d29c343911 | |||
| f3ef51bffb |
@ -227,4 +227,32 @@ class CompanyController extends Controller
|
||||
'success' => true
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload the Admin Avatar to public storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function uploadAdminAvatar(Request $request)
|
||||
{
|
||||
$data = json_decode($request->admin_avatar);
|
||||
|
||||
if($data) {
|
||||
$user = auth()->user();
|
||||
|
||||
if($user) {
|
||||
$user->clearMediaCollection('admin_avatar');
|
||||
|
||||
$user->addMediaFromBase64($data->data)
|
||||
->usingFileName($data->name)
|
||||
->toMediaCollection('admin_avatar');
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'user' => $user,
|
||||
'success' => true
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,6 +160,7 @@ class CustomersController extends Controller
|
||||
if ($verifyEmail) {
|
||||
if ($verifyEmail->id !== $customer->id) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'error' => 'Email already in use'
|
||||
]);
|
||||
}
|
||||
|
||||
@ -99,6 +99,32 @@ class OnboardingController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function uploadAdminAvatar(Request $request)
|
||||
{
|
||||
$setting = Setting::getSetting('profile_complete');
|
||||
|
||||
if ($setting == '1' || $setting == 'COMPLETED') {
|
||||
return response()->json(['error' => 'Profile already created.']);
|
||||
}
|
||||
$data = json_decode($request->admin_avatar);
|
||||
|
||||
if($data) {
|
||||
$user = User::find($data->id);
|
||||
if($user) {
|
||||
$user->clearMediaCollection('admin_avatar');
|
||||
|
||||
$user->addMediaFromBase64($data->data)
|
||||
->usingFileName($data->name)
|
||||
->toMediaCollection('admin_avatar');
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'user' => $user,
|
||||
'success' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function adminCompany(CompanyRequest $request)
|
||||
{
|
||||
$setting = Setting::getSetting('profile_complete');
|
||||
|
||||
@ -83,8 +83,7 @@ class ReportController extends Controller
|
||||
{
|
||||
$company = Company::where('unique_hash', $hash)->first();
|
||||
|
||||
$items = InvoiceItem::with('item')
|
||||
->whereCompany($company->id)
|
||||
$items = InvoiceItem::whereCompany($company->id)
|
||||
->applyInvoiceFilters($request->only(['from_date', 'to_date']))
|
||||
->itemAttributes()
|
||||
->get();
|
||||
|
||||
@ -36,7 +36,6 @@ class CustomerRequest extends FormRequest
|
||||
'name' => 'required',
|
||||
'addresses.*.address_street_1' => 'max:255',
|
||||
'addresses.*.address_street_2' => 'max:255',
|
||||
'email' => 'email|nullable|unique:users,email,'.$this->id
|
||||
];
|
||||
break;
|
||||
default:
|
||||
|
||||
@ -25,11 +25,11 @@ class DatabaseEnvironmentRequest extends FormRequest
|
||||
{
|
||||
return [
|
||||
'app_url' => 'required|url',
|
||||
'database_connection' => 'required|string|max:50',
|
||||
'database_hostname' => 'required|string|max:50',
|
||||
'database_connection' => 'required|string',
|
||||
'database_hostname' => 'required|string',
|
||||
'database_port' => 'required|numeric',
|
||||
'database_name' => 'required|string|max:50',
|
||||
'database_username' => 'required|string|max:50',
|
||||
'database_name' => 'required|string',
|
||||
'database_username' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,55 +26,55 @@ class MailEnvironmentRequest extends FormRequest
|
||||
switch ($this->get('mail_driver')) {
|
||||
case 'smtp':
|
||||
return [
|
||||
'mail_driver' => 'required|string|max:50',
|
||||
'mail_host' => 'required|string|max:50',
|
||||
'mail_port' => 'required|max:50',
|
||||
'mail_driver' => 'required|string',
|
||||
'mail_host' => 'required|string',
|
||||
'mail_port' => 'required',
|
||||
'mail_username' => 'required|string',
|
||||
'mail_password' => 'required|string',
|
||||
'mail_encryption' => 'required|string|max:50',
|
||||
'from_name' => 'required|string|max:50',
|
||||
'from_mail' => 'required|string|max:50',
|
||||
'mail_encryption' => 'required|string',
|
||||
'from_name' => 'required|string',
|
||||
'from_mail' => 'required|string',
|
||||
];
|
||||
break;
|
||||
|
||||
case 'mailgun':
|
||||
return [
|
||||
'mail_driver' => 'required|string|max:50',
|
||||
'mail_host' => 'required|string|max:50',
|
||||
'mail_port' => 'required|max:50',
|
||||
'mail_driver' => 'required|string',
|
||||
'mail_host' => 'required|string',
|
||||
'mail_port' => 'required',
|
||||
'mail_mailgun_domain' => 'required|string',
|
||||
'mail_mailgun_secret' => 'required|string',
|
||||
'mail_mailgun_endpoint' => 'required|string',
|
||||
'mail_encryption' => 'required|string|max:50',
|
||||
'from_name' => 'required|string|max:50',
|
||||
'from_mail' => 'required|string|max:50',
|
||||
'mail_encryption' => 'required|string',
|
||||
'from_name' => 'required|string',
|
||||
'from_mail' => 'required|string',
|
||||
];
|
||||
break;
|
||||
|
||||
case 'ses':
|
||||
return [
|
||||
'mail_driver' => 'required|string|max:50',
|
||||
'mail_host' => 'required|string|max:50',
|
||||
'mail_port' => 'required|max:50',
|
||||
'mail_driver' => 'required|string',
|
||||
'mail_host' => 'required|string',
|
||||
'mail_port' => 'required',
|
||||
'mail_ses_key' => 'required|string',
|
||||
'mail_ses_secret' => 'required|string',
|
||||
'mail_encryption' => 'required|string|max:50',
|
||||
'from_name' => 'required|string|max:50',
|
||||
'from_mail' => 'required|string|max:50',
|
||||
'mail_encryption' => 'required|string',
|
||||
'from_name' => 'required|string',
|
||||
'from_mail' => 'required|string',
|
||||
];
|
||||
break;
|
||||
|
||||
case 'mail':
|
||||
return [
|
||||
'from_name' => 'required|string|max:50',
|
||||
'from_mail' => 'required|string|max:50',
|
||||
'from_name' => 'required|string',
|
||||
'from_mail' => 'required|string',
|
||||
];
|
||||
break;
|
||||
|
||||
case 'sendmail':
|
||||
return [
|
||||
'from_name' => 'required|string|max:50',
|
||||
'from_mail' => 'required|string|max:50',
|
||||
'from_name' => 'required|string',
|
||||
'from_mail' => 'required|string',
|
||||
];
|
||||
break;
|
||||
}
|
||||
|
||||
@ -78,7 +78,8 @@ class InvoiceItem extends Model
|
||||
public function scopeItemAttributes($query)
|
||||
{
|
||||
$query->select(
|
||||
DB::raw('sum(quantity) as total_quantity, sum(total) as total_amount, item_id')
|
||||
)->groupBy('item_id');
|
||||
DB::raw('sum(quantity) as total_quantity, sum(total) as total_amount, invoice_items.name')
|
||||
)->groupBy('invoice_items.name');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,10 +12,10 @@ class Listener
|
||||
* @param $event
|
||||
* @return boolean
|
||||
*/
|
||||
protected function check($event)
|
||||
protected function isListenerFired($event)
|
||||
{
|
||||
// Do not apply to the same or newer versions
|
||||
if (version_compare(static::VERSION, $event->old, '>')) {
|
||||
if (version_compare(static::VERSION, $event->old, '<')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
115
app/Listeners/Updates/v1/Version110.php
Normal file
115
app/Listeners/Updates/v1/Version110.php
Normal file
@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
namespace Crater\Listeners\Updates\v1;
|
||||
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Crater\Events\UpdateFinished;
|
||||
use Crater\Listeners\Updates\Listener;
|
||||
use Crater\Setting;
|
||||
use Crater\Currency;
|
||||
|
||||
class Version110 extends Listener
|
||||
{
|
||||
const VERSION = '1.1.0';
|
||||
|
||||
/**
|
||||
* Create the event listener.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param object $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle(UpdateFinished $event)
|
||||
{
|
||||
if ($this->isListenerFired($event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add currencies
|
||||
$this->addCurrencies();
|
||||
|
||||
// Update Crater app version
|
||||
Setting::setSetting('version', static::VERSION);
|
||||
}
|
||||
|
||||
private function addCurrencies() {
|
||||
|
||||
$currencies = [
|
||||
'13' => [
|
||||
'symbol' => 'S$'
|
||||
],
|
||||
'16' => [
|
||||
'symbol' => '₫'
|
||||
],
|
||||
'17' => [
|
||||
'symbol' => 'Fr.'
|
||||
],
|
||||
'21' => [
|
||||
'symbol' => '฿'
|
||||
],
|
||||
'22' => [
|
||||
'symbol' => '₦'
|
||||
],
|
||||
'26' => [
|
||||
'symbol' => 'HK$'
|
||||
],
|
||||
'35' => [
|
||||
'symbol' => 'NAƒ'
|
||||
],
|
||||
'38' => [
|
||||
'symbol' => 'GH₵'
|
||||
],
|
||||
'39' => [
|
||||
'symbol' => 'Лв.'
|
||||
],
|
||||
'42' => [
|
||||
'symbol' => 'RON'
|
||||
],
|
||||
'44' => [
|
||||
'symbol' => 'SِAR'
|
||||
],
|
||||
'46' => [
|
||||
'symbol' => 'Rf'
|
||||
],
|
||||
'47' => [
|
||||
'symbol' => '₡'
|
||||
],
|
||||
'54' => [
|
||||
'symbol' => 'د.ت'
|
||||
],
|
||||
'55' => [
|
||||
'symbol' => '₽'
|
||||
],
|
||||
'57' => [
|
||||
'symbol' => 'ر.ع.'
|
||||
],
|
||||
'58' => [
|
||||
'symbol' => '₴'
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
foreach ($currencies as $key => $currency) {
|
||||
Currency::updateOrCreate(['id' => $key], $currency);
|
||||
}
|
||||
|
||||
Currency::create([
|
||||
'name' => 'Kuwaiti Dinar',
|
||||
'code' => 'KWD',
|
||||
'symbol' => 'KWD ',
|
||||
'precision' => '3',
|
||||
'thousand_separator' => ',',
|
||||
'decimal_separator' => '.'
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -5,6 +5,7 @@ use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvi
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
|
||||
use Crater\Events\UpdateFinished;
|
||||
use Crater\Listeners\Updates\v1\Version110;
|
||||
|
||||
class EventServiceProvider extends ServiceProvider
|
||||
{
|
||||
@ -15,6 +16,7 @@ class EventServiceProvider extends ServiceProvider
|
||||
*/
|
||||
protected $listen = [
|
||||
UpdateFinished::class=> [
|
||||
Version110::class,
|
||||
],
|
||||
Registered::class => [
|
||||
SendEmailVerificationNotification::class,
|
||||
|
||||
@ -54,7 +54,7 @@ class EnvironmentManager
|
||||
'error' => 'connection_failed'
|
||||
];
|
||||
} else {
|
||||
if(count(DB::connection()->select('SHOW TABLES'))) {
|
||||
if(\Schema::hasTable('users') ) {
|
||||
return [
|
||||
'error' => 'database_should_be_empty'
|
||||
];
|
||||
|
||||
12
app/User.php
12
app/User.php
@ -51,7 +51,8 @@ class User extends Authenticatable implements HasMedia
|
||||
];
|
||||
|
||||
protected $appends = [
|
||||
'formattedCreatedAt'
|
||||
'formattedCreatedAt',
|
||||
'avatar'
|
||||
];
|
||||
|
||||
/**
|
||||
@ -244,4 +245,13 @@ class User extends Authenticatable implements HasMedia
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getAvatarAttribute()
|
||||
{
|
||||
$avatar = $this->getMedia('admin_avatar')->first();
|
||||
if ($avatar) {
|
||||
return asset($avatar->getUrl());
|
||||
}
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,11 +4,11 @@ return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Crater Requirements
|
||||
| Crater Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
*/
|
||||
|
||||
'version' => '1.0.0',
|
||||
'version' => '1.1.0',
|
||||
|
||||
];
|
||||
|
||||
@ -122,7 +122,7 @@ class CurrenciesTableSeeder extends Seeder
|
||||
[
|
||||
'name' => 'Singapore Dollar',
|
||||
'code' => 'SGD',
|
||||
'symbol' => '',
|
||||
'symbol' => 'S$',
|
||||
'precision' => '2',
|
||||
'thousand_separator' => ',',
|
||||
'decimal_separator' => '.'
|
||||
@ -147,7 +147,7 @@ class CurrenciesTableSeeder extends Seeder
|
||||
[
|
||||
'name' => 'Vietnamese Dong',
|
||||
'code' => 'VND',
|
||||
'symbol' => '',
|
||||
'symbol' => '₫',
|
||||
'precision' => '0',
|
||||
'thousand_separator' => '.',
|
||||
'decimal_separator' => ','
|
||||
@ -155,7 +155,7 @@ class CurrenciesTableSeeder extends Seeder
|
||||
[
|
||||
'name' => 'Swiss Franc',
|
||||
'code' => 'CHF',
|
||||
'symbol' => '',
|
||||
'symbol' => 'Fr.',
|
||||
'precision' => '2',
|
||||
'thousand_separator' => '\'',
|
||||
'decimal_separator' => '.'
|
||||
@ -187,7 +187,7 @@ class CurrenciesTableSeeder extends Seeder
|
||||
[
|
||||
'name' => 'Thai Baht',
|
||||
'code' => 'THB',
|
||||
'symbol' => '',
|
||||
'symbol' => '฿',
|
||||
'precision' => '2',
|
||||
'thousand_separator' => ',',
|
||||
'decimal_separator' => '.'
|
||||
@ -195,7 +195,7 @@ class CurrenciesTableSeeder extends Seeder
|
||||
[
|
||||
'name' => 'Nigerian Naira',
|
||||
'code' => 'NGN',
|
||||
'symbol' => '',
|
||||
'symbol' => '₦',
|
||||
'precision' => '2',
|
||||
'thousand_separator' => ',',
|
||||
'decimal_separator' => '.'
|
||||
@ -227,7 +227,7 @@ class CurrenciesTableSeeder extends Seeder
|
||||
[
|
||||
'name' => 'Hong Kong Dollar',
|
||||
'code' => 'HKD',
|
||||
'symbol' => '',
|
||||
'symbol' => 'HK$',
|
||||
'precision' => '2',
|
||||
'thousand_separator' => ',',
|
||||
'decimal_separator' => '.'
|
||||
@ -299,7 +299,7 @@ class CurrenciesTableSeeder extends Seeder
|
||||
[
|
||||
'name' => 'Netherlands Antillean Guilder',
|
||||
'code' => 'ANG',
|
||||
'symbol' => '',
|
||||
'symbol' => 'NAƒ',
|
||||
'precision' => '2',
|
||||
'thousand_separator' => '.',
|
||||
'decimal_separator' => ','
|
||||
@ -323,7 +323,7 @@ class CurrenciesTableSeeder extends Seeder
|
||||
[
|
||||
'name' => 'Ghanaian Cedi',
|
||||
'code' => 'GHS',
|
||||
'symbol' => '',
|
||||
'symbol' => 'GH₵',
|
||||
'precision' => '2',
|
||||
'thousand_separator' => ',',
|
||||
'decimal_separator' => '.'
|
||||
@ -331,7 +331,7 @@ class CurrenciesTableSeeder extends Seeder
|
||||
[
|
||||
'name' => 'Bulgarian Lev',
|
||||
'code' => 'BGN',
|
||||
'symbol' => '',
|
||||
'symbol' => 'Лв.',
|
||||
'precision' => '2',
|
||||
'thousand_separator' => ' ',
|
||||
'decimal_separator' => '.'
|
||||
@ -355,7 +355,7 @@ class CurrenciesTableSeeder extends Seeder
|
||||
[
|
||||
'name' => 'Romanian New Leu',
|
||||
'code' => 'RON',
|
||||
'symbol' => '',
|
||||
'symbol' => 'RON',
|
||||
'precision' => '2',
|
||||
'thousand_separator' => ',',
|
||||
'decimal_separator' => '.'
|
||||
@ -371,7 +371,7 @@ class CurrenciesTableSeeder extends Seeder
|
||||
[
|
||||
'name' => 'Saudi Riyal',
|
||||
'code' => 'SAR',
|
||||
'symbol' => '',
|
||||
'symbol' => 'SِAR',
|
||||
'precision' => '2',
|
||||
'thousand_separator' => ',',
|
||||
'decimal_separator' => '.'
|
||||
@ -387,7 +387,7 @@ class CurrenciesTableSeeder extends Seeder
|
||||
[
|
||||
'name' => 'Maldivian Rufiyaa',
|
||||
'code' => 'MVR',
|
||||
'symbol' => '',
|
||||
'symbol' => 'Rf',
|
||||
'precision' => '2',
|
||||
'thousand_separator' => ',',
|
||||
'decimal_separator' => '.'
|
||||
@ -395,7 +395,7 @@ class CurrenciesTableSeeder extends Seeder
|
||||
[
|
||||
'name' => 'Costa Rican Colón',
|
||||
'code' => 'CRC',
|
||||
'symbol' => '',
|
||||
'symbol' => '₡',
|
||||
'precision' => '2',
|
||||
'thousand_separator' => ',',
|
||||
'decimal_separator' => '.'
|
||||
@ -454,7 +454,7 @@ class CurrenciesTableSeeder extends Seeder
|
||||
[
|
||||
'name' => 'Tunisian Dinar',
|
||||
'code' => 'TND',
|
||||
'symbol' => '',
|
||||
'symbol' => 'د.ت',
|
||||
'precision' => '2',
|
||||
'thousand_separator' => ',',
|
||||
'decimal_separator' => '.'
|
||||
@ -462,7 +462,7 @@ class CurrenciesTableSeeder extends Seeder
|
||||
[
|
||||
'name' => 'Russian Ruble',
|
||||
'code' => 'RUB',
|
||||
'symbol' => '',
|
||||
'symbol' => '₽',
|
||||
'precision' => '2',
|
||||
'thousand_separator' => ',',
|
||||
'decimal_separator' => '.'
|
||||
@ -479,7 +479,7 @@ class CurrenciesTableSeeder extends Seeder
|
||||
[
|
||||
'name' => 'Omani Rial',
|
||||
'code' => 'OMR',
|
||||
'symbol' => '',
|
||||
'symbol' => 'ر.ع.',
|
||||
'precision' => '2',
|
||||
'thousand_separator' => ',',
|
||||
'decimal_separator' => '.'
|
||||
@ -487,7 +487,7 @@ class CurrenciesTableSeeder extends Seeder
|
||||
[
|
||||
'name' => 'Ukrainian Hryvnia',
|
||||
'code' => 'UAH',
|
||||
'symbol' => '',
|
||||
'symbol' => '₴',
|
||||
'precision' => '2',
|
||||
'thousand_separator' => ',',
|
||||
'decimal_separator' => '.'
|
||||
|
||||
2
public/assets/css/crater.css
vendored
2
public/assets/css/crater.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
BIN
public/images/default-avatar.jpg
Normal file
BIN
public/images/default-avatar.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
@ -1,4 +1,4 @@
|
||||
{
|
||||
"/assets/js/app.js": "/assets/js/app.js?id=0de16e5183b0d24fd95d",
|
||||
"/assets/css/crater.css": "/assets/css/crater.css?id=361d275866b6299acb36"
|
||||
"/assets/js/app.js": "/assets/js/app.js?id=af9e4f2aa907d480466c",
|
||||
"/assets/css/crater.css": "/assets/css/crater.css?id=9d83f53c3915b38417e0"
|
||||
}
|
||||
|
||||
@ -603,6 +603,7 @@ export default {
|
||||
updated_message: 'Company information updated successfully'
|
||||
},
|
||||
account_settings: {
|
||||
profile_picture: 'Profile Picture',
|
||||
name: 'Name',
|
||||
email: 'Email',
|
||||
password: 'Password',
|
||||
@ -688,7 +689,7 @@ export default {
|
||||
update: 'Update Now',
|
||||
update_progress: 'Update in progress...',
|
||||
progress_text: 'It will just take a few minutes. Please do not refresh the screen or close the window before the update finishes',
|
||||
update_success: 'App has been updated successfully',
|
||||
update_success: 'App has been updated! Please wait while your browser window gets reloaded automatically.',
|
||||
latest_message: 'No update available! You are on the latest version.',
|
||||
current_version: 'Current Version'
|
||||
}
|
||||
@ -739,6 +740,8 @@ export default {
|
||||
},
|
||||
permissions: {
|
||||
permissions: 'Permissions',
|
||||
permission_confirm_title: 'Are you sure you want to continue?',
|
||||
permission_confirm_desc: 'Folder permission check failed',
|
||||
permission_desc: 'Below is the list of folder permissions which are required in order for the app to work. If the permission check fails, make sure to update your folder permissions.'
|
||||
},
|
||||
mail: {
|
||||
|
||||
@ -599,6 +599,7 @@ export default {
|
||||
updated_message: 'Información de la empresa actualizada con éxito'
|
||||
},
|
||||
account_settings: {
|
||||
profile_picture: 'Foto de perfil',
|
||||
name: 'Nombre',
|
||||
email: 'Email',
|
||||
password: 'Contraseña',
|
||||
@ -684,7 +685,7 @@ export default {
|
||||
update: 'Actualizar',
|
||||
update_progress: 'Actualización en progreso...',
|
||||
progress_text: 'Solo tomará unos minutos. No actualice la pantalla ni cierre la ventana antes de que finalice la actualización.',
|
||||
update_success: 'La aplicación se actualizó correctamente',
|
||||
update_success: '¡La aplicación ha sido actualizada! Espere mientras la ventana de su navegador se vuelve a cargar automáticamente.',
|
||||
latest_message: '¡Actualización no disponible! Estás en la última versión.',
|
||||
current_version: 'Versión actual'
|
||||
}
|
||||
@ -734,6 +735,8 @@ export default {
|
||||
},
|
||||
permissions: {
|
||||
permissions: 'Permisos',
|
||||
permission_confirm_title: 'Estás seguro de que quieres continuar?',
|
||||
permission_confirm_desc: 'Error de verificación de permisos de carpeta',
|
||||
permission_desc: 'A continuación se muestra la lista de permisos de carpeta necesarios para que la aplicación funcione. Si la verificación de permisos falla, asegúrese de actualizar los permisos de su carpeta.'
|
||||
},
|
||||
mail: {
|
||||
|
||||
@ -599,6 +599,7 @@ export default {
|
||||
updated_message: 'Informations sur la société mises à jour avec succès'
|
||||
},
|
||||
account_settings: {
|
||||
profile_picture: 'Image de profil',
|
||||
name: 'Nom',
|
||||
email: 'Email',
|
||||
password: 'Mot de passe',
|
||||
@ -687,7 +688,7 @@ export default {
|
||||
update: 'Mettre à jour maintenant',
|
||||
update_progress: 'Mise à jour en cours...',
|
||||
progress_text: "Cela ne prendra que quelques minutes. S'il vous plaît ne pas actualiser l'écran ou fermer la fenêtre avant la fin de la mise à jour",
|
||||
update_success: "L'application a été mise à jour avec succès",
|
||||
update_success: 'App a été mis à jour! Veuillez patienter pendant le rechargement automatique de la fenêtre de votre navigateur.',
|
||||
latest_message: 'Pas de mise a jour disponible! Vous êtes sur la dernière version.',
|
||||
current_version: 'Version actuelle'
|
||||
}
|
||||
@ -737,6 +738,8 @@ export default {
|
||||
},
|
||||
permissions: {
|
||||
permissions: 'Les permissions',
|
||||
permission_confirm_title: 'Es-tu sur de vouloir continuer?',
|
||||
permission_confirm_desc: 'La vérification de l\'autorisation du dossier a échoué',
|
||||
permission_desc: "Vous trouverez ci-dessous la liste des autorisations de dossier requises pour le fonctionnement de l'application. Si la vérification des autorisations échoue, veillez à mettre à jour vos autorisations de dossier."
|
||||
},
|
||||
mail: {
|
||||
|
||||
@ -36,7 +36,9 @@ export const addCustomer = ({ commit, dispatch, state }, data) => {
|
||||
export const updateCustomer = ({ commit, dispatch, state }, data) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
window.axios.put(`/api/customers/${data.id}`, data).then((response) => {
|
||||
commit(types.UPDATE_CUSTOMER, response.data)
|
||||
if(response.data.success){
|
||||
commit(types.UPDATE_CUSTOMER, response.data)
|
||||
}
|
||||
resolve(response)
|
||||
}).catch((err) => {
|
||||
reject(err)
|
||||
|
||||
@ -12,11 +12,7 @@ export const loadData = ({ commit, dispatch, state }, id) => {
|
||||
|
||||
export const editCompany = ({ commit, dispatch, state }, data) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
window.axios.post('/api/settings/company', data, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
}).then((response) => {
|
||||
window.axios.post('/api/settings/company', data).then((response) => {
|
||||
// commit(types.UPDATE_ITEM, response.data)
|
||||
resolve(response)
|
||||
}).catch((err) => {
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
// import * as types from './mutation-types'
|
||||
import * as types from './mutation-types'
|
||||
|
||||
export const loadData = ({ commit, dispatch, state }, id) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
window.axios.get(`/api/settings/profile`).then((response) => {
|
||||
commit(types.SET_USER, response.data)
|
||||
resolve(response)
|
||||
}).catch((err) => {
|
||||
reject(err)
|
||||
@ -13,7 +14,29 @@ export const loadData = ({ commit, dispatch, state }, id) => {
|
||||
export const editUser = ({ commit, dispatch, state }, data) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
window.axios.put('/api/settings/profile', data).then((response) => {
|
||||
// commit(types.UPDATE_USER, response.data)
|
||||
commit(types.UPDATE_USER, response.data)
|
||||
resolve(response)
|
||||
}).catch((err) => {
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export const uploadOnboardAvatar = ({ commit, dispatch, state }, data) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
window.axios.post(`/api/admin/profile/upload-avatar`, data).then((response) => {
|
||||
commit(types.UPDATE_USER, response.data.user)
|
||||
resolve(response)
|
||||
}).catch((err) => {
|
||||
reject(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export const uploadAvatar = ({ commit, dispatch, state }, data) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
window.axios.post('/api/settings/profile/upload-avatar', data).then((response) => {
|
||||
commit(types.UPDATE_USER, response.data.user)
|
||||
resolve(response)
|
||||
}).catch((err) => {
|
||||
reject(err)
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
export const SET_USER = 'SET_USER'
|
||||
export const UPDATE_USER = 'UPDATE_USER'
|
||||
export const UPDATE_USER_AVATAR = 'UPDATE_USER_AVATAR'
|
||||
|
||||
@ -2,10 +2,14 @@ import * as types from './mutation-types'
|
||||
|
||||
export default {
|
||||
[types.SET_USER] (state, data) {
|
||||
state.user = data.user
|
||||
state.user = data
|
||||
},
|
||||
|
||||
[types.UPDATE_USER] (state, data) {
|
||||
state.user = data
|
||||
},
|
||||
|
||||
[types.UPDATE_USER_AVATAR] (state, data) {
|
||||
state.user.avatar = data.avatar
|
||||
}
|
||||
}
|
||||
|
||||
@ -586,11 +586,16 @@ export default {
|
||||
this.isLoading = true
|
||||
try {
|
||||
let response = await this.updateCustomer(this.formData)
|
||||
if (response.data) {
|
||||
if (response.data.success) {
|
||||
window.toastr['success'](this.$t('customers.updated_message'))
|
||||
this.$router.push('/admin/customers')
|
||||
this.isLoading = false
|
||||
return true
|
||||
} else {
|
||||
this.isLoading = false
|
||||
if (response.data.error) {
|
||||
window.toastr['error'](this.$t('validation.email_already_taken'))
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.response.data.errors.email) {
|
||||
|
||||
@ -353,7 +353,7 @@ export default {
|
||||
this.item.name = val
|
||||
},
|
||||
deselectItem () {
|
||||
this.item = {...EstimateStub, id: this.item.id}
|
||||
this.item = {...EstimateStub, id: this.item.id, taxes: [{...TaxStub, id: Guid.raw()}]}
|
||||
this.$nextTick(() => {
|
||||
this.$refs.itemSelect.$refs.baseSelect.$refs.search.focus()
|
||||
})
|
||||
|
||||
@ -354,7 +354,7 @@ export default {
|
||||
this.item.name = val
|
||||
},
|
||||
deselectItem () {
|
||||
this.item = {...InvoiceStub, id: this.item.id}
|
||||
this.item = {...InvoiceStub, id: this.item.id, taxes: [{...TaxStub, id: Guid.raw()}]}
|
||||
this.$nextTick(() => {
|
||||
this.$refs.itemSelect.$refs.baseSelect.$refs.search.focus()
|
||||
})
|
||||
|
||||
@ -58,7 +58,7 @@
|
||||
aria-expanded="false"
|
||||
class="avatar"
|
||||
>
|
||||
<img src="/images/avatar.png" alt="Avatar">
|
||||
<img :src="profilePicture" alt="Avatar">
|
||||
</a>
|
||||
<v-dropdown-item>
|
||||
<router-link class="dropdown-item" to="/admin/settings">
|
||||
@ -83,7 +83,25 @@
|
||||
import { mapGetters, mapActions } from 'vuex'
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapGetters('userProfile', [
|
||||
'user'
|
||||
]),
|
||||
profilePicture () {
|
||||
if (this.user && this.user.avatar !== null) {
|
||||
return this.user.avatar
|
||||
} else {
|
||||
return '/images/default-avatar.jpg'
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.loadData()
|
||||
},
|
||||
methods: {
|
||||
...mapActions('userProfile', [
|
||||
'loadData'
|
||||
]),
|
||||
...mapActions({
|
||||
companySelect: 'changeCompany'
|
||||
}),
|
||||
|
||||
@ -1,459 +0,0 @@
|
||||
<template>
|
||||
<div class="header-bottom">
|
||||
<div class="header-nav vue-dropdown-menu">
|
||||
<v-dropdown active-url="/admin/dashboard">
|
||||
<template slot="title">
|
||||
<a href="#">
|
||||
<i class="icon-fa icon-fa-dashboard"/>{{ $t('navigation.dashboard') }}
|
||||
<span class="icon-fa arrow icon-fa-fw"/>
|
||||
</a>
|
||||
</template>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/dashboard/basic">
|
||||
Basic
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/dashboard/ecommerce">
|
||||
Ecommerce
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/dashboard/finance">
|
||||
Finance
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
</v-dropdown>
|
||||
<v-dropdown active-url="/admin/layouts">
|
||||
<template slot="title">
|
||||
<a href="#">
|
||||
<i class="icon-fa icon-fa-th-large"/>Layouts
|
||||
<span class="icon-fa arrow icon-fa-fw"/>
|
||||
</a>
|
||||
</template>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/layouts/sidebar">
|
||||
Sidebar
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/layouts/horizontal">
|
||||
Horizontal
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/layouts/icons-sidebar">
|
||||
Icon Sidebar
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
</v-dropdown>
|
||||
<v-dropdown active-url="/admin/basic-ui">
|
||||
<template slot="title">
|
||||
<a href="#">
|
||||
<i class="icon-fa icon-fa-star"/>Basic UI
|
||||
<span class="icon-fa arrow icon-fa-fw"/>
|
||||
</a>
|
||||
</template>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/basic-ui/buttons">
|
||||
Buttons
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/basic-ui/cards">
|
||||
Cards
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/basic-ui/tabs">
|
||||
Tabs & Accordians
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/basic-ui/typography">
|
||||
Typography
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/basic-ui/tables">
|
||||
Tables
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/basic-ui/modals">
|
||||
Modals
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/basic-ui/progress-bars">
|
||||
Progress Bar
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
</v-dropdown>
|
||||
<v-dropdown active-url="/admin/components">
|
||||
<template slot="title">
|
||||
<a href="#">
|
||||
<i class="icon-fa icon-fa-puzzle-piece"/>Components
|
||||
<span class="icon-fa arrow icon-fa-fw"/>
|
||||
</a>
|
||||
</template>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/components/calendar">
|
||||
Calendar
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/components/datatables">
|
||||
Jquery Datatables
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/components/mail-box">
|
||||
MailBox
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/components/calendar">
|
||||
Calendar
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/components/datatables">
|
||||
Jquery Datatables
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/components/image-cropper">
|
||||
ImageCropper
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/components/image-zoom">
|
||||
ImageZoom
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/components/nestable-list">
|
||||
Nestable List
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/components/nestable-tree">
|
||||
Nestable Tree
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/components/notifications">
|
||||
Notifications
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown active-url="/admin/layouts">
|
||||
<template slot="title">
|
||||
<a href="#">
|
||||
<i class="icon-fa icon-fa-th-large"/>Layouts
|
||||
<span class="icon-fa arrow icon-fa-fw"/>
|
||||
</a>
|
||||
</template>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/layouts/sidebar">
|
||||
Sidebar
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/layouts/horizontal">
|
||||
Horizontal
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
</v-dropdown>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/components/sweet-modals">
|
||||
Sweet Modals
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/components/image-zoom">
|
||||
ImageZoom
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/components/mail-box">
|
||||
MailBox
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
</v-dropdown>
|
||||
<v-dropdown active-url="/admin/chart">
|
||||
<template slot="title">
|
||||
<a href="#">
|
||||
<i class="icon-fa icon-fa-bar-chart"/>Charts
|
||||
<span class="icon-fa arrow icon-fa-fw"/>
|
||||
</a>
|
||||
</template>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/charts/amchart">
|
||||
AM Charts
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/charts/chartjs">
|
||||
Chart JS
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/charts/gauge">
|
||||
Gauges
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/charts/morris">
|
||||
Morris
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/charts/sparkline">
|
||||
Sparkline
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
</v-dropdown>
|
||||
<v-dropdown active-url="/admin/icons">
|
||||
<template slot="title">
|
||||
<a href="#">
|
||||
<i class="icon-fa icon-fa-eye"/>Icons
|
||||
<span class="icon-fa arrow icon-fa-fw"/>
|
||||
</a>
|
||||
</template>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/icons/icomoon">
|
||||
IcoMoon
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/icons/fontawesome">
|
||||
Font Awesome
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
</v-dropdown>
|
||||
<v-dropdown active-url="/admin/forms">
|
||||
<template slot="title">
|
||||
<a href="#">
|
||||
<i class="icon-fa icon-fa-rocket"/>Form
|
||||
<span class="icon-fa arrow icon-fa-fw"/>
|
||||
</a>
|
||||
</template>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/forms/general">
|
||||
General Elements
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/forms/advanced">
|
||||
Advanced Elements
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item><v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/forms/layouts">
|
||||
Form Layouts
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/forms/validation">
|
||||
Form Validation
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/forms/wizards">
|
||||
Form Wizard
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/forms/wizards-2">
|
||||
Form Wizard 2
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/forms/wizards-3">
|
||||
Form Wizard 3
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/forms/editors">
|
||||
Editors
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/forms/vee">
|
||||
Vee Validate
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/forms/vuelidate">
|
||||
Vuelidate
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
</v-dropdown>
|
||||
<v-dropdown active-url="/admin/gallery">
|
||||
<template slot="title">
|
||||
<a href="#">
|
||||
<i class="icon-fa icon-fa-image"/>Gallery
|
||||
<span class="icon-fa arrow icon-fa-fw"/>
|
||||
</a>
|
||||
</template>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/gallery/grid">
|
||||
Grid
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/gallery/masonry-grid">
|
||||
Masonry Grid
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
</v-dropdown>
|
||||
<v-dropdown active-url="/admin/users">
|
||||
<template slot="title">
|
||||
<a href="#">
|
||||
<i class="icon-fa icon-fa-user"/>Users
|
||||
<span class="icon-fa arrow icon-fa-fw"/>
|
||||
</a>
|
||||
</template>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/users/profile">
|
||||
Profile
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
<v-dropdown-item>
|
||||
<template slot="item-title">
|
||||
<router-link to="/admin/users">
|
||||
All Users
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown-item>
|
||||
</v-dropdown>
|
||||
<v-dropdown active-url="/admin/todo-item">
|
||||
<template slot="title">
|
||||
<router-link to="/admin/todo-item">
|
||||
<i class="icon-fa icon-fa-check"/>Todos
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown>
|
||||
<v-dropdown active-url="/admin/settings">
|
||||
<template slot="title">
|
||||
<router-link to="/admin/settings">
|
||||
<i class="icon-fa icon-fa-cogs"/>Settings
|
||||
</router-link>
|
||||
</template>
|
||||
</v-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/babel">
|
||||
import VDropdown from '../../../components/dropdown/VDropdown'
|
||||
import VDropdownItem from '../../../components/dropdown/VDropdownItem'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
VDropdown,
|
||||
VDropdownItem
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
sidebar: 'sidebar'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -83,7 +83,7 @@
|
||||
:allow-empty="false"
|
||||
:disabled="isEdit"
|
||||
:placeholder="$t('invoices.select_invoice')"
|
||||
label="invoice_number"
|
||||
:custom-label="invoiceWithAmount"
|
||||
track-by="invoice_number"
|
||||
/>
|
||||
</div>
|
||||
@ -246,10 +246,10 @@ export default {
|
||||
watch: {
|
||||
customer (newValue) {
|
||||
this.formData.user_id = newValue.id
|
||||
this.invoice = null
|
||||
this.formData.amount = 0
|
||||
this.invoiceList = []
|
||||
if (!this.isEdit) {
|
||||
this.invoice = null
|
||||
this.formData.amount = 0
|
||||
this.invoiceList = []
|
||||
this.fetchCustomerInvoices(newValue.id)
|
||||
}
|
||||
},
|
||||
@ -263,9 +263,6 @@ export default {
|
||||
}
|
||||
},
|
||||
async mounted () {
|
||||
// if (!this.$route.params.id) {
|
||||
// this.$refs.baseSelect.$refs.search.focus()
|
||||
// }
|
||||
this.$nextTick(() => {
|
||||
this.loadData()
|
||||
if (this.$route.params.id && !this.isEdit) {
|
||||
@ -283,6 +280,9 @@ export default {
|
||||
'updatePayment',
|
||||
'fetchPayment'
|
||||
]),
|
||||
invoiceWithAmount ({ invoice_number, due_amount }) {
|
||||
return `${invoice_number} (${this.$utils.formatGraphMoney(due_amount, this.customer.currency)})`
|
||||
},
|
||||
async loadData () {
|
||||
if (this.isEdit) {
|
||||
let response = await this.fetchPayment(this.$route.params.id)
|
||||
@ -291,7 +291,6 @@ export default {
|
||||
this.customer = response.data.payment.user
|
||||
this.formData.payment_date = moment(response.data.payment.payment_date, 'YYYY-MM-DD').toString()
|
||||
this.formData.amount = parseFloat(response.data.payment.amount)
|
||||
this.maxPayableAmount = response.data.payment.amount
|
||||
if (response.data.payment.invoice !== null) {
|
||||
this.maxPayableAmount = parseInt(response.data.payment.amount) + parseInt(response.data.payment.invoice.due_amount)
|
||||
this.invoice = response.data.payment.invoice
|
||||
|
||||
@ -215,15 +215,14 @@
|
||||
</table-column>
|
||||
</table-component>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapGetters } from 'vuex'
|
||||
import { SweetModal, SweetModalTab } from 'sweet-modal-vue'
|
||||
import CapsuleIcon from '../../components/icon/CapsuleIcon'
|
||||
import BaseButton from '../../../js/components/base/BaseButton'
|
||||
import { request } from 'http'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
||||
@ -4,13 +4,6 @@
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<label class="report-label">{{ $t('reports.sales.date_range') }}</label>
|
||||
<!-- <base-date-picker
|
||||
v-model="range"
|
||||
:invalid="$v.range.$error"
|
||||
format="yyyy"
|
||||
minimum-view="year"
|
||||
@change="$v.range.$touch()"
|
||||
/> -->
|
||||
<base-select
|
||||
v-model="selectedRange"
|
||||
:options="dateRange"
|
||||
|
||||
@ -52,6 +52,7 @@
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
watch: {
|
||||
|
||||
@ -12,6 +12,9 @@
|
||||
<div class="col-md-6">
|
||||
<label class="input-label">{{ $tc('settings.company_info.company_logo') }}</label>
|
||||
<div id="pick-avatar" class="image-upload-box">
|
||||
<div class="overlay">
|
||||
<font-awesome-icon class="white-icon" icon="camera"/>
|
||||
</div>
|
||||
<img v-if="previewLogo" :src="previewLogo" class="preview-logo">
|
||||
<div v-else class="upload-content">
|
||||
<font-awesome-icon class="upload-icon" icon="cloud-upload-alt"/>
|
||||
@ -174,7 +177,6 @@ export default {
|
||||
isFetchingData: false,
|
||||
formData: {
|
||||
name: null,
|
||||
logo: '',
|
||||
email: '',
|
||||
phone: '',
|
||||
zip: '',
|
||||
@ -301,17 +303,8 @@ export default {
|
||||
return true
|
||||
}
|
||||
this.isLoading = true
|
||||
let data = new FormData()
|
||||
data.append('name', this.formData.name)
|
||||
data.append('address_street_1', this.formData.address_street_1)
|
||||
data.append('address_street_2', this.formData.address_street_2)
|
||||
data.append('city_id', this.formData.city_id)
|
||||
data.append('state_id', this.formData.state_id)
|
||||
data.append('country_id', this.formData.country_id)
|
||||
data.append('zip', this.formData.zip)
|
||||
data.append('phone', this.formData.phone)
|
||||
|
||||
let response = await this.editCompany(data)
|
||||
let response = await this.editCompany(this.formData)
|
||||
if (response.data.success) {
|
||||
this.isLoading = false
|
||||
if (this.fileObject && this.previewLogo) {
|
||||
|
||||
@ -17,9 +17,7 @@
|
||||
<h3 class="page-title mb-3">{{ $t('settings.update_app.avail_update') }}</h3>
|
||||
<label class="input-label">{{ $t('settings.update_app.next_version') }}</label><br>
|
||||
<label class="version">{{ updateData.version }}</label>
|
||||
<p class="page-sub-title">
|
||||
{{ description }}
|
||||
</p>
|
||||
<p class="page-sub-title" style="white-space: pre-wrap;">{{ description }}</p>
|
||||
<base-button size="large" icon="rocket" color="theme" @click="onUpdateApp">
|
||||
{{ $t('settings.update_app.update') }}
|
||||
</base-button>
|
||||
@ -55,13 +53,22 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
created () {
|
||||
window.addEventListener('beforeunload', (event) => {
|
||||
if (this.isUpdating) {
|
||||
event.returnValue = 'Update is in progress!'
|
||||
}
|
||||
})
|
||||
},
|
||||
mounted () {
|
||||
window.axios.get('/api/settings/app/version').then((res) => {
|
||||
this.currentVersion = res.data.version
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
closeHandler () {
|
||||
console.log('closing')
|
||||
},
|
||||
async onUpdateApp () {
|
||||
try {
|
||||
this.isUpdating = true
|
||||
@ -73,6 +80,10 @@ export default {
|
||||
this.isUpdateAvailable = false
|
||||
window.toastr['success'](this.$t('settings.update_app.update_success'))
|
||||
this.currentVersion = this.updateData.version
|
||||
|
||||
setTimeout(() => {
|
||||
location.reload()
|
||||
}, 2000)
|
||||
} else {
|
||||
console.log(res.data)
|
||||
window.toastr['error'](res.data.error)
|
||||
|
||||
@ -8,6 +8,31 @@
|
||||
{{ $t('settings.account_settings.section_description') }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<label class="input-label">{{ $tc('settings.account_settings.profile_picture') }}</label>
|
||||
<div id="pick-avatar" class="image-upload-box avatar-upload">
|
||||
<div class="overlay">
|
||||
<font-awesome-icon class="white-icon" icon="camera"/>
|
||||
</div>
|
||||
<img v-if="previewAvatar" :src="previewAvatar" class="preview-logo">
|
||||
<div v-if="!previewAvatar" class="upload-content">
|
||||
<font-awesome-icon class="upload-icon" icon="cloud-upload-alt"/>
|
||||
<p class="upload-text"> {{ $tc('general.choose_file') }} </p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<avatar-cropper
|
||||
:labels="{ submit: 'submit', cancel: 'Cancle'}"
|
||||
:cropper-options="cropperOptions"
|
||||
:output-options="cropperOutputOptions"
|
||||
:output-quality="0.8"
|
||||
:upload-handler="cropperHandler"
|
||||
trigger="#pick-avatar"
|
||||
@changed="setFileObject"
|
||||
@error="handleUploadError"
|
||||
/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-4 form-group">
|
||||
<label class="input-label">{{ $tc('settings.account_settings.name') }}</label>
|
||||
@ -81,19 +106,33 @@
|
||||
<script>
|
||||
import { validationMixin } from 'vuelidate'
|
||||
import { mapActions } from 'vuex'
|
||||
import AvatarCropper from 'vue-avatar-cropper'
|
||||
const { required, requiredIf, sameAs, email, minLength } = require('vuelidate/lib/validators')
|
||||
|
||||
export default {
|
||||
components: { AvatarCropper },
|
||||
mixins: [validationMixin],
|
||||
data () {
|
||||
return {
|
||||
isLoading: false,
|
||||
cropperOutputOptions: {
|
||||
width: 150,
|
||||
height: 150
|
||||
},
|
||||
cropperOptions: {
|
||||
autoCropArea: 1,
|
||||
viewMode: 0,
|
||||
movable: true,
|
||||
zoomable: true
|
||||
},
|
||||
formData: {
|
||||
name: null,
|
||||
email: null,
|
||||
password: null,
|
||||
confirm_password: null
|
||||
}
|
||||
},
|
||||
isLoading: false,
|
||||
previewAvatar: null,
|
||||
fileObject: null
|
||||
}
|
||||
},
|
||||
validations: {
|
||||
@ -128,12 +167,27 @@ export default {
|
||||
methods: {
|
||||
...mapActions('userProfile', [
|
||||
'loadData',
|
||||
'editUser'
|
||||
'editUser',
|
||||
'uploadAvatar'
|
||||
]),
|
||||
cropperHandler (cropper) {
|
||||
this.previewAvatar = cropper.getCroppedCanvas().toDataURL(this.cropperOutputMime)
|
||||
},
|
||||
setFileObject (file) {
|
||||
this.fileObject = file
|
||||
},
|
||||
handleUploadError (message, type, xhr) {
|
||||
window.toastr['error']('Oops! Something went wrong...')
|
||||
},
|
||||
async setInitialData () {
|
||||
let response = await this.loadData()
|
||||
this.formData.name = response.data.name
|
||||
this.formData.email = response.data.email
|
||||
if (response.data.avatar) {
|
||||
this.previewAvatar = response.data.avatar
|
||||
} else {
|
||||
this.previewAvatar = '/images/default-avatar.jpg'
|
||||
}
|
||||
},
|
||||
async updateUserData () {
|
||||
this.$v.formData.$touch()
|
||||
@ -151,6 +205,14 @@ export default {
|
||||
let response = await this.editUser(data)
|
||||
if (response.data.success) {
|
||||
this.isLoading = false
|
||||
if (this.fileObject && this.previewAvatar) {
|
||||
let avatarData = new FormData()
|
||||
avatarData.append('admin_avatar', JSON.stringify({
|
||||
name: this.fileObject.name,
|
||||
data: this.previewAvatar
|
||||
}))
|
||||
this.uploadAvatar(avatarData)
|
||||
}
|
||||
window.toastr['success'](this.$t('settings.account_settings.updated_message'))
|
||||
return true
|
||||
}
|
||||
|
||||
@ -7,6 +7,9 @@
|
||||
<div class="col-md-6">
|
||||
<label class="input-label">{{ $tc('settings.company_info.company_logo') }}</label>
|
||||
<div id="pick-avatar" class="image-upload-box">
|
||||
<div class="overlay">
|
||||
<font-awesome-icon class="white-icon" icon="camera"/>
|
||||
</div>
|
||||
<img v-if="previewLogo" :src="previewLogo" class="preview-logo">
|
||||
<div v-else class="upload-content">
|
||||
<font-awesome-icon class="upload-icon" icon="cloud-upload-alt"/>
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<base-button
|
||||
v-if="!errors"
|
||||
v-if="isContinue"
|
||||
class="pull-right mt-5"
|
||||
icon="arrow-right"
|
||||
right-icon
|
||||
@ -39,7 +39,8 @@ export default {
|
||||
return {
|
||||
loading: false,
|
||||
permissions: [],
|
||||
errors: false
|
||||
errors: false,
|
||||
isContinue: false
|
||||
}
|
||||
},
|
||||
created () {
|
||||
@ -54,6 +55,24 @@ export default {
|
||||
if (response.data) {
|
||||
this.permissions = response.data.permissions.permissions
|
||||
this.errors = response.data.permissions.errors
|
||||
let self = this
|
||||
|
||||
if (this.errors) {
|
||||
swal({
|
||||
title: this.$t('wizard.permissions.permission_confirm_title'),
|
||||
text: this.$t('wizard.permissions.permission_confirm_desc'),
|
||||
icon: 'warning',
|
||||
buttons: true,
|
||||
dangerMode: true
|
||||
}).then(async (willConfirm) => {
|
||||
if (willConfirm) {
|
||||
self.isContinue = true
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.isContinue = true
|
||||
}
|
||||
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
@ -3,6 +3,31 @@
|
||||
<form action="" @submit.prevent="next()">
|
||||
<p class="form-title">{{ $t('wizard.account_info') }}</p>
|
||||
<p class="form-desc">{{ $t('wizard.account_info_desc') }}</p>
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">{{ $tc('settings.account_settings.profile_picture') }}</label>
|
||||
<div id="pick-avatar" class="image-upload-box avatar-upload">
|
||||
<div class="overlay">
|
||||
<font-awesome-icon class="white-icon" icon="camera"/>
|
||||
</div>
|
||||
<img v-if="previewAvatar" :src="previewAvatar" class="preview-logo">
|
||||
<div v-else class="upload-content">
|
||||
<font-awesome-icon class="upload-icon" icon="cloud-upload-alt"/>
|
||||
<p class="upload-text"> {{ $tc('general.choose_file') }} </p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<avatar-cropper
|
||||
:labels="{ submit: 'submit', cancel: 'Cancle'}"
|
||||
:cropper-options="cropperOptions"
|
||||
:output-options="cropperOutputOptions"
|
||||
:output-quality="0.8"
|
||||
:upload-handler="cropperHandler"
|
||||
trigger="#pick-avatar"
|
||||
@changed="setFileObject"
|
||||
@error="handleUploadError"
|
||||
/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">{{ $t('wizard.name') }}</label><span class="text-danger"> *</span>
|
||||
@ -75,24 +100,37 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import MultiSelect from 'vue-multiselect'
|
||||
import AvatarCropper from 'vue-avatar-cropper'
|
||||
import { validationMixin } from 'vuelidate'
|
||||
import { mapActions } from 'vuex'
|
||||
const { required, requiredIf, sameAs, minLength, email } = require('vuelidate/lib/validators')
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MultiSelect
|
||||
AvatarCropper
|
||||
},
|
||||
mixins: [validationMixin],
|
||||
data () {
|
||||
return {
|
||||
cropperOutputOptions: {
|
||||
width: 150,
|
||||
height: 150
|
||||
},
|
||||
cropperOptions: {
|
||||
autoCropArea: 1,
|
||||
viewMode: 0,
|
||||
movable: true,
|
||||
zoomable: true
|
||||
},
|
||||
profileData: {
|
||||
name: null,
|
||||
email: null,
|
||||
password: null,
|
||||
confirm_password: null
|
||||
},
|
||||
loading: false
|
||||
loading: false,
|
||||
previewAvatar: '/images/default-avatar.jpg',
|
||||
fileObject: null
|
||||
}
|
||||
},
|
||||
validations: {
|
||||
@ -124,6 +162,18 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions('userProfile', [
|
||||
'uploadOnboardAvatar'
|
||||
]),
|
||||
cropperHandler (cropper) {
|
||||
this.previewAvatar = cropper.getCroppedCanvas().toDataURL(this.cropperOutputMime)
|
||||
},
|
||||
setFileObject (file) {
|
||||
this.fileObject = file
|
||||
},
|
||||
handleUploadError (message, type, xhr) {
|
||||
window.toastr['error']('Oops! Something went wrong...')
|
||||
},
|
||||
async next () {
|
||||
this.$v.profileData.$touch()
|
||||
if (this.$v.profileData.$invalid) {
|
||||
@ -131,7 +181,19 @@ export default {
|
||||
}
|
||||
this.loading = true
|
||||
let response = await window.axios.post('/api/admin/onboarding/profile', this.profileData)
|
||||
console.log('user_id', response.data.user.id)
|
||||
|
||||
if (response.data) {
|
||||
if (this.fileObject && this.previewAvatar) {
|
||||
let avatarData = new FormData()
|
||||
avatarData.append('admin_avatar', JSON.stringify({
|
||||
name: this.fileObject.name,
|
||||
data: this.previewAvatar,
|
||||
id: response.data.user.id
|
||||
}))
|
||||
|
||||
this.uploadOnboardAvatar(avatarData)
|
||||
}
|
||||
this.$emit('next')
|
||||
this.loading = false
|
||||
}
|
||||
|
||||
@ -53,7 +53,8 @@ import {
|
||||
faPaperPlane,
|
||||
faEyeSlash,
|
||||
faSyncAlt,
|
||||
faRocket
|
||||
faRocket,
|
||||
faCamera
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { far } from '@fortawesome/free-regular-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
|
||||
@ -117,7 +118,8 @@ library.add(
|
||||
faCopy,
|
||||
faPaperPlane,
|
||||
faSyncAlt,
|
||||
faRocket
|
||||
faRocket,
|
||||
faCamera
|
||||
)
|
||||
|
||||
Vue.component('font-awesome-icon', FontAwesomeIcon)
|
||||
|
||||
@ -11,8 +11,10 @@
|
||||
cursor: pointer;
|
||||
|
||||
.preview-logo {
|
||||
max-height: 50%;
|
||||
max-height: 80%;
|
||||
position: absolute;
|
||||
opacity: 1;
|
||||
animation: fadeIn 2s ease;
|
||||
}
|
||||
|
||||
.upload-content {
|
||||
@ -35,4 +37,51 @@
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.white-icon {
|
||||
font-size: 30px;
|
||||
line-height: 23px;
|
||||
color: $white;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 10;
|
||||
transition: .5s ease;
|
||||
background-color: rgba(0,0,0,0.5);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.overlay {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.avatar-upload {
|
||||
height: 130px;
|
||||
width: 130px;
|
||||
.preview-logo {
|
||||
max-width: 80% !important;
|
||||
}
|
||||
|
||||
@keyframes fadeIn{
|
||||
0%{
|
||||
opacity: 0;
|
||||
}
|
||||
100%{
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -351,7 +351,8 @@ fieldset[disabled] .multiselect {
|
||||
color: $ls-color-gray;
|
||||
}
|
||||
|
||||
.multiselect--disabled .multiselect__input {
|
||||
.multiselect--disabled .multiselect__input,
|
||||
.multiselect--disabled .multiselect__single {
|
||||
background: $ls-color-gray--light;
|
||||
color: $ls-color-gray;
|
||||
}
|
||||
|
||||
1
resources/assets/sass/partials/header.scss
vendored
1
resources/assets/sass/partials/header.scss
vendored
@ -62,6 +62,7 @@
|
||||
|
||||
.avatar img {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
|
||||
@ -96,6 +96,11 @@ Route::group(['middleware' => 'redirect-if-installed'], function () {
|
||||
'uses' => 'OnboardingController@adminProfile'
|
||||
]);
|
||||
|
||||
Route::post('/admin/profile/upload-avatar', [
|
||||
'as' => 'admin.on_boarding.avatar',
|
||||
'uses' => 'OnboardingController@uploadAdminAvatar'
|
||||
]);
|
||||
|
||||
Route::post('/admin/onboarding/company', [
|
||||
'as' => 'admin.company',
|
||||
'uses' => 'OnboardingController@adminCompany'
|
||||
@ -308,6 +313,11 @@ Route::group(['middleware' => 'api'], function () {
|
||||
'uses' => 'CompanyController@updateAdminProfile'
|
||||
]);
|
||||
|
||||
Route::post('/profile/upload-avatar', [
|
||||
'as' => 'admin.profile.avatar',
|
||||
'uses' => 'CompanyController@uploadAdminAvatar'
|
||||
]);
|
||||
|
||||
Route::post('/company/upload-logo', [
|
||||
'as' => 'upload.admin.company.logo',
|
||||
'uses' => 'CompanyController@uploadCompanyLogo'
|
||||
|
||||
@ -47,6 +47,8 @@ Route::group(['prefix' => 'reports'], function () {
|
||||
'uses' => 'ReportController@profitLossReport'
|
||||
]);
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user