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:
2025-11-22 00:31:20 +02:00
parent bd41a3406e
commit f52aa27bdc
14 changed files with 1691 additions and 1144 deletions

View File

@@ -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 */