No known key found for this signature in database
GPG Key ID: B49A324B7EAD6D80
19 changed files with 2285 additions and 19 deletions
-
5app/Http/Controllers/Import/Nordigen/SelectionController.php
-
22resources/js/v2/src/pages/configuration/index.js
-
195resources/js/v2/src/pages/conversion/index.js
-
53resources/js/v2/src/pages/selection/gocardless.js
-
187resources/js/v2/src/pages/submit/index.js
-
12resources/js/v2/vite.config.js
-
2resources/views/v1/import/004-configure/index.twig
-
72resources/views/v2/components/conversion-messages.blade.php
-
22resources/views/v2/components/firefly-iii-account-generic.blade.php
-
31resources/views/v2/components/importer-account-title.blade.php
-
21resources/views/v2/components/importer-account.blade.php
-
861resources/views/v2/import/004-configure/index.blade.php
-
140resources/views/v2/import/005-roles/index-csv.blade.php
-
129resources/views/v2/import/006-mapping/index.blade.php
-
109resources/views/v2/import/007-convert/index.blade.php
-
104resources/views/v2/import/008-submit/index.blade.php
-
211resources/views/v2/import/009-selection/index.blade.php
-
116resources/views/v2/import/011-connection/index.blade.php
-
12resources/views/v2/index.blade.php
@ -0,0 +1,195 @@ |
|||
/* |
|||
* index.js |
|||
* Copyright (c) 2024 james@firefly-iii.org |
|||
* |
|||
* This file is part of the Firefly III Data Importer |
|||
* (https://github.com/firefly-iii/data-importer).
|
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU Affero General Public License as |
|||
* published by the Free Software Foundation, either version 3 of the |
|||
* License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU Affero General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Affero General Public License |
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
import '../../boot/bootstrap.js'; |
|||
|
|||
|
|||
let index = function () { |
|||
return { |
|||
flow: '', |
|||
identifier: '', |
|||
nextUrl: '', |
|||
pageStatus: { |
|||
triedToStart: false, |
|||
status: 'init', |
|||
}, |
|||
post: { |
|||
result: '', |
|||
errored: false, |
|||
running: false, |
|||
done: false, |
|||
}, |
|||
messages: { |
|||
messages: [], |
|||
warnings: [], |
|||
errors: [], |
|||
}, |
|||
checkCount: 0, |
|||
maxCheckCount: 600, |
|||
functionName() { |
|||
|
|||
}, |
|||
showJobMessages() { |
|||
return this.messages.messages.length > 0 || this.messages.warnings.length > 0 || this.messages.errors.length > 0; |
|||
}, |
|||
showStartButton() { |
|||
return('init' === this.pageStatus.status || 'waiting_to_start' === this.pageStatus.status) && false === this.pageStatus.triedToStart && false === this.post.errored; |
|||
}, |
|||
showWaitingButton() { |
|||
return 'waiting_to_start' === this.pageStatus.status && true === this.pageStatus.triedToStart && false === this.post.errored; |
|||
}, |
|||
showTooManyChecks() { |
|||
return 'too_long_checks' === this.pageStatus.status; |
|||
}, |
|||
showPostError() { |
|||
return 'conv_errored' === this.pageStatus.status || this.post.errored |
|||
}, |
|||
showWhenRunning() { |
|||
return 'conv_running' === this.pageStatus.status; |
|||
}, |
|||
showWhenDone() { |
|||
return 'conv_done' === this.pageStatus.status; |
|||
}, |
|||
showIfError() { |
|||
return 'conv_errored' === this.pageStatus.status; |
|||
}, |
|||
init() { |
|||
this.flow = document.querySelector('#data-helper').dataset.flow; |
|||
this.identifier = document.querySelector('#data-helper').dataset.identifier; |
|||
this.nextUrl = document.querySelector('#data-helper').dataset.url; |
|||
console.log('Flow is ' + this.flow); |
|||
console.log('Identifier is ' + this.identifier); |
|||
this.getJobStatus(); |
|||
}, |
|||
startJobButton() { |
|||
this.pageStatus.triedToStart = true; |
|||
this.pageStatus.status = 'waiting_to_start'; |
|||
this.postJobStart(); |
|||
}, |
|||
postJobStart() { |
|||
this.triedToStart = true; |
|||
this.post.running = true; |
|||
const jobStartUrl = './import/convert/start'; |
|||
window.axios.post(jobStartUrl, null,{params: {identifier: this.identifier}}).then((response) => { |
|||
console.log('POST was OK'); |
|||
this.getJobStatus(); |
|||
this.post.running = false; |
|||
}).catch((error) => { |
|||
console.error('JOB HAS FAILED :('); |
|||
this.post.result = error; |
|||
this.post.errored = true; |
|||
}).finally(() => { |
|||
this.getJobStatus(); |
|||
this.triedToStart = true; |
|||
} |
|||
); |
|||
this.getJobStatus(); |
|||
this.triedToStart = true; |
|||
}, |
|||
redirectToImport() { |
|||
window.location.href = this.nextUrl; |
|||
}, |
|||
getJobStatus() { |
|||
this.checkCount++; |
|||
if (this.checkCount >= this.maxCheckCount) { |
|||
console.log('Block getJobStatus (' + this.checkCount + ')'); |
|||
this.pageStatus.status = 'too_long_checks'; |
|||
return; |
|||
} |
|||
const statusUrl = './import/convert/status'; |
|||
window.axios.get(statusUrl, {params: {identifier: this.identifier}}).then((response) => { |
|||
this.pageStatus.status = response.data.status; |
|||
console.log('Status is now ' + response.data.status + ' (' + this.checkCount + ')'); |
|||
|
|||
if (this.checkCount >= this.maxCheckCount) { |
|||
// error
|
|||
this.pageStatus.status = 'too_long_checks'; |
|||
console.log('Status is now ' + this.pageStatus.status + ' (' + this.checkCount + ')'); |
|||
} |
|||
|
|||
// process messages, warnings and errors:
|
|||
this.messages.errors = response.data.errors; |
|||
this.messages.warnings = response.data.warnings; |
|||
this.messages.messages = response.data.messages; |
|||
|
|||
// job has not started yet. Let's wait.
|
|||
if (false === this.pageStatus.triedToStart && 'waiting_to_start' === this.pageStatus.status) { |
|||
this.pageStatus.status = response.data.status; |
|||
return; |
|||
} |
|||
// user pressed start, but it takes a moment.
|
|||
if (true === this.pageStatus.triedToStart && 'waiting_to_start' === this.pageStatus.status) { |
|||
//console.log('Job hasn\'t started yet, but its been tried.');
|
|||
} |
|||
|
|||
if (true === this.pageStatus.triedToStart && 'conv_errored' === this.pageStatus.status) { |
|||
console.error('Job status noticed job failed.'); |
|||
this.status = response.data.status; |
|||
return; |
|||
} |
|||
|
|||
if ('conv_running' === this.pageStatus.status) { |
|||
console.log('Conversion is running...') |
|||
} |
|||
if ('conv_done' === this.pageStatus.status) { |
|||
console.log('Job is done!'); |
|||
this.post.done = true; |
|||
setTimeout(function () { |
|||
console.log('Do redirect!') |
|||
this.redirectToImport(); |
|||
}.bind(this), 4000); |
|||
return; |
|||
} |
|||
if ('conv_errored' === this.pageStatus.status) { |
|||
console.error('Job is kill.'); |
|||
console.error(response.data); |
|||
return; |
|||
} |
|||
}).catch((error) => { |
|||
console.error('JOB HAS FAILED :('); |
|||
this.post.result = error; |
|||
this.post.errored = true; |
|||
}); |
|||
if (this.checkCount < this.maxCheckCount && !this.post.errored && !this.post.done) { |
|||
setTimeout(function () { |
|||
this.getJobStatus(); |
|||
}.bind(this), 1000); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
function loadPage() { |
|||
Alpine.data('index', () => index()); |
|||
Alpine.start(); |
|||
} |
|||
|
|||
// wait for load until bootstrapped event is received.
|
|||
document.addEventListener('data-importer-bootstrapped', () => { |
|||
console.log('Loaded through event listener.'); |
|||
loadPage(); |
|||
}); |
|||
// or is bootstrapped before event is triggered.
|
|||
if (window.bootstrapped) { |
|||
console.log('Loaded through window variable.'); |
|||
loadPage(); |
|||
} |
@ -0,0 +1,53 @@ |
|||
/* |
|||
* gocardless.js |
|||
* Copyright (c) 2024 james@firefly-iii.org |
|||
* |
|||
* This file is part of the Firefly III Data Importer |
|||
* (https://github.com/firefly-iii/data-importer).
|
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU Affero General Public License as |
|||
* published by the Free Software Foundation, either version 3 of the |
|||
* License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU Affero General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Affero General Public License |
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
import '../../boot/bootstrap.js'; |
|||
|
|||
|
|||
let gocardless = function () { |
|||
return { |
|||
selectedCountry: 'XX', |
|||
selectedBank: '', |
|||
functionName() { |
|||
|
|||
}, |
|||
init() { |
|||
console.log('hello gocardless'); |
|||
}, |
|||
} |
|||
} |
|||
|
|||
|
|||
function loadPage() { |
|||
Alpine.data('gocardless', () => gocardless()); |
|||
Alpine.start(); |
|||
} |
|||
|
|||
// wait for load until bootstrapped event is received.
|
|||
document.addEventListener('data-importer-bootstrapped', () => { |
|||
console.log('Loaded through event listener.'); |
|||
loadPage(); |
|||
}); |
|||
// or is bootstrapped before event is triggered.
|
|||
if (window.bootstrapped) { |
|||
console.log('Loaded through window variable.'); |
|||
loadPage(); |
|||
} |
@ -0,0 +1,187 @@ |
|||
/* |
|||
* index.js |
|||
* Copyright (c) 2024 james@firefly-iii.org |
|||
* |
|||
* This file is part of the Firefly III Data Importer |
|||
* (https://github.com/firefly-iii/data-importer).
|
|||
* |
|||
* This program is free software: you can redistribute it and/or modify |
|||
* it under the terms of the GNU Affero General Public License as |
|||
* published by the Free Software Foundation, either version 3 of the |
|||
* License, or (at your option) any later version. |
|||
* |
|||
* This program is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
* GNU Affero General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Affero General Public License |
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
import '../../boot/bootstrap.js'; |
|||
|
|||
|
|||
let index = function () { |
|||
return { |
|||
identifier: '', |
|||
pageStatus: { |
|||
triedToStart: false, |
|||
status: 'init', |
|||
}, |
|||
post: { |
|||
result: '', |
|||
errored: false, |
|||
running: false, |
|||
done: false, |
|||
}, |
|||
messages: { |
|||
messages: [], |
|||
warnings: [], |
|||
errors: [], |
|||
}, |
|||
checkCount: 0, |
|||
maxCheckCount: 600, |
|||
functionName() { |
|||
|
|||
}, |
|||
showJobMessages() { |
|||
return this.messages.messages.length > 0 || this.messages.warnings.length > 0 || this.messages.errors.length > 0; |
|||
}, |
|||
showStartButton() { |
|||
return('init' === this.pageStatus.status || 'waiting_to_start' === this.pageStatus.status) && false === this.pageStatus.triedToStart && false === this.post.errored; |
|||
}, |
|||
showWaitingButton() { |
|||
return 'waiting_to_start' === this.pageStatus.status && true === this.pageStatus.triedToStart && false === this.post.errored; |
|||
}, |
|||
showTooManyChecks() { |
|||
return 'too_long_checks' === this.pageStatus.status; |
|||
}, |
|||
showPostError() { |
|||
return 'submission_errored' === this.pageStatus.status || this.post.errored |
|||
}, |
|||
showWhenRunning() { |
|||
return 'submission_running' === this.pageStatus.status; |
|||
}, |
|||
showWhenDone() { |
|||
return 'submission_done' === this.pageStatus.status; |
|||
}, |
|||
showIfError() { |
|||
return 'submission_errored' === this.pageStatus.status; |
|||
}, |
|||
init() { |
|||
this.identifier = document.querySelector('#data-helper').dataset.identifier; |
|||
console.log('Identifier is ' + this.identifier); |
|||
this.getJobStatus(); |
|||
}, |
|||
startJobButton() { |
|||
this.pageStatus.triedToStart = true; |
|||
this.pageStatus.status = 'waiting_to_start'; |
|||
this.postJobStart(); |
|||
}, |
|||
postJobStart() { |
|||
this.triedToStart = true; |
|||
this.post.running = true; |
|||
const jobStartUrl = './import/submit/start'; |
|||
window.axios.post(jobStartUrl, null,{params: {identifier: this.identifier}}).then((response) => { |
|||
console.log('POST was OK'); |
|||
this.getJobStatus(); |
|||
this.post.running = false; |
|||
}).catch((error) => { |
|||
console.error('JOB HAS FAILED :('); |
|||
this.post.result = error; |
|||
this.post.errored = true; |
|||
}).finally(() => { |
|||
this.getJobStatus(); |
|||
this.triedToStart = true; |
|||
} |
|||
); |
|||
this.getJobStatus(); |
|||
this.triedToStart = true; |
|||
}, |
|||
getJobStatus() { |
|||
this.checkCount++; |
|||
if (this.checkCount >= this.maxCheckCount) { |
|||
console.log('Block getJobStatus (' + this.checkCount + ')'); |
|||
this.pageStatus.status = 'too_long_checks'; |
|||
return; |
|||
} |
|||
const submitUrl = './import/submit/status'; |
|||
window.axios.get(submitUrl, {params: {identifier: this.identifier}}).then((response) => { |
|||
this.pageStatus.status = response.data.status; |
|||
console.log('Status is now ' + response.data.status + ' (' + this.checkCount + ')'); |
|||
|
|||
if (this.checkCount >= this.maxCheckCount) { |
|||
// error
|
|||
this.pageStatus.status = 'too_long_checks'; |
|||
console.log('Status is now ' + this.pageStatus.status + ' (' + this.checkCount + ')'); |
|||
} |
|||
|
|||
// process messages, warnings and errors:
|
|||
this.messages.errors = response.data.errors; |
|||
this.messages.warnings = response.data.warnings; |
|||
this.messages.messages = response.data.messages; |
|||
|
|||
// job has not started yet. Let's wait.
|
|||
if (false === this.pageStatus.triedToStart && 'waiting_to_start' === this.pageStatus.status) { |
|||
this.pageStatus.status = response.data.status; |
|||
return; |
|||
} |
|||
// user pressed start, but it takes a moment.
|
|||
if (true === this.pageStatus.triedToStart && 'waiting_to_start' === this.pageStatus.status) { |
|||
//console.log('Job hasn\'t started yet, but it\'s been tried.');
|
|||
} |
|||
|
|||
if (true === this.pageStatus.triedToStart && 'submission_errored' === this.pageStatus.status) { |
|||
console.error('Job status noticed job failed.'); |
|||
this.status = response.data.status; |
|||
return; |
|||
} |
|||
|
|||
if ('submission_running' === this.pageStatus.status) { |
|||
console.log('Conversion is running...') |
|||
} |
|||
if ('submission_done' === this.pageStatus.status) { |
|||
console.log('Job is done!'); |
|||
this.post.done = true; |
|||
setTimeout(function () { |
|||
console.log('Do redirect!') |
|||
this.redirectToImport(); |
|||
}.bind(this), 4000); |
|||
return; |
|||
} |
|||
if ('submission_errored' === this.pageStatus.status) { |
|||
console.error('Job is kill.'); |
|||
console.error(response.data); |
|||
return; |
|||
} |
|||
}).catch((error) => { |
|||
console.error('JOB HAS FAILED :('); |
|||
this.post.result = error; |
|||
this.post.errored = true; |
|||
}); |
|||
if (this.checkCount < this.maxCheckCount && !this.post.errored && !this.post.done) { |
|||
setTimeout(function () { |
|||
this.getJobStatus(); |
|||
}.bind(this), 1000); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
function loadPage() { |
|||
Alpine.data('index', () => index()); |
|||
Alpine.start(); |
|||
} |
|||
|
|||
// wait for load until bootstrapped event is received.
|
|||
document.addEventListener('data-importer-bootstrapped', () => { |
|||
console.log('Loaded through event listener.'); |
|||
loadPage(); |
|||
}); |
|||
// or is bootstrapped before event is triggered.
|
|||
if (window.bootstrapped) { |
|||
console.log('Loaded through window variable.'); |
|||
loadPage(); |
|||
} |
@ -0,0 +1,72 @@ |
|||
<div x-show="showJobMessages()"> |
|||
<div x-show="messages.errors.length > 0"> |
|||
<strong class="text-danger">Error(s) from the import process</strong> |
|||
<ul> |
|||
<template x-for="(messageList, index) in messages.errors" :key="index"> |
|||
<li> |
|||
Line #<span x-text="index"></span>:
|
|||
<template x-if="messageList.length === 1"> |
|||
<template x-for="message in messageList"> |
|||
<span x-text="message"></span> |
|||
</template> |
|||
</template> |
|||
<template x-if="messageList.length > 1"> |
|||
<ol> |
|||
<template x-for="message in messageList"> |
|||
<li x-text="message"></li> |
|||
</template> |
|||
</ol> |
|||
</template> |
|||
</li> |
|||
</template> |
|||
</ul> |
|||
</div> |
|||
|
|||
<div x-show="messages.warnings.length > 0"> |
|||
<strong class="text-warning">Warning(s) from the import process</strong> |
|||
<ul> |
|||
<template x-for="(messageList, index) in messages.warnings" :key="index"> |
|||
<li> |
|||
Line #<span x-text="index"></span>:
|
|||
<template x-if="messageList.length === 1"> |
|||
<template x-for="message in messageList"> |
|||
<span x-text="message"></span> |
|||
</template> |
|||
</template> |
|||
<template x-if="messageList.length > 1"> |
|||
<ol> |
|||
<template x-for="message in messageList"> |
|||
<li x-text="message"></li> |
|||
</template> |
|||
</ol> |
|||
</template> |
|||
</li> |
|||
</template> |
|||
</ul> |
|||
</div> |
|||
|
|||
|
|||
<div x-show="messages.messages.length > 0"> |
|||
<strong class="text-info">Message(s) from the import process</strong> |
|||
<ul> |
|||
<template x-for="(messageList, index) in messages.messages" :key="index"> |
|||
<li> |
|||
Line #<span x-text="index"></span>:
|
|||
<template x-if="messageList.length === 1"> |
|||
<template x-for="message in messageList"> |
|||
<span x-text="message"></span> |
|||
</template> |
|||
</template> |
|||
<template x-if="messageList.length > 1"> |
|||
<ol> |
|||
<template x-for="message in messageList"> |
|||
<li x-text="message"></li> |
|||
</template> |
|||
</ol> |
|||
</template> |
|||
</li> |
|||
</template> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
|
@ -0,0 +1,22 @@ |
|||
@if('disabled' !== $account['import_account']->status) |
|||
<select style="width:100%;" |
|||
class="custom-select custom-select-sm form-control" |
|||
name="accounts[{{ $account['import_account']->id }}]"> |
|||
<!-- loop all Firefly III accounts --> |
|||
@foreach($account['firefly_iii_accounts'] as $ff3Account) |
|||
<option value="{{ $ff3Account->id }}" |
|||
{{-- loop configuration --}} |
|||
@foreach($configuration->getAccounts() as $key => $preConfig) |
|||
{{-- if this account matches, pre-select dropdown. --}} |
|||
@if($key === $account['import_account']->id && $preConfig === $ff3Account->id) selected="selected" |
|||
@endif |
|||
@endforeach |
|||
label="{{ $ff3Account->name }} @if($ff3Account->iban) ({{ $ff3Account->iban }}) @endif"> |
|||
{{ $ff3Account->id }}: |
|||
{{ $ff3Account->name }} @if($ff3Account->iban) |
|||
({{ $ff3Account->iban }}) |
|||
@endif |
|||
</option> |
|||
@endforeach |
|||
</select> |
|||
@endif |
@ -0,0 +1,31 @@ |
|||
<input |
|||
id="do_import_{{ $account['import_account']->id }}" |
|||
type="checkbox" |
|||
name="do_import[{{ $account['import_account']->id }}]" |
|||
value="1" |
|||
aria-describedby="accountsHelp" |
|||
@if('disabled' === $account['import_account']->status) disabled="disabled" @endif |
|||
@if(0 !== ($configuration->getAccounts()[$account['import_account']->id] ?? '')) checked="checked" @endif |
|||
/> |
|||
<label |
|||
class="form-check-label" |
|||
for="do_import_{{ $account['import_account']->id }}" |
|||
@if('' !== $account['import_account']->iban) title="IBAN: {{ $account['import_account']->iban }}" @endif |
|||
> |
|||
@if('' !== $account['import_account']->name) |
|||
Account "{{ $account['import_account']->name }}" |
|||
@else |
|||
Account with no name |
|||
@endif |
|||
</label> |
|||
<br> |
|||
<small> |
|||
@foreach($account['import_account']->extra as $key => $item) |
|||
@if('' !== $item) |
|||
{{ $key }}: {{ $item }}<br> |
|||
@endif |
|||
@endforeach |
|||
</small> |
|||
@if('disabled' === $account['import_account']->status) |
|||
<small class="text-danger">(this account is disabled)</small> |
|||
@endif |
@ -0,0 +1,21 @@ |
|||
<tr> |
|||
<td style="width:45%"> |
|||
<x-importer-account-title :account="$account" :configuration="$configuration"/> |
|||
</td> |
|||
<td style="width:10%"> |
|||
@if('disabled' !== $account['import_account']->status) |
|||
@if(count($account['firefly_iii_accounts']) > 0) |
|||
→ |
|||
@endif |
|||
@endif |
|||
</td> |
|||
<td style="width:45%"> |
|||
<!-- TODO this is one of those things to merge into one generic type --> |
|||
@if(0 === count($account['firefly_iii_accounts'])) |
|||
<span class="text-danger">There are no Firefly III accounts to import into</span> |
|||
@endif |
|||
@if(0 !== count($account['firefly_iii_accounts'])) |
|||
<x-firefly-iii-account-generic :account="$account" :configuration="$configuration"/> |
|||
@endif |
|||
</td> |
|||
</tr> |
@ -0,0 +1,140 @@ |
|||
@extends('layout.v2') |
|||
@section('content') |
|||
<div class="container"> |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<h1>{{ $mainTitle }}</h1> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
{{ $subTitle }} |
|||
</div> |
|||
<div class="card-body"> |
|||
<p> |
|||
Set up the meaning of each column in your file. |
|||
</p> |
|||
<p> |
|||
Each column in your importable file has a role, it contains a specific type of content. |
|||
By configuring these roles here, you tell the importer how to approach and treat |
|||
the data in each column. <a target="_blank" |
|||
href="https://docs.firefly-iii.org/references/data-importer/roles/">Read |
|||
the documentation</a> to learn more |
|||
about this process. |
|||
</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
@if(!$errors->isEmpty()) |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
Errors :( |
|||
</div> |
|||
<div class="card-body"> |
|||
<p class="text-danger">Some error(s) occurred:</p> |
|||
<ul> |
|||
@foreach($errors->all() as $error) |
|||
<li class="text-danger">{{ $error }}</li> |
|||
@endforeach |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
@endif |
|||
|
|||
<div class="row mt-3"> |
|||
<div class="col-lg-12"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
Role configuration |
|||
</div> |
|||
<div class="card-body"> |
|||
<form method="post" action="{{ route('005-roles.post') }}" accept-charset="UTF-8"> |
|||
<input type="hidden" name="_token" value="{{ csrf_token() }}"/> |
|||
<table class="table"> |
|||
<tr> |
|||
<th>Column</th> |
|||
<th>Example data</th> |
|||
<th>Role</th> |
|||
<th>Map data?</th> |
|||
</tr> |
|||
@foreach($columns as $index => $column) |
|||
<tr> |
|||
<td>{{ $column }} |
|||
@if($index === $configuration->getUniqueColumnIndex() && 'cell' === $configuration->getDuplicateDetectionMethod()) |
|||
<br/> |
|||
<span class="text-muted small">This is the unique column</span> |
|||
@endif |
|||
</td> |
|||
<td> |
|||
@if(count($examples[$index] ?? []) > 0) |
|||
@foreach($examples[$index] as $example) |
|||
<pre style="color:#e83e8c;margin-bottom:0;">{{ $example }}</pre> |
|||
@endforeach |
|||
@endif |
|||
</td> |
|||
<td> |
|||
@if($index === $configuration->getUniqueColumnIndex() && 'cell' === $configuration->getDuplicateDetectionMethod()) |
|||
<p class="form-text"> |
|||
<span class="text-muted small"> |
|||
This column is your unique identifier, so it will be fixed to |
|||
</span> |
|||
<code class="small">{{ $configuration->getUniqueColumnType() }}</code> |
|||
</p> |
|||
<input type="hidden" name="roles[{{ $index }}]" |
|||
value="{{ $configuration->getUniqueColumnType() }}"/> |
|||
@else |
|||
<select name="roles[{{ $index }}]" id="roles_{{ $index }}" |
|||
class="form-control"> |
|||
@foreach($roles as $key => $role) |
|||
<option value="{{ $key }}" |
|||
@if(($configuredRoles[$index] ?? false) === $key) selected @endif |
|||
label="{{ __('import.column_' . $key) }}">{{ __('import.column_'. $key) }}</option> |
|||
@endforeach |
|||
</select> |
|||
@endif |
|||
</td> |
|||
<td> |
|||
<label for="do_mapping_{{ $index }}"> |
|||
{{-- reverse if statement is pretty sloppy but OK. --}} |
|||
@if($index === $configuration->getUniqueColumnIndex() && 'cell' === $configuration->getDuplicateDetectionMethod()) |
|||
|
|||
@else |
|||
<input type="checkbox" |
|||
@if($configuredDoMapping[$index] ?? false) checked @endif |
|||
name="do_mapping[{{ $index }}]" id="do_mapping_{{ $index }}" |
|||
value="1"/> |
|||
@endif |
|||
</label> |
|||
</td> |
|||
</tr> |
|||
@endforeach |
|||
</table> |
|||
<button type="submit" class="float-end btn btn-primary">Submit →</button> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-body"> |
|||
<div class="btn-group btn-group-sm"> |
|||
<a href="{{ route('back.config') }}" class="btn btn-secondary"><span |
|||
class="fas fa-arrow-left"></span> Go back to configuration</a> |
|||
<a href="{{ route('flush') }}" class="btn btn-danger text-white btn-sm"><span |
|||
class="fas fa-redo-alt"></span> Start over</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
@endsection |
@ -0,0 +1,129 @@ |
|||
@extends('layout.v2') |
|||
@section('content') |
|||
<div class="container"> |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<h1>{{ $mainTitle }}</h1> |
|||
</div> |
|||
</div> |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
{{ $subTitle }} |
|||
</div> |
|||
<div class="card-body"> |
|||
<p>Map data in your import to your Firefly III instance.</p> |
|||
<p> |
|||
Entries in your import may already exist in another form in your own Firefly III |
|||
instance. Be sure to <a target="_blank" |
|||
href="https://docs.firefly-iii.org/how-to/data-importer/import/map-data/"> |
|||
check out the documentation</a>, because this is where |
|||
the magic happens. |
|||
</p> |
|||
<p class="text-info"> |
|||
Account names with "lots of spaces" may seemingly lose |
|||
those spaces. Fear not, those will be perfectly preserved. |
|||
</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
@if(!$errors->isEmpty()) |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
Errors :( |
|||
</div> |
|||
<div class="card-body"> |
|||
<ul> |
|||
@foreach($errors->all() as $error) |
|||
<li class="text-danger">{{ $error }}</li> |
|||
@endforeach |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
@endif |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-12"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
Form |
|||
</div> |
|||
<div class="card-body"> |
|||
<form method="post" action="{{ route('006-mapping.post') }}" accept-charset="UTF-8"> |
|||
<input type="hidden" name="_token" value="{{ csrf_token() }}"/> |
|||
@foreach($data as $index => $row) |
|||
<h3>{{ $index }}: {{ __('import.column_'.$row['role']) }}</h3> |
|||
<table class="table"> |
|||
<tr> |
|||
<th style="width:50%;">Field value</th> |
|||
<th style="width:50%;">Mapped to</th> |
|||
</tr> |
|||
@foreach($row['values'] as $valueIndex => $value) |
|||
<tr> |
|||
<td> |
|||
<pre style="color:#e83e8c;">{{ $value }}</pre> |
|||
<input type="hidden" name="values[{{ $index }}][{{ $loop->index }}]" |
|||
value="{{ $value }}"/> |
|||
</td> |
|||
<td> |
|||
<select name="mapping[{{ $index }}][{{ $loop->index }}]" |
|||
class="form-control"> |
|||
<option value="0" label="(do not map / automap)">(do not map / |
|||
automap) |
|||
</option> |
|||
@foreach($row['mapping_data'] as $key => $maps) |
|||
<!-- if is array go one level deeper --> |
|||
@if(is_iterable($maps)) |
|||
<optgroup label="{{ $key }}"> |
|||
@foreach($maps as $singleId => $singleEntry) |
|||
<option |
|||
@if($singleId === ($row['mapped'][$value] ?? false)) selected @endif |
|||
label="{{ $singleEntry }}" |
|||
value="{{ $singleId }}"> |
|||
{{ $singleEntry }} |
|||
</option> |
|||
@endforeach |
|||
</optgroup> |
|||
@else |
|||
<option |
|||
@if($key === ($row['mapped'][$value] ?? false)) selected @endif |
|||
label="{{ $maps }}" value="{{ $key }}"> |
|||
{{ $maps }} |
|||
</option> |
|||
@endif |
|||
@endforeach |
|||
</select> |
|||
</td> |
|||
</tr> |
|||
@endforeach |
|||
</table> |
|||
@endforeach |
|||
<button type="submit" class="btn btn-primary float-end">Submit →</button> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-body"> |
|||
<div class="btn-group btn-group-sm"> |
|||
<a href="{{ route('back.roles') }}" class="btn btn-secondary"><span |
|||
class="fas fa-arrow-left"></span> Go back to role selection</a> |
|||
<a href="{{ route('flush') }}" class="btn btn-danger text-white btn-sm"><span |
|||
class="fas fa-redo-alt"></span> Start over</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
@endsection |
@ -0,0 +1,109 @@ |
|||
@extends('layout.v2') |
|||
@section('content') |
|||
|
|||
<!-- another tiny hack to get data from a to b --> |
|||
<span id="data-helper" data-flow="{{ $flow }}" data-identifier="{{ $identifier }}" data-url="{{ $nextUrl }}"></span> |
|||
|
|||
<div class="container" x-data="index"> |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<h1>{{ $mainTitle }}</h1> |
|||
</div> |
|||
</div> |
|||
<div id="app"> |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
Data conversion |
|||
</div> |
|||
<!-- show start of process button --> |
|||
<div x-show="showStartButton()" class="card-body"> |
|||
<p> |
|||
The first step in the import process is a <strong>conversion</strong>. |
|||
<span x-show="'file' === flow">The CSV file you uploaded</span> |
|||
<span x-show="'nordigen' === flow">The transactions downloaded from GoCardless</span> |
|||
<span x-show="'spectre' === flow">The transactions downloaded from Spectre</span> |
|||
will be converted to Firefly III compatible transactions. Please press <strong>Start |
|||
job</strong> to start. |
|||
</p> |
|||
<p> |
|||
<button class="btn btn-success float-end text-white" type="button" @click="startJobButton">Start job |
|||
→ |
|||
</button> |
|||
</p> |
|||
</div> |
|||
<div x-show="showWaitingButton()" class="card-body"> |
|||
<p><span class="fas fa-cog fa-spin"></span> Please wait for the job to start..</p> |
|||
</div> |
|||
<div x-show="showTooManyChecks()" class="card-body"> |
|||
<p> |
|||
<em class="fa-solid fa-face-dizzy"></em> |
|||
The data importer has been polling for more than <span x-text="checkCount"></span> seconds. It has stopped, to prevent eternal loops.</p> |
|||
</div> |
|||
<div x-show="showPostError()" class="card-body"> |
|||
<p class="text-danger"> |
|||
The conversion could not be started, or failed due to an error. Please check the log files. |
|||
Sorry about this :( |
|||
</p> |
|||
<p x-show="'' !== post.result" x-text="post.result"></p> |
|||
<x-conversion-messages /> |
|||
</div> |
|||
|
|||
<div x-show="showWhenRunning()" class="card-body"> |
|||
<p> |
|||
<span class="fas fa-cog fa-spin"></span> The conversion is running, please wait. Messages may appear below the progress bar. |
|||
</p> |
|||
<div class="progress"> |
|||
<div aria-valuemax="100" aria-valuemin="0" |
|||
aria-valuenow="100" class="progress-bar progress-bar-striped progress-bar-animated" |
|||
role="progressbar" style="width: 100%"></div> |
|||
</div> |
|||
<x-conversion-messages /> |
|||
</div> |
|||
<div x-show="showWhenDone()" class="card-body"> |
|||
<p> |
|||
<span class="fas fa-sync fa-spin"></span> The conversion routine has finished 🎉. Please wait to be redirected! |
|||
</p> |
|||
<x-conversion-messages /> |
|||
</div> |
|||
<div x-show="showIfError()" class="card-body"> |
|||
<p class="text-danger"> |
|||
The conversion could not be started, or failed due to an error. Please check the log files. |
|||
Sorry about this :( |
|||
</p> |
|||
<x-conversion-messages /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-body"> |
|||
<div class="btn-group btn-group-sm"> |
|||
<a href="{{ $jobBackUrl }}" class="btn btn-secondary"><span class="fas fa-arrow-left"></span> |
|||
Go back to the previous step</a> |
|||
<a class="btn btn-danger text-white btn-sm" href="{{ route('flush') }}" data-bs-toggle="tooltip" |
|||
data-bs-placement="top" title="If the conversion seems stuck, you can reset it."><span |
|||
class="fas fa-redo-alt"></span> Start over</a> |
|||
<a class="btn btn-info text-white btn-sm" href="{{ route('004-configure.download') }}" |
|||
data-bs-toggle="tooltip" data-bs-placement="top" |
|||
title="You can download a configuration file of your import, so you can make a quick start the next time you import."> |
|||
<span class="fas fa-download"></span> Download configuration file |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
</div> |
|||
@endsection |
|||
@section('scripts') |
|||
@vite(['src/pages/conversion/index.js']) |
|||
@endsection |
@ -0,0 +1,104 @@ |
|||
@extends('layout.v2') |
|||
@section('content') |
|||
|
|||
<!-- another tiny hack to get data from a to b --> |
|||
<span id="data-helper" data-identifier="{{ $identifier }}"></span> |
|||
|
|||
<div class="container" x-data="index"> |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<h1>{{ $mainTitle }}</h1> |
|||
</div> |
|||
</div> |
|||
<div id="app"> |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
Data submission to Firefly III |
|||
</div> |
|||
<!-- show start of process button --> |
|||
<div x-show="showStartButton()" class="card-body"> |
|||
<p> |
|||
Your converted content will be submitted to your Firefly III installation. Press <strong>Start job</strong> to start. |
|||
</p> |
|||
<p> |
|||
<button class="btn btn-success float-end text-white" type="button" @click="startJobButton">Start job |
|||
→ |
|||
</button> |
|||
</p> |
|||
</div> |
|||
<div x-show="showWaitingButton()" class="card-body"> |
|||
<p><span class="fas fa-cog fa-spin"></span> Please wait for the job to start..</p> |
|||
</div> |
|||
<div x-show="showTooManyChecks()" class="card-body"> |
|||
<p> |
|||
<em class="fa-solid fa-face-dizzy"></em> |
|||
The data importer has been polling for more than <span x-text="checkCount"></span> seconds. It has stopped, to prevent eternal loops.</p> |
|||
</div> |
|||
<div x-show="showPostError()" class="card-body"> |
|||
<p class="text-danger"> |
|||
The conversion could not be started, or failed due to an error. Please check the log files. |
|||
Sorry about this :( |
|||
</p> |
|||
<p x-show="'' !== post.result" x-text="post.result"></p> |
|||
<x-conversion-messages /> |
|||
</div> |
|||
|
|||
<div x-show="showWhenRunning()" class="card-body"> |
|||
<p> |
|||
<span class="fas fa-cog fa-spin"></span> The conversion is running, please wait. Messages may appear below the progress bar. |
|||
</p> |
|||
<div class="progress"> |
|||
<div aria-valuemax="100" aria-valuemin="0" |
|||
aria-valuenow="100" class="progress-bar progress-bar-striped progress-bar-animated" |
|||
role="progressbar" style="width: 100%"></div> |
|||
</div> |
|||
<x-conversion-messages /> |
|||
</div> |
|||
<div x-show="showWhenDone()" class="card-body"> |
|||
<p> |
|||
The submission routine has finished 🎉. Errors and messages can be seen below. |
|||
</p> |
|||
<x-conversion-messages /> |
|||
</div> |
|||
<div x-show="showIfError()" class="card-body"> |
|||
<p class="text-danger"> |
|||
The conversion could not be started, or failed due to an error. Please check the log files. |
|||
Sorry about this :( |
|||
</p> |
|||
<x-conversion-messages /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-body"> |
|||
<div class="btn-group btn-group-sm"> |
|||
<a href="{{ $jobBackUrl }}" class="btn btn-secondary"><span class="fas fa-arrow-left"></span> |
|||
Go back to the previous step</a> |
|||
<a class="btn btn-danger text-white btn-sm" href="{{ route('flush') }}" data-bs-toggle="tooltip" |
|||
data-bs-placement="top" title="If the conversion seems stuck, you can reset it."><span |
|||
class="fas fa-redo-alt"></span> Start over</a> |
|||
<a class="btn btn-info text-white btn-sm" href="{{ route('004-configure.download') }}" |
|||
data-bs-toggle="tooltip" data-bs-placement="top" |
|||
title="You can download a configuration file of your import, so you can make a quick start the next time you import."> |
|||
<span class="fas fa-download"></span> Download configuration file |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
</div> |
|||
@endsection |
|||
@section('scripts') |
|||
@vite(['src/pages/submit/index.js']) |
|||
@endsection |
@ -0,0 +1,211 @@ |
|||
@extends('layout.v2') |
|||
@section('content') |
|||
|
|||
<div class="container" x-data="gocardless"> |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<h1>{{ $mainTitle }}</h1> |
|||
</div> |
|||
</div> |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
{{ $subTitle }} |
|||
</div> |
|||
<div class="card-body"> |
|||
<p> |
|||
Select your country and the bank you would like to import from. |
|||
If you would like some support, <a href="https://docs.firefly-iii.org/how-to/data-importer/import/gocardless/" |
|||
target="_blank">check out the documentation for this |
|||
page.</a> |
|||
</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
@if(!$errors->isEmpty()) |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
Errors |
|||
</div> |
|||
<div class="card-body"> |
|||
<p class="text-danger">Some error(s) occurred:</p> |
|||
<ul> |
|||
@foreach($errors->all() as $error) |
|||
<li class="text-danger">{{ $error }}</li> |
|||
@endforeach |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
@endif |
|||
<form method="post" action="{{ route('009-selection.post') }}" accept-charset="UTF-8"> |
|||
<input type="hidden" name="_token" value="{{ csrf_token() }}"/> |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
Select country |
|||
</div> |
|||
<div class="card-body"> |
|||
<div class="form-group row"> |
|||
<label for="country" class="col-sm-3 col-form-label">Country</label> |
|||
<div class="col-sm-9"> |
|||
<select class="form-control" |
|||
name="country" |
|||
x-model="selectedCountry"> |
|||
<option label="(no selection)" value="XX">(no selection)</option> |
|||
@foreach($response as $country) |
|||
<option label="{{ $countries[$country->code] ?? 'Unknown' }}" |
|||
@if($country->code == $configuration->getNordigenCountry())selected="selected"@endif |
|||
value="{{ $country->code }}">{{ $countries[$country->code] ?? 'Unknown' }}</option> |
|||
@endforeach |
|||
</select> |
|||
|
|||
<small class="form-text text-muted"> |
|||
Which country is your bank in? |
|||
</small> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
@foreach($response as $country) |
|||
<div class="row mt-3 bank-box" x-show="'{{ $country->code }}' === selectedCountry && 'XX' !== selectedCountry"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
Select your bank in {{ $country->code }} |
|||
</div> |
|||
<div class="card-body"> |
|||
<div class="form-group row"> |
|||
<label for="bank_{{ $country->code }}" class="col-sm-3 col-form-label">Bank |
|||
({{ $country->code }})</label> |
|||
<div class="col-sm-9"> |
|||
<select class="form-control bank-selector" name="bank_{{ $country->code }}" |
|||
x-model="selectedBank"> |
|||
<option label="(no bank)" value="XX" data-days="0">(no bank)</option> |
|||
@foreach($country->banks as $bank) |
|||
<option label="{{ $bank->name }}" |
|||
@if($bank->id === $configuration->getNordigenBank())selected="selected"@endif |
|||
data-days="{{ $bank->transactionTotalDays }}" |
|||
value="{{ $bank->id }}">{{ $bank->name }}</option> |
|||
@endforeach |
|||
</select> |
|||
</div> |
|||
</div> |
|||
|
|||
<p class="mt-3 text-info bank-selected" style="display: none;">Imports from this bank |
|||
go no further back than <strong class="days">XX</strong> days. |
|||
</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
@endforeach |
|||
|
|||
<div class="row mt-3 bank-box" x-show="'XX' === selectedCountry"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
Select a bank |
|||
</div> |
|||
<div class="card-body"> |
|||
<small class="form-text text-muted"> |
|||
(Please select a country first) |
|||
</small> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="row mt-3 bank-selected" x-show="'XX' !== selectedCountry && '' !== selectedBank"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
Number of days the connection is valid |
|||
</div> |
|||
<div class="card-body"> |
|||
<div class="form-group row"> |
|||
<label for="days" class="col-sm-3 col-form-label">Number of days the connection is valid</label> |
|||
<div class="col-sm-9"> |
|||
<input name="days" class="form-control" step="1" min="1" type="number" |
|||
value="{{ $configuration->getNordigenMaxDays() }}"/> |
|||
<small class="form-text text-muted"> |
|||
The connection to your bank can be recycled for this number of days. It will be stored |
|||
in the import configuration file. Keep in mind most banks don't support more than 90 days. |
|||
</small> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
<div class="row mt-3 submit-button" x-show="'XX' !== selectedCountry && '' !== selectedBank"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
|
|||
<div class="card-header"> |
|||
Submit! |
|||
</div> |
|||
<div class="card-body"> |
|||
<button type="submit" class="float-end btn btn-primary">Submit →</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</form> |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-body"> |
|||
<div class="btn-group btn-group-sm"> |
|||
<a href="{{ route('back.upload') }}" class="btn btn-secondary"><span |
|||
class="fas fa-arrow-left"></span> Go back to upload</a> |
|||
<a href="{{ route('flush') }}" class="btn btn-danger text-white btn-sm"><span |
|||
class="fas fa-redo-alt"></span> Start over</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- |
|||
<script type="text/javascript"> |
|||
$(document).ready(function () { |
|||
$('#country').change(selectCountry) |
|||
$('.bank-selector').change(showDayCounter); |
|||
selectCountry(); |
|||
}); |
|||
|
|||
function showDayCounter() { |
|||
$('.bank-selected').show(); |
|||
let val = $('#country').val(); |
|||
|
|||
let maxDays = parseInt($('#bank_' + val + ' option:selected').data('days')); |
|||
|
|||
$('.days').text(maxDays); |
|||
} |
|||
|
|||
function selectCountry() { |
|||
$('.bank-selected').hide(); |
|||
var val = $('#country').val(); |
|||
$('.country-code').text(val); |
|||
$('.bank-box').hide(); |
|||
$('.submit-button').show(); |
|||
$('#' + val + '-box').show(); |
|||
} |
|||
</script> |
|||
--> |
|||
|
|||
@endsection |
|||
@section('scripts') |
|||
@vite(['src/pages/selection/gocardless.js']) |
|||
@endsection |
@ -0,0 +1,116 @@ |
|||
@extends('layout.v2') |
|||
@section('content') |
|||
<div class="container"> |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<h1>{{ $mainTitle }}</h1> |
|||
</div> |
|||
</div> |
|||
<form method="post" action="{{ route('011-connections.post') }}" accept-charset="UTF-8" id="store"> |
|||
<input type="hidden" name="_token" value="{{ csrf_token() }}"/> |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
{{ $subTitle }} |
|||
</div> |
|||
<div class="card-body"> |
|||
<p>Select the connection to use or make a new connection</p> |
|||
<p> |
|||
Spectre creates connections; a representation of the connection to your financial |
|||
institution. |
|||
Select below which one the importer must use, or opt to create a new connection if no |
|||
connections are visible. |
|||
Please read |
|||
<a href="https://docs.firefly-iii.org/" |
|||
target="_blank"> the documentation for this page</a> if you want to know more. |
|||
</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-header"> |
|||
Connections |
|||
</div> |
|||
<div class="card-body"> |
|||
<table class="table table-bordered table-striped"> |
|||
<thead> |
|||
<tr> |
|||
<th> </th> |
|||
<th>Bank</th> |
|||
<th>Last used</th> |
|||
<th>Status</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
@foreach($list as $item) |
|||
<tr> |
|||
<td> |
|||
<input |
|||
id="{{ $item->id }}" |
|||
type="radio" |
|||
name="spectre_connection_id" |
|||
value="{{ $item->id }}" |
|||
|
|||
@if('disabled' === $item->status) disabled @endif |
|||
@if($configuration->getConnection() === $item->id) checked @endif |
|||
@if(1 === count($list)) checked @endif |
|||
> |
|||
</td> |
|||
<td> |
|||
<label for="{{ $item->id }}"> |
|||
{{ $item->providerName }} ({{ $item->countryCode }}) |
|||
</label> |
|||
</td> |
|||
<td> |
|||
Last success: {{ $item->lastSuccess->format("Y-m-d H:i:s") }}<br> |
|||
Updated at: {{ $item->updatedAt->format("Y-m-d H:i:s") }}<br> |
|||
</td> |
|||
<td> |
|||
{{ $item->status }} |
|||
</td> |
|||
</tr> |
|||
@endforeach |
|||
<tr> |
|||
<td> |
|||
<input id="new_login" type="radio" name="spectre_connection_id" value="00" |
|||
@if(0 === $configuration->getConnection()) checked @endif |
|||
> |
|||
</td> |
|||
<td colspan="3"> |
|||
<label for="new_login"><em> |
|||
Create a new connection |
|||
</em> |
|||
</label> |
|||
</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
<button type="submit" class="btn btn-primary float-end">Submit →</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- end of selection --> |
|||
<div class="row mt-3"> |
|||
<div class="col-lg-10 offset-lg-1"> |
|||
<div class="card"> |
|||
<div class="card-body"> |
|||
<div class="btn-group btn-group-sm"> |
|||
<a href="{{ route('back.upload') }}" class="btn btn-secondary"><span |
|||
class="fas fa-arrow-left"></span> Go back to upload</a> |
|||
<a href="{{ route('flush') }}" class="btn btn-danger text-white btn-sm"><span |
|||
class="fas fa-redo-alt"></span> Start over</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</form> |
|||
</div> |
|||
@endsection |
|||
@section('scripts') |
|||
@endsection |
Write
Preview
Loading…
Cancel
Save
Reference in new issue