|
|
@@ -1,147 +1,447 @@
|
|
|
<template>
|
|
|
<q-card-section class="no-padding">
|
|
|
- <div class="column q-gutter-y-lg q-pt-md">
|
|
|
- <div
|
|
|
- class="bg-surface q-pa-lg rounded-borders flex flex-center column q-gutter-y-sm"
|
|
|
- @click="openSubStep('id_front', 'document_front')"
|
|
|
- >
|
|
|
- <q-icon name="mdi-card-account-details-outline" size="48px" color="primary" />
|
|
|
- <div class="text-subtitle1 text-weight-bold text-center">{{ $t('provider.login.steps.step_4.document_front') }}</div>
|
|
|
- <div class="text-caption text-grey-7 text-center">{{ $t('provider.login.steps.step_4.document_front_desc') }}</div>
|
|
|
- <div v-if="form.document_front" class="row items-center q-gutter-x-sm text-green text-weight-bold">
|
|
|
- <q-icon name="check_circle" size="20px" />
|
|
|
- <span>{{ $t('provider.login.steps.step_4.photo_captured') }}</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
+ <div class="column items-center q-gutter-y-xl q-pt-md">
|
|
|
|
|
|
- <div
|
|
|
- class="bg-surface q-pa-lg rounded-borders flex flex-center column q-gutter-y-sm"
|
|
|
- @click="openSubStep('id_back', 'document_back')"
|
|
|
- >
|
|
|
- <q-icon name="mdi-card-bulleted-outline" size="48px" color="primary" />
|
|
|
- <div class="text-subtitle1 text-weight-bold text-center">{{ $t('provider.login.steps.step_4.document_back') }}</div>
|
|
|
- <div class="text-caption text-grey-7 text-center">{{ $t('provider.login.steps.step_4.document_back_desc') }}</div>
|
|
|
- <div v-if="form.document_back" class="row items-center q-gutter-x-sm text-green text-weight-bold">
|
|
|
- <q-icon name="check_circle" size="20px" />
|
|
|
- <span>{{ $t('provider.login.steps.step_4.photo_captured') }}</span>
|
|
|
+ <div class="column items-center q-gutter-y-sm">
|
|
|
+ <div
|
|
|
+ class="photo-circle"
|
|
|
+ :class="selfieDone ? 'photo-circle--done' : 'photo-circle--pending'"
|
|
|
+ @click="subStep = 'selfie_camera'"
|
|
|
+ >
|
|
|
+ <q-icon name="photo_camera" size="52px" color="white" />
|
|
|
</div>
|
|
|
+
|
|
|
+ <template v-if="!selfieDone">
|
|
|
+ <q-btn
|
|
|
+ color="primary-button"
|
|
|
+ :label="t('provider.login.steps.step_4.btn_selfie')"
|
|
|
+ rounded
|
|
|
+ no-caps
|
|
|
+ padding="10px 28px"
|
|
|
+ @click="subStep = 'selfie_camera'"
|
|
|
+ />
|
|
|
+ <div class="text-caption text-grey-6 text-center">{{ t('provider.login.steps.step_4.selfie_hint') }}</div>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <div class="row items-center q-gutter-x-xs">
|
|
|
+ <q-icon name="check_circle" color="positive" size="18px" />
|
|
|
+ <span class="text-body2 text-weight-bold text-primary">{{ t('provider.login.steps.step_4.selfie_sent') }}</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
</div>
|
|
|
|
|
|
- <div
|
|
|
- class="bg-surface q-pa-lg rounded-borders flex flex-center column q-gutter-y-sm"
|
|
|
- @click="openSubStep('selfie', 'selfie_with_document')"
|
|
|
- >
|
|
|
- <q-icon name="mdi-camera-account" size="48px" color="primary" />
|
|
|
- <div class="text-subtitle1 text-weight-bold text-center">{{ $t('provider.login.steps.step_4.selfie') }}</div>
|
|
|
- <div class="text-caption text-grey-7 text-center">{{ $t('provider.login.steps.step_4.selfie_desc') }}</div>
|
|
|
- <div v-if="form.selfie_with_document" class="row items-center q-gutter-x-sm text-green text-weight-bold">
|
|
|
- <q-icon name="check_circle" size="20px" />
|
|
|
- <span>{{ $t('provider.login.steps.step_4.photo_captured') }}</span>
|
|
|
+ <div class="column items-center q-gutter-y-sm">
|
|
|
+ <div
|
|
|
+ class="photo-circle"
|
|
|
+ :class="docDone ? 'photo-circle--done' : 'photo-circle--pending'"
|
|
|
+ @click="subStep = 'doc_front_camera'"
|
|
|
+ >
|
|
|
+ <q-icon name="mdi-card-account-details-outline" size="52px" color="white" />
|
|
|
</div>
|
|
|
+
|
|
|
+ <template v-if="!docDone">
|
|
|
+ <q-btn
|
|
|
+ color="primary-button"
|
|
|
+ :label="t('provider.login.steps.step_4.btn_document')"
|
|
|
+ rounded
|
|
|
+ no-caps
|
|
|
+ padding="10px 28px"
|
|
|
+ @click="subStep = 'doc_front_camera'"
|
|
|
+ />
|
|
|
+ <div class="text-caption text-grey-6 text-center">{{ t('provider.login.steps.step_4.document_hint') }}</div>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <div class="row items-center q-gutter-x-xs">
|
|
|
+ <q-icon name="check_circle" color="positive" size="18px" />
|
|
|
+ <span class="text-body2 text-weight-bold text-primary">{{ t('provider.login.steps.step_4.document_sent') }}</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
</div>
|
|
|
+
|
|
|
</div>
|
|
|
|
|
|
<Teleport to="body">
|
|
|
- <div v-if="showCamera" class="camera-overlay">
|
|
|
- <div class="camera-header q-pa-md flex justify-between items-center bg-white text-black">
|
|
|
- <q-btn flat round icon="arrow_back" color="black" @click="closeCamera" />
|
|
|
- <div class="text-black text-weight-bold text-subtitle1">{{ currentSubStepTitle }}</div>
|
|
|
- <div style="width: 32px"></div>
|
|
|
+ <div v-if="subStep !== 'main'" class="fs-overlay">
|
|
|
+ <div v-if="isCameraState" class="fs-camera">
|
|
|
+ <div class="fs-header">
|
|
|
+ <q-btn flat dense round icon="chevron_left" color="black" @click="goBack" />
|
|
|
+ <span class="text-weight-bold text-body1">{{ t('common.actions.back') }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="fs-viewport">
|
|
|
+ <video ref="videoRef" autoplay playsinline muted class="fs-video" />
|
|
|
+ <div v-if="subStep === 'selfie_camera'" class="guide-oval" />
|
|
|
+ <div v-else class="guide-doc" />
|
|
|
+ </div>
|
|
|
+ <div class="fs-footer">
|
|
|
+ <q-btn
|
|
|
+ color="primary-button"
|
|
|
+ :label="captureLabel"
|
|
|
+ rounded
|
|
|
+ no-caps
|
|
|
+ padding="14px 32px"
|
|
|
+ class="full-width"
|
|
|
+ @click="capturePhoto"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
</div>
|
|
|
|
|
|
- <div class="camera-container flex flex-center">
|
|
|
- <div v-if="cameraMode === 'selfie'" class="selfie-guide"></div>
|
|
|
- <div v-else class="document-guide"></div>
|
|
|
+ <div v-else-if="isPreviewState" class="fs-camera">
|
|
|
+ <div class="fs-header">
|
|
|
+ <q-btn flat dense round icon="chevron_left" color="black" @click="goBack" />
|
|
|
+ <span class="text-weight-bold text-body1">{{ t('common.actions.back') }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="fs-viewport">
|
|
|
+ <img :src="currentPreview" class="fs-video fs-preview-img" />
|
|
|
+ <div v-if="subStep === 'selfie_preview'" class="guide-oval" />
|
|
|
+ <div v-else class="guide-doc" />
|
|
|
+ </div>
|
|
|
+ <div class="fs-footer column q-gutter-y-sm">
|
|
|
+ <q-btn
|
|
|
+ outline
|
|
|
+ color="primary-button"
|
|
|
+ :label="t('provider.login.steps.step_4.btn_retake')"
|
|
|
+ rounded
|
|
|
+ no-caps
|
|
|
+ padding="12px 32px"
|
|
|
+ class="full-width"
|
|
|
+ @click="retakePhoto"
|
|
|
+ />
|
|
|
+ <q-btn
|
|
|
+ color="primary-button"
|
|
|
+ :label="subStep === 'selfie_preview' ? t('provider.login.steps.step_4.btn_confirm') : t('provider.login.steps.step_4.btn_use_photo')"
|
|
|
+ rounded
|
|
|
+ no-caps
|
|
|
+ padding="12px 32px"
|
|
|
+ class="full-width"
|
|
|
+ @click="usePhoto"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
</div>
|
|
|
|
|
|
- <div class="camera-footer q-pa-lg flex flex-center">
|
|
|
- <q-btn round color="primary" size="22px" icon="photo_camera" @click="capturePhoto" />
|
|
|
+ <div v-else class="fs-result">
|
|
|
+ <q-img :src="LogoDiaria" class="q-mb-xl" style="max-width: 150px" />
|
|
|
+
|
|
|
+ <div
|
|
|
+ class="result-circle"
|
|
|
+ :class="docResult === 'error' ? 'result-circle--error' : 'result-circle--done'"
|
|
|
+ >
|
|
|
+ <q-icon
|
|
|
+ :name="subStep === 'selfie_result' ? 'photo_camera' : 'mdi-card-account-details-outline'"
|
|
|
+ size="56px"
|
|
|
+ color="white"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <template v-if="docResult === 'error'">
|
|
|
+ <q-icon name="cancel" color="negative" size="28px" class="q-mt-md" />
|
|
|
+ <div class="text-body1 text-weight-bold text-primary text-center q-mt-xs q-px-lg">
|
|
|
+ {{ t('provider.login.steps.step_4.error_message') }}
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <div v-else class="text-body1 text-weight-bold text-primary text-center q-mt-md q-px-lg">
|
|
|
+ {{ subStep === 'selfie_result' ? t('provider.login.steps.step_4.selfie_success') : t('provider.login.steps.step_4.document_success') }}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <q-btn
|
|
|
+ color="primary-button"
|
|
|
+ :label="docResult === 'error' ? t('provider.login.steps.step_4.btn_retry') : t('provider.login.steps.step_4.btn_continue')"
|
|
|
+ rounded
|
|
|
+ no-caps
|
|
|
+ padding="14px 48px"
|
|
|
+ class="q-mt-xl"
|
|
|
+ style="min-width: 200px"
|
|
|
+ @click="handleResult"
|
|
|
+ />
|
|
|
+
|
|
|
+ <div v-if="subStep === 'doc_result' && docResult !== 'error'" class="text-caption text-grey-6 text-center q-mt-sm">
|
|
|
+ {{ t('provider.login.steps.step_4.document_hint_result') }}
|
|
|
+ </div>
|
|
|
</div>
|
|
|
+
|
|
|
</div>
|
|
|
</Teleport>
|
|
|
+
|
|
|
+ <canvas ref="canvasRef" style="display: none" />
|
|
|
</q-card-section>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
-import { ref, watch } from 'vue';
|
|
|
+import { ref, computed, watch, onUnmounted, nextTick } from 'vue';
|
|
|
+import { useI18n } from 'vue-i18n';
|
|
|
+import LogoDiaria from 'src/assets/logo_diaria_campos_login.svg';
|
|
|
+
|
|
|
+const { t } = useI18n();
|
|
|
|
|
|
const form = defineModel({ type: Object, required: true });
|
|
|
const emit = defineEmits(['update:show-sub-step']);
|
|
|
|
|
|
const subStep = ref('main');
|
|
|
+const docResult = ref(null); // null | 'success' | 'error'
|
|
|
+
|
|
|
+const tempDocFront = ref(null);
|
|
|
+const tempDocBack = ref(null);
|
|
|
+const tempSelfie = ref(null);
|
|
|
+
|
|
|
+const stream = ref(null);
|
|
|
+const videoRef = ref(null);
|
|
|
+const canvasRef = ref(null);
|
|
|
+
|
|
|
+const CAMERA_STATES = ['selfie_camera', 'doc_front_camera', 'doc_back_camera'];
|
|
|
+const PREVIEW_STATES = ['selfie_preview', 'doc_front_preview', 'doc_back_preview'];
|
|
|
+
|
|
|
+const isCameraState = computed(() => CAMERA_STATES.includes(subStep.value));
|
|
|
+const isPreviewState = computed(() => PREVIEW_STATES.includes(subStep.value));
|
|
|
+
|
|
|
+const selfieDone = computed(() => !!form.value.selfie_base64);
|
|
|
+const docDone = computed(() => !!form.value.document_front_base64 && !!form.value.document_back_base64);
|
|
|
+
|
|
|
+const currentPreview = computed(() => {
|
|
|
+ if (subStep.value === 'selfie_preview') return tempSelfie.value;
|
|
|
+ if (subStep.value === 'doc_front_preview') return tempDocFront.value;
|
|
|
+ if (subStep.value === 'doc_back_preview') return tempDocBack.value;
|
|
|
+ return null;
|
|
|
+});
|
|
|
+
|
|
|
+const captureLabel = computed(() => {
|
|
|
+ if (subStep.value === 'selfie_camera') return t('provider.login.steps.step_4.btn_capture_selfie');
|
|
|
+ if (subStep.value === 'doc_front_camera') return t('provider.login.steps.step_4.btn_capture_front');
|
|
|
+ return t('provider.login.steps.step_4.btn_capture_back');
|
|
|
+});
|
|
|
+
|
|
|
+const stopCamera = () => {
|
|
|
+ if (stream.value) {
|
|
|
+ stream.value.getTracks().forEach((t) => t.stop());
|
|
|
+ stream.value = null;
|
|
|
+ }
|
|
|
+ if (videoRef.value) {
|
|
|
+ videoRef.value.srcObject = null;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const startCameraStream = async (facingMode) => {
|
|
|
+ try {
|
|
|
+ stopCamera();
|
|
|
+ stream.value = await navigator.mediaDevices.getUserMedia({
|
|
|
+ video: { facingMode, width: { ideal: 1280 }, height: { ideal: 720 } },
|
|
|
+ });
|
|
|
+ await nextTick();
|
|
|
+ if (videoRef.value) {
|
|
|
+ videoRef.value.srcObject = stream.value;
|
|
|
+ await videoRef.value.play().catch(() => {});
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ } catch {
|
|
|
+ stopCamera();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+watch(subStep, async (newStep) => {
|
|
|
+ emit('update:show-sub-step', newStep !== 'main');
|
|
|
|
|
|
-watch(subStep, (val) => {
|
|
|
- emit('update:show-sub-step', val !== 'main');
|
|
|
+ if (!CAMERA_STATES.includes(newStep)) {
|
|
|
+ stopCamera();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (newStep === 'selfie_camera') {
|
|
|
+ const ok = await startCameraStream('user');
|
|
|
+ if (!ok) subStep.value = 'main';
|
|
|
+ } else if (newStep === 'doc_front_camera' || newStep === 'doc_back_camera') {
|
|
|
+ const ok = await startCameraStream('environment');
|
|
|
+ if (!ok) {
|
|
|
+ docResult.value = 'error';
|
|
|
+ subStep.value = 'doc_result';
|
|
|
+ }
|
|
|
+ }
|
|
|
});
|
|
|
|
|
|
-const openSubStep = (step) => {
|
|
|
- subStep.value = step;
|
|
|
+onUnmounted(stopCamera);
|
|
|
+
|
|
|
+const capturePhoto = () => {
|
|
|
+ const video = videoRef.value;
|
|
|
+ const canvas = canvasRef.value;
|
|
|
+ if (!video || !canvas) return;
|
|
|
+
|
|
|
+ canvas.width = video.videoWidth || 1280;
|
|
|
+ canvas.height = video.videoHeight || 720;
|
|
|
+ canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
|
+ const dataUrl = canvas.toDataURL('image/jpeg', 0.85);
|
|
|
+
|
|
|
+ if (subStep.value === 'selfie_camera') {
|
|
|
+ tempSelfie.value = dataUrl;
|
|
|
+ subStep.value = 'selfie_preview';
|
|
|
+ } else if (subStep.value === 'doc_front_camera') {
|
|
|
+ tempDocFront.value = dataUrl;
|
|
|
+ subStep.value = 'doc_front_preview';
|
|
|
+ } else if (subStep.value === 'doc_back_camera') {
|
|
|
+ tempDocBack.value = dataUrl;
|
|
|
+ subStep.value = 'doc_back_preview';
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const usePhoto = () => {
|
|
|
+ if (subStep.value === 'selfie_preview') {
|
|
|
+ form.value.selfie_base64 = tempSelfie.value;
|
|
|
+ subStep.value = 'selfie_result';
|
|
|
+ } else if (subStep.value === 'doc_front_preview') {
|
|
|
+ subStep.value = 'doc_back_camera';
|
|
|
+ } else if (subStep.value === 'doc_back_preview') {
|
|
|
+ form.value.document_front_base64 = tempDocFront.value;
|
|
|
+ form.value.document_back_base64 = tempDocBack.value;
|
|
|
+ docResult.value = 'success';
|
|
|
+ subStep.value = 'doc_result';
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const retakePhoto = () => {
|
|
|
+ const retakeMap = {
|
|
|
+ selfie_preview: 'selfie_camera',
|
|
|
+ doc_front_preview: 'doc_front_camera',
|
|
|
+ doc_back_preview: 'doc_back_camera',
|
|
|
+ };
|
|
|
+ subStep.value = retakeMap[subStep.value] || 'main';
|
|
|
};
|
|
|
|
|
|
+const goBack = () => {
|
|
|
+ const backMap = {
|
|
|
+ selfie_camera: 'main',
|
|
|
+ selfie_preview: 'selfie_camera',
|
|
|
+ doc_front_camera: 'main',
|
|
|
+ doc_front_preview: 'doc_front_camera',
|
|
|
+ doc_back_camera: 'doc_front_preview',
|
|
|
+ doc_back_preview: 'doc_back_camera',
|
|
|
+ };
|
|
|
+ subStep.value = backMap[subStep.value] || 'main';
|
|
|
+};
|
|
|
+
|
|
|
+const handleResult = () => {
|
|
|
+ if (docResult.value === 'error') {
|
|
|
+ docResult.value = null;
|
|
|
+ tempDocFront.value = null;
|
|
|
+ tempDocBack.value = null;
|
|
|
+ subStep.value = 'doc_front_camera';
|
|
|
+ } else {
|
|
|
+ subStep.value = 'main';
|
|
|
+ }
|
|
|
+};
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
-.capture-screen {
|
|
|
- background: #f8f8f8;
|
|
|
+.photo-circle {
|
|
|
+ width: 130px;
|
|
|
+ height: 130px;
|
|
|
+ border-radius: 50%;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: transform 0.15s ease;
|
|
|
+
|
|
|
+ &:active { transform: scale(0.95); }
|
|
|
+
|
|
|
+ &--pending {
|
|
|
+ background-color: rgba(139, 92, 246, 0.18);
|
|
|
+ }
|
|
|
+
|
|
|
+ &--done {
|
|
|
+ background: linear-gradient(-90deg, #ec48d1 5%, #6b11cb 65%, #2574fc 100%);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.fs-overlay {
|
|
|
position: fixed;
|
|
|
- top: 0;
|
|
|
- left: 0;
|
|
|
- right: 0;
|
|
|
- bottom: 0;
|
|
|
+ inset: 0;
|
|
|
z-index: 9999;
|
|
|
+ background: white;
|
|
|
+}
|
|
|
+
|
|
|
+.fs-camera {
|
|
|
+ height: 100%;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
}
|
|
|
|
|
|
-.capture-content {
|
|
|
- flex: 1;
|
|
|
- padding: 20px;
|
|
|
+.fs-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 4px;
|
|
|
+ padding: 8px 12px;
|
|
|
+ background: white;
|
|
|
+ flex-shrink: 0;
|
|
|
}
|
|
|
|
|
|
-.capture-placeholder-container {
|
|
|
- position: relative;
|
|
|
- width: 100%;
|
|
|
- max-width: 320px;
|
|
|
+.fs-viewport {
|
|
|
+ flex: 1;
|
|
|
background: #000;
|
|
|
- aspect-ratio: 9/16;
|
|
|
- border-radius: 20px;
|
|
|
+ position: relative;
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
|
|
|
-.capture-placeholder {
|
|
|
+.fs-video {
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
- background: #1a1a1a;
|
|
|
- display: flex;
|
|
|
- flex-direction: column;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
+ object-fit: cover;
|
|
|
+ display: block;
|
|
|
}
|
|
|
|
|
|
-.capture-preview {
|
|
|
- width: 100%;
|
|
|
- height: 100%;
|
|
|
+.fs-preview-img {
|
|
|
+ object-fit: contain;
|
|
|
+}
|
|
|
+
|
|
|
+.fs-footer {
|
|
|
+ padding: 16px 20px;
|
|
|
+ background: white;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.guide-oval {
|
|
|
+ position: absolute;
|
|
|
+ top: 50%;
|
|
|
+ left: 50%;
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
+ width: 62%;
|
|
|
+ height: 70%;
|
|
|
+ border: 3px solid #8b5cf6;
|
|
|
+ border-radius: 50%;
|
|
|
+ pointer-events: none;
|
|
|
}
|
|
|
|
|
|
-.selfie-oval-overlay {
|
|
|
+.guide-doc {
|
|
|
position: absolute;
|
|
|
top: 50%;
|
|
|
left: 50%;
|
|
|
transform: translate(-50%, -50%);
|
|
|
- width: 70%;
|
|
|
- height: 60%;
|
|
|
- border: 4px solid var(--q-primary);
|
|
|
- border-radius: 50% / 50%;
|
|
|
+ width: 86%;
|
|
|
+ height: 52%;
|
|
|
+ border: 2px solid rgba(255, 255, 255, 0.65);
|
|
|
+ border-radius: 14px;
|
|
|
pointer-events: none;
|
|
|
}
|
|
|
|
|
|
-.bg-primary-fade {
|
|
|
- background-color: rgba(var(--q-primary-rgb), 0.2);
|
|
|
+.fs-result {
|
|
|
+ height: 100%;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ padding: 40px 28px;
|
|
|
+ background: white;
|
|
|
}
|
|
|
|
|
|
-.capture-placeholder-container.column.flex-center {
|
|
|
- &.doc {
|
|
|
- aspect-ratio: 1/1.4; // Slightly wider for ID card
|
|
|
+.result-circle {
|
|
|
+ width: 150px;
|
|
|
+ height: 150px;
|
|
|
+ border-radius: 50%;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+
|
|
|
+ &--done {
|
|
|
+ background: linear-gradient(-90deg, #ec48d1 5%, #6b11cb 65%, #2574fc 100%);
|
|
|
+ }
|
|
|
+
|
|
|
+ &--error {
|
|
|
+ background-color: rgba(139, 92, 246, 0.18);
|
|
|
}
|
|
|
}
|
|
|
</style>
|