chore: Fix .env.test quotes and format frontend code
- Fix TEST_ORACLE_USER quotes in .env.test for shell source compatibility - Format 14 frontend files with Prettier (stores, views, utils) - All 122 tests passing (77 telegram + 35 backend + 10 E2E) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,192 +1,189 @@
|
||||
<template>
|
||||
<main class="main-content">
|
||||
<div class="app-container">
|
||||
|
||||
<!-- Page Header -->
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">Telegram Bot</h1>
|
||||
<p class="page-subtitle">Conectează-ți contul pentru acces rapid din Telegram</p>
|
||||
</div>
|
||||
|
||||
<!-- Loading State -->
|
||||
<div v-if="loading" class="loading-state">
|
||||
<div class="loading-spinner"></div>
|
||||
<p>Se generează codul...</p>
|
||||
</div>
|
||||
|
||||
<!-- Main Card -->
|
||||
<div v-else class="card">
|
||||
|
||||
<!-- Generate Button -->
|
||||
<div class="generate-section">
|
||||
<button
|
||||
@click="generateCode"
|
||||
:disabled="loading"
|
||||
class="btn btn-primary btn-lg"
|
||||
>
|
||||
{{ loading ? 'Se generează...' : 'Generează Cod' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Code Display & Actions -->
|
||||
<div v-if="linkingCode" class="code-section">
|
||||
|
||||
<!-- Code Display -->
|
||||
<div class="code-display">
|
||||
<div class="code-header">
|
||||
<span class="code-label">Cod</span>
|
||||
<span class="code-timer">{{ formatTime(timeRemaining) }}</span>
|
||||
</div>
|
||||
<div class="code-value">{{ linkingCode }}</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
:href="telegramDeepLink"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="btn btn-primary action-btn"
|
||||
>
|
||||
Deschide Telegram
|
||||
</a>
|
||||
<Button
|
||||
:label="showQR ? 'Ascunde QR' : 'Arată QR'"
|
||||
@click="showQR = !showQR"
|
||||
class="action-btn"
|
||||
outlined
|
||||
/>
|
||||
<Button
|
||||
label="Copiază Cod"
|
||||
@click="copyCode"
|
||||
class="action-btn"
|
||||
outlined
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- QR Code Display -->
|
||||
<div v-if="showQR" class="qr-section">
|
||||
<QRCodeVue :value="telegramDeepLink" :size="200" level="H" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="app-container">
|
||||
<!-- Page Header -->
|
||||
<div class="page-header">
|
||||
<h1 class="page-title">Telegram Bot</h1>
|
||||
<p class="page-subtitle">
|
||||
Conectează-ți contul pentru acces rapid din Telegram
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Loading State -->
|
||||
<div v-if="loading" class="loading-state">
|
||||
<div class="loading-spinner"></div>
|
||||
<p>Se generează codul...</p>
|
||||
</div>
|
||||
|
||||
<!-- Main Card -->
|
||||
<div v-else class="card">
|
||||
<!-- Generate Button -->
|
||||
<div class="generate-section">
|
||||
<button
|
||||
@click="generateCode"
|
||||
:disabled="loading"
|
||||
class="btn btn-primary btn-lg"
|
||||
>
|
||||
{{ loading ? "Se generează..." : "Generează Cod" }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Code Display & Actions -->
|
||||
<div v-if="linkingCode" class="code-section">
|
||||
<!-- Code Display -->
|
||||
<div class="code-display">
|
||||
<div class="code-header">
|
||||
<span class="code-label">Cod</span>
|
||||
<span class="code-timer">{{ formatTime(timeRemaining) }}</span>
|
||||
</div>
|
||||
<div class="code-value">{{ linkingCode }}</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="action-buttons">
|
||||
<a
|
||||
:href="telegramDeepLink"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="btn btn-primary action-btn"
|
||||
>
|
||||
Deschide Telegram
|
||||
</a>
|
||||
<Button
|
||||
:label="showQR ? 'Ascunde QR' : 'Arată QR'"
|
||||
@click="showQR = !showQR"
|
||||
class="action-btn"
|
||||
outlined
|
||||
/>
|
||||
<Button
|
||||
label="Copiază Cod"
|
||||
@click="copyCode"
|
||||
class="action-btn"
|
||||
outlined
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- QR Code Display -->
|
||||
<div v-if="showQR" class="qr-section">
|
||||
<QRCodeVue :value="telegramDeepLink" :size="200" level="H" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onUnmounted } from 'vue'
|
||||
import { useToast } from 'primevue/usetoast'
|
||||
import Button from 'primevue/button'
|
||||
import Toast from 'primevue/toast'
|
||||
import QRCodeVue from 'qrcode.vue'
|
||||
import { apiService } from '../services/api'
|
||||
import { ref, computed, onUnmounted } from "vue";
|
||||
import { useToast } from "primevue/usetoast";
|
||||
import Button from "primevue/button";
|
||||
import Toast from "primevue/toast";
|
||||
import QRCodeVue from "qrcode.vue";
|
||||
import { apiService } from "../services/api";
|
||||
|
||||
const toast = useToast()
|
||||
const toast = useToast();
|
||||
|
||||
// State
|
||||
const linkingCode = ref('')
|
||||
const timeRemaining = ref(0)
|
||||
const loading = ref(false)
|
||||
const showQR = ref(false)
|
||||
const linkingCode = ref("");
|
||||
const timeRemaining = ref(0);
|
||||
const loading = ref(false);
|
||||
const showQR = ref(false);
|
||||
|
||||
let countdownInterval = null
|
||||
let countdownInterval = null;
|
||||
|
||||
// Config
|
||||
const BOT_USERNAME = import.meta.env.VITE_TELEGRAM_BOT_USERNAME || 'roa2web_bot'
|
||||
const BOT_USERNAME =
|
||||
import.meta.env.VITE_TELEGRAM_BOT_USERNAME || "roa2web_bot";
|
||||
|
||||
// Computed
|
||||
const telegramDeepLink = computed(() => {
|
||||
if (!linkingCode.value) return ''
|
||||
return `https://t.me/${BOT_USERNAME}?start=${linkingCode.value}`
|
||||
})
|
||||
if (!linkingCode.value) return "";
|
||||
return `https://t.me/${BOT_USERNAME}?start=${linkingCode.value}`;
|
||||
});
|
||||
|
||||
// Methods
|
||||
const generateCode = async () => {
|
||||
loading.value = true
|
||||
showQR.value = false
|
||||
loading.value = true;
|
||||
showQR.value = false;
|
||||
|
||||
try {
|
||||
const response = await apiService.post('/telegram/auth/generate-code')
|
||||
linkingCode.value = response.data.linking_code
|
||||
timeRemaining.value = response.data.expires_in_minutes * 60
|
||||
const response = await apiService.post("/telegram/auth/generate-code");
|
||||
linkingCode.value = response.data.linking_code;
|
||||
timeRemaining.value = response.data.expires_in_minutes * 60;
|
||||
|
||||
toast.add({
|
||||
severity: 'success',
|
||||
summary: 'Cod Generat',
|
||||
detail: 'Alege o metodă de conectare',
|
||||
life: 3000
|
||||
})
|
||||
severity: "success",
|
||||
summary: "Cod Generat",
|
||||
detail: "Alege o metodă de conectare",
|
||||
life: 3000,
|
||||
});
|
||||
|
||||
startCountdown()
|
||||
startCountdown();
|
||||
} catch (error) {
|
||||
console.error('Error generating code:', error)
|
||||
console.error("Error generating code:", error);
|
||||
toast.add({
|
||||
severity: 'error',
|
||||
summary: 'Eroare',
|
||||
detail: error.response?.data?.detail || 'Nu am putut genera codul',
|
||||
life: 5000
|
||||
})
|
||||
severity: "error",
|
||||
summary: "Eroare",
|
||||
detail: error.response?.data?.detail || "Nu am putut genera codul",
|
||||
life: 5000,
|
||||
});
|
||||
} finally {
|
||||
loading.value = false
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const startCountdown = () => {
|
||||
if (countdownInterval) clearInterval(countdownInterval)
|
||||
if (countdownInterval) clearInterval(countdownInterval);
|
||||
|
||||
countdownInterval = setInterval(() => {
|
||||
if (timeRemaining.value > 0) {
|
||||
timeRemaining.value--
|
||||
timeRemaining.value--;
|
||||
} else {
|
||||
clearInterval(countdownInterval)
|
||||
linkingCode.value = ''
|
||||
clearInterval(countdownInterval);
|
||||
linkingCode.value = "";
|
||||
toast.add({
|
||||
severity: 'warn',
|
||||
summary: 'Cod Expirat',
|
||||
detail: 'Generează un cod nou',
|
||||
life: 4000
|
||||
})
|
||||
severity: "warn",
|
||||
summary: "Cod Expirat",
|
||||
detail: "Generează un cod nou",
|
||||
life: 4000,
|
||||
});
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const formatTime = (seconds) => {
|
||||
const minutes = Math.floor(seconds / 60)
|
||||
const secs = seconds % 60
|
||||
return `${minutes}:${secs.toString().padStart(2, '0')}`
|
||||
}
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const secs = seconds % 60;
|
||||
return `${minutes}:${secs.toString().padStart(2, "0")}`;
|
||||
};
|
||||
|
||||
const copyCode = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(linkingCode.value)
|
||||
await navigator.clipboard.writeText(linkingCode.value);
|
||||
toast.add({
|
||||
severity: 'success',
|
||||
summary: 'Copiat',
|
||||
detail: 'Cod copiat în clipboard',
|
||||
life: 2000
|
||||
})
|
||||
severity: "success",
|
||||
summary: "Copiat",
|
||||
detail: "Cod copiat în clipboard",
|
||||
life: 2000,
|
||||
});
|
||||
} catch (error) {
|
||||
const tempInput = document.createElement('input')
|
||||
tempInput.value = linkingCode.value
|
||||
document.body.appendChild(tempInput)
|
||||
tempInput.select()
|
||||
document.execCommand('copy')
|
||||
document.body.removeChild(tempInput)
|
||||
const tempInput = document.createElement("input");
|
||||
tempInput.value = linkingCode.value;
|
||||
document.body.appendChild(tempInput);
|
||||
tempInput.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(tempInput);
|
||||
toast.add({
|
||||
severity: 'success',
|
||||
summary: 'Copiat',
|
||||
life: 2000
|
||||
})
|
||||
severity: "success",
|
||||
summary: "Copiat",
|
||||
life: 2000,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onUnmounted(() => {
|
||||
if (countdownInterval) clearInterval(countdownInterval)
|
||||
})
|
||||
if (countdownInterval) clearInterval(countdownInterval);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -214,7 +211,11 @@ onUnmounted(() => {
|
||||
|
||||
/* Code Display */
|
||||
.code-display {
|
||||
background: linear-gradient(135deg, rgba(67, 97, 238, 0.08), rgba(67, 97, 238, 0.02));
|
||||
background: linear-gradient(
|
||||
135deg,
|
||||
rgba(67, 97, 238, 0.08),
|
||||
rgba(67, 97, 238, 0.02)
|
||||
);
|
||||
border: 2px solid var(--color-primary);
|
||||
border-radius: var(--radius-md);
|
||||
padding: var(--space-md);
|
||||
@@ -237,7 +238,7 @@ onUnmounted(() => {
|
||||
.code-timer {
|
||||
color: var(--color-primary);
|
||||
font-weight: var(--font-bold);
|
||||
font-family: 'Courier New', monospace;
|
||||
font-family: "Courier New", monospace;
|
||||
}
|
||||
|
||||
.code-value {
|
||||
@@ -245,7 +246,7 @@ onUnmounted(() => {
|
||||
font-weight: var(--font-bold);
|
||||
color: var(--color-primary);
|
||||
letter-spacing: 0.3em;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-family: "Courier New", monospace;
|
||||
}
|
||||
|
||||
/* Action Buttons - Use global .btn patterns */
|
||||
|
||||
Reference in New Issue
Block a user