Skip to content

Commit

Permalink
Merge pull request #20 from european-epc-competence-center/scan-select
Browse files Browse the repository at this point in the history
Add scan option on select
  • Loading branch information
F-Node-Karlsruhe authored Dec 8, 2022
2 parents cf3056e + 8640679 commit eaf164d
Show file tree
Hide file tree
Showing 6 changed files with 235 additions and 10 deletions.
89 changes: 86 additions & 3 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"vue": "^3.2.41",
"vue-router": "^4.1.6",
"vue-toastification": "^2.0.0-rc.5",
"vue3-qrcode-reader": "^0.0.1",
"vuex": "^4.1.0"
},
"devDependencies": {
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/styles/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,14 @@ html, body {margin: 0; height: 100%; overflow: hidden}
}

.credentialid {
font-size: 0.7rem;
font-size: 0.6rem;
}

.scanqr {
border: 1px solid #ced4da !important;
border-right: none !important;
color: #212529 !important;
}

#backbutton {
position: absolute;
Expand Down
16 changes: 14 additions & 2 deletions frontend/src/views/Entry.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<h5>Credential File</h5>
<form v-on:submit.prevent="submitFile">
<div class="input-group">
<button @click="scan='file'" data-bs-toggle="modal" type="button" data-bs-target="#scan-modal" class="btn btn-outline-light scanqr"><i class="bi-qr-code" role="img" aria-label="QR-Code"></i></button>
<input multiple v-on:change="onFileChange" id="credentialId" type="file" class="form-control" placeholder="credential.json" aria-label="Credential" aria-describedby="credentialHelp">
<button class="btn btn-outline-primary" type="submit">Verify</button>
</div>
Expand All @@ -24,6 +25,7 @@
<h5>Credential Id</h5>
<form v-on:submit.prevent="submitId">
<div class="input-group">
<button @click="scan='credid'" data-bs-toggle="modal" type="button" data-bs-target="#scan-modal" class="btn btn-outline-light scanqr"><i class="bi-qr-code" role="img" aria-label="QR-Code"></i></button>
<input v-model="credentialId" id="credentialId" type="text" class="form-control" placeholder="https://registry.org/vc/uuid" aria-label="Credential Id" aria-describedby="credentialIdHelp">
<button class="btn btn-outline-primary" type="submit">Verify</button>
</div>
Expand All @@ -35,29 +37,39 @@
<h5>Subject Id</h5>
<form v-on:submit.prevent="submitSubject">
<div class="input-group">
<button @click="scan='subid'" data-bs-toggle="modal" type="button" data-bs-target="#scan-modal" class="btn btn-outline-light scanqr"><i class="bi-qr-code" role="img" aria-label="QR-Code"></i></button>
<input v-model="subjectId" id="credentialId" type="text" class="form-control" placeholder="https://gs1.org/123455" aria-label="Subject Id" aria-describedby="subjectIdHelp">
<button class="btn btn-outline-primary" type="submit">Verify</button>
</div>
<div class="form-text">Provide the subject id for which the credentials shall be queried</div>
</form>
</div>
<ScanModal :scan="scan"/>
</div>
</div>
</template>
<script>
import { useToast } from "vue-toastification";
import ScanModal from "./ScanModal.vue"
import 'bootstrap/js/dist/modal'
export default {
name: 'Entry',
components: {
ScanModal
},
data() {
return {
toast: useToast(),
credentialId: '',
subjectId: ''
subjectId: '',
scan: ''
}
},
mounted() {
document.getElementById('scan-modal').addEventListener('hidden.bs.modal', () => { this.scan = '' });
},
methods: {
onFileChange(e) {
var files = Array.from(e.target.files || e.dataTransfer.files);
Expand Down
118 changes: 118 additions & 0 deletions frontend/src/views/ScanModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<template>
<div class="modal fade" id="scan-modal" aria-hidden="true" tabindex="-1">
<div class="modal-dialog modal-dialog-centered modal-fullscreen-md-down">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5 text-primary" id="exampleModalToggleLabel">Scan {{getRequestType()}}</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body text-center">
<qrcode-stream v-if="scan!=''" @init="onInit" @decode="onDecode" :camera="camera">
<div v-if="!cameraReady" class="spinner-border text-primary m-5" role="status">
<span class="visually-hidden">Waiting for camera ...</span>
</div>
</qrcode-stream>
<qrcode-drop-zone @decode="onDecode" class="p-5 mb-3 text-center shadow"><h5>... or drop image here</h5></qrcode-drop-zone>
</div>
</div>
</div>
</div>
</template>

<script>
import { QrcodeStream, QrcodeDropZone } from 'vue3-qrcode-reader'
import { useToast } from "vue-toastification";
import { Modal } from 'bootstrap';
export default {
name: 'ScanModal',
props: {
scan: String
},
components: {
QrcodeStream,
QrcodeDropZone
},
data() {
return {
toast: useToast(),
cameraReady: false,
modal: null,
}
},
mounted() {
this.modal = new Modal(document.getElementById('scan-modal'));
},
computed: {
camera() {
if(this.scan == '') return ''
return 'auto'
}
},
methods: {
async onInit (promise) {
try {
await promise
this.cameraReady = true
} catch (error) {
if (error.name === 'NotAllowedError') {
this.toast.error("ERROR: you need to grant camera access permission")
} else if (error.name === 'NotFoundError') {
this.toast.error("ERROR: no camera on this device")
} else if (error.name === 'NotSupportedError') {
this.toast.error("ERROR: secure context required (HTTPS, localhost)")
} else if (error.name === 'NotReadableError') {
this.toast.error("ERROR: is the camera already in use?")
} else if (error.name === 'OverconstrainedError') {
this.toast.error("ERROR: installed cameras are not suitable")
} else if (error.name === 'StreamApiNotSupportedError') {
this.toast.error("ERROR: Stream API is not supported in this browser")
} else if (error.name === 'InsecureContextError') {
this.toast.error('ERROR: Camera access is only permitted in secure context. Use HTTPS or localhost rather than HTTP.')
} else {
this.toast.error(`${error}`);
}
}
},
onDecode(decodedString) {
try{
if (decodedString.length != 0) {
if (this.scan == 'file') {
const credential = JSON.parse(decodedString);
if (Array.isArray(credential)) {
this.$store.dispatch("addCredentials", credential);
} else {
this.$store.dispatch("addCredential", credential);
}
this.$router.push({ path: '/verify' })
} else if (this.scan == 'credid') {
this.$router.push({ path: '/verify', query: { credentialId: encodeURIComponent(decodedString) } })
} else {
this.$router.push({ path: '/verify', query: { subjectId: encodeURIComponent(decodedString) } })
}
this.modal.hide()
// modal backdrop does not disappear on hide ....
document.getElementsByClassName('modal-backdrop').forEach((el) => el.remove());
}
} catch (error) {
this.toast.warning(`Error reading the credential/s!\n${error}`);
}
},
getRequestType() {
if (this.scan == 'file') return 'Credential';
if (this.scan == 'credid') return 'Credential Id'
return 'Subject Id'
}
}
}
</script>
Loading

0 comments on commit eaf164d

Please sign in to comment.