1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-05 18:52:44 +01:00

Flutter Client ! (#3325)

* Working on emailing invoices

* Working on emailing and displaying email

* Working on emailing and displaying email

* Email invoices

* Fixes for html emails

* Restart queue after self-update

* Email Invoices

* Push Flutter Web Clientgit statusgit status!
This commit is contained in:
David Bomba 2020-02-13 22:27:42 +11:00 committed by GitHub
parent bf25b7db95
commit 4a3d37a42b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 172681 additions and 52 deletions

View File

@ -59,5 +59,13 @@ class ArtisanUpgrade extends Command
\Log::error("I wasn't able to optimize.");
}
try {
Artisan::call('queue:restart');
}catch(Exception $e) {
\Log::error("I wasn't able to restart the queue");
}
}
}

View File

@ -338,4 +338,8 @@ class BaseController extends Controller
}
public function flutterRoute()
{
return redirect('/index.html');
}
}

View File

@ -74,7 +74,8 @@ class CreateInvoicePdf implements ShouldQueue {
$path = $this->invoice->client->client_hash . '/invoices/';
$file_path = $path . $this->invoice->number . '-' . $this->contact->contact_key .'.pdf';
//$file_path = $path . $this->invoice->number . '-' . $this->contact->contact_key .'.pdf';
$file_path = $path . $this->invoice->number . '.pdf';
$modern = new Modern();
$designer = new Designer($modern, $this->invoice->client->getSetting('invoice_variables'));

View File

@ -64,15 +64,22 @@ class EmailInvoice implements ShouldQueue
$this->invoice->invitations->each(function ($invitation) use ($template_style) {
if ($invitation->contact->send_invoice && $invitation->contact->email) {
$message_array = $this->invoice->getEmailData('', $invitation->contact);
$message_array['title'] = &$message_array['subject'];
$message_array['footer'] = "Sent to ".$invitation->contact->present()->name();
//$message_array['footer'] = "Sent to ".$invitation->contact->present()->name();
$message_array['footer'] = "<a href='{$invitation->getLink()}'>Invoice Link</a>";
//change the runtime config of the mail provider here:
//send message
Mail::to($invitation->contact->email, $invitation->contact->present()->name())
->send(new TemplateEmail($message_array, $template_style, $invitation->contact->user, $invitation->contact->client));
->send(new TemplateEmail($message_array,
$template_style,
$invitation->contact->user,
$invitation->contact->client));
if (count(Mail::failures()) > 0) {
event(new InvoiceWasEmailedAndFailed($this->invoice, Mail::failures()));

View File

@ -346,17 +346,18 @@ class Invoice extends BaseModel
// $storage_path = 'public/' . $this->client->client_hash . '/invoices/'. $this->number . '.pdf';
$public_path = $this->client->client_hash . '/invoices/'. $this->number . '.pdf';
$public_path = $this->client->client_hash . '/invoices/'. $this->number . '.pdf';
$storage_path = $this->client->client_hash . '/invoices/'. $this->number . '.pdf';
$storage_path = 'storage/' . $this->client->client_hash . '/invoices/'. $this->number . '.pdf';
$disk = config('filesystems.default');
if (!Storage::exists($storage_path)) {
if (!Storage::disk($disk)->exists($public_path)) {
event(new InvoiceWasUpdated($this, $this->company));
CreateInvoicePdf::dispatch($this, $this->company, $this->client->primary_contact()->first());
}
return $public_path;
return $storage_path;
}
public function pdf_file_path()

View File

@ -25,7 +25,7 @@ class GetInvoicePdf
$path = $invoice->client->client_hash . '/invoices/';
$file_path = $path . $invoice->number . '-' . $contact->contact_key . '.pdf';
$file_path = $path . $invoice->number . '.pdf';
$disk = config('filesystems.default');

View File

@ -24,7 +24,6 @@ use Parsedown;
trait InvoiceEmailBuilder
{
/**
* Builds the correct template to send
* @param string $reminder_template The template name ie reminder1
@ -66,8 +65,10 @@ trait InvoiceEmailBuilder
}
}
$data['body'] = $this->parseTemplate($body_template, false, $contact);
$data['subject'] = $this->parseTemplate($subject_template, true, $contact);
$data['body'] = $this->parseTemplate($body_template, true, $contact);
$data['subject'] = $this->parseTemplate($subject_template, false, $contact);
if ($client->getSetting('pdf_email_attachment') !== false) {
$data['files'][] = $this->pdf_file_path();
@ -76,7 +77,7 @@ trait InvoiceEmailBuilder
return $data;
}
private function parseTemplate(string $template_data, bool $is_markdown = true, $contact) :string
private function parseTemplate(string $template_data, bool $is_markdown = true, $contact = null) :string
{
$invoice_variables = $this->makeValues($contact);
@ -85,11 +86,10 @@ trait InvoiceEmailBuilder
//process markdown
if ($is_markdown) {
//$data = Parsedown::instance()->line($data);
$converter = new CommonMarkConverter([
'html_input' => 'strip',
'allow_unsafe_links' => false,
'html_input' => 'allow',
'allow_unsafe_links' => true,
]);
$data = $converter->convertToHtml($data);

View File

@ -59,6 +59,9 @@ trait MakesDates
*/
public function formatDate($date, string $format) :string
{
if(!$date || strlen($date) < 1)
return '';
if (is_string($date)) {
$date = $this->convertToDateObject($date);
}

View File

@ -5,7 +5,7 @@ return [
'web_url' => 'https://www.invoiceninja.com',
'app_name' => env('APP_NAME'),
'site_url' => env('APP_URL', ''),
'app_domain' => env('APP_DOMAIN', 'invoiceninja.com'),
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
'app_version' => '0.0.1',
'api_version' => '0.0.1',
'terms_version' => '1.0.1',

View File

@ -0,0 +1 @@
{"web/assets/fonts/Roboto-Regular.ttf":["web/assets/fonts/Roboto-Regular.ttf"],"packages/font_awesome_flutter/lib/fonts/fa-brands-400.ttf":["packages/font_awesome_flutter/lib/fonts/fa-brands-400.ttf"],"packages/font_awesome_flutter/lib/fonts/fa-regular-400.ttf":["packages/font_awesome_flutter/lib/fonts/fa-regular-400.ttf"],"packages/font_awesome_flutter/lib/fonts/fa-solid-900.ttf":["packages/font_awesome_flutter/lib/fonts/fa-solid-900.ttf"],"assets/images/logo.png":["assets/images/logo.png"]}

View File

@ -0,0 +1,45 @@
[
{
"family": "Roboto",
"fonts": [
{
"asset": "fonts/Roboto-Regular.ttf"
}
]
},
{
"family": "MaterialIcons",
"fonts": [
{
"asset": "https://fonts.gstatic.com/s/materialicons/v42/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.woff2"
}
]
},
{
"family": "packages/font_awesome_flutter/FontAwesomeBrands",
"fonts": [
{
"weight": 400,
"asset": "packages/font_awesome_flutter/lib/fonts/fa-brands-400.ttf"
}
]
},
{
"family": "packages/font_awesome_flutter/FontAwesomeRegular",
"fonts": [
{
"weight": 400,
"asset": "packages/font_awesome_flutter/lib/fonts/fa-regular-400.ttf"
}
]
},
{
"family": "packages/font_awesome_flutter/FontAwesomeSolid",
"fonts": [
{
"weight": 900,
"asset": "packages/font_awesome_flutter/lib/fonts/fa-solid-900.ttf"
}
]
}
]

16269
public/assets/LICENSE Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

42
public/flutter_service_worker.js vendored Normal file
View File

@ -0,0 +1,42 @@
'use strict';
const CACHE_NAME = 'flutter-app-cache';
const RESOURCES = {
"/main.dart.js": "fa2c639a9577f242475de037e8c3c909",
"/index.html": "2b291048759a26809bc606c981d175ad",
"/assets/FontManifest.json": "280b2f61f6810d59bd1bcd4cf01d3bf4",
"/assets/packages/font_awesome_flutter/lib/fonts/fa-regular-400.ttf": "bdd8d75eb9e6832ccd3117e06c51f0d3",
"/assets/packages/font_awesome_flutter/lib/fonts/fa-solid-900.ttf": "d21f791b837673851dd14f7c132ef32e",
"/assets/packages/font_awesome_flutter/lib/fonts/fa-brands-400.ttf": "3ca122272cfac33efb09d0717efde2fa",
"/assets/LICENSE": "0134b30d1e80505d7a68fb6fa8d3e2cd",
"/assets/AssetManifest.json": "90d7b27343fcf361dbf6385b3055ce4f",
"/assets/fonts/Roboto-Regular.ttf": "3e1af3ef546b9e6ecef9f3ba197bf7d2",
"/assets/fonts/MaterialIcons-Regular.ttf": "56d3ffdef7a25659eab6a68a3fbfaf16",
"/assets/assets/images/logo.png": "090f69e23311a4b6d851b3880ae52541",
"/assets/web/assets/fonts/Roboto-Regular.ttf": "3e1af3ef546b9e6ecef9f3ba197bf7d2"
};
self.addEventListener('activate', function (event) {
event.waitUntil(
caches.keys().then(function (cacheName) {
return caches.delete(cacheName);
}).then(function (_) {
return caches.open(CACHE_NAME);
}).then(function (cache) {
return cache.addAll(Object.keys(RESOURCES));
})
);
});
self.addEventListener('fetch', function (event) {
event.respondWith(
caches.match(event.request)
.then(function (response) {
if (response) {
return response;
}
return fetch(event.request, {
credentials: 'include'
});
})
);
});

26
public/index.html Normal file
View File

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Invoice Ninja</title>
</head>
<body style="background-color:#888888;">
<!--
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('/flutter_service_worker.js');
});
}
</script>
-->
<script src="main.dart.js" type="application/javascript"></script>
<center style="font-family:Tahoma,Geneva,sans-serif;font-size:28px;color:white;padding-top:100px">
Loading...
</center>
</body>
</html>

156208
public/main.dart.js vendored Normal file

File diff suppressed because one or more lines are too long

16
public/main.dart.js.map Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,13 @@
{{ $body }}
<!DOCTYPE html>
<html lang="{{ App::getLocale() }}">
<head>
<meta charset="utf-8">
</head>
<body>
{!! $body !!}
{{ $footer }}
{!! $footer !!}
</body>
</html>

View File

@ -16,8 +16,7 @@ return $request->user();
});
*/
Route::group(['middleware' => ['api_secret_check']],
function () {
Route::group(['middleware' => ['api_secret_check']], function () {
Route::post('api/v1/signup', 'AccountController@store')->name('signup.submit');
Route::post('api/v1/oauth_login', 'Auth\LoginController@oauthApiLogin');
@ -85,7 +84,7 @@ Route::group(['middleware' => ['api_db', 'token_auth', 'locale'], 'prefix' => 'a
Route::post('payments/bulk', 'PaymentController@bulk')->name('payments.bulk');
Route::post('migrate', 'Migration\MigrateController@index')->name('migrate.start');
Route::post('migrate', 'MigrationController@index')->name('migrate.start');
// Route::resource('users', 'UserController')->middleware('password_protected'); // name = (users. index / create / show / update / destroy / edit
Route::get('users', 'UserController@index');

View File

@ -3,38 +3,39 @@
* Authentication Routes Laravel Defaults... replaces //Auth::routes();
*/
Route::get('login', 'Auth\LoginController@showLoginForm')->name('login');
Route::post('login', 'Auth\LoginController@login');
Route::post('logout', 'Auth\LoginController@logout')->name('logout');
// Route::get('login', 'Auth\LoginController@showLoginForm')->name('login');
// Route::post('login', 'Auth\LoginController@login');
// Route::post('logout', 'Auth\LoginController@logout')->name('logout');
/*
* Signup Routes
*/
Route::redirect('/', '/login', 301);
Route::get('signup', 'AccountController@index')->name('signup');
Route::post('signup', 'AccountController@store')->name('signup.submit');
Route::get('/', 'BaseController@flutterRoute')->middleware('guest');
// Route::get('signup', 'AccountController@index')->name('signup');
// Route::post('signup', 'AccountController@store')->name('signup.submit');
/*
* Password Reset Routes...
*/
Route::get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request');
Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email');
Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');
Route::post('password/reset', 'Auth\ResetPasswordController@reset')->name('password.update');
// Route::get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request');
// Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email');
// Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');
// Route::post('password/reset', 'Auth\ResetPasswordController@reset')->name('password.update');
/*
* Social authentication
*/
Route::get('auth/{provider}', 'Auth\LoginController@redirectToProvider');
Route::get('auth/{provider}/create', 'Auth\LoginController@redirectToProviderAndCreate');
// Route::get('auth/{provider}', 'Auth\LoginController@redirectToProvider');
// Route::get('auth/{provider}/create', 'Auth\LoginController@redirectToProviderAndCreate');
/*
* Authenticated User Routes
*/
/*
Route::group(['middleware' => ['auth:user', 'web_db']], function () {
Route::resource('dashboard', 'DashboardController'); // name = (dashboard. index / create / show / update / destroy / edit
@ -58,7 +59,7 @@ Route::group(['middleware' => ['auth:user', 'web_db']], function () {
Route::post('clients/bulk', 'ClientController@bulk')->name('clients.bulk');
Route::resource('client_statement', 'ClientStatementController@statement'); // name = (client_statement. index / create / show / update / destroy / edit
/*
Route::resource('tasks', 'TaskController'); // name = (tasks. index / create / show / update / destroy / edit
Route::post('tasks/bulk', 'TaskController@bulk')->name('tasks.bulk');
@ -78,33 +79,22 @@ Route::group(['middleware' => ['auth:user', 'web_db']], function () {
Route::resource('user', 'UserProfileController'); // name = (clients. index / create / show / update / destroy / edit
Route::get('settings', 'SettingsController@index')->name('user.settings');
*/
});
*/
/*
* Inbound routes requiring DB Lookup
*/
Route::group(['middleware' => ['url_db']], function () {
// Route::group(['middleware' => ['url_db']], function () {
Route::get('/user/confirm/{confirmation_code}', 'UserController@confirm');
// Route::get('/user/confirm/{confirmation_code}', 'UserController@confirm');
});
// });
/*
* Injects users translation strings in json format for frontend consumption.
*/
Route::get('js/lang.js', 'TranslationController@index')->name('assets.lang');
/* Dev Playground
Route::get('/mailable', function () {
$user = App\Models\User::find(1);
return new App\Mail\VerifyUser($user);
});
*/
//Route::get('js/lang.js', 'TranslationController@index')->name('assets.lang');