Set new UI for the Import view

This commit is contained in:
Bubka 2023-11-21 13:10:50 +01:00
parent da134a4f76
commit 73b2c60311
4 changed files with 141 additions and 57 deletions

View File

@ -1281,3 +1281,30 @@ footer.main .field.is-grouped {
transform: translateY(-2rem);
}
}
:root[data-theme="dark"] .table {
background-color: $black-ter;
color: $white-bis;
}
:root[data-theme="dark"] .table th,
:root[data-theme="dark"] .table thead th {
color: $grey;
}
:root[data-theme="dark"] .table td,
:root[data-theme="dark"] .table th {
border: 1px solid $grey-darker;
border-width: 0 0 1px;
}
:root[data-theme="dark"] .card {
background-color: $black-ter;
border: 1px solid $grey-darker;
}
:root[data-theme="dark"] .card-footer {
border-top: 1px solid $grey-darker;
}
:root[data-theme="dark"] .card-footer-item:not(:last-child) {
border-right: 1px solid $grey-darker;
}

View File

@ -1,5 +1,3 @@
// import Vue from 'vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
@ -42,7 +40,10 @@ import {
faSun,
faMoon,
faDesktop,
faCircleNotch
faCircleNotch,
faCircleCheck,
faTriangleExclamation,
faFileLines
} from '@fortawesome/free-solid-svg-icons'
import {
@ -89,9 +90,10 @@ library.add(
faSun,
faMoon,
faDesktop,
faCircleNotch
faCircleNotch,
faCircleCheck,
faTriangleExclamation,
faFileLines
);
export default FontAwesomeIcon
// Vue.component('font-awesome-icon', FontAwesomeIcon)
export default FontAwesomeIcon

View File

@ -2,6 +2,7 @@
import Form from '@/components/formElements/Form'
import twofaccountService from '@/services/twofaccountService'
import OtpDisplay from '@/components/OtpDisplay.vue'
import Spinner from '@/components/Spinner.vue'
import { useNotifyStore } from '@/stores/notify'
import { useUserStore } from '@/stores/user'
import { useBusStore } from '@/stores/bus'
@ -15,9 +16,7 @@
const twofaccounts = useTwofaccounts()
const otpDisplay = ref(null)
const fileInput = ref(null)
const fileInputLabel = ref(null)
const qrcodeInput = ref(null)
const qrcodeInputLabel = ref(null)
const form = reactive(new Form({
service: '',
account: '',
@ -234,53 +233,111 @@
<h1 class="title has-text-grey-dark">
{{ $t('twofaccounts.import.import') }}
</h1>
<div v-if="exportedAccounts.length == 0">
<div v-if="!isFetching && exportedAccounts.length == 0">
<div class="block is-size-7-mobile" v-html="$t('twofaccounts.import.import_legend')"></div>
<h5 class="title is-5 mb-3 has-text-grey">{{ $t('twofaccounts.import.qr_code') }}</h5>
<!-- scan button that launch camera stream -->
<div class="block">
<div class="buttons mb-0">
<RouterLink id="btnCapture" :to="{ name: 'capture' }" class="button is-link is-rounded mr-0">
<span class="icon">
<FontAwesomeIcon :icon="['fas', 'camera']" />
</span>
<span>{{ $t('twofaccounts.import.scan') }}</span>
</RouterLink>
<span class="p-2 mb-2">{{ $t('commons.or') }}</span>
<!-- upload a qr code (with basic file field and backend decoding) -->
<label role="button" tabindex="0" class="button is-link is-rounded is-outlined" ref="qrcodeInputLabel" @keyup.enter="qrcodeInputLabel.click()">
{{ $t('twofaccounts.import.upload') }}
<input aria-hidden="true" tabindex="-1" class="file-input" type="file" accept="image/*" v-on:change="submitQrCode" ref="qrcodeInput">
</label>
</div>
<FieldError v-if="qrcodeForm.errors.hasAny('qrcode')" :error="qrcodeForm.errors.get('qrcode')" :field="'qrcode'" />
<p class="help">{{ $t('twofaccounts.import.supported_formats_for_qrcode_upload') }}</p>
</div>
<h5 class="title is-5 mb-3 has-text-grey">{{ $t('commons.file') }}</h5>
<!-- upload a file -->
<div class="block mb-6">
<label role="button" tabindex="0" class="button is-link is-rounded is-outlined" ref="fileInputLabel" @keyup.enter="fileInputLabel.click()">
<input aria-hidden="true" tabindex="-1" class="file-input" type="file" accept="text/plain,application/json,text/csv,.2fas" v-on:change="submitFile" ref="fileInput">
{{ $t('twofaccounts.import.upload') }}
</label>
<FieldError v-if="fileForm.errors.hasAny('file')" :error="fileForm.errors.get('file')" :field="'file'" />
<p class="help">{{ $t('twofaccounts.import.supported_formats_for_file_upload') }}</p>
</div>
<!-- Supported migration resources -->
<h5 class="title is-6 mb-3 has-text-grey-dark">{{ $t('twofaccounts.import.supported_migration_formats') }}</h5>
<div class="field is-grouped is-grouped-multiline pt-0">
<div v-for="(source, index) in supportedSources" :key="index" class="control">
<div class="tags has-addons">
<UseColorMode v-slot="{ mode }">
<span class="tag" :class="mode == 'dark' ? 'is-dark' : 'is-white'">{{ source.app }}</span>
<span class="tag" :class="mode == 'dark' ? 'is-black' : 'has-background-grey-lighter has-text-black'">{{ source.format }}</span>
</UseColorMode>
<div class="columns">
<div class="column">
<div class="block">
<div class="card">
<div class="card-content">
<div class="media">
<div class="media-left">
<figure class="image is-32x32">
<FontAwesomeIcon :icon="['fas', 'qrcode']" size="2x" class="has-text-grey-darker" />
</figure>
</div>
<div class="media-content">
<p class="title is-5 has-text-grey" v-html="$t('twofaccounts.import.qr_code')" />
<p class="subtitle is-6 is-size-7-mobile">{{ $t('twofaccounts.import.supported_formats_for_qrcode_upload') }}</p>
</div>
</div>
<FieldError v-if="qrcodeForm.errors.hasAny('qrcode')" :error="qrcodeForm.errors.get('qrcode')" :field="'qrcode'" />
</div>
<footer class="card-footer">
<RouterLink id="btnCapture" :to="{ name: 'capture' }" class="card-footer-item">
{{ $t('twofaccounts.import.scan') }}
</RouterLink>
<a role="button" tabindex="0" class="card-footer-item is-relative" @keyup.enter="qrcodeInput.click()">
<input aria-hidden="true" tabindex="-1" class="file-input" type="file" accept="image/*" v-on:change="submitQrCode" ref="qrcodeInput">
{{ $t('twofaccounts.import.upload') }}
</a>
</footer>
</div>
</div>
<div class="block">
<div class="card">
<div class="card-content">
<div class="media">
<div class="media-left">
<figure class="image is-32x32">
<FontAwesomeIcon :icon="['fas', 'file-lines']" size="2x" class="has-text-grey-darker" />
</figure>
</div>
<div class="media-content">
<p class="title is-5 has-text-grey">{{ $t('twofaccounts.import.text_file') }}</p>
<p class="subtitle is-6 is-size-7-mobile">{{ $t('twofaccounts.import.supported_formats_for_file_upload') }}</p>
</div>
</div>
<FieldError v-if="fileForm.errors.hasAny('file')" :error="fileForm.errors.get('file')" :field="'file'" />
</div>
<footer class="card-footer">
<a role="button" tabindex="0" class="card-footer-item is-relative" @keyup.enter="fileInput.click()">
<input aria-hidden="true" tabindex="-1" class="file-input" type="file" accept="text/plain,application/json,text/csv,.2fas" v-on:change="submitFile" ref="fileInput">
{{ $t('twofaccounts.import.upload') }}
</a>
</footer>
</div>
</div>
</div>
</div>
<span class="is-size-7" v-html="$t('twofaccounts.import.do_not_set_password_or_encryption')"></span>
<!-- Supported migration resources -->
<h2 class="title is-5 has-text-grey-dark">{{ $t('twofaccounts.import.supported_migration_formats') }}</h2>
<div class="block is-size-7-mobile">
<FontAwesomeIcon :icon="['fas', 'fa-triangle-exclamation']" class="has-text-warning-dark" />
{{ $t('twofaccounts.import.do_not_set_password_or_encryption') }}
</div>
<table class="table is-size-7-mobile is-fullwidth">
<thead>
<tr>
<th></th>
<th>Plain text</th>
<th>QR code</th>
<th>JSON</th>
</tr>
</thead>
<tbody>
<tr>
<th>Google Authenticator</th>
<td></td>
<td><FontAwesomeIcon :icon="['fas', 'circle-check']" /></td>
<td></td>
</tr>
<tr>
<th>Aegis Auth</th>
<td><FontAwesomeIcon :icon="['fas', 'circle-check']" /></td>
<td></td>
<td><FontAwesomeIcon :icon="['fas', 'circle-check']" /></td>
</tr>
<tr>
<th>2FAS auth</th>
<td></td>
<td></td>
<td><FontAwesomeIcon :icon="['fas', 'circle-check']" /></td>
</tr>
<tr>
<th>2FAuth</th>
<td></td>
<td></td>
<td><FontAwesomeIcon :icon="['fas', 'circle-check']" /></td>
</tr>
</tbody>
</table>
</div>
<div v-else-if="isFetching && exportedAccounts.length === 0">
<Spinner :type="'fullscreen-overlay'" :isVisible="true" :message="'twofaccounts.import.parsing_data'" />
</div>
<div v-else>
<div class="block is-size-7-mobile" v-html="$t('twofaccounts.import.submitted_data_parsed_now_accounts_are_awaiting_import')"></div>
<div v-for="(account, index) in exportedAccounts" :key="account.name" class="group-item is-size-5 is-size-6-mobile">
<div class="is-flex is-justify-content-space-between">
<!-- Account name -->
@ -338,11 +395,6 @@
<button @click="exportedAccounts = []" class="has-text-grey button is-small is-ghost">{{ $t('commons.clear') }}</button>
</div>
</div>
<div v-if="isFetching && exportedAccounts.length === 0" class="has-text-centered">
<span class="is-size-4">
<FontAwesomeIcon :icon="['fas', 'spinner']" spin />
</span>
</div>
<!-- footer -->
<VueFooter :showButtons="true">
<!-- Import all button -->

View File

@ -135,18 +135,21 @@
'import' => [
'import' => 'Import',
'to_import' => 'Import',
'import_legend' => '2FAuth can import data from various 2FA apps.<br />Use the Export feature of these apps to get a migration resource (a QR code or a file) and load it using your preferred method below.',
'import_legend' => '2FAuth can import data from various 2FA apps.<br />Use the Export feature of these apps to get a migration resource like a QR code or a JSON file then load it here.',
'upload' => 'Upload',
'scan' => 'Scan',
'supported_formats_for_qrcode_upload' => 'Accepted: jpg, jpeg, png, bmp, gif, svg, or webp',
'supported_formats_for_file_upload' => 'Accepted: Plain text, json, 2fas',
'supported_migration_formats' => 'Supported migration formats',
'qr_code' => 'QR Code',
'text_file' => 'Text file',
'plain_text' => 'Plain text',
'parsing_data' => 'Parsing data...',
'issuer' => 'Issuer',
'imported' => 'Imported',
'failure' => 'Failure',
'x_valid_accounts_found' => ':count valid accounts found',
'submitted_data_parsed_now_accounts_are_awaiting_import' => 'The following 2FA accounts were found in the migration resource, so far none of them have been added to 2FAuth. Use the available buttons to permanently save them to your 2FA collection or discard them.',
'import_all' => 'Import all',
'import_this_account' => 'Import this account',
'discard_all' => 'Discard all',
@ -156,7 +159,7 @@
'possible_duplicate' => 'An account with the exact same data already exists',
'invalid_account' => '- invalid account -',
'invalid_service' => '- invalid service -',
'do_not_set_password_or_encryption' => 'Do NOT enable Password protection or Encryption when you export data (from a 2FA app) you want to import into 2FAuth.',
'do_not_set_password_or_encryption' => 'Do NOT enable Password protection or Encryption when you export data from a 2FA app otherwise 2FAuth will not be able to decipher them.',
],
];