mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Improved search.
This commit is contained in:
parent
1ebb59b352
commit
22a2fe3f61
@ -14,7 +14,7 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use FireflyIII\Support\Search\SearchInterface;
|
||||
use Input;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* Class SearchController
|
||||
@ -30,12 +30,6 @@ class SearchController extends Controller
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -45,16 +39,21 @@ class SearchController extends Controller
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function index(SearchInterface $searcher)
|
||||
public function index(Request $request, SearchInterface $searcher)
|
||||
{
|
||||
|
||||
$minSearchLen = 1;
|
||||
$subTitle = null;
|
||||
$query = null;
|
||||
$result = [];
|
||||
$title = trans('firefly.search');
|
||||
$limit = 20;
|
||||
$mainTitleIcon = 'fa-search';
|
||||
if (!is_null(Input::get('q')) && strlen(Input::get('q')) > 0) {
|
||||
$query = trim(Input::get('q'));
|
||||
|
||||
// set limit for search:
|
||||
$searcher->setLimit($limit);
|
||||
|
||||
if (!is_null($request->get('q')) && strlen($request->get('q')) >= $minSearchLen) {
|
||||
$query = trim(strtolower($request->get('q')));
|
||||
$words = explode(' ', $query);
|
||||
$subTitle = trans('firefly.search_results_for', ['query' => $query]);
|
||||
|
||||
@ -67,7 +66,7 @@ class SearchController extends Controller
|
||||
|
||||
}
|
||||
|
||||
return view('search.index', compact('title', 'subTitle', 'mainTitleIcon', 'query', 'result'));
|
||||
return view('search.index', compact('title', 'subTitle', 'limit', 'mainTitleIcon', 'query', 'result'));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -94,7 +94,6 @@ class FireflyServiceProvider extends ServiceProvider
|
||||
);
|
||||
|
||||
$this->app->bind('FireflyIII\Repositories\Currency\CurrencyRepositoryInterface', 'FireflyIII\Repositories\Currency\CurrencyRepository');
|
||||
$this->app->bind('FireflyIII\Support\Search\SearchInterface', 'FireflyIII\Support\Search\Search');
|
||||
$this->app->bind('FireflyIII\Repositories\User\UserRepositoryInterface', 'FireflyIII\Repositories\User\UserRepository');
|
||||
$this->app->bind('FireflyIII\Helpers\Attachments\AttachmentHelperInterface', 'FireflyIII\Helpers\Attachments\AttachmentHelper');
|
||||
$this->app->bind(
|
||||
|
59
app/Providers/SearchServiceProvider.php
Normal file
59
app/Providers/SearchServiceProvider.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* AccountServiceProvider.php
|
||||
* Copyright (C) 2016 thegrumpydictator@gmail.com
|
||||
*
|
||||
* This software may be modified and distributed under the terms of the
|
||||
* Creative Commons Attribution-ShareAlike 4.0 International License.
|
||||
*
|
||||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
declare(strict_types = 1);
|
||||
|
||||
|
||||
namespace FireflyIII\Providers;
|
||||
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
/**
|
||||
* Class SearchServiceProvider
|
||||
*
|
||||
* @package FireflyIII\Providers
|
||||
*/
|
||||
class SearchServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap the application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->app->bind(
|
||||
'FireflyIII\Support\Search\SearchInterface',
|
||||
function (Application $app, array $arguments) {
|
||||
if (!isset($arguments[0]) && $app->auth->check()) {
|
||||
return app('FireflyIII\Support\Search\Search', [auth()->user()]);
|
||||
}
|
||||
if (!isset($arguments[0]) && !$app->auth->check()) {
|
||||
throw new FireflyException('There is no user present.');
|
||||
}
|
||||
|
||||
return app('FireflyIII\Support\Search\Search', $arguments);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
@ -78,7 +78,7 @@ class TransactionMatcher
|
||||
do {
|
||||
// Fetch a batch of transactions from the database
|
||||
$collector = new JournalCollector(auth()->user());
|
||||
$collector->setAllAssetAccounts()->setLimit($pageSize * 2)->setPage($page)->setTypes($this->transactionTypes);
|
||||
$collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page)->setTypes($this->transactionTypes);
|
||||
$set = $collector->getPaginatedJournals();
|
||||
Log::debug(sprintf('Found %d journals to check. ', $set->count()));
|
||||
|
||||
@ -105,7 +105,7 @@ class TransactionMatcher
|
||||
Log::debug(sprintf('Page is now %d, processed is %d', $page, $processed));
|
||||
|
||||
// Check for conditions to finish the loop
|
||||
$reachedEndOfList = $set->count() < $pageSize;
|
||||
$reachedEndOfList = $set->count() < 1;
|
||||
$foundEnough = $result->count() >= $this->limit;
|
||||
$searchedEnough = ($processed >= $this->range);
|
||||
|
||||
|
@ -14,11 +14,15 @@ declare(strict_types = 1);
|
||||
namespace FireflyIII\Support\Search;
|
||||
|
||||
|
||||
use FireflyIII\Helpers\Collector\JournalCollector;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Budget;
|
||||
use FireflyIII\Models\Category;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||
use FireflyIII\Models\Tag;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class Search
|
||||
@ -27,20 +31,46 @@ use Illuminate\Support\Collection;
|
||||
*/
|
||||
class Search implements SearchInterface
|
||||
{
|
||||
/** @var int */
|
||||
private $limit = 100;
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* AttachmentRepository constructor.
|
||||
*
|
||||
* @param User $user
|
||||
*/
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* The search will assume that the user does not have so many accounts
|
||||
* that this search should be paginated.
|
||||
*
|
||||
* @param array $words
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function searchAccounts(array $words): Collection
|
||||
{
|
||||
return auth()->user()->accounts()->with('accounttype')->where(
|
||||
function (EloquentBuilder $q) use ($words) {
|
||||
foreach ($words as $word) {
|
||||
$q->orWhere('name', 'LIKE', '%' . e($word) . '%');
|
||||
$accounts = $this->user->accounts()->get();
|
||||
/** @var Collection $result */
|
||||
$result = $accounts->filter(
|
||||
function (Account $account) use ($words) {
|
||||
if ($this->strpos_arr(strtolower($account->name), $words)) {
|
||||
return $account;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
)->get();
|
||||
);
|
||||
|
||||
$result = $result->slice(0, $this->limit);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -51,46 +81,46 @@ class Search implements SearchInterface
|
||||
public function searchBudgets(array $words): Collection
|
||||
{
|
||||
/** @var Collection $set */
|
||||
$set = auth()->user()->budgets()->get();
|
||||
$newSet = $set->filter(
|
||||
function (Budget $b) use ($words) {
|
||||
$found = 0;
|
||||
foreach ($words as $word) {
|
||||
if (!(strpos(strtolower($b->name), strtolower($word)) === false)) {
|
||||
$found++;
|
||||
}
|
||||
$set = auth()->user()->budgets()->get();
|
||||
/** @var Collection $result */
|
||||
$result = $set->filter(
|
||||
function (Budget $budget) use ($words) {
|
||||
if ($this->strpos_arr(strtolower($budget->name), $words)) {
|
||||
return $budget;
|
||||
}
|
||||
|
||||
return $found > 0;
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
return $newSet;
|
||||
$result = $result->slice(0, $this->limit);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search assumes the user does not have that many categories. So no paginated search.
|
||||
*
|
||||
* @param array $words
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function searchCategories(array $words): Collection
|
||||
{
|
||||
/** @var Collection $set */
|
||||
$set = auth()->user()->categories()->get();
|
||||
$newSet = $set->filter(
|
||||
function (Category $c) use ($words) {
|
||||
$found = 0;
|
||||
foreach ($words as $word) {
|
||||
if (!(strpos(strtolower($c->name), strtolower($word)) === false)) {
|
||||
$found++;
|
||||
}
|
||||
$categories = $this->user->categories()->get();
|
||||
/** @var Collection $result */
|
||||
$result = $categories->filter(
|
||||
function (Category $category) use ($words) {
|
||||
if ($this->strpos_arr(strtolower($category->name), $words)) {
|
||||
return $category;
|
||||
}
|
||||
|
||||
return $found > 0;
|
||||
return false;
|
||||
}
|
||||
);
|
||||
$result = $result->slice(0, $this->limit);
|
||||
|
||||
return $newSet;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,7 +131,21 @@ class Search implements SearchInterface
|
||||
*/
|
||||
public function searchTags(array $words): Collection
|
||||
{
|
||||
return new Collection;
|
||||
$tags = $this->user->tags()->get();
|
||||
|
||||
/** @var Collection $result */
|
||||
$result = $tags->filter(
|
||||
function (Tag $tag) use ($words) {
|
||||
if ($this->strpos_arr(strtolower($tag->tag), $words)) {
|
||||
return $tag;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
$result = $result->slice(0, $this->limit);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -111,40 +155,86 @@ class Search implements SearchInterface
|
||||
*/
|
||||
public function searchTransactions(array $words): Collection
|
||||
{
|
||||
// decrypted transaction journals:
|
||||
$decrypted = auth()->user()->transactionJournals()->expanded()->where('transaction_journals.encrypted', 0)->where(
|
||||
function (EloquentBuilder $q) use ($words) {
|
||||
foreach ($words as $word) {
|
||||
$q->orWhere('transaction_journals.description', 'LIKE', '%' . e($word) . '%');
|
||||
}
|
||||
}
|
||||
)->get(TransactionJournal::queryFields());
|
||||
$pageSize = 100;
|
||||
$processed = 0;
|
||||
$page = 1;
|
||||
$result = new Collection();
|
||||
do {
|
||||
$collector = new JournalCollector($this->user);
|
||||
$collector->setAllAssetAccounts()->setLimit($pageSize)->setPage($page);
|
||||
$set = $collector->getPaginatedJournals();
|
||||
Log::debug(sprintf('Found %d journals to check. ', $set->count()));
|
||||
|
||||
// encrypted
|
||||
$all = auth()->user()->transactionJournals()->expanded()->where('transaction_journals.encrypted', 1)->get(TransactionJournal::queryFields());
|
||||
$set = $all->filter(
|
||||
function (TransactionJournal $journal) use ($words) {
|
||||
foreach ($words as $word) {
|
||||
$haystack = strtolower($journal->description);
|
||||
$word = strtolower($word);
|
||||
if (!(strpos($haystack, $word) === false)) {
|
||||
return $journal;
|
||||
// Filter transactions that match the given triggers.
|
||||
$filtered = $set->filter(
|
||||
function (Transaction $transaction) use ($words) {
|
||||
// check descr of journal:
|
||||
if ($this->strpos_arr(strtolower(strval($transaction->description)), $words)) {
|
||||
return $transaction;
|
||||
}
|
||||
|
||||
// check descr of transaction
|
||||
if ($this->strpos_arr(strtolower(strval($transaction->transaction_description)), $words)) {
|
||||
return $transaction;
|
||||
}
|
||||
|
||||
// return false:
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
return null;
|
||||
Log::debug(sprintf('Found %d journals that match.', $filtered->count()));
|
||||
|
||||
// merge:
|
||||
/** @var Collection $result */
|
||||
$result = $result->merge($filtered);
|
||||
Log::debug(sprintf('Total count is now %d', $result->count()));
|
||||
|
||||
// Update counters
|
||||
$page++;
|
||||
$processed += count($set);
|
||||
|
||||
Log::debug(sprintf('Page is now %d, processed is %d', $page, $processed));
|
||||
|
||||
// Check for conditions to finish the loop
|
||||
$reachedEndOfList = $set->count() < 1;
|
||||
$foundEnough = $result->count() >= $this->limit;
|
||||
|
||||
Log::debug(sprintf('reachedEndOfList: %s', var_export($reachedEndOfList, true)));
|
||||
Log::debug(sprintf('foundEnough: %s', var_export($foundEnough, true)));
|
||||
|
||||
} while (!$reachedEndOfList && !$foundEnough);
|
||||
|
||||
$result = $result->slice(0, $this->limit);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $limit
|
||||
*/
|
||||
public function setLimit(int $limit)
|
||||
{
|
||||
$this->limit = $limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $haystack
|
||||
* @param array $needle
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function strpos_arr(string $haystack, array $needle)
|
||||
{
|
||||
if (strlen($haystack) === 0) {
|
||||
return false;
|
||||
}
|
||||
foreach ($needle as $what) {
|
||||
if (($pos = strpos($haystack, $what)) !== false) {
|
||||
return true;
|
||||
}
|
||||
);
|
||||
$filtered = $set->merge($decrypted);
|
||||
$filtered = $filtered->sortBy(
|
||||
function (TransactionJournal $journal) {
|
||||
return intval($journal->date->format('U'));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$filtered = $filtered->reverse();
|
||||
|
||||
return $filtered;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -208,6 +208,7 @@ return [
|
||||
FireflyIII\Providers\PiggyBankServiceProvider::class,
|
||||
FireflyIII\Providers\RuleServiceProvider::class,
|
||||
FireflyIII\Providers\RuleGroupServiceProvider::class,
|
||||
FireflyIII\Providers\SearchServiceProvider::class,
|
||||
FireflyIII\Providers\TagServiceProvider::class,
|
||||
|
||||
|
||||
|
@ -67,7 +67,19 @@ return [
|
||||
'warning_much_data' => ':days days of data may take a while to load.',
|
||||
'registered' => 'You have registered successfully!',
|
||||
'search' => 'Search',
|
||||
'search_found_accounts' => 'Found :count account(s) for your query.',
|
||||
'search_found_categories' => 'Found :count category(ies) for your query.',
|
||||
'search_found_budgets' => 'Found :count budget(s) for your query.',
|
||||
'search_found_tags' => 'Found :count tag(s) for your query.',
|
||||
'search_found_transactions' => 'Found :count transaction(s) for your query.',
|
||||
'results_limited' => 'The results are limited to :count entries.',
|
||||
'tagbalancingAct' => 'Balancing act',
|
||||
'tagadvancePayment' => 'Advance payment',
|
||||
'tagnothing' => '',
|
||||
'Default asset account' => 'Default asset account',
|
||||
'no_budget_pointer' => 'You seem to have no budgets yet. You should create some on the <a href="/budgets">budgets</a>-page. Budgets can help you keep track of expenses.',
|
||||
'Savings account' => 'Savings account',
|
||||
'Credit card' => 'Credit card',
|
||||
'source_accounts' => 'Source account(s)',
|
||||
'destination_accounts' => 'Destination account(s)',
|
||||
'user_id_is' => 'Your user id is <strong>:user</strong>',
|
||||
|
@ -5,19 +5,35 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if query %}
|
||||
{% if query == "" %}
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<p>{{ 'no_results_for_empty_search'|_ }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if query %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<p>{{ trans('firefly.results_limited', {count: limit}) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
||||
|
||||
{% if result.transactions|length > 0 %}
|
||||
<div class="col-lg-6 col-md-12 col-sm-12">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Transactions</h3>
|
||||
<h3 class="box-title">{{ 'transactions'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body no-padding">
|
||||
{% include 'list.journals-tiny' with {'transactions' : result.transactions} %}
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<span class="pull-right">Results: {{ result.transactions|length }}</span>
|
||||
<div class="box-body">
|
||||
<p>
|
||||
{{ trans('firefly.search_found_transactions', {count: result.transactions|length}) }}
|
||||
</p>
|
||||
{% include 'search.partials.transactions' with {'transactions' : result.transactions} %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -26,19 +42,13 @@
|
||||
<div class="col-lg-6 col-md-12 col-sm-12">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Categories</h3>
|
||||
<h3 class="box-title">{{ 'categories'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body no-padding">
|
||||
<div class="list-group">
|
||||
{% for category in result.categories %}
|
||||
<a class="list-group-item" title="{{ category.name }}" href="{{ route('categories.show',category.id) }}">
|
||||
{{ category.name }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<span class="pull-right">Results: {{ result['categories']|length }}</span>
|
||||
<div class="box-body">
|
||||
<p>
|
||||
{{ trans('firefly.search_found_categories', {count: result.categories|length}) }}
|
||||
</p>
|
||||
{% include 'search.partials.categories' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -47,13 +57,13 @@
|
||||
<div class="col-lg-6 col-md-12 col-sm-12">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Tags</h3>
|
||||
<h3 class="box-title">{{ 'tags'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body no-padding">
|
||||
<p>Bla bla</p>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<span class="pull-right">Results: {{ result['tags']|length }}</span>
|
||||
<div class="box-body">
|
||||
<p>
|
||||
{{ trans('firefly.search_found_tags', {count: result.tags|length}) }}
|
||||
</p>
|
||||
{% include 'search.partials.tags' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -62,19 +72,13 @@
|
||||
<div class="col-lg-6 col-md-12 col-sm-12">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Accounts</h3>
|
||||
<h3 class="box-title">{{ 'accounts'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body no-padding">
|
||||
<div class="list-group">
|
||||
{% for account in result.accounts %}
|
||||
<a class="list-group-item" title="{{ account.name }}" href="{{ route('accounts.show',account.id) }}">
|
||||
{{ account.name }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<span class="pull-right">Results: {{ result['accounts'].count() }}</span>
|
||||
<div class="box-body">
|
||||
<p>
|
||||
{{ trans('firefly.search_found_accounts', {count: result.accounts|length}) }}
|
||||
</p>
|
||||
{% include 'search.partials.accounts' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -83,19 +87,13 @@
|
||||
<div class="col-lg-6 col-md-12 col-sm-12">
|
||||
<div class="box">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Budgets</h3>
|
||||
<h3 class="box-title">{{ 'budgets'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="list-group">
|
||||
{% for budget in result.budgets %}
|
||||
<a class="list-group-item" title="{{ budget.name }}" href="{{ route('budgets.show',budget.id) }}">
|
||||
{{ budget.name }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer">
|
||||
<span class="pull-right">Results: {{ result['budgets'].count() }}</span>
|
||||
<p>
|
||||
{{ trans('firefly.search_found_budgets', {count: result.budgets|length}) }}
|
||||
</p>
|
||||
{% include 'search.partials.budgets' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -106,8 +104,8 @@
|
||||
|
||||
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script type="text/javascript">
|
||||
var query = '{{ query }}';
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script type="text/javascript">
|
||||
var query = '{{ query }}';
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
34
resources/views/search/partials/accounts.twig
Normal file
34
resources/views/search/partials/accounts.twig
Normal file
@ -0,0 +1,34 @@
|
||||
<table class="table table-hover sortable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="hidden-sm hidden-xs" data-defaultsort="disabled"> </th>
|
||||
<th>{{ trans('list.name') }}</th>
|
||||
<th class="hidden-sm hidden-xs">{{ trans('list.type') }}</th>
|
||||
<th class="hidden-sm hidden-xs">{{ trans('list.role') }}</th>
|
||||
<th class="hidden-sm hidden-xs">{{ trans('list.iban') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for account in result.accounts %}
|
||||
<tr>
|
||||
<td class="hidden-sm hidden-xs">
|
||||
<div class="btn-group btn-group-xs">
|
||||
<a class="btn btn-default btn-xs" href="{{ route('accounts.edit',account.id) }}"><i class="fa fa-fw fa-pencil"></i></a>
|
||||
<a class="btn btn-danger btn-xs" href="{{ route('accounts.delete',account.id) }}"><i class="fa fa-fw fa-trash-o"></i></a>
|
||||
</div>
|
||||
</td>
|
||||
<td><a href="{{ route('accounts.show',account.id) }}">{{ account.name }}</a></td>
|
||||
<td>{{ trans('firefly.'~account.accountType.type) }}</td>
|
||||
<td class="hidden-sm hidden-xs">
|
||||
{% for entry in account.accountmeta %}
|
||||
{% if entry.name == 'accountRole' %}
|
||||
{{ trans('firefly.'~entry.data|getAccountRole) }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td class="hidden-sm hidden-xs">{{ account.iban }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
</table>
|
22
resources/views/search/partials/budgets.twig
Normal file
22
resources/views/search/partials/budgets.twig
Normal file
@ -0,0 +1,22 @@
|
||||
<table class="table table-hover sortable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="hidden-sm hidden-xs" data-defaultsort="disabled"> </th>
|
||||
<th>{{ trans('list.name') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for budget in result.budgets %}
|
||||
<tr>
|
||||
<td class="hidden-sm hidden-xs" style="width:10%;">
|
||||
<div class="btn-group btn-group-xs">
|
||||
<a class="btn btn-default btn-xs" href="{{ route('budgets.edit', budget.id) }}"><i class="fa fa-fw fa-pencil"></i></a>
|
||||
<a class="btn btn-danger btn-xs" href="{{ route('budgets.delete', budget.id) }}"><i class="fa fa-fw fa-trash-o"></i></a>
|
||||
</div>
|
||||
</td>
|
||||
<td><a href="{{ route('budgets.show',budget.id) }}">{{ budget.name }}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
</table>
|
22
resources/views/search/partials/categories.twig
Normal file
22
resources/views/search/partials/categories.twig
Normal file
@ -0,0 +1,22 @@
|
||||
<table class="table table-hover sortable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="hidden-sm hidden-xs" data-defaultsort="disabled"> </th>
|
||||
<th>{{ trans('list.name') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for category in result.categories %}
|
||||
<tr>
|
||||
<td class="hidden-sm hidden-xs" style="width:10%;">
|
||||
<div class="btn-group btn-group-xs">
|
||||
<a class="btn btn-default btn-xs" href="{{ route('categories.edit',category.id) }}"><i class="fa fa-fw fa-pencil"></i></a>
|
||||
<a class="btn btn-danger btn-xs" href="{{ route('categories.delete',category.id) }}"><i class="fa fa-fw fa-trash-o"></i></a>
|
||||
</div>
|
||||
</td>
|
||||
<td><a href="{{ route('categories.show',category.id) }}">{{ category.name }}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
</table>
|
24
resources/views/search/partials/tags.twig
Normal file
24
resources/views/search/partials/tags.twig
Normal file
@ -0,0 +1,24 @@
|
||||
<table class="table table-hover sortable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="hidden-sm hidden-xs" data-defaultsort="disabled"> </th>
|
||||
<th>{{ trans('list.name') }}</th>
|
||||
<th>{{ trans('list.type') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for tag in result.tags %}
|
||||
<tr>
|
||||
<td class="hidden-sm hidden-xs" style="width:10%;">
|
||||
<div class="btn-group btn-group-xs">
|
||||
<a class="btn btn-default btn-xs" href="{{ route('tags.edit', tag.id) }}"><i class="fa fa-fw fa-pencil"></i></a>
|
||||
<a class="btn btn-danger btn-xs" href="{{ route('tags.delete', tag.id) }}"><i class="fa fa-fw fa-trash-o"></i></a>
|
||||
</div>
|
||||
</td>
|
||||
<td><a href="{{ route('tags.show',tag.id) }}">{{ tag.tag }}</a></td>
|
||||
<td>{{ ('tag'~tag.tagMode)|_ }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
</table>
|
81
resources/views/search/partials/transactions.twig
Normal file
81
resources/views/search/partials/transactions.twig
Normal file
@ -0,0 +1,81 @@
|
||||
{{ journals.render|raw }}
|
||||
|
||||
<table class="table table-hover table-compressed {% if sorting %}sortable-table{% endif %}">
|
||||
<thead>
|
||||
<tr class="ignore">
|
||||
<th class="hidden-xs" colspan="2"> </th>
|
||||
<th>{{ trans('list.description') }}</th>
|
||||
<th>{{ trans('list.amount') }}</th>
|
||||
<th class="hidden-sm hidden-xs">{{ trans('list.date') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for transaction in transactions %}
|
||||
<tr class="drag" data-date="{{ transaction.date.format('Y-m-d') }}" data-id="{{ transaction.journal_id }}">
|
||||
<td class="hidden-xs">
|
||||
<div class="btn-group btn-group-xs edit_buttons">
|
||||
<a href="{{ route('transactions.edit',transaction.journal_id) }}" class="btn btn-xs btn-default"><i class="fa fa-fw fa-pencil"></i></a>
|
||||
<a href="{{ route('transactions.delete',transaction.journal_id) }}" class="btn btn-xs btn-danger"><i class="fa fa-fw fa-trash-o"></i></a>
|
||||
</div>
|
||||
</td>
|
||||
<td class="hidden-xs">
|
||||
{{ transaction|typeIconTransaction }}
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ route('transactions.show',transaction.journal_id) }}">
|
||||
|
||||
{% if transaction.transaction_description|length > 0 %}
|
||||
{{ transaction.transaction_description }} ({{ transaction.description }})
|
||||
{% else %}
|
||||
{{ transaction.description }}
|
||||
{% endif %}
|
||||
</a>
|
||||
{{ splitJournalIndicator(transaction.journal_id) }}
|
||||
|
||||
{% if transaction.transactionJournal.attachments|length > 0 %}
|
||||
<i class="fa fa-paperclip"
|
||||
title="{{ Lang.choice('firefly.nr_of_attachments', journal.attachments|length, {count: journal.attachments|length}) }}"></i>
|
||||
{% endif %}
|
||||
|
||||
</td>
|
||||
<td>
|
||||
<!-- format amount of transaction -->
|
||||
{{ formatAmountWithCode(transaction.transaction_amount, transaction.transaction_currency_code) }}
|
||||
<!-- and then amount of journal itself. -->
|
||||
{{ optionalJournalAmount(transaction.journal_id, transaction.transaction_amount,
|
||||
transaction.transaction_currency_code, transaction.transaction_type_type) }}
|
||||
|
||||
|
||||
</td>
|
||||
<td class="hidden-sm hidden-xs">
|
||||
{{ transaction.date.formatLocalized(monthAndDayFormat) }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="row mass_edit_all" style="display: none;">
|
||||
<div class="col-lg-6 col-md-12 col-sm-12 col-xs-12">
|
||||
<div class="mass_button_options btn-group btn-group" style="display:none;">
|
||||
<a href="#" class="btn btn-default mass_edit"><i class="fa fa-fw fa-pencil"></i> <span>{{ 'edit_selected'|_ }}</span></a>
|
||||
<a href="#" class="btn btn-danger mass_delete"><i class="fa fa-fw fa-trash"></i> <span>{{ 'delete_selected'|_ }}</span></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6 col-md-12 col-sm-12 col-xs-12">
|
||||
|
||||
<div class="mass_buttons btn-group btn-group pull-right">
|
||||
<a href="#" class="btn btn-default mass_select"><i class="fa fa-fw fa-check-square-o"></i> {{ 'select_transactions'|_ }}</a>
|
||||
<a href="#" class="btn btn-default mass_stop_select" style="display:none;"><i class="fa faw-fw fa-square-o"
|
||||
aria-hidden="true"></i> {{ 'stop_selection'|_ }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
{{ journals.render|raw }}
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var edit_selected_txt = "{{ 'edit_selected'|_ }}";
|
||||
var delete_selected_txt = "{{ 'delete_selected'|_ }}";
|
||||
</script>
|
Loading…
Reference in New Issue
Block a user