mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-06-01 11:04:34 -04:00
Working on proposals
This commit is contained in:
parent
63fcac391f
commit
7c3cbc3a7e
@ -42,6 +42,10 @@ if (! defined('APP_NAME')) {
|
|||||||
define('ENTITY_RECURRING_EXPENSE', 'recurring_expense');
|
define('ENTITY_RECURRING_EXPENSE', 'recurring_expense');
|
||||||
define('ENTITY_CUSTOMER', 'customer');
|
define('ENTITY_CUSTOMER', 'customer');
|
||||||
define('ENTITY_SUBSCRIPTION', 'subscription');
|
define('ENTITY_SUBSCRIPTION', 'subscription');
|
||||||
|
define('ENTITY_PROPOSAL', 'proposal');
|
||||||
|
define('ENTITY_PROPOSAL_TEMPLATE', 'proposal_template');
|
||||||
|
define('ENTITY_PROPOSAL_SNIPPET', 'proposal_snippet');
|
||||||
|
define('ENTITY_PROPOSAL_CATEGORY', 'proposal_category');
|
||||||
|
|
||||||
define('INVOICE_TYPE_STANDARD', 1);
|
define('INVOICE_TYPE_STANDARD', 1);
|
||||||
define('INVOICE_TYPE_QUOTE', 2);
|
define('INVOICE_TYPE_QUOTE', 2);
|
||||||
|
144
app/Http/Controllers/ProposalController.php
Normal file
144
app/Http/Controllers/ProposalController.php
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Jobs\GenerateProposalChartData;
|
||||||
|
use App\Http\Requests\CreateProposalRequest;
|
||||||
|
use App\Http\Requests\ProposalRequest;
|
||||||
|
use App\Http\Requests\UpdateProposalRequest;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Models\Proposal;
|
||||||
|
use App\Models\ProposalTemplate;
|
||||||
|
use App\Ninja\Datatables\ProposalDatatable;
|
||||||
|
use App\Ninja\Repositories\ProposalRepository;
|
||||||
|
use App\Services\ProposalService;
|
||||||
|
use Auth;
|
||||||
|
use Input;
|
||||||
|
use Session;
|
||||||
|
use View;
|
||||||
|
|
||||||
|
class ProposalController extends BaseController
|
||||||
|
{
|
||||||
|
protected $proposalRepo;
|
||||||
|
protected $proposalService;
|
||||||
|
protected $entityType = ENTITY_PROPOSAL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
public function __construct(ProposalRepository $proposalRepo, ProposalService $proposalService)
|
||||||
|
{
|
||||||
|
$this->proposalRepo = $proposalRepo;
|
||||||
|
$this->proposalService = $proposalService;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a listing of the resource.
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
|
*/
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return View::make('list_wrapper', [
|
||||||
|
'entityType' => ENTITY_PROPOSAL,
|
||||||
|
'datatable' => new ProposalDatatable(),
|
||||||
|
'title' => trans('texts.proposals'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDatatable($expensePublicId = null)
|
||||||
|
{
|
||||||
|
$search = Input::get('sSearch');
|
||||||
|
$userId = Auth::user()->filterId();
|
||||||
|
|
||||||
|
return $this->proposalService->getDatatable($search, $userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
public function show(ProposalRequest $request)
|
||||||
|
{
|
||||||
|
$account = auth()->user()->account;
|
||||||
|
$proposal = $request->entity();
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'account' => auth()->user()->account,
|
||||||
|
'proposal' => $proposal,
|
||||||
|
'title' => trans('texts.view_proposal'),
|
||||||
|
'showBreadcrumbs' => false,
|
||||||
|
];
|
||||||
|
|
||||||
|
return View::make('proposals.show', $data);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
public function create(ProposalRequest $request)
|
||||||
|
{
|
||||||
|
$data = [
|
||||||
|
'account' => auth()->user()->account,
|
||||||
|
'proposal' => null,
|
||||||
|
'method' => 'POST',
|
||||||
|
'url' => 'proposals',
|
||||||
|
'title' => trans('texts.new_proposal'),
|
||||||
|
'quotes' => Invoice::scope()->with('client.contacts')->quotes()->orderBy('id')->get(),
|
||||||
|
'templates' => ProposalTemplate::scope()->orderBy('name')->get(),
|
||||||
|
'quotePublicId' => $request->quote_id,
|
||||||
|
];
|
||||||
|
|
||||||
|
return View::make('proposals.edit', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function edit(ProposalRequest $request)
|
||||||
|
{
|
||||||
|
$proposal = $request->entity();
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'account' => auth()->user()->account,
|
||||||
|
'proposal' => $proposal,
|
||||||
|
'method' => 'PUT',
|
||||||
|
'url' => 'proposals/' . $proposal->public_id,
|
||||||
|
'title' => trans('texts.edit_proposal'),
|
||||||
|
'clients' => Client::scope()->with('contacts')->orderBy('name')->get(),
|
||||||
|
'clientPublicId' => $proposal->client ? $proposal->client->public_id : null,
|
||||||
|
];
|
||||||
|
|
||||||
|
return View::make('proposals.edit', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store(CreateProposalRequest $request)
|
||||||
|
{
|
||||||
|
$proposal = $this->proposalService->save($request->input());
|
||||||
|
|
||||||
|
Session::flash('message', trans('texts.created_proposal'));
|
||||||
|
|
||||||
|
return redirect()->to($proposal->getRoute());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(UpdateProposalRequest $request)
|
||||||
|
{
|
||||||
|
$proposal = $this->proposalService->save($request->input(), $request->entity());
|
||||||
|
|
||||||
|
Session::flash('message', trans('texts.updated_proposal'));
|
||||||
|
|
||||||
|
$action = Input::get('action');
|
||||||
|
if (in_array($action, ['archive', 'delete', 'restore'])) {
|
||||||
|
return self::bulk();
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->to($proposal->getRoute());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bulk()
|
||||||
|
{
|
||||||
|
$action = Input::get('action');
|
||||||
|
$ids = Input::get('public_id') ? Input::get('public_id') : Input::get('ids');
|
||||||
|
|
||||||
|
$count = $this->proposalService->bulk($ids, $action);
|
||||||
|
|
||||||
|
if ($count > 0) {
|
||||||
|
$field = $count == 1 ? "{$action}d_proposal" : "{$action}d_proposals";
|
||||||
|
$message = trans("texts.$field", ['count' => $count]);
|
||||||
|
Session::flash('message', $message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return redirect()->to('/proposals');
|
||||||
|
}
|
||||||
|
}
|
8
app/Http/Requests/ProposalRequest.php
Normal file
8
app/Http/Requests/ProposalRequest.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
class ProposalRequest extends EntityRequest
|
||||||
|
{
|
||||||
|
protected $entityType = ENTITY_PROPOSAL;
|
||||||
|
}
|
8
app/Http/Requests/ProposalSnippetRequest.php
Normal file
8
app/Http/Requests/ProposalSnippetRequest.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
class ProposalSnippetRequest extends EntityRequest
|
||||||
|
{
|
||||||
|
protected $entityType = ENTITY_PROPOSAL_SNIPPET;
|
||||||
|
}
|
8
app/Http/Requests/ProposalTemplateRequest.php
Normal file
8
app/Http/Requests/ProposalTemplateRequest.php
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
class ProposalTemplateRequest extends EntityRequest
|
||||||
|
{
|
||||||
|
protected $entityType = ENTITY_PROPOSAL_TEMPLATE;
|
||||||
|
}
|
@ -321,6 +321,7 @@ class EntityModel extends Eloquent
|
|||||||
'recurring_expenses' => 'files-o',
|
'recurring_expenses' => 'files-o',
|
||||||
'credits' => 'credit-card',
|
'credits' => 'credit-card',
|
||||||
'quotes' => 'file-text-o',
|
'quotes' => 'file-text-o',
|
||||||
|
'proposals' => 'tasks',
|
||||||
'tasks' => 'clock-o',
|
'tasks' => 'clock-o',
|
||||||
'expenses' => 'file-image-o',
|
'expenses' => 'file-image-o',
|
||||||
'vendors' => 'building',
|
'vendors' => 'building',
|
||||||
|
76
app/Models/Proposal.php
Normal file
76
app/Models/Proposal.php
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Laracasts\Presenter\PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ExpenseCategory.
|
||||||
|
*/
|
||||||
|
class Proposal extends EntityModel
|
||||||
|
{
|
||||||
|
use SoftDeletes;
|
||||||
|
use PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $dates = ['deleted_at'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
//protected $presenter = 'App\Ninja\Presenters\ProjectPresenter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getEntityType()
|
||||||
|
{
|
||||||
|
return ENTITY_PROPOSAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getRoute()
|
||||||
|
{
|
||||||
|
return "/proposals/{$this->public_id}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function account()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\Account');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function quote()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\Invoice')->withTrashed();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDisplayName()
|
||||||
|
{
|
||||||
|
return 'TODO';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Proposal::creating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
||||||
|
|
||||||
|
Proposal::updating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
70
app/Models/ProposalCategory.php
Normal file
70
app/Models/ProposalCategory.php
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Laracasts\Presenter\PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ExpenseCategory.
|
||||||
|
*/
|
||||||
|
class ProposalCategory extends EntityModel
|
||||||
|
{
|
||||||
|
use SoftDeletes;
|
||||||
|
use PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $dates = ['deleted_at'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
//protected $presenter = 'App\Ninja\Presenters\ProjectPresenter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getEntityType()
|
||||||
|
{
|
||||||
|
return ENTITY_PROPOSAL_CATEGORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getRoute()
|
||||||
|
{
|
||||||
|
return "/proposal_categories/{$this->public_id}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function account()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\Account');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDisplayName()
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Proposal::creating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
||||||
|
|
||||||
|
Proposal::updating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
||||||
|
*/
|
70
app/Models/ProposalSnippet.php
Normal file
70
app/Models/ProposalSnippet.php
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Laracasts\Presenter\PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ExpenseCategory.
|
||||||
|
*/
|
||||||
|
class ProposalSnippet extends EntityModel
|
||||||
|
{
|
||||||
|
use SoftDeletes;
|
||||||
|
use PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $dates = ['deleted_at'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
//protected $presenter = 'App\Ninja\Presenters\ProjectPresenter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getEntityType()
|
||||||
|
{
|
||||||
|
return ENTITY_PROPOSAL_SNIPPET;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getRoute()
|
||||||
|
{
|
||||||
|
return "/proposal_snippets/{$this->public_id}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function account()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\Account');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDisplayName()
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Proposal::creating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
||||||
|
|
||||||
|
Proposal::updating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
||||||
|
*/
|
70
app/Models/ProposalTemplate.php
Normal file
70
app/Models/ProposalTemplate.php
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Laracasts\Presenter\PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ExpenseCategory.
|
||||||
|
*/
|
||||||
|
class ProposalTemplate extends EntityModel
|
||||||
|
{
|
||||||
|
use SoftDeletes;
|
||||||
|
use PresentableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $dates = ['deleted_at'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected $fillable = [
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
//protected $presenter = 'App\Ninja\Presenters\ProjectPresenter';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getEntityType()
|
||||||
|
{
|
||||||
|
return ENTITY_PROPOSAL_TEMPLATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getRoute()
|
||||||
|
{
|
||||||
|
return "/proposal_templates/{$this->public_id}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||||
|
*/
|
||||||
|
public function account()
|
||||||
|
{
|
||||||
|
return $this->belongsTo('App\Models\Account');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDisplayName()
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Proposal::creating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
||||||
|
|
||||||
|
Proposal::updating(function ($project) {
|
||||||
|
$project->setNullValues();
|
||||||
|
});
|
||||||
|
*/
|
40
app/Ninja/Datatables/ProposalCategoryDatatable.php
Normal file
40
app/Ninja/Datatables/ProposalCategoryDatatable.php
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ninja\Datatables;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use URL;
|
||||||
|
use Utils;
|
||||||
|
|
||||||
|
class ProposalCategoryDatatable extends EntityDatatable
|
||||||
|
{
|
||||||
|
public $entityType = ENTITY_PROPOSAL_CATEGORY;
|
||||||
|
public $sortCol = 1;
|
||||||
|
|
||||||
|
public function columns()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'name',
|
||||||
|
function ($model) {
|
||||||
|
return $model->name;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actions()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
trans('texts.edit_category'),
|
||||||
|
function ($model) {
|
||||||
|
return URL::to("proposal_categories/{$model->public_id}/edit");
|
||||||
|
},
|
||||||
|
function ($model) {
|
||||||
|
return Auth::user()->can('editByOwner', [ENTITY_PROPOSAL_CATEGORY, $model->user_id]);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
64
app/Ninja/Datatables/ProposalDatatable.php
Normal file
64
app/Ninja/Datatables/ProposalDatatable.php
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ninja\Datatables;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use URL;
|
||||||
|
use Utils;
|
||||||
|
|
||||||
|
class ProposalDatatable extends EntityDatatable
|
||||||
|
{
|
||||||
|
public $entityType = ENTITY_PROPOSAL;
|
||||||
|
public $sortCol = 1;
|
||||||
|
|
||||||
|
public function columns()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'quote',
|
||||||
|
function ($model) {
|
||||||
|
if (! Auth::user()->can('viewByOwner', [ENTITY_QUOTE, $model->user_id])) {
|
||||||
|
return $model->quote_number;
|
||||||
|
}
|
||||||
|
|
||||||
|
return link_to("quotes/{$model->quote_public_id}", $model->quote_number)->toHtml();
|
||||||
|
//$str = link_to("quotes/{$model->quote_public_id}", $model->quote_number)->toHtml();
|
||||||
|
//return $this->addNote($str, $model->private_notes);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'template',
|
||||||
|
function ($model) {
|
||||||
|
return $model->template_name;
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'created',
|
||||||
|
function ($model) {
|
||||||
|
return Utils::fromSqlDate($model->created_at);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'valid_until',
|
||||||
|
function ($model) {
|
||||||
|
return Utils::fromSqlDate($model->due_date);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actions()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
trans('texts.edit_proposal'),
|
||||||
|
function ($model) {
|
||||||
|
return URL::to("proposals/{$model->public_id}/edit");
|
||||||
|
},
|
||||||
|
function ($model) {
|
||||||
|
return Auth::user()->can('editByOwner', [ENTITY_PROPOSAL, $model->user_id]);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
46
app/Ninja/Datatables/ProposalSnippetDatatable.php
Normal file
46
app/Ninja/Datatables/ProposalSnippetDatatable.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ninja\Datatables;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use URL;
|
||||||
|
use Utils;
|
||||||
|
|
||||||
|
class ProposalSnippetDatatable extends EntityDatatable
|
||||||
|
{
|
||||||
|
public $entityType = ENTITY_PROPOSAL_SNIPPET;
|
||||||
|
public $sortCol = 1;
|
||||||
|
|
||||||
|
public function columns()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'name',
|
||||||
|
function ($model) {
|
||||||
|
if (! Auth::user()->can('editByOwner', [ENTITY_PROPOSAL_SNIPPET, $model->user_id])) {
|
||||||
|
return $model->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return link_to("proposal_snippets/{$model->public_id}", $model->name)->toHtml();
|
||||||
|
//$str = link_to("quotes/{$model->quote_public_id}", $model->quote_number)->toHtml();
|
||||||
|
//return $this->addNote($str, $model->private_notes);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actions()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
trans('texts.edit_snippet'),
|
||||||
|
function ($model) {
|
||||||
|
return URL::to("proposals_snippets/{$model->public_id}/edit");
|
||||||
|
},
|
||||||
|
function ($model) {
|
||||||
|
return Auth::user()->can('editByOwner', [ENTITY_PROPOSAL_SNIPPET, $model->user_id]);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
46
app/Ninja/Datatables/ProposalTemplateDatatable.php
Normal file
46
app/Ninja/Datatables/ProposalTemplateDatatable.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Ninja\Datatables;
|
||||||
|
|
||||||
|
use Auth;
|
||||||
|
use URL;
|
||||||
|
use Utils;
|
||||||
|
|
||||||
|
class ProposalTemplateDatatable extends EntityDatatable
|
||||||
|
{
|
||||||
|
public $entityType = ENTITY_PROPOSAL_TEMPLATE;
|
||||||
|
public $sortCol = 1;
|
||||||
|
|
||||||
|
public function columns()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
'quote',
|
||||||
|
function ($model) {
|
||||||
|
if (! Auth::user()->can('editByOwner', [ENTITY_PROPOSAL_TEMPLATE, $model->user_id])) {
|
||||||
|
return $model->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return link_to("proposal_templates/{$model->public_id}", $model->name)->toHtml();
|
||||||
|
//$str = link_to("quotes/{$model->quote_public_id}", $model->quote_number)->toHtml();
|
||||||
|
//return $this->addNote($str, $model->private_notes);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function actions()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
[
|
||||||
|
trans('texts.edit_template'),
|
||||||
|
function ($model) {
|
||||||
|
return URL::to("proposal_templates/{$model->public_id}/edit");
|
||||||
|
},
|
||||||
|
function ($model) {
|
||||||
|
return Auth::user()->can('editByOwner', [ENTITY_PROPOSAL_TEMPLATE, $model->user_id]);
|
||||||
|
},
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
7
app/Policies/ProposalCategoryPolicy.php
Normal file
7
app/Policies/ProposalCategoryPolicy.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Policies;
|
||||||
|
|
||||||
|
class ProposalCategoryPolicy extends EntityPolicy
|
||||||
|
{
|
||||||
|
}
|
7
app/Policies/ProposalPolicy.php
Normal file
7
app/Policies/ProposalPolicy.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Policies;
|
||||||
|
|
||||||
|
class ProposalPolicy extends EntityPolicy
|
||||||
|
{
|
||||||
|
}
|
7
app/Policies/ProposalSnippetPolicy.php
Normal file
7
app/Policies/ProposalSnippetPolicy.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Policies;
|
||||||
|
|
||||||
|
class ProposalSnippetPolicy extends EntityPolicy
|
||||||
|
{
|
||||||
|
}
|
7
app/Policies/ProposalTemplatePolicy.php
Normal file
7
app/Policies/ProposalTemplatePolicy.php
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Policies;
|
||||||
|
|
||||||
|
class ProposalTemplatePolicy extends EntityPolicy
|
||||||
|
{
|
||||||
|
}
|
@ -34,6 +34,10 @@ class AuthServiceProvider extends ServiceProvider
|
|||||||
\App\Models\PaymentTerm::class => \App\Policies\PaymentTermPolicy::class,
|
\App\Models\PaymentTerm::class => \App\Policies\PaymentTermPolicy::class,
|
||||||
\App\Models\Project::class => \App\Policies\ProjectPolicy::class,
|
\App\Models\Project::class => \App\Policies\ProjectPolicy::class,
|
||||||
\App\Models\AccountGatewayToken::class => \App\Policies\CustomerPolicy::class,
|
\App\Models\AccountGatewayToken::class => \App\Policies\CustomerPolicy::class,
|
||||||
|
\App\Models\Proposal::class => \App\Policies\Proposal::class,
|
||||||
|
\App\Models\ProposalSnippet::class => \App\Policies\ProposalSnippet::class,
|
||||||
|
\App\Models\ProposalTemplate::class => \App\Policies\ProposalTemplate::class,
|
||||||
|
\App\Models\ProposalCategory::class => \App\Policies\ProposalCategory::class,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -40,7 +40,8 @@
|
|||||||
"toastr": "^2.1.3",
|
"toastr": "^2.1.3",
|
||||||
"jt.timepicker": "jquery-timepicker-jt#^1.11.12",
|
"jt.timepicker": "jquery-timepicker-jt#^1.11.12",
|
||||||
"qrcode.js": "qrcode-js#*",
|
"qrcode.js": "qrcode-js#*",
|
||||||
"money.js": "^0.1.3"
|
"money.js": "^0.1.3",
|
||||||
|
"grapesjs": "^0.13.8"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"jquery": "~1.11"
|
"jquery": "~1.11"
|
||||||
|
@ -338,7 +338,6 @@ class ConfideSetupUsersTable extends Migration
|
|||||||
$t->timestamp('viewed_date')->nullable();
|
$t->timestamp('viewed_date')->nullable();
|
||||||
|
|
||||||
$t->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
$t->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||||
;
|
|
||||||
$t->foreign('contact_id')->references('id')->on('contacts')->onDelete('cascade');
|
$t->foreign('contact_id')->references('id')->on('contacts')->onDelete('cascade');
|
||||||
$t->foreign('invoice_id')->references('id')->on('invoices')->onDelete('cascade');
|
$t->foreign('invoice_id')->references('id')->on('invoices')->onDelete('cascade');
|
||||||
|
|
||||||
|
@ -20,6 +20,83 @@ class AddSubscriptionFormat extends Migration
|
|||||||
Schema::table('accounts', function ($table) {
|
Schema::table('accounts', function ($table) {
|
||||||
$table->boolean('ubl_email_attachment')->default(false);
|
$table->boolean('ubl_email_attachment')->default(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Schema::create('proposal_categories', function ($table) {
|
||||||
|
$table->increments('id');
|
||||||
|
$table->unsignedInteger('account_id');
|
||||||
|
$table->unsignedInteger('user_id');
|
||||||
|
$table->timestamps();
|
||||||
|
$table->softDeletes();
|
||||||
|
|
||||||
|
$table->string('name');
|
||||||
|
|
||||||
|
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
|
||||||
|
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||||
|
|
||||||
|
$table->unsignedInteger('public_id')->index();
|
||||||
|
$table->unique(['account_id', 'public_id']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create('proposal_snippets', function ($table) {
|
||||||
|
$table->increments('id');
|
||||||
|
$table->unsignedInteger('account_id');
|
||||||
|
$table->unsignedInteger('user_id');
|
||||||
|
$table->timestamps();
|
||||||
|
$table->softDeletes();
|
||||||
|
|
||||||
|
$table->unsignedInteger('proposal_category_id');
|
||||||
|
$table->string('name');
|
||||||
|
|
||||||
|
$table->mediumText('html');
|
||||||
|
$table->mediumText('css');
|
||||||
|
|
||||||
|
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
|
||||||
|
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||||
|
|
||||||
|
$table->unsignedInteger('public_id')->index();
|
||||||
|
$table->unique(['account_id', 'public_id']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create('proposal_templates', function ($table) {
|
||||||
|
$table->increments('id');
|
||||||
|
$table->unsignedInteger('account_id');
|
||||||
|
$table->unsignedInteger('user_id');
|
||||||
|
$table->timestamps();
|
||||||
|
$table->softDeletes();
|
||||||
|
|
||||||
|
$table->string('name');
|
||||||
|
$table->text('tags');
|
||||||
|
$table->mediumText('html');
|
||||||
|
$table->mediumText('css');
|
||||||
|
|
||||||
|
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
|
||||||
|
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||||
|
|
||||||
|
$table->unsignedInteger('public_id')->index();
|
||||||
|
$table->unique(['account_id', 'public_id']);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::create('proposals', function ($table) {
|
||||||
|
$table->increments('id');
|
||||||
|
$table->unsignedInteger('account_id');
|
||||||
|
$table->unsignedInteger('user_id');
|
||||||
|
$table->timestamps();
|
||||||
|
$table->softDeletes();
|
||||||
|
|
||||||
|
$table->unsignedInteger('quote_id')->index();
|
||||||
|
$table->unsignedInteger('temlate_id')->index();
|
||||||
|
$table->mediumText('html');
|
||||||
|
$table->mediumText('css');
|
||||||
|
|
||||||
|
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
|
||||||
|
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
|
||||||
|
$table->foreign('quote_id')->references('id')->on('invoices')->onDelete('cascade');
|
||||||
|
$table->foreign('temlate_id')->references('id')->on('proposal_templates')->onDelete('cascade');
|
||||||
|
|
||||||
|
$table->unsignedInteger('public_id')->index();
|
||||||
|
$table->unique(['account_id', 'public_id']);
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -36,5 +113,10 @@ class AddSubscriptionFormat extends Migration
|
|||||||
Schema::table('accounts', function ($table) {
|
Schema::table('accounts', function ($table) {
|
||||||
$table->dropColumn('ubl_email_attachment');
|
$table->dropColumn('ubl_email_attachment');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Schema::dropIfExists('proposals');
|
||||||
|
Schema::dropIfExists('proposal_templates');
|
||||||
|
Schema::dropIfExists('proposal_snippets');
|
||||||
|
Schema::dropIfExists('proposal_categories');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
11
gulpfile.js
11
gulpfile.js
@ -67,6 +67,11 @@ elixir(function(mix) {
|
|||||||
bowerDir + '/bootstrap-daterangepicker/daterangepicker.css'
|
bowerDir + '/bootstrap-daterangepicker/daterangepicker.css'
|
||||||
], 'public/css/daterangepicker.css');
|
], 'public/css/daterangepicker.css');
|
||||||
|
|
||||||
|
mix.styles([
|
||||||
|
bowerDir + '/grapesjs/dist/css/grapes.min.css',
|
||||||
|
//'grapesjs-preset-newsletter.css',
|
||||||
|
], 'public/css/grapesjs.css');
|
||||||
|
|
||||||
mix.styles([
|
mix.styles([
|
||||||
bowerDir + '/jt.timepicker/jquery.timepicker.css'
|
bowerDir + '/jt.timepicker/jquery.timepicker.css'
|
||||||
], 'public/css/jquery.timepicker.css');
|
], 'public/css/jquery.timepicker.css');
|
||||||
@ -103,6 +108,12 @@ elixir(function(mix) {
|
|||||||
bowerDir + '/bootstrap-daterangepicker/daterangepicker.js'
|
bowerDir + '/bootstrap-daterangepicker/daterangepicker.js'
|
||||||
], 'public/js/daterangepicker.min.js');
|
], 'public/js/daterangepicker.min.js');
|
||||||
|
|
||||||
|
mix.scripts([
|
||||||
|
bowerDir + '/grapesjs/dist/grapes.js',
|
||||||
|
'grapesjs-blocks-basic.min.js',
|
||||||
|
'grapesjs-preset-newsletter.min.js',
|
||||||
|
], 'public/js/grapesjs.min.js');
|
||||||
|
|
||||||
mix.scripts([
|
mix.scripts([
|
||||||
bowerDir + '/jt.timepicker/jquery.timepicker.js'
|
bowerDir + '/jt.timepicker/jquery.timepicker.js'
|
||||||
], 'public/js/jquery.timepicker.js');
|
], 'public/js/jquery.timepicker.js');
|
||||||
|
6
public/css/grapes.css
vendored
Normal file
6
public/css/grapes.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/css/grapes.css.map
Normal file
1
public/css/grapes.css.map
Normal file
File diff suppressed because one or more lines are too long
5
public/css/grapesjs.css
vendored
Normal file
5
public/css/grapesjs.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/css/grapesjs.css.map
Normal file
1
public/css/grapesjs.css.map
Normal file
File diff suppressed because one or more lines are too long
BIN
public/fonts/main-fonts.woff
Normal file
BIN
public/fonts/main-fonts.woff
Normal file
Binary file not shown.
47711
public/js/grapes.min.js
vendored
Normal file
47711
public/js/grapes.min.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
public/js/grapes.min.js.map
Normal file
1
public/js/grapes.min.js.map
Normal file
File diff suppressed because one or more lines are too long
29
public/js/grapesjs.min.js
vendored
Normal file
29
public/js/grapesjs.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/js/grapesjs.min.js.map
Normal file
1
public/js/grapesjs.min.js.map
Normal file
File diff suppressed because one or more lines are too long
211
resources/assets/css/grapesjs-preset-newsletter.css
vendored
Normal file
211
resources/assets/css/grapesjs-preset-newsletter.css
vendored
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
/* Class names prefixes */
|
||||||
|
/* Colors / Theme */
|
||||||
|
.gjs-clm-tags .gjs-sm-title,
|
||||||
|
.gjs-sm-sector .gjs-sm-title {
|
||||||
|
border-top: none; }
|
||||||
|
|
||||||
|
.gjs-clm-tags .gjs-clm-tag {
|
||||||
|
background-color: #4c9790;
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
padding: 5px 8px;
|
||||||
|
text-shadow: none; }
|
||||||
|
|
||||||
|
.gjs-field {
|
||||||
|
background-color: rgba(0, 0, 0, 0.15);
|
||||||
|
box-shadow: none; }
|
||||||
|
|
||||||
|
.gjs-btnt.gjs-pn-active,
|
||||||
|
.gjs-pn-btn.gjs-pn-active {
|
||||||
|
box-shadow: none; }
|
||||||
|
|
||||||
|
.gjs-pn-btn:hover {
|
||||||
|
color: rgba(255, 255, 255, 0.75); }
|
||||||
|
|
||||||
|
.gjs-btnt.gjs-pn-active,
|
||||||
|
.gjs-color-active,
|
||||||
|
.gjs-pn-btn.gjs-pn-active,
|
||||||
|
.gjs-pn-btn:active,
|
||||||
|
.gjs-block:hover {
|
||||||
|
color: #35d7bb; }
|
||||||
|
|
||||||
|
#gjs-rte-toolbar .gjs-rte-btn,
|
||||||
|
.gjs-btn-prim,
|
||||||
|
.gjs-btnt,
|
||||||
|
.gjs-clm-tags .gjs-sm-composite.gjs-clm-field,
|
||||||
|
.gjs-clm-tags .gjs-sm-field.gjs-sm-composite,
|
||||||
|
.gjs-clm-tags .gjs-sm-stack #gjs-sm-add,
|
||||||
|
.gjs-color-main,
|
||||||
|
.gjs-mdl-dialog,
|
||||||
|
.gjs-off-prv,
|
||||||
|
.gjs-pn-btn,
|
||||||
|
.gjs-pn-panel,
|
||||||
|
.gjs-sm-sector .gjs-sm-composite.gjs-clm-field,
|
||||||
|
.gjs-sm-sector .gjs-sm-field.gjs-sm-composite,
|
||||||
|
.gjs-sm-sector .gjs-sm-stack #gjs-sm-add {
|
||||||
|
color: #a0aabf; }
|
||||||
|
|
||||||
|
#gjs-rte-toolbar,
|
||||||
|
.gjs-bg-main,
|
||||||
|
.gjs-clm-select option,
|
||||||
|
.gjs-clm-tags .gjs-sm-colorp-c,
|
||||||
|
.gjs-editor,
|
||||||
|
.gjs-mdl-dialog,
|
||||||
|
.gjs-nv-item .gjs-nv-title-c,
|
||||||
|
.gjs-off-prv,
|
||||||
|
.gjs-pn-panel,
|
||||||
|
.gjs-block,
|
||||||
|
.gjs-select option,
|
||||||
|
.gjs-sm-sector .gjs-sm-colorp-c,
|
||||||
|
.gjs-sm-select option,
|
||||||
|
.gjs-sm-unit option,
|
||||||
|
.sp-container {
|
||||||
|
background-color: #373d49; }
|
||||||
|
|
||||||
|
.gjs-import-label,
|
||||||
|
.gjs-export-label {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-size: 13px; }
|
||||||
|
|
||||||
|
.gjs-mdl-dialog .gjs-btn-import {
|
||||||
|
margin-top: 10px; }
|
||||||
|
|
||||||
|
.CodeMirror {
|
||||||
|
border-radius: 3px;
|
||||||
|
height: 450px;
|
||||||
|
font-family: sans-serif, monospace;
|
||||||
|
letter-spacing: 0.3px;
|
||||||
|
font-size: 12px; }
|
||||||
|
|
||||||
|
/* Extra */
|
||||||
|
.gjs-block {
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: 3px;
|
||||||
|
margin: 10px 2.5% 5px;
|
||||||
|
box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.15);
|
||||||
|
transition: box-shadow, color 0.2s ease 0s; }
|
||||||
|
|
||||||
|
.gjs-block:hover {
|
||||||
|
box-shadow: 0 3px 4px 0 rgba(0, 0, 0, 0.15); }
|
||||||
|
|
||||||
|
#gjs-pn-views-container.gjs-pn-panel {
|
||||||
|
padding: 39px 0 0; }
|
||||||
|
|
||||||
|
#gjs-pn-views.gjs-pn-panel {
|
||||||
|
padding: 0;
|
||||||
|
border: none; }
|
||||||
|
|
||||||
|
#gjs-pn-views .gjs-pn-btn {
|
||||||
|
margin: 0;
|
||||||
|
height: 40px;
|
||||||
|
padding: 10px;
|
||||||
|
width: 25%;
|
||||||
|
border-bottom: 2px solid rgba(0, 0, 0, 0.3); }
|
||||||
|
|
||||||
|
#gjs-pn-views .gjs-pn-active {
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
border-bottom: 2px solid #35d7bb;
|
||||||
|
border-radius: 0; }
|
||||||
|
|
||||||
|
#gjs-pn-devices-c {
|
||||||
|
padding-left: 30px; }
|
||||||
|
|
||||||
|
#gjs-pn-options {
|
||||||
|
padding-right: 30px; }
|
||||||
|
|
||||||
|
.gjs-sm-composite .gjs-sm-properties {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
justify-content: space-between; }
|
||||||
|
|
||||||
|
#gjs-sm-border-top-left-radius,
|
||||||
|
#gjs-sm-border-top-right-radius,
|
||||||
|
#gjs-sm-border-bottom-left-radius,
|
||||||
|
#gjs-sm-border-bottom-right-radius,
|
||||||
|
#gjs-sm-margin-top,
|
||||||
|
#gjs-sm-margin-bottom,
|
||||||
|
#gjs-sm-margin-right,
|
||||||
|
#gjs-sm-margin-left,
|
||||||
|
#gjs-sm-padding-top,
|
||||||
|
#gjs-sm-padding-bottom,
|
||||||
|
#gjs-sm-padding-right,
|
||||||
|
#gjs-sm-padding-left {
|
||||||
|
flex: 999 1 60px; }
|
||||||
|
|
||||||
|
#gjs-sm-border-width,
|
||||||
|
#gjs-sm-border-style,
|
||||||
|
#gjs-sm-border-color {
|
||||||
|
flex: 999 1 80px; }
|
||||||
|
|
||||||
|
#gjs-sm-margin-left,
|
||||||
|
#gjs-sm-padding-left {
|
||||||
|
order: 2; }
|
||||||
|
|
||||||
|
#gjs-sm-margin-right,
|
||||||
|
#gjs-sm-padding-right {
|
||||||
|
order: 3; }
|
||||||
|
|
||||||
|
#gjs-sm-margin-bottom,
|
||||||
|
#gjs-sm-padding-bottom {
|
||||||
|
order: 4; }
|
||||||
|
|
||||||
|
.gjs-field-radio {
|
||||||
|
width: 100%; }
|
||||||
|
|
||||||
|
.gjs-field-radio #gjs-sm-input-holder {
|
||||||
|
display: flex; }
|
||||||
|
|
||||||
|
.gjs-radio-item {
|
||||||
|
flex: 1 0 auto;
|
||||||
|
text-align: center; }
|
||||||
|
|
||||||
|
.gjs-sm-sector .gjs-sm-property.gjs-sm-list {
|
||||||
|
width: 50%; }
|
||||||
|
|
||||||
|
.gjs-mdl-content {
|
||||||
|
border-top: none; }
|
||||||
|
|
||||||
|
.gjs-sm-sector .gjs-sm-property .gjs-sm-layer.gjs-sm-active {
|
||||||
|
background-color: rgba(255, 255, 255, 0.09); }
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
#gjs-pn-views-container,
|
||||||
|
#gjs-pn-views{
|
||||||
|
min-width: 270px;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
.gjs-f-button::before {
|
||||||
|
content: 'B'; }
|
||||||
|
|
||||||
|
.gjs-f-divider::before {
|
||||||
|
content: 'D'; }
|
||||||
|
|
||||||
|
.gjs-mdl-dialog-sm {
|
||||||
|
width: 300px; }
|
||||||
|
|
||||||
|
.gjs-mdl-dialog form .gjs-sm-property {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-bottom: 15px; }
|
||||||
|
|
||||||
|
.gjs-mdl-dialog form .gjs-sm-label {
|
||||||
|
margin-bottom: 5px; }
|
||||||
|
|
||||||
|
#gjs-clm-status-c {
|
||||||
|
display: none; }
|
||||||
|
|
||||||
|
.anim-spin {
|
||||||
|
animation: 0.5s linear 0s normal none infinite running spin; }
|
||||||
|
|
||||||
|
.form-status {
|
||||||
|
float: right;
|
||||||
|
font-size: 14px; }
|
||||||
|
|
||||||
|
.text-danger {
|
||||||
|
color: #f92929; }
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg); }
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg); } }
|
2
resources/assets/js/grapesjs-blocks-basic.min.js
vendored
Normal file
2
resources/assets/js/grapesjs-blocks-basic.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
15
resources/assets/js/grapesjs-preset-newsletter.min.js
vendored
Normal file
15
resources/assets/js/grapesjs-preset-newsletter.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -2659,6 +2659,8 @@ $LANG = array(
|
|||||||
'tax_amount' => 'Tax Amount',
|
'tax_amount' => 'Tax Amount',
|
||||||
'tax_paid' => 'Tax Paid',
|
'tax_paid' => 'Tax Paid',
|
||||||
'none' => 'None',
|
'none' => 'None',
|
||||||
|
'proposals' => 'Proposals',
|
||||||
|
'new_proposal' => 'New Proposal',
|
||||||
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -348,6 +348,7 @@
|
|||||||
'recurring_invoices' => 'recurring',
|
'recurring_invoices' => 'recurring',
|
||||||
'credits' => false,
|
'credits' => false,
|
||||||
'quotes' => false,
|
'quotes' => false,
|
||||||
|
'proposals' => false,
|
||||||
'projects' => false,
|
'projects' => false,
|
||||||
'tasks' => false,
|
'tasks' => false,
|
||||||
'expenses' => false,
|
'expenses' => false,
|
||||||
@ -376,6 +377,7 @@
|
|||||||
'recurring_invoices',
|
'recurring_invoices',
|
||||||
'credits',
|
'credits',
|
||||||
'quotes',
|
'quotes',
|
||||||
|
'proposals',
|
||||||
'projects',
|
'projects',
|
||||||
'tasks',
|
'tasks',
|
||||||
'expenses',
|
'expenses',
|
||||||
|
122
resources/views/proposals/edit.blade.php
Normal file
122
resources/views/proposals/edit.blade.php
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
@extends('header')
|
||||||
|
|
||||||
|
@section('head')
|
||||||
|
@parent
|
||||||
|
|
||||||
|
<script src="{{ asset('js/grapesjs.min.js') }}?no_cache={{ NINJA_VERSION }}" type="text/javascript"></script>
|
||||||
|
<link href="{{ asset('css/grapesjs.css') }}?no_cache={{ NINJA_VERSION }}" rel="stylesheet" type="text/css"/>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.gjs-four-color {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
.gjs-block.fa {
|
||||||
|
font-size: 4em !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
@stop
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
|
||||||
|
{!! Former::open() !!}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
{!! Former::select('quote_id')->addOption('', '')
|
||||||
|
->label(trans('texts.quote'))
|
||||||
|
->addGroupClass('quote-select') !!}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
{!! Former::select('template_id')->addOption('', '')
|
||||||
|
->label(trans('texts.template'))
|
||||||
|
->addGroupClass('template-select') !!}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<center class="buttons">
|
||||||
|
{!! Button::normal(trans('texts.cancel'))
|
||||||
|
->appendIcon(Icon::create('remove-circle'))
|
||||||
|
->asLinkTo(HTMLUtils::previousUrl('/proposals')) !!}
|
||||||
|
|
||||||
|
{!! Button::success(trans("texts.save"))
|
||||||
|
->withAttributes(array('id' => 'saveButton', 'onclick' => 'onSaveClick()'))
|
||||||
|
->appendIcon(Icon::create('floppy-disk')) !!}
|
||||||
|
</center>
|
||||||
|
|
||||||
|
{!! Former::close() !!}
|
||||||
|
|
||||||
|
<div id="gjs"></div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
var quotes = {!! $quotes !!};
|
||||||
|
var quoteMap = {};
|
||||||
|
|
||||||
|
var templates = {!! $templates !!};
|
||||||
|
var templateMap = {};
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
var quoteId = {{ $quotePublicId ?: 0 }};
|
||||||
|
var $quoteSelect = $('select#quote_id');
|
||||||
|
for (var i = 0; i < quotes.length; i++) {
|
||||||
|
var quote = quotes[i];
|
||||||
|
quoteMap[quote.public_id] = quote;
|
||||||
|
$quoteSelect.append(new Option(quote.invoice_number + ' - ' + getClientDisplayName(quote.client), quote.public_id));
|
||||||
|
}
|
||||||
|
@include('partials/entity_combobox', ['entityType' => ENTITY_QUOTE])
|
||||||
|
|
||||||
|
var $proposal_templateSelect = $('select#template_id');
|
||||||
|
for (var i = 0; i < templates.length; i++) {
|
||||||
|
var template = templates[i];
|
||||||
|
templateMap[template.public_id] = template;
|
||||||
|
$templateSelect.append(new Option(template.name, template.public_id));
|
||||||
|
}
|
||||||
|
@include('partials/entity_combobox', ['entityType' => ENTITY_PROPOSAL_TEMPLATE])
|
||||||
|
|
||||||
|
var editor = grapesjs.init({
|
||||||
|
container : '#gjs',
|
||||||
|
components: '',
|
||||||
|
style: '',
|
||||||
|
showDevices: false,
|
||||||
|
plugins: ['gjs-preset-newsletter'],
|
||||||
|
//plugins: ['gjs-blocks-basic'],
|
||||||
|
storageManager: {type: 'none'},
|
||||||
|
panels: {
|
||||||
|
Xdefaults : [{
|
||||||
|
id : 'commands',
|
||||||
|
buttons : [{
|
||||||
|
id : 'smile',
|
||||||
|
className : 'fa fa-smile-o',
|
||||||
|
attributes : { title: 'Smile' }
|
||||||
|
}],
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
var blockManager = editor.BlockManager;
|
||||||
|
blockManager.add('h1-block', {
|
||||||
|
label: 'Heading',
|
||||||
|
category: 'Basic',
|
||||||
|
content: '<h1>Put your title here</h1>',
|
||||||
|
attributes: {
|
||||||
|
title: 'Insert h1 block',
|
||||||
|
class:'fa fa-smile-o'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
@stop
|
@ -205,6 +205,18 @@ Route::group(['middleware' => ['lookup:user', 'auth:user']], function () {
|
|||||||
Route::get('api/quotes/{client_id?}', 'QuoteController@getDatatable');
|
Route::get('api/quotes/{client_id?}', 'QuoteController@getDatatable');
|
||||||
Route::post('quotes/bulk', 'QuoteController@bulk');
|
Route::post('quotes/bulk', 'QuoteController@bulk');
|
||||||
|
|
||||||
|
Route::get('proposals/create/{quote_id?}', 'ProposalController@create');
|
||||||
|
Route::resource('proposals', 'ProposalController');
|
||||||
|
|
||||||
|
Route::get('proposal_templates/create', 'ProposalTemplateController@create');
|
||||||
|
Route::resource('proposal_templates', 'ProposalTemplateController');
|
||||||
|
|
||||||
|
Route::get('proposal_snippets/create', 'ProposalSnippetController@create');
|
||||||
|
Route::resource('proposal_snippets', 'ProposalSnippetController');
|
||||||
|
|
||||||
|
Route::get('proposal_categories/create', 'ProposalCategoryController@create');
|
||||||
|
Route::resource('proposal_categories', 'ProposalController');
|
||||||
|
|
||||||
Route::resource('payments', 'PaymentController');
|
Route::resource('payments', 'PaymentController');
|
||||||
Route::get('payments/create/{client_id?}/{invoice_id?}', 'PaymentController@create');
|
Route::get('payments/create/{client_id?}/{invoice_id?}', 'PaymentController@create');
|
||||||
Route::get('api/payments/{client_id?}', 'PaymentController@getDatatable');
|
Route::get('api/payments/{client_id?}', 'PaymentController@getDatatable');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user