Compare commits

...

42 Commits
1.0.0 ... 1.1.0

Author SHA1 Message Date
3158955d00 Merge branch 'master' of https://github.com/bytefury/crater 2019-11-23 19:31:17 +05:30
00a218fe87 build version110 2019-11-23 18:21:03 +05:30
4a92469465 Merge branch 'version-110' into 'master'
add currencies symbol in listener

See merge request mohit.panjvani/crater-web!76
2019-11-23 12:49:28 +00:00
cd67ebd487 fix customer validation issue 2019-11-23 18:01:46 +05:30
6853d0bb57 add currencies symbol in listener 2019-11-23 16:57:36 +05:30
9afc19268a build version 110 2019-11-23 15:28:01 +05:30
f6faf10d43 fix description line-endings 2019-11-23 15:21:39 +05:30
b061102c23 build version-110 2019-11-23 14:20:40 +05:30
e8b8cd9f04 Merge branch 'version-110' into 'master'
fix permission success issue

See merge request mohit.panjvani/crater-web!75
2019-11-23 08:39:49 +00:00
9f4b5e02e8 fix permission success issue 2019-11-23 14:08:01 +05:30
1c84c1f843 rename BaseDatePicker 2019-11-23 13:52:00 +05:30
23beb5cac7 Merge branch 'master' 2019-11-23 13:48:06 +05:30
2fb2f1fcc0 update comment 2019-11-23 13:47:42 +05:30
7c1529f890 build 110 2019-11-23 13:46:24 +05:30
8b54b48805 stop window from closing when update in progress 2019-11-23 13:43:29 +05:30
07601a7130 refactor update listener and reload browser window on update success 2019-11-23 13:15:58 +05:30
7fd1224502 Merge branch 'version-110' into 'master'
add confirm box for folder permission on installation

See merge request mohit.panjvani/crater-web!73
2019-11-23 07:16:00 +00:00
1d56bd9f36 fix permission confirm box title issue 2019-11-23 12:41:47 +05:30
a8783cc929 Merge branch 'master' 2019-11-23 12:39:37 +05:30
841ff06005 clean unused code 2019-11-23 12:39:21 +05:30
3a54167ec7 Merge branch 'currency-changes' into 'master'
Add currency symbol

See merge request mohit.panjvani/crater-web!74
2019-11-23 07:07:51 +00:00
bc18f9b198 add confirm box for folder permission on installation 2019-11-23 12:34:10 +05:30
6741f9849d fix multiselect disabled style 2019-11-23 12:33:33 +05:30
14c0f7f486 Add currency symbol 2019-11-23 12:31:30 +05:30
095f3c0b31 Merge branch 'master' of gitlab.com:mohit.panjvani/crater-web 2019-11-23 12:18:48 +05:30
f2ad8286d7 fix label class 2019-11-23 12:18:27 +05:30
18ad27d513 Merge branch 'customer' into 'master'
Fix customer email already exist validation when edit customer

See merge request mohit.panjvani/crater-web!71
2019-11-23 06:42:41 +00:00
06807275b9 fix item reports issue 2019-11-23 11:54:35 +05:30
2ad39bfbd6 Merge branch 'version-110' into 'master'
fix installation issue & add ver update listener

See merge request mohit.panjvani/crater-web!68
2019-11-23 06:15:49 +00:00
9f38947fe7 Merge branch 'user-avatar' into 'master'
User Avatar

See merge request mohit.panjvani/crater-web!67
2019-11-23 06:09:08 +00:00
81c1e78bed fix taxes issue 2019-11-22 16:07:31 +05:30
dd0817709b fix installation issue & add ver update listener 2019-11-22 15:11:11 +05:30
66a1156d5c Merge branch 'admin-profile' into 'master'
add admin profile avatar

See merge request mohit.panjvani/crater-web!62
2019-11-22 09:20:15 +00:00
1be2c7122e Merge branch 'fix-payment' into 'master'
Fix payment edit

See merge request mohit.panjvani/crater-web!61
2019-11-22 09:06:21 +00:00
e9218e4d21 add default-avatar and change hover icon 2019-11-22 12:42:03 +05:30
812afc2dab add avatar on account-settings 2019-11-21 18:45:22 +05:30
4ec34550ed add admin profile avatar 2019-11-21 11:59:23 +05:30
ca6ab997de refactor amount problem in payment 2019-11-20 18:20:05 +05:30
8b05909b74 fix customer email validation 2019-11-20 17:46:45 +05:30
1e6372cbb1 add due amount on invoice select 2019-11-20 16:57:31 +05:30
d29c343911 Merge branch 'master' of https://gitlab.com/mohit.panjvani/crater-web into fix-payment 2019-11-20 15:16:19 +05:30
f3ef51bffb fix edit payment issue 2019-11-20 15:15:52 +05:30
48 changed files with 564 additions and 577 deletions

View File

@ -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
]);
}
}

View File

@ -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'
]);
}

View File

@ -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');

View File

@ -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();

View File

@ -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:

View File

@ -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',
];
}
}

View File

@ -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;
}

View File

@ -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');
}
}

View File

@ -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;
}

View 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' => '.'
]);
}
}

View File

@ -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,

View File

@ -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'
];

View File

@ -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 ;
}
}

View File

@ -4,11 +4,11 @@ return [
/*
|--------------------------------------------------------------------------
| Crater Requirements
| Crater Configuration
|--------------------------------------------------------------------------
|
*/
'version' => '1.0.0',
'version' => '1.1.0',
];

View File

@ -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' => '.'

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -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"
}

View File

@ -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: {

View File

@ -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: {

View File

@ -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: {

View File

@ -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)

View File

@ -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) => {

View File

@ -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)

View File

@ -1,2 +1,3 @@
export const SET_USER = 'SET_USER'
export const UPDATE_USER = 'UPDATE_USER'
export const UPDATE_USER_AVATAR = 'UPDATE_USER_AVATAR'

View File

@ -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
}
}

View File

@ -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) {

View File

@ -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()
})

View File

@ -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()
})

View File

@ -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'
}),

View File

@ -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 &amp; 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>

View File

@ -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

View File

@ -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: {

View File

@ -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"

View File

@ -52,6 +52,7 @@
</transition>
</div>
</template>
<script>
export default {
watch: {

View File

@ -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) {

View File

@ -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)

View File

@ -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
}

View File

@ -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"/>

View File

@ -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
}
},

View File

@ -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
}

View File

@ -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)

View File

@ -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;
}
}
}

View File

@ -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;
}

View File

@ -62,6 +62,7 @@
.avatar img {
width: 36px;
height: 36px;
border-radius: 2px;
}

View File

@ -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'

View File

@ -47,6 +47,8 @@ Route::group(['prefix' => 'reports'], function () {
'uses' => 'ReportController@profitLossReport'
]);
});