2292 lines
61 KiB
Vue
2292 lines
61 KiB
Vue
<template>
|
|
<div class="warranty-activation">
|
|
<!-- Hero Section -->
|
|
<section class="hero-section">
|
|
<div class="hero-background">
|
|
<div class="hero-overlay"></div>
|
|
<div class="hero-pattern"></div>
|
|
</div>
|
|
<v-container class="hero-content">
|
|
<v-row justify="center" align="center" class="min-height-screen">
|
|
<v-col cols="12" md="10" lg="8" xl="6">
|
|
|
|
<!-- Main Card -->
|
|
<v-card class="main-card" elevation="0">
|
|
<v-card-text class="pa-6 pa-md-8">
|
|
<!-- Modern Progress Steps -->
|
|
<div class="modern-stepper mb-10">
|
|
<div class="stepper-track">
|
|
<div
|
|
class="stepper-progress"
|
|
:style="{ width: `${((currentStep - 1) / (steps.length - 1)) * 100}%` }"
|
|
></div>
|
|
</div>
|
|
|
|
<div class="stepper-steps">
|
|
<div
|
|
v-for="(step, index) in steps"
|
|
:key="index"
|
|
class="stepper-step"
|
|
:class="{
|
|
'active': currentStep === index + 1,
|
|
'completed': currentStep > index + 1,
|
|
'pending': currentStep < index + 1
|
|
}"
|
|
>
|
|
<div class="stepper-circle">
|
|
<div class="stepper-circle-inner">
|
|
<v-icon v-if="currentStep > index + 1" size="18">mdi-check</v-icon>
|
|
<span v-else class="stepper-number">{{ index + 1 }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="stepper-content">
|
|
<div class="stepper-title">{{ step.title }}</div>
|
|
<div class="stepper-description">{{ step.subtitle }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 1: Enter Code -->
|
|
<div v-if="currentStep === 1" class="step-content-wrapper">
|
|
<div class="step-header text-center mb-6">
|
|
<div class="step-icon-wrapper mb-4">
|
|
<v-icon size="48" color="primary">mdi-qrcode-scan</v-icon>
|
|
</div>
|
|
<h2 class="step-main-title mb-2">کد گارانتی را وارد کنید</h2>
|
|
<p class="step-description">
|
|
کد گارانتی روی برچسب کالا یا فاکتور خرید موجود است
|
|
</p>
|
|
</div>
|
|
|
|
<v-form ref="codeForm" v-model="codeFormValid" @submit.prevent="checkWarrantyCode">
|
|
<div class="input-section mb-6">
|
|
|
|
<v-text-field
|
|
v-model="warrantyCode"
|
|
label="کد گارانتی"
|
|
placeholder="مثال: WR-123456789"
|
|
outlined
|
|
:rules="codeRules"
|
|
:loading="loading"
|
|
prepend-inner-icon="mdi-shield-outline"
|
|
class="custom-input"
|
|
hide-details="auto"
|
|
@keyup.enter="checkWarrantyCode"
|
|
>
|
|
<template v-slot:append>
|
|
<v-btn
|
|
icon
|
|
small
|
|
@click="scanQrCode"
|
|
:disabled="loading"
|
|
class="qr-scan-btn"
|
|
>
|
|
<v-icon size="20">mdi-qrcode-scan</v-icon>
|
|
</v-btn>
|
|
</template>
|
|
</v-text-field>
|
|
</div>
|
|
|
|
<v-alert
|
|
v-if="errorMessage"
|
|
type="error"
|
|
dismissible
|
|
class="mb-4 custom-alert"
|
|
border="left"
|
|
colored-border
|
|
>
|
|
{{ errorMessage }}
|
|
</v-alert>
|
|
|
|
<div class="action-buttons mb-6">
|
|
<v-btn
|
|
color="primary"
|
|
large
|
|
block
|
|
:loading="loading"
|
|
:disabled="!codeFormValid"
|
|
@click="checkWarrantyCode"
|
|
class="primary-btn mb-4"
|
|
elevation="2"
|
|
>
|
|
<v-icon left>mdi-magnify</v-icon>
|
|
بررسی کد گارانتی
|
|
</v-btn>
|
|
</div>
|
|
</v-form>
|
|
</div>
|
|
|
|
<!-- Step 2: Confirm Information -->
|
|
<div v-if="currentStep === 2" class="step-content-wrapper">
|
|
<div class="step-header text-center mb-6">
|
|
<div class="step-icon-wrapper mb-4">
|
|
<v-icon size="48" color="primary">mdi-check-circle-outline</v-icon>
|
|
</div>
|
|
<h2 class="step-main-title mb-2">تأیید اطلاعات کالا</h2>
|
|
<p class="step-description">
|
|
لطفاً اطلاعات زیر را بررسی و در صورت صحت، تأیید کنید
|
|
</p>
|
|
</div>
|
|
|
|
<div class="product-info-card mb-6">
|
|
<div class="product-info-grid">
|
|
<div class="info-item">
|
|
<div class="info-icon">
|
|
<v-icon size="24" color="primary">mdi-package-variant</v-icon>
|
|
</div>
|
|
<div class="info-content">
|
|
<div class="info-label">نام کالا</div>
|
|
<div class="info-value">{{ productInfo.productName }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="info-item">
|
|
<div class="info-icon">
|
|
<v-icon size="24" color="primary">mdi-qrcode-scan</v-icon>
|
|
</div>
|
|
<div class="info-content">
|
|
<div class="info-label">کد کالا</div>
|
|
<div class="info-value">{{ productInfo.productCode }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="info-item">
|
|
<div class="info-icon">
|
|
<v-icon size="24" color="primary">mdi-calendar</v-icon>
|
|
</div>
|
|
<div class="info-content">
|
|
<div class="info-label">تاریخ ثبت</div>
|
|
<div class="info-value">{{ formatDate(productInfo.submitDate) }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="info-item">
|
|
<div class="info-icon">
|
|
<v-icon size="24" color="primary">mdi-shield-check</v-icon>
|
|
</div>
|
|
<div class="info-content">
|
|
<div class="info-label">وضعیت گارانتی</div>
|
|
<div class="info-value">{{ warrantyStatusText }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="info-item info-item-wide">
|
|
<div class="info-icon">
|
|
<v-icon size="24" color="primary">mdi-barcode</v-icon>
|
|
</div>
|
|
<div class="info-content">
|
|
<div class="info-label">سریال کالا</div>
|
|
<div class="info-value">{{ productInfo.commoditySerial || 'بدون سریال' }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="info-item info-item-wide">
|
|
<div class="info-icon">
|
|
<v-icon size="24" color="primary">mdi-receipt</v-icon>
|
|
</div>
|
|
<div class="info-content">
|
|
<div class="info-label">توضیحات</div>
|
|
<div class="info-value">{{ productInfo.description || 'بدون توضیح' }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Activation Secret Input (isolated from info grid to avoid rerenders) -->
|
|
<div class="product-info-card mb-6" v-show="productInfo.requireActivationSecret">
|
|
<div class="info-item info-item-wide">
|
|
<div class="info-icon">
|
|
<v-icon size="24" color="primary">mdi-lock-check</v-icon>
|
|
</div>
|
|
<div class="info-content">
|
|
<div class="info-label">کد فعالسازی حواله</div>
|
|
<v-text-field
|
|
v-model.trim="activationSecret"
|
|
placeholder="کد 8 کاراکتری فعالسازی"
|
|
:rules="activationSecretRules"
|
|
maxlength="8"
|
|
variant="outlined"
|
|
density="comfortable"
|
|
hide-details="auto"
|
|
autocomplete="one-time-code"
|
|
spellcheck="false"
|
|
autocapitalize="off"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Time Warning -->
|
|
<div v-if="timeRemaining && productInfo.activation === 'deactive'" class="time-warning mb-6">
|
|
<div class="warning-content">
|
|
<div class="warning-icon">
|
|
<v-icon size="28" color="warning">mdi-clock-alert-outline</v-icon>
|
|
</div>
|
|
<div class="warning-text">
|
|
<div class="warning-title">مهلت فعالسازی</div>
|
|
<div class="warning-subtitle">{{ timeRemaining }} روز باقی مانده</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Status Warning -->
|
|
<div v-if="productInfo.activation !== 'deactive' && productInfo.activation !== 'active'" class="status-warning mb-6">
|
|
<div class="warning-content">
|
|
<div class="warning-icon" :class="{ 'error-icon': productInfo.activation === 'expired' }">
|
|
<v-icon size="28" color="error">mdi-alert-circle-outline</v-icon>
|
|
</div>
|
|
<div class="warning-text">
|
|
<div class="warning-title" :class="{ 'error-title': productInfo.activation === 'expired' }">وضعیت گارانتی</div>
|
|
<div class="warning-subtitle" :class="{ 'error-subtitle': productInfo.activation === 'expired' }">
|
|
این گارانتی {{ warrantyStatusText }} است و قابل فعالسازی نمیباشد
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="action-buttons">
|
|
<v-row no-gutters class="gap-3">
|
|
<v-col>
|
|
<v-btn
|
|
outlined
|
|
large
|
|
block
|
|
@click="currentStep = 1"
|
|
class="back-btn"
|
|
elevation="0"
|
|
>
|
|
<v-icon left>mdi-arrow-right</v-icon>
|
|
بازگشت
|
|
</v-btn>
|
|
</v-col>
|
|
<v-col>
|
|
<v-btn
|
|
color="success"
|
|
large
|
|
block
|
|
:loading="loading"
|
|
:disabled="productInfo.activation !== 'deactive' || productInfo.status !== 'consumed' || !canActivate"
|
|
@click="activateWarranty"
|
|
:class="{ 'success-btn': productInfo.activation === 'deactive' || productInfo.activation === 'active', 'error-btn': productInfo.activation === 'expired' }"
|
|
elevation="2"
|
|
>
|
|
<v-icon left>mdi-shield-check</v-icon>
|
|
{{ productInfo.activation === 'deactive' ? 'فعالسازی گارانتی' : productInfo.activation === 'active' ? 'این گارانتی قبلا فعال شده است' : productInfo.activation === 'expired' ? 'این گارانتی منقضی شده است' : '' }}
|
|
</v-btn>
|
|
</v-col>
|
|
</v-row>
|
|
<v-alert
|
|
v-if="activationErrors.length"
|
|
type="error"
|
|
dismissible
|
|
class="mt-4 custom-alert"
|
|
border="left"
|
|
colored-border
|
|
>
|
|
{{ activationErrors }}
|
|
</v-alert>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 3: Success -->
|
|
<div v-if="currentStep === 3" class="step-content-wrapper">
|
|
<div class="step-header text-center mb-6">
|
|
<div class="success-icon-wrapper mb-4">
|
|
<v-icon size="64" color="success">mdi-check-circle</v-icon>
|
|
<div class="success-ring"></div>
|
|
</div>
|
|
<h2 class="success-title mb-3">گارانتی فعال شد!</h2>
|
|
<p class="success-description">
|
|
گارانتی کالا شما با موفقیت فعال شد و اطلاعات آن ثبت گردید
|
|
</p>
|
|
</div>
|
|
|
|
<div class="success-info-card mb-6">
|
|
<div class="success-info-grid">
|
|
<div class="success-info-item success-info-main">
|
|
<div class="success-info-icon">
|
|
<v-icon size="28" color="success">mdi-shield-check</v-icon>
|
|
</div>
|
|
<div class="success-info-content">
|
|
<div class="success-info-label">کد گارانتی</div>
|
|
<div class="success-info-value">{{ warrantyCode }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="success-info-item">
|
|
<div class="success-info-icon">
|
|
<v-icon size="24" color="success">mdi-calendar-check</v-icon>
|
|
</div>
|
|
<div class="success-info-content">
|
|
<div class="success-info-label">تاریخ فعالسازی</div>
|
|
<div class="success-info-value">{{ activationDate }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="success-info-item">
|
|
<div class="success-info-icon">
|
|
<v-icon size="24" color="success">mdi-calendar-end</v-icon>
|
|
</div>
|
|
<div class="success-info-content">
|
|
<div class="success-info-label">تاریخ انقضا</div>
|
|
<div class="success-info-value">{{ productInfo.warrantyEndDate ? formatDate(productInfo.warrantyEndDate) : 'بدون تاریخ انقضا' }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="action-buttons">
|
|
<v-row no-gutters class="gap-3">
|
|
<!-- <v-col>
|
|
<v-btn
|
|
outlined
|
|
large
|
|
block
|
|
@click="downloadCertificate"
|
|
class="download-btn"
|
|
elevation="0"
|
|
>
|
|
<v-icon left>mdi-download</v-icon>
|
|
دانلود گواهی
|
|
</v-btn>
|
|
</v-col> -->
|
|
<v-col>
|
|
<v-btn
|
|
color="primary"
|
|
large
|
|
block
|
|
@click="resetForm"
|
|
class="primary-btn"
|
|
elevation="2"
|
|
>
|
|
<v-icon left>mdi-plus</v-icon>
|
|
فعالسازی جدید
|
|
</v-btn>
|
|
</v-col>
|
|
</v-row>
|
|
</div>
|
|
</div>
|
|
</v-card-text>
|
|
</v-card>
|
|
</v-col>
|
|
</v-row>
|
|
</v-container>
|
|
</section>
|
|
|
|
<!-- QR Scanner Dialog -->
|
|
<v-dialog v-model="showQrScanner" max-width="500" persistent>
|
|
<v-card class="qr-dialog-card">
|
|
<v-card-title class="qr-dialog-title">
|
|
<v-icon left color="primary">mdi-qrcode-scan</v-icon>
|
|
اسکن کد QR/بارکد
|
|
</v-card-title>
|
|
<v-card-text>
|
|
<div class="text-center">
|
|
<!-- Loading State -->
|
|
<div v-show="!cameraReady && !scanError" class="qr-scanner-placeholder">
|
|
<v-icon size="100" color="primary" class="qr-icon">mdi-qrcode-scan</v-icon>
|
|
<p class="mt-4 qr-loading-text">{{ cameraStatus }}</p>
|
|
<div class="qr-loading-spinner mt-4">
|
|
<v-progress-circular indeterminate color="primary" size="32"></v-progress-circular>
|
|
</div>
|
|
<p class="text-caption qr-instruction mt-4">
|
|
اگر پیام دسترسی به دوربین نمایش داده شد، لطفاً "اجازه دادن" را انتخاب کنید
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Error State -->
|
|
<div v-show="scanError && !cameraReady" class="qr-scanner-error">
|
|
<v-icon size="100" color="error" class="qr-icon">mdi-camera-off</v-icon>
|
|
<p class="mt-4 qr-error-text">{{ scanError }}</p>
|
|
<v-btn
|
|
color="primary"
|
|
class="mt-4"
|
|
@click="scanQrCode"
|
|
:loading="loading"
|
|
>
|
|
تلاش مجدد
|
|
</v-btn>
|
|
</div>
|
|
|
|
<!-- Camera Video - Always in DOM -->
|
|
<div class="qr-camera-container" v-show="cameraReady && !scanError">
|
|
<video
|
|
ref="qrVideo"
|
|
class="qr-video"
|
|
autoplay
|
|
muted
|
|
playsinline
|
|
></video>
|
|
<!-- <div class="qr-overlay">
|
|
<div class="qr-scan-area">
|
|
<div class="qr-corner qr-corner-tl"></div>
|
|
<div class="qr-corner qr-corner-tr"></div>
|
|
<div class="qr-corner qr-corner-bl"></div>
|
|
<div class="qr-corner qr-corner-br"></div>
|
|
<div class="qr-scan-line"></div>
|
|
</div>
|
|
</div> -->
|
|
<p class="qr-scan-instruction">کد QR یا بارکد را در کادر قرار دهید</p>
|
|
</div>
|
|
|
|
<!-- Hidden video element to ensure it's always available -->
|
|
<video
|
|
v-if="!qrVideo"
|
|
ref="qrVideo"
|
|
style="display: none;"
|
|
autoplay
|
|
muted
|
|
playsinline
|
|
></video>
|
|
|
|
<!-- Error Message -->
|
|
<v-alert
|
|
v-if="scanError"
|
|
type="error"
|
|
dense
|
|
class="mt-4"
|
|
>
|
|
{{ scanError }}
|
|
</v-alert>
|
|
</div>
|
|
</v-card-text>
|
|
<v-card-actions>
|
|
<v-btn
|
|
v-if="cameraReady"
|
|
text
|
|
color="primary"
|
|
@click="toggleFlashlight"
|
|
class="qr-flash-btn"
|
|
>
|
|
<v-icon left>{{ flashlightOn ? 'mdi-flashlight-off' : 'mdi-flashlight' }}</v-icon>
|
|
{{ flashlightOn ? 'خاموش کردن فلش' : 'روشن کردن فلش' }}
|
|
</v-btn>
|
|
<v-spacer></v-spacer>
|
|
<v-btn
|
|
text
|
|
@click="stopScanner"
|
|
class="qr-cancel-btn"
|
|
>
|
|
انصراف
|
|
</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-dialog>
|
|
|
|
<!-- Modern Footer -->
|
|
<footer class="modern-footer">
|
|
<div class="footer-content">
|
|
<v-container>
|
|
<v-row justify="center" align="center">
|
|
<v-col cols="12" md="8" class="text-center">
|
|
|
|
<!-- Footer Links -->
|
|
<!-- <div class="footer-links mb-6">
|
|
<v-btn
|
|
text
|
|
class="footer-link-btn mx-2"
|
|
@click="showHelpDialog = true"
|
|
>
|
|
<v-icon left small>mdi-help-circle-outline</v-icon>
|
|
راهنما
|
|
</v-btn>
|
|
<v-btn
|
|
text
|
|
class="footer-link-btn mx-2"
|
|
href="tel:02188888888"
|
|
>
|
|
<v-icon left small>mdi-phone</v-icon>
|
|
پشتیبانی
|
|
</v-btn>
|
|
<v-btn
|
|
text
|
|
class="footer-link-btn mx-2"
|
|
href="mailto:support@example.com"
|
|
>
|
|
<v-icon left small>mdi-email-outline</v-icon>
|
|
تماس
|
|
</v-btn>
|
|
</div> -->
|
|
|
|
<!-- Footer Copyright -->
|
|
<div class="footer-copyright">
|
|
<div class="copyright-divider mb-4"></div>
|
|
<!-- <p class="copyright-text">
|
|
© 1404 حسابیکس - تمامی حقوق محفوظ است
|
|
</p> -->
|
|
|
|
<!-- Developer Credit -->
|
|
<div class="developer-credit">
|
|
<div class="developer-info">
|
|
<v-icon size="16" color="white" class="mr-2">mdi-code-tags</v-icon>
|
|
<span class="developer-text">توسعهدهنده:</span>
|
|
<a
|
|
href="https://pirouz.xyz"
|
|
target="_blank"
|
|
class="developer-link"
|
|
rel="noopener noreferrer"
|
|
>
|
|
محمد رضائی
|
|
</a>
|
|
</div>
|
|
<div class="developer-website" style="display: flex; align-items: center; gap: 5px;">
|
|
<v-icon size="14" color="rgba(255,255,255,0.6)" class="mr-1">mdi-web</v-icon>
|
|
<a
|
|
href="https://pirouz.xyz"
|
|
target="_blank"
|
|
class="website-link"
|
|
rel="noopener noreferrer"
|
|
>
|
|
pirouz.xyz
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</v-col>
|
|
</v-row>
|
|
</v-container>
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
|
|
import { useRoute } from 'vue-router'
|
|
import axios from 'axios'
|
|
import QrScanner from 'qr-scanner'
|
|
|
|
export default {
|
|
name: 'WarrantyActivation',
|
|
setup() {
|
|
// Route
|
|
const route = useRoute()
|
|
|
|
// Reactive data
|
|
const currentStep = ref(1)
|
|
const warrantyCode = ref('')
|
|
const loading = ref(false)
|
|
const codeFormValid = ref(false)
|
|
const errorMessage = ref('')
|
|
const activationErrors = ref([])
|
|
const businessId = ref(route.params.businessId)
|
|
const showHelpDialog = ref(false)
|
|
const showQrScanner = ref(false)
|
|
|
|
// QR Scanner data
|
|
const qrVideo = ref(null)
|
|
const cameraReady = ref(false)
|
|
const cameraStatus = ref('در حال دسترسی به دوربین...')
|
|
const scanError = ref('')
|
|
const flashlightOn = ref(false)
|
|
|
|
// QR Scanner instance variables
|
|
let qrScanner = null
|
|
let currentStream = null
|
|
|
|
// Product information (will be fetched from backend)
|
|
const productInfo = ref({
|
|
serialNumber: '',
|
|
productName: '',
|
|
productCode: '',
|
|
description: '',
|
|
submitDate: '',
|
|
warrantyStartDate: '',
|
|
warrantyEndDate: '',
|
|
status: '',
|
|
notes: '',
|
|
activationTimeLimit: 0,
|
|
daysRemaining: 0,
|
|
submitter: '',
|
|
businessName: '',
|
|
activationTicketCode: '',
|
|
requireActivationSecret: false
|
|
})
|
|
|
|
const activationSecret = ref('')
|
|
const activationSecretRules = [
|
|
v => !productInfo.value.requireActivationSecret || (!!v && v.length === 8) || 'کد فعالسازی 8 کاراکتری الزامی است'
|
|
]
|
|
const canActivate = computed(() => {
|
|
if (productInfo.value.requireActivationSecret) {
|
|
return activationSecret.value && activationSecret.value.length === 8
|
|
}
|
|
return true
|
|
})
|
|
|
|
// Steps data
|
|
const steps = ref([
|
|
{
|
|
title: 'وارد کردن کد',
|
|
subtitle: 'کد گارانتی را وارد کنید'
|
|
},
|
|
{
|
|
title: 'تأیید اطلاعات',
|
|
subtitle: 'اطلاعات کالا را بررسی کنید'
|
|
},
|
|
{
|
|
title: 'فعالسازی',
|
|
subtitle: 'گارانتی فعال شد'
|
|
}
|
|
])
|
|
|
|
|
|
|
|
// Form validation rules
|
|
const codeRules = [
|
|
v => !!v || 'کد گارانتی الزامی است',
|
|
// v => (v && v.length >= 8) || 'کد گارانتی باید حداقل 8 کاراکتر باشد',
|
|
// v => /^[A-Za-z0-9-]+$/.test(v) || 'کد گارانتی فقط شامل حروف، اعداد و خط تیره باشد'
|
|
]
|
|
|
|
// Computed properties
|
|
const timeRemaining = computed(() => {
|
|
// Use daysRemaining from backend response
|
|
return productInfo.value.daysRemaining || 0
|
|
})
|
|
|
|
// Convert warranty status to Persian
|
|
const warrantyStatusText = computed(() => {
|
|
const status = productInfo.value.activation
|
|
switch (status) {
|
|
case 'active':
|
|
return 'فعال'
|
|
case 'deactive':
|
|
return 'غیرفعال'
|
|
case 'expired':
|
|
return 'منقضی شده'
|
|
case 'suspended':
|
|
return 'معلق'
|
|
default:
|
|
return status || 'نامشخص'
|
|
}
|
|
})
|
|
|
|
// Convert date to Jalali
|
|
const formatDate = (dateString) => {
|
|
if (!dateString) return 'نامشخص'
|
|
try {
|
|
const date = new Date(dateString)
|
|
return date.toLocaleDateString('fa-IR')
|
|
} catch {
|
|
return dateString
|
|
}
|
|
}
|
|
|
|
const activationDate = computed(() => {
|
|
const today = new Date()
|
|
return today.toLocaleDateString('fa-IR')
|
|
})
|
|
|
|
const expirationDate = computed(() => {
|
|
// Mock calculation - will be calculated from backend
|
|
const today = new Date()
|
|
const expiry = new Date(today.getTime() + (18 * 30 * 24 * 60 * 60 * 1000)) // 18 months
|
|
return expiry.toLocaleDateString('fa-IR')
|
|
})
|
|
|
|
// Methods
|
|
const checkWarrantyCode = async () => {
|
|
if (!codeFormValid.value) return
|
|
|
|
loading.value = true
|
|
errorMessage.value = ''
|
|
|
|
try {
|
|
const response = await axios.get(`/api/public/${businessId.value}/warranty/check/${warrantyCode.value}`)
|
|
|
|
if (response.data.success) {
|
|
productInfo.value = response.data.data
|
|
currentStep.value = 2
|
|
} else {
|
|
throw new Error(response.data.message || 'خطا در بررسی کد گارانتی')
|
|
}
|
|
|
|
} catch (error) {
|
|
if (error.response && error.response.data && error.response.data.message) {
|
|
errorMessage.value = error.response.data.message
|
|
} else {
|
|
errorMessage.value = 'خطا در بررسی کد گارانتی'
|
|
}
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
const activateWarranty = async () => {
|
|
loading.value = true
|
|
|
|
try {
|
|
const response = await axios.post(`/api/public/${businessId.value}/warranty/activate/${warrantyCode.value}`, {
|
|
activationSecret: activationSecret.value
|
|
})
|
|
|
|
if (response.data.success) {
|
|
currentStep.value = 3
|
|
} else {
|
|
const msgs = Array.response.data?.message ? response.data.message : ''
|
|
activationErrors.value = msgs ? msgs : response.data.message || 'خطا در فعالسازی گارانتی'
|
|
throw new Error(response.data.message || 'خطا در فعالسازی گارانتی')
|
|
}
|
|
|
|
} catch (error) {
|
|
const data = error?.response?.data
|
|
const msgs = Array.data?.message ? data.message : ''
|
|
activationErrors.value = msgs ? msgs : data?.message || 'خطا در فعالسازی گارانتی'
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
const scanQrCode = async () => {
|
|
showQrScanner.value = true
|
|
cameraReady.value = false
|
|
cameraStatus.value = 'در حال دسترسی به دوربین...'
|
|
scanError.value = ''
|
|
|
|
try {
|
|
// First check if we have camera permissions
|
|
try {
|
|
await navigator.mediaDevices.getUserMedia({ video: true })
|
|
} catch (permissionError) {
|
|
throw new Error('لطفاً دسترسی به دوربین را مجاز کنید')
|
|
}
|
|
|
|
// Check if QrScanner is supported
|
|
const hasCamera = await QrScanner.hasCamera()
|
|
if (!hasCamera) {
|
|
throw new Error('دوربین در دسترس نیست یا پشتیبانی نمیشود')
|
|
}
|
|
|
|
cameraStatus.value = 'در حال آمادهسازی دوربین...'
|
|
|
|
// Wait for DOM to update and video element to be available with retry logic
|
|
let retries = 0
|
|
const maxRetries = 10
|
|
|
|
while (!qrVideo.value && retries < maxRetries) {
|
|
await nextTick()
|
|
await new Promise(resolve => setTimeout(resolve, 100))
|
|
retries++
|
|
}
|
|
|
|
if (!qrVideo.value) {
|
|
throw new Error('عنصر ویدئو در دسترس نیست - لطفاً صفحه را رفرش کنید')
|
|
}
|
|
|
|
// Initialize QR Scanner with better error handling
|
|
qrScanner = new QrScanner(
|
|
qrVideo.value,
|
|
result => {
|
|
warrantyCode.value = result.data
|
|
stopScanner()
|
|
},
|
|
{
|
|
onDecodeError: (error) => {
|
|
// Silent error - normal when no QR code is visible
|
|
},
|
|
highlightScanRegion: true,
|
|
highlightCodeOutline: true,
|
|
maxScansPerSecond: 5,
|
|
preferredCamera: 'environment', // Use back camera if available
|
|
returnDetailedScanResult: true
|
|
}
|
|
)
|
|
|
|
// Start the scanner
|
|
await qrScanner.start()
|
|
cameraReady.value = true
|
|
cameraStatus.value = 'دوربین آماده است - کد QR یا بارکد را در کادر قرار دهید'
|
|
|
|
} catch (error) {
|
|
let errorMsg = 'خطا در راهاندازی دوربین'
|
|
|
|
if (error.name === 'NotAllowedError') {
|
|
errorMsg = 'لطفاً دسترسی به دوربین را مجاز کنید'
|
|
} else if (error.name === 'NotFoundError') {
|
|
errorMsg = 'دوربین پیدا نشد'
|
|
} else if (error.name === 'NotSupportedError') {
|
|
errorMsg = 'دوربین پشتیبانی نمیشود'
|
|
} else if (error.message) {
|
|
errorMsg = error.message
|
|
}
|
|
|
|
scanError.value = errorMsg
|
|
cameraReady.value = false
|
|
}
|
|
}
|
|
|
|
const downloadCertificate = async () => {
|
|
try {
|
|
const response = await axios.get(`/api/public/${businessId.value}/warranty/certificate/${warrantyCode.value}`)
|
|
|
|
if (response.data.success && response.data.downloadUrl) {
|
|
// Open download link in new tab
|
|
window.open(response.data.downloadUrl, '_blank')
|
|
} else {
|
|
alert('خطا در دانلود گواهی گارانتی')
|
|
}
|
|
} catch (error) {
|
|
alert('خطا در دانلود گواهی گارانتی')
|
|
}
|
|
}
|
|
|
|
const stopScanner = () => {
|
|
try {
|
|
if (qrScanner) {
|
|
qrScanner.stop()
|
|
qrScanner.destroy()
|
|
qrScanner = null
|
|
}
|
|
|
|
// Stop all video tracks
|
|
const video = qrVideo.value
|
|
if (video && video.srcObject) {
|
|
const stream = video.srcObject
|
|
stream.getTracks().forEach(track => track.stop())
|
|
video.srcObject = null
|
|
}
|
|
|
|
if (currentStream) {
|
|
currentStream.getTracks().forEach(track => track.stop())
|
|
currentStream = null
|
|
}
|
|
|
|
} catch (error) {
|
|
} finally {
|
|
showQrScanner.value = false
|
|
cameraReady.value = false
|
|
flashlightOn.value = false
|
|
scanError.value = ''
|
|
}
|
|
}
|
|
|
|
const toggleFlashlight = async () => {
|
|
if (qrScanner && cameraReady.value) {
|
|
try {
|
|
if (flashlightOn.value) {
|
|
await qrScanner.turnFlashlightOff()
|
|
flashlightOn.value = false
|
|
} else {
|
|
await qrScanner.turnFlashlightOn()
|
|
flashlightOn.value = true
|
|
}
|
|
} catch (error) {
|
|
scanError.value = 'خطا در کنترل فلش دوربین یا دستگاه از فلش پشتیبانی نمیکند'
|
|
}
|
|
}
|
|
}
|
|
|
|
const resetForm = () => {
|
|
stopScanner() // Stop scanner if running
|
|
currentStep.value = 1
|
|
warrantyCode.value = ''
|
|
errorMessage.value = ''
|
|
productInfo.value = {
|
|
serialNumber: '',
|
|
productName: '',
|
|
productCode: '',
|
|
description: '',
|
|
submitDate: '',
|
|
warrantyStartDate: '',
|
|
warrantyEndDate: '',
|
|
status: '',
|
|
notes: '',
|
|
activationTimeLimit: 0,
|
|
daysRemaining: 0,
|
|
submitter: '',
|
|
businessName: ''
|
|
}
|
|
}
|
|
|
|
// Cleanup on component unmount
|
|
onUnmounted(() => {
|
|
stopScanner()
|
|
})
|
|
|
|
return {
|
|
// Reactive data
|
|
currentStep,
|
|
warrantyCode,
|
|
loading,
|
|
codeFormValid,
|
|
errorMessage,
|
|
showHelpDialog,
|
|
showQrScanner,
|
|
productInfo,
|
|
steps,
|
|
|
|
// QR Scanner data
|
|
qrVideo,
|
|
cameraReady,
|
|
cameraStatus,
|
|
scanError,
|
|
flashlightOn,
|
|
|
|
// Form rules
|
|
codeRules,
|
|
|
|
// Computed
|
|
timeRemaining,
|
|
warrantyStatusText,
|
|
formatDate,
|
|
activationDate,
|
|
expirationDate,
|
|
|
|
// Methods
|
|
checkWarrantyCode,
|
|
activateWarranty,
|
|
scanQrCode,
|
|
stopScanner,
|
|
toggleFlashlight,
|
|
downloadCertificate,
|
|
resetForm,
|
|
activationErrors,
|
|
activationSecret,
|
|
activationSecretRules,
|
|
canActivate,
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* ===== MAIN CONTAINER ===== */
|
|
.warranty-activation {
|
|
min-height: 80vh;
|
|
background: linear-gradient(135deg, #3b82f6 0%, #1e40af 50%, #1e293b 100%);
|
|
font-family: 'IranSans', sans-serif;
|
|
position: relative;
|
|
}
|
|
|
|
.warranty-activation::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background:
|
|
radial-gradient(circle at 20% 30%, rgba(255, 255, 255, 0.1) 0%, transparent 50%),
|
|
radial-gradient(circle at 80% 70%, rgba(255, 255, 255, 0.08) 0%, transparent 50%),
|
|
linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(30, 41, 59, 0.1) 100%);
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* ===== HERO SECTION ===== */
|
|
.hero-section {
|
|
position: relative;
|
|
min-height: 80vh;
|
|
display: flex;
|
|
align-items: center;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.hero-background {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: transparent;
|
|
}
|
|
|
|
.hero-overlay {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background: rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.hero-pattern {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background-image:
|
|
radial-gradient(circle at 25% 25%, rgba(255, 255, 255, 0.1) 0%, transparent 50%),
|
|
radial-gradient(circle at 75% 75%, rgba(255, 255, 255, 0.1) 0%, transparent 50%);
|
|
background-size: 100px 100px;
|
|
animation: float 20s ease-in-out infinite;
|
|
}
|
|
|
|
@keyframes float {
|
|
0%, 100% { transform: translateY(0px); }
|
|
50% { transform: translateY(-20px); }
|
|
}
|
|
|
|
.hero-content {
|
|
position: relative;
|
|
z-index: 3;
|
|
margin-bottom: 50px;
|
|
}
|
|
|
|
.min-height-screen {
|
|
min-height: 80vh;
|
|
}
|
|
|
|
/* ===== HERO TEXT ===== */
|
|
.hero-icon-wrapper {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 120px;
|
|
height: 120px;
|
|
background: rgba(255, 255, 255, 0.15);
|
|
border-radius: 50%;
|
|
backdrop-filter: blur(20px);
|
|
border: 2px solid rgba(255, 255, 255, 0.25);
|
|
animation: pulse 2s infinite;
|
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
@keyframes pulse {
|
|
0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.4); }
|
|
70% { transform: scale(1.05); box-shadow: 0 0 0 10px rgba(255, 255, 255, 0); }
|
|
100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(255, 255, 255, 0); }
|
|
}
|
|
|
|
.hero-title {
|
|
font-size: 3.5rem;
|
|
font-weight: 700;
|
|
color: white;
|
|
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
margin: 0;
|
|
}
|
|
|
|
.hero-subtitle {
|
|
font-size: 1.25rem;
|
|
color: rgba(255, 255, 255, 0.9);
|
|
font-weight: 300;
|
|
max-width: 500px;
|
|
margin: 0 auto;
|
|
line-height: 1.6;
|
|
}
|
|
|
|
/* ===== MAIN CARD ===== */
|
|
.main-card {
|
|
background: rgba(255, 255, 255, 0.9);
|
|
border-radius: 24px;
|
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.1);
|
|
backdrop-filter: blur(20px);
|
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
transition: all 0.3s ease;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.main-card:hover {
|
|
transform: translateY(-5px);
|
|
box-shadow: 0 30px 80px rgba(0, 0, 0, 0.15);
|
|
}
|
|
|
|
/* ===== MODERN STEPPER ===== */
|
|
.modern-stepper {
|
|
position: relative;
|
|
padding: 0 20px;
|
|
}
|
|
|
|
.stepper-track {
|
|
position: absolute;
|
|
top: 32px;
|
|
left: 50px;
|
|
right: 50px;
|
|
height: 4px;
|
|
background: #f1f5f9;
|
|
border-radius: 2px;
|
|
z-index: 1;
|
|
}
|
|
|
|
.stepper-progress {
|
|
height: 100%;
|
|
background: linear-gradient(90deg, #10b981 0%, #059669 100%);
|
|
border-radius: 2px;
|
|
transition: width 0.8s cubic-bezier(0.4, 0, 0.2, 1);
|
|
position: relative;
|
|
}
|
|
|
|
.stepper-progress::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: -2px;
|
|
right: -4px;
|
|
width: 8px;
|
|
height: 8px;
|
|
background: #059669;
|
|
border-radius: 50%;
|
|
box-shadow: 0 0 0 2px white, 0 2px 8px rgba(5, 150, 105, 0.3);
|
|
}
|
|
|
|
.stepper-steps {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
position: relative;
|
|
z-index: 2;
|
|
}
|
|
|
|
.stepper-step {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
text-align: center;
|
|
flex: 1;
|
|
position: relative;
|
|
}
|
|
|
|
.stepper-circle {
|
|
width: 64px;
|
|
height: 64px;
|
|
border-radius: 50%;
|
|
background: white;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-bottom: 16px;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
|
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
}
|
|
|
|
.stepper-circle-inner {
|
|
width: 48px;
|
|
height: 48px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: #f8fafc;
|
|
color: #94a3b8;
|
|
font-weight: 600;
|
|
font-size: 18px;
|
|
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
border: 2px solid #e2e8f0;
|
|
}
|
|
|
|
.stepper-step.active .stepper-circle {
|
|
transform: scale(1.1);
|
|
box-shadow: 0 8px 24px rgba(59, 130, 246, 0.2);
|
|
}
|
|
|
|
.stepper-step.active .stepper-circle-inner {
|
|
background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
|
|
color: white;
|
|
border-color: #3b82f6;
|
|
animation: pulse-ring 2s infinite;
|
|
}
|
|
|
|
.stepper-step.completed .stepper-circle {
|
|
transform: scale(1.05);
|
|
box-shadow: 0 6px 20px rgba(16, 185, 129, 0.2);
|
|
}
|
|
|
|
.stepper-step.completed .stepper-circle-inner {
|
|
background: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
|
color: white;
|
|
border-color: #10b981;
|
|
}
|
|
|
|
.stepper-step.pending .stepper-circle-inner {
|
|
background: #f8fafc;
|
|
color: #cbd5e1;
|
|
border-color: #f1f5f9;
|
|
}
|
|
|
|
@keyframes pulse-ring {
|
|
0% {
|
|
box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4);
|
|
}
|
|
70% {
|
|
box-shadow: 0 0 0 10px rgba(59, 130, 246, 0);
|
|
}
|
|
100% {
|
|
box-shadow: 0 0 0 0 rgba(59, 130, 246, 0);
|
|
}
|
|
}
|
|
|
|
.stepper-content {
|
|
max-width: 120px;
|
|
}
|
|
|
|
.stepper-title {
|
|
font-weight: 700;
|
|
color: #1e293b;
|
|
font-size: 15px;
|
|
margin-bottom: 6px;
|
|
line-height: 1.3;
|
|
}
|
|
|
|
.stepper-step.active .stepper-title {
|
|
color: #3b82f6;
|
|
}
|
|
|
|
.stepper-step.completed .stepper-title {
|
|
color: #10b981;
|
|
}
|
|
|
|
.stepper-description {
|
|
color: #64748b;
|
|
font-size: 13px;
|
|
line-height: 1.4;
|
|
font-weight: 400;
|
|
}
|
|
|
|
.stepper-step.pending .stepper-description {
|
|
color: #94a3b8;
|
|
}
|
|
|
|
/* ===== STEP CONTENT ===== */
|
|
.step-content-wrapper {
|
|
animation: fadeInUp 0.5s ease;
|
|
}
|
|
|
|
@keyframes fadeInUp {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
.step-header {
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.step-icon-wrapper {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 80px;
|
|
height: 80px;
|
|
background: linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(59, 130, 246, 0.2) 100%);
|
|
border-radius: 20px;
|
|
margin-bottom: 1rem;
|
|
border: 1px solid rgba(59, 130, 246, 0.2);
|
|
backdrop-filter: blur(10px);
|
|
}
|
|
|
|
.step-main-title {
|
|
font-size: 1.875rem;
|
|
font-weight: 700;
|
|
color: #1e293b;
|
|
margin: 0;
|
|
}
|
|
|
|
.step-description {
|
|
font-size: 1rem;
|
|
color: #64748b;
|
|
line-height: 1.6;
|
|
margin: 0;
|
|
}
|
|
|
|
/* ===== FORM ELEMENTS ===== */
|
|
.input-section {
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.input-section >>> .v-field__field {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.custom-input >>> .v-input__control {
|
|
min-height: 60px;
|
|
}
|
|
|
|
.custom-input >>> .v-text-field__details {
|
|
margin-top: 8px;
|
|
}
|
|
|
|
.custom-input >>> .v-input__slot {
|
|
border-radius: 12px !important;
|
|
background: #f8fafc;
|
|
border: 2px solid #e2e8f0 !important;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.custom-input >>> .v-input__slot:hover {
|
|
border-color: #3b82f6 !important;
|
|
}
|
|
|
|
.custom-input >>> .v-text-field--focused .v-input__slot {
|
|
border-color: #3b82f6 !important;
|
|
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
|
}
|
|
|
|
.qr-scan-btn {
|
|
background: #f1f5f9 !important;
|
|
border: 1px solid #e2e8f0;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.qr-scan-btn:hover {
|
|
background: #e2e8f0 !important;
|
|
transform: scale(1.05);
|
|
}
|
|
|
|
/* ===== BUTTONS ===== */
|
|
.primary-btn {
|
|
height: 56px !important;
|
|
border-radius: 12px !important;
|
|
font-weight: 600 !important;
|
|
font-size: 1rem !important;
|
|
text-transform: none !important;
|
|
letter-spacing: 0 !important;
|
|
background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%) !important;
|
|
transition: all 0.3s ease !important;
|
|
}
|
|
|
|
.primary-btn >>> .v-btn__content {
|
|
gap: 8px !important;
|
|
display: flex !important;
|
|
align-items: center !important;
|
|
}
|
|
|
|
.primary-btn:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 10px 30px rgba(59, 130, 246, 0.3) !important;
|
|
}
|
|
|
|
.primary-btn:disabled {
|
|
opacity: 0.6 !important;
|
|
transform: none !important;
|
|
}
|
|
|
|
.help-btn {
|
|
color: #64748b !important;
|
|
font-weight: 500 !important;
|
|
text-transform: none !important;
|
|
transition: all 0.3s ease !important;
|
|
}
|
|
|
|
.help-btn:hover {
|
|
color: #3b82f6 !important;
|
|
background: rgba(59, 130, 246, 0.05) !important;
|
|
}
|
|
|
|
/* ===== ALERTS ===== */
|
|
.custom-alert {
|
|
border-radius: 12px;
|
|
border-left-width: 4px !important;
|
|
}
|
|
|
|
/* ===== RESPONSIVE ===== */
|
|
@media (max-width: 960px) {
|
|
.hero-title {
|
|
font-size: 2.5rem;
|
|
}
|
|
|
|
.hero-subtitle {
|
|
font-size: 1.125rem;
|
|
}
|
|
|
|
.hero-icon-wrapper {
|
|
width: 100px;
|
|
height: 100px;
|
|
}
|
|
|
|
/* Mobile Stepper */
|
|
.stepper-track {
|
|
display: none;
|
|
}
|
|
|
|
.stepper-steps {
|
|
flex-direction: column;
|
|
gap: 24px;
|
|
}
|
|
|
|
.stepper-step {
|
|
flex-direction: row;
|
|
text-align: right;
|
|
gap: 16px;
|
|
align-items: center;
|
|
}
|
|
|
|
.stepper-circle {
|
|
width: 56px;
|
|
height: 56px;
|
|
margin-bottom: 0;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.stepper-circle-inner {
|
|
width: 40px;
|
|
height: 40px;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.stepper-content {
|
|
max-width: none;
|
|
text-align: right;
|
|
}
|
|
|
|
.stepper-title {
|
|
font-size: 16px;
|
|
}
|
|
|
|
.stepper-description {
|
|
font-size: 14px;
|
|
}
|
|
|
|
/* Footer Mobile */
|
|
.footer-links {
|
|
flex-direction: column;
|
|
align-items: center;
|
|
}
|
|
|
|
.footer-link-btn {
|
|
width: 200px;
|
|
justify-content: center;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 600px) {
|
|
.hero-title {
|
|
font-size: 2rem;
|
|
}
|
|
|
|
.hero-subtitle {
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.step-main-title {
|
|
font-size: 1.5rem;
|
|
}
|
|
|
|
.custom-input >>> .v-input__control {
|
|
min-height: 50px;
|
|
}
|
|
|
|
.primary-btn {
|
|
height: 48px !important;
|
|
}
|
|
|
|
/* Mobile Step Content */
|
|
.product-info-grid {
|
|
grid-template-columns: 1fr;
|
|
gap: 16px;
|
|
}
|
|
|
|
.info-item {
|
|
padding: 12px;
|
|
gap: 12px;
|
|
}
|
|
|
|
.info-icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
}
|
|
|
|
.info-value {
|
|
font-size: 14px;
|
|
}
|
|
|
|
.warning-content {
|
|
flex-direction: column;
|
|
text-align: center;
|
|
gap: 12px;
|
|
}
|
|
|
|
.warning-icon {
|
|
width: 48px;
|
|
height: 48px;
|
|
}
|
|
|
|
.success-icon-wrapper {
|
|
width: 100px;
|
|
height: 100px;
|
|
}
|
|
|
|
.success-title {
|
|
font-size: 1.75rem;
|
|
}
|
|
|
|
.success-description {
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.success-info-item {
|
|
padding: 12px;
|
|
gap: 12px;
|
|
}
|
|
|
|
.success-info-main {
|
|
padding: 16px;
|
|
}
|
|
|
|
.success-info-icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
}
|
|
|
|
.action-buttons .v-btn {
|
|
height: 48px !important;
|
|
font-size: 0.9rem !important;
|
|
}
|
|
}
|
|
|
|
/* ===== ANIMATIONS ===== */
|
|
.v-enter-active, .v-leave-active {
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.v-enter-from, .v-leave-to {
|
|
opacity: 0;
|
|
transform: translateY(20px);
|
|
}
|
|
|
|
/* ===== QR SCANNER ===== */
|
|
.qr-dialog-card {
|
|
background: rgba(255, 255, 255, 0.95) !important;
|
|
backdrop-filter: blur(20px);
|
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
border-radius: 20px !important;
|
|
}
|
|
|
|
.qr-dialog-title {
|
|
background: linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(59, 130, 246, 0.05) 100%);
|
|
border-bottom: 1px solid rgba(59, 130, 246, 0.1);
|
|
font-weight: 600 !important;
|
|
color: #1e293b;
|
|
}
|
|
|
|
.qr-scanner-placeholder {
|
|
padding: 3rem 2rem;
|
|
border: 2px dashed rgba(59, 130, 246, 0.3);
|
|
border-radius: 16px;
|
|
background: linear-gradient(135deg, rgba(59, 130, 246, 0.05) 0%, rgba(59, 130, 246, 0.1) 100%);
|
|
text-align: center;
|
|
backdrop-filter: blur(10px);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.qr-scanner-placeholder:hover {
|
|
border-color: rgba(59, 130, 246, 0.5);
|
|
background: linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(59, 130, 246, 0.15) 100%);
|
|
}
|
|
|
|
.qr-scanner-error {
|
|
padding: 3rem 2rem;
|
|
border: 2px dashed rgba(239, 68, 68, 0.3);
|
|
border-radius: 16px;
|
|
background: linear-gradient(135deg, rgba(239, 68, 68, 0.05) 0%, rgba(239, 68, 68, 0.1) 100%);
|
|
text-align: center;
|
|
backdrop-filter: blur(10px);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.qr-error-text {
|
|
color: #dc2626;
|
|
font-weight: 500;
|
|
font-size: 1rem;
|
|
}
|
|
|
|
.qr-loading-spinner {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
.qr-icon {
|
|
opacity: 0.8;
|
|
animation: qr-pulse 2s infinite;
|
|
}
|
|
|
|
@keyframes qr-pulse {
|
|
0%, 100% { transform: scale(1); opacity: 0.8; }
|
|
50% { transform: scale(1.05); opacity: 1; }
|
|
}
|
|
|
|
.qr-loading-text {
|
|
font-weight: 600;
|
|
color: #3b82f6;
|
|
margin: 0;
|
|
}
|
|
|
|
.qr-instruction {
|
|
color: #64748b;
|
|
margin: 0;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.qr-cancel-btn {
|
|
color: #64748b !important;
|
|
font-weight: 600 !important;
|
|
text-transform: none !important;
|
|
transition: all 0.3s ease !important;
|
|
}
|
|
|
|
.qr-cancel-btn:hover {
|
|
color: #3b82f6 !important;
|
|
background: rgba(59, 130, 246, 0.05) !important;
|
|
}
|
|
|
|
.qr-flash-btn {
|
|
color: #64748b !important;
|
|
font-weight: 600 !important;
|
|
text-transform: none !important;
|
|
transition: all 0.3s ease !important;
|
|
}
|
|
|
|
.qr-flash-btn:hover {
|
|
color: #f59e0b !important;
|
|
background: rgba(245, 158, 11, 0.05) !important;
|
|
}
|
|
|
|
/* ===== QR CAMERA ===== */
|
|
.qr-camera-container {
|
|
position: relative;
|
|
width: 100%;
|
|
max-width: 400px;
|
|
margin: 0 auto;
|
|
border-radius: 16px;
|
|
overflow: hidden;
|
|
background: #000;
|
|
}
|
|
|
|
.qr-video {
|
|
width: 100%;
|
|
height: 300px;
|
|
object-fit: cover;
|
|
border-radius: 16px;
|
|
}
|
|
|
|
.qr-overlay {
|
|
position: absolute;
|
|
top: -75px;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.qr-scan-area {
|
|
position: relative;
|
|
width: 200px;
|
|
height: 200px;
|
|
border: 2px solid rgba(255, 255, 255, 0.3);
|
|
border-radius: 12px;
|
|
}
|
|
|
|
.qr-corner {
|
|
position: absolute;
|
|
width: 20px;
|
|
height: 20px;
|
|
border: 3px solid #3b82f6;
|
|
}
|
|
|
|
.qr-corner-tl {
|
|
top: -3px;
|
|
left: -3px;
|
|
border-right: none;
|
|
border-bottom: none;
|
|
border-top-left-radius: 12px;
|
|
}
|
|
|
|
.qr-corner-tr {
|
|
top: -3px;
|
|
right: -3px;
|
|
border-left: none;
|
|
border-bottom: none;
|
|
border-top-right-radius: 12px;
|
|
}
|
|
|
|
.qr-corner-bl {
|
|
bottom: -3px;
|
|
left: -3px;
|
|
border-right: none;
|
|
border-top: none;
|
|
border-bottom-left-radius: 12px;
|
|
}
|
|
|
|
.qr-corner-br {
|
|
bottom: -3px;
|
|
right: -3px;
|
|
border-left: none;
|
|
border-top: none;
|
|
border-bottom-right-radius: 12px;
|
|
}
|
|
|
|
.qr-scan-line {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 2px;
|
|
background: linear-gradient(90deg, transparent 0%, #3b82f6 50%, transparent 100%);
|
|
animation: qr-scan-line 2s infinite;
|
|
}
|
|
|
|
@keyframes qr-scan-line {
|
|
0% {
|
|
top: 0;
|
|
opacity: 1;
|
|
}
|
|
50% {
|
|
top: 50%;
|
|
opacity: 0.8;
|
|
}
|
|
100% {
|
|
top: 100%;
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
.qr-scan-instruction {
|
|
color: rgba(255, 255, 255, 0.9);
|
|
font-weight: 600;
|
|
margin-top: 16px;
|
|
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
|
|
font-size: 14px;
|
|
}
|
|
|
|
/* ===== MODERN FOOTER ===== */
|
|
.modern-footer {
|
|
background: rgba(0, 0, 0, 0.2);
|
|
/* margin-top: 80px; */
|
|
position: relative;
|
|
overflow: hidden;
|
|
backdrop-filter: blur(20px);
|
|
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
}
|
|
|
|
.modern-footer::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background-image:
|
|
radial-gradient(circle at 20% 20%, rgba(255, 255, 255, 0.03) 0%, transparent 50%),
|
|
radial-gradient(circle at 80% 80%, rgba(255, 255, 255, 0.03) 0%, transparent 50%);
|
|
background-size: 200px 200px;
|
|
}
|
|
|
|
.footer-content {
|
|
position: relative;
|
|
z-index: 2;
|
|
padding: 0px 60px 0 40px;
|
|
}
|
|
|
|
.footer-brand {
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.footer-logo {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 80px;
|
|
height: 80px;
|
|
background: rgba(59, 130, 246, 0.1);
|
|
border-radius: 20px;
|
|
margin: 0 auto;
|
|
backdrop-filter: blur(10px);
|
|
border: 1px solid rgba(59, 130, 246, 0.2);
|
|
}
|
|
|
|
.footer-title {
|
|
font-size: 1.75rem;
|
|
font-weight: 700;
|
|
color: white;
|
|
margin: 0 0 8px 0;
|
|
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
.footer-subtitle {
|
|
font-size: 1rem;
|
|
color: rgba(255, 255, 255, 0.7);
|
|
margin: 0;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.footer-links {
|
|
display: flex;
|
|
justify-content: center;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
}
|
|
|
|
.footer-link-btn {
|
|
color: rgba(255, 255, 255, 0.8) !important;
|
|
background: rgba(59, 130, 246, 0.3) !important;
|
|
font-weight: 500 !important;
|
|
text-transform: none !important;
|
|
border-radius: 12px !important;
|
|
padding: 8px 16px !important;
|
|
transition: all 0.3s ease !important;
|
|
backdrop-filter: blur(10px) !important;
|
|
border: 1px solid rgba(255, 255, 255, 0.1) !important;
|
|
display: flex !important;
|
|
align-items: center !important;
|
|
justify-content: center !important;
|
|
min-height: 40px !important;
|
|
}
|
|
|
|
.footer-link-btn >>> .v-btn__content {
|
|
gap: 8px !important;
|
|
display: flex !important;
|
|
align-items: center !important;
|
|
}
|
|
|
|
.footer-link-btn:hover {
|
|
color: white !important;
|
|
background: rgba(59, 130, 246, 0.2) !important;
|
|
border-color: rgba(59, 130, 246, 0.3) !important;
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 8px 24px rgba(59, 130, 246, 0.2);
|
|
}
|
|
|
|
.footer-copyright {
|
|
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
padding-top: 24px;
|
|
}
|
|
|
|
.copyright-divider {
|
|
width: 60px;
|
|
height: 3px;
|
|
background: linear-gradient(90deg, #3b82f6 0%, #10b981 100%);
|
|
border-radius: 2px;
|
|
margin: 0 auto 16px;
|
|
}
|
|
|
|
.copyright-text {
|
|
font-size: 14px;
|
|
color: rgba(255, 255, 255, 0.8);
|
|
margin: 0 0 8px 0;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.copyright-subtext {
|
|
font-size: 13px;
|
|
color: rgba(255, 255, 255, 0.6);
|
|
margin: 0;
|
|
font-weight: 400;
|
|
}
|
|
|
|
.developer-credit {
|
|
margin-top: 16px;
|
|
padding: 16px;
|
|
background: transparent;
|
|
}
|
|
|
|
.developer-info {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-bottom: 8px;
|
|
flex-wrap: wrap;
|
|
gap: 4px;
|
|
}
|
|
|
|
.developer-text {
|
|
font-size: 13px;
|
|
color: rgba(255, 255, 255, 0.8);
|
|
margin-left: 6px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.developer-link {
|
|
font-size: 14px;
|
|
color: #3b82f6 !important;
|
|
text-decoration: none;
|
|
font-weight: 600;
|
|
transition: all 0.3s ease;
|
|
padding: 2px 6px;
|
|
border-radius: 6px;
|
|
background: rgba(59, 130, 246, 0.1);
|
|
border: 1px solid rgba(59, 130, 246, 0.2);
|
|
}
|
|
|
|
.developer-link:hover {
|
|
color: white !important;
|
|
background: rgba(59, 130, 246, 0.3);
|
|
border-color: rgba(59, 130, 246, 0.4);
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.2);
|
|
}
|
|
|
|
.developer-website {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.website-link {
|
|
font-size: 12px;
|
|
color: rgba(255, 255, 255, 0.6) !important;
|
|
text-decoration: none;
|
|
transition: all 0.3s ease;
|
|
font-weight: 400;
|
|
}
|
|
|
|
.website-link:hover {
|
|
color: #3b82f6 !important;
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
/* ===== MODERN STEP CONTENT ===== */
|
|
|
|
/* Product Info Card - Step 2 */
|
|
.product-info-card {
|
|
background: rgba(255, 255, 255, 0.8);
|
|
border-radius: 20px;
|
|
padding: 24px;
|
|
border: 1px solid rgba(59, 130, 246, 0.1);
|
|
backdrop-filter: blur(10px);
|
|
box-shadow: 0 8px 32px rgba(59, 130, 246, 0.08);
|
|
}
|
|
|
|
.product-info-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
gap: 20px;
|
|
}
|
|
|
|
.info-item {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 16px;
|
|
padding: 16px;
|
|
background: rgba(248, 250, 252, 0.8);
|
|
border-radius: 16px;
|
|
border: 1px solid rgba(226, 232, 240, 0.5);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.info-item:hover {
|
|
background: rgba(59, 130, 246, 0.05);
|
|
border-color: rgba(59, 130, 246, 0.2);
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 8px 24px rgba(59, 130, 246, 0.1);
|
|
}
|
|
|
|
.info-item-wide {
|
|
grid-column: 1 / -1;
|
|
}
|
|
|
|
.info-icon {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 48px;
|
|
height: 48px;
|
|
background: linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(59, 130, 246, 0.2) 100%);
|
|
border-radius: 12px;
|
|
border: 1px solid rgba(59, 130, 246, 0.2);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.info-content {
|
|
flex: 1;
|
|
}
|
|
|
|
.info-label {
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: #64748b;
|
|
margin-bottom: 4px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
}
|
|
|
|
.info-value {
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: #1e293b;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
/* Time Warning */
|
|
.time-warning {
|
|
background: linear-gradient(135deg, rgba(245, 158, 11, 0.1) 0%, rgba(245, 158, 11, 0.2) 100%);
|
|
border: 1px solid rgba(245, 158, 11, 0.3);
|
|
border-radius: 16px;
|
|
padding: 20px;
|
|
backdrop-filter: blur(10px);
|
|
}
|
|
|
|
/* Status Warning */
|
|
.status-warning {
|
|
background: linear-gradient(135deg, rgba(239, 68, 68, 0.1) 0%, rgba(239, 68, 68, 0.2) 100%);
|
|
border: 1px solid rgba(239, 68, 68, 0.3);
|
|
border-radius: 16px;
|
|
padding: 20px;
|
|
backdrop-filter: blur(10px);
|
|
}
|
|
|
|
.warning-content {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16px;
|
|
}
|
|
|
|
.warning-icon {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 56px;
|
|
height: 56px;
|
|
background: rgba(245, 158, 11, 0.1);
|
|
border-radius: 12px;
|
|
border: 1px solid rgba(245, 158, 11, 0.2);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.error-icon {
|
|
background: rgba(239, 68, 68, 0.1);
|
|
border: 1px solid rgba(239, 68, 68, 0.2);
|
|
}
|
|
|
|
.error-subtitle {
|
|
color: #b91c1c !important;
|
|
}
|
|
|
|
.error-title {
|
|
color: #b91c1c !important;
|
|
}
|
|
|
|
.warning-text {
|
|
flex: 1;
|
|
}
|
|
|
|
.warning-title {
|
|
font-size: 16px;
|
|
font-weight: 700;
|
|
color: #92400e;
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.warning-subtitle {
|
|
font-size: 14px;
|
|
color: #a16207;
|
|
font-weight: 500;
|
|
}
|
|
|
|
/* Success Step */
|
|
.success-icon-wrapper {
|
|
position: relative;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 120px;
|
|
height: 120px;
|
|
background: linear-gradient(135deg, rgba(16, 185, 129, 0.1) 0%, rgba(16, 185, 129, 0.2) 100%);
|
|
border-radius: 50%;
|
|
border: 2px solid rgba(16, 185, 129, 0.3);
|
|
backdrop-filter: blur(10px);
|
|
animation: success-pulse 2s infinite;
|
|
}
|
|
|
|
.success-ring {
|
|
position: absolute;
|
|
top: -10px;
|
|
left: -10px;
|
|
right: -10px;
|
|
bottom: -10px;
|
|
border: 2px solid rgba(16, 185, 129, 0.2);
|
|
border-radius: 50%;
|
|
animation: success-ring 2s infinite;
|
|
}
|
|
|
|
@keyframes success-pulse {
|
|
0%, 100% { transform: scale(1); }
|
|
50% { transform: scale(1.05); }
|
|
}
|
|
|
|
@keyframes success-ring {
|
|
0% { transform: scale(1); opacity: 1; }
|
|
100% { transform: scale(1.2); opacity: 0; }
|
|
}
|
|
|
|
.success-title {
|
|
font-size: 2.25rem;
|
|
font-weight: 700;
|
|
color: #10b981;
|
|
margin: 0;
|
|
text-shadow: 0 2px 4px rgba(16, 185, 129, 0.1);
|
|
}
|
|
|
|
.success-description {
|
|
font-size: 1.125rem;
|
|
color: #64748b;
|
|
line-height: 1.6;
|
|
margin: 0;
|
|
max-width: 400px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
/* Success Info Card */
|
|
.success-info-card {
|
|
background: linear-gradient(135deg, rgba(16, 185, 129, 0.05) 0%, rgba(16, 185, 129, 0.1) 100%);
|
|
border-radius: 20px;
|
|
padding: 24px;
|
|
border: 1px solid rgba(16, 185, 129, 0.2);
|
|
backdrop-filter: blur(10px);
|
|
box-shadow: 0 8px 32px rgba(16, 185, 129, 0.1);
|
|
}
|
|
|
|
.success-info-grid {
|
|
display: grid;
|
|
grid-template-columns: 1fr;
|
|
gap: 16px;
|
|
}
|
|
|
|
.success-info-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16px;
|
|
padding: 16px;
|
|
background: rgba(255, 255, 255, 0.8);
|
|
border-radius: 16px;
|
|
border: 1px solid rgba(16, 185, 129, 0.2);
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.success-info-main {
|
|
background: linear-gradient(135deg, rgba(16, 185, 129, 0.1) 0%, rgba(16, 185, 129, 0.15) 100%);
|
|
border: 2px solid rgba(16, 185, 129, 0.3);
|
|
padding: 20px;
|
|
}
|
|
|
|
.success-info-icon {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 48px;
|
|
height: 48px;
|
|
background: rgba(16, 185, 129, 0.1);
|
|
border-radius: 12px;
|
|
border: 1px solid rgba(16, 185, 129, 0.2);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.success-info-content {
|
|
flex: 1;
|
|
}
|
|
|
|
.success-info-label {
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: #059669;
|
|
margin-bottom: 4px;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
}
|
|
|
|
.success-info-value {
|
|
font-size: 16px;
|
|
font-weight: 700;
|
|
color: #064e3b;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.success-info-main .success-info-value {
|
|
font-size: 18px;
|
|
font-weight: 800;
|
|
color: #10b981;
|
|
}
|
|
|
|
/* Action Buttons */
|
|
.back-btn {
|
|
height: 56px !important;
|
|
border-radius: 12px !important;
|
|
font-weight: 600 !important;
|
|
font-size: 1rem !important;
|
|
text-transform: none !important;
|
|
letter-spacing: 0 !important;
|
|
border: 2px solid rgba(100, 116, 139, 0.3) !important;
|
|
color: #64748b !important;
|
|
transition: all 0.3s ease !important;
|
|
}
|
|
|
|
.back-btn >>> .v-btn__content {
|
|
gap: 8px !important;
|
|
display: flex !important;
|
|
align-items: center !important;
|
|
}
|
|
|
|
.back-btn:hover {
|
|
border-color: #3b82f6 !important;
|
|
color: #3b82f6 !important;
|
|
background: rgba(59, 130, 246, 0.05) !important;
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.success-btn {
|
|
height: 56px !important;
|
|
border-radius: 12px !important;
|
|
font-weight: 600 !important;
|
|
font-size: 1rem !important;
|
|
text-transform: none !important;
|
|
letter-spacing: 0 !important;
|
|
background: linear-gradient(135deg, #10b981 0%, #059669 100%) !important;
|
|
transition: all 0.3s ease !important;
|
|
}
|
|
|
|
.success-btn >>> .v-btn__content {
|
|
gap: 8px !important;
|
|
display: flex !important;
|
|
align-items: center !important;
|
|
}
|
|
|
|
.success-btn:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 10px 30px rgba(16, 185, 129, 0.3) !important;
|
|
}
|
|
|
|
.error-btn {
|
|
height: 56px !important;
|
|
border-radius: 12px !important;
|
|
font-weight: 600 !important;
|
|
font-size: 1rem !important;
|
|
text-transform: none !important;
|
|
letter-spacing: 0 !important;
|
|
background: linear-gradient(135deg, #ef4444 0%, #b91c1c 100%) !important;
|
|
transition: all 0.3s ease !important;
|
|
}
|
|
|
|
.error-btn >>> .v-btn__content {
|
|
gap: 8px !important;
|
|
display: flex !important;
|
|
align-items: center !important;
|
|
}
|
|
|
|
.error-btn:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 10px 30px rgba(239, 68, 68, 0.3) !important;
|
|
}
|
|
|
|
.download-btn {
|
|
height: 56px !important;
|
|
border-radius: 12px !important;
|
|
font-weight: 600 !important;
|
|
font-size: 1rem !important;
|
|
text-transform: none !important;
|
|
letter-spacing: 0 !important;
|
|
border: 2px solid rgba(16, 185, 129, 0.3) !important;
|
|
color: #10b981 !important;
|
|
transition: all 0.3s ease !important;
|
|
}
|
|
|
|
.download-btn:hover {
|
|
border-color: #10b981 !important;
|
|
background: rgba(16, 185, 129, 0.05) !important;
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.v-btn__content {
|
|
gap: 8px !important;
|
|
}
|
|
</style>
|