🎨 Improve code readability by reformatting and updating function definitions and comments

This commit is contained in:
2026-01-28 18:18:55 +00:00
parent 1944b43af8
commit a24f766e37
154 changed files with 7261 additions and 117 deletions

View File

@@ -0,0 +1,580 @@
<template>
<div class="api-key-manager">
<div class="container">
<div class="header">
<router-link to="/" class="back-button"> Back Home </router-link>
<h1>API Key Manager</h1>
</div>
<!-- Current Status -->
<div class="section">
<h2>Current Status</h2>
<div v-if="isKeyStored" class="status success">
<div class="status-icon"></div>
<div class="status-content">
<p><strong>API Key Stored</strong></p>
<p class="key-display">{{ maskedKey }}</p>
<button @click="showDeleteConfirm = true" class="btn btn-danger">
Clear Stored Key
</button>
</div>
</div>
<div v-else class="status warning">
<div class="status-icon"></div>
<div class="status-content">
<p><strong>No API Key Stored</strong></p>
<p>Add your Challonge API key below to get started</p>
</div>
</div>
</div>
<!-- Add/Update Key -->
<div class="section">
<div class="section-header">
<h2>{{ isKeyStored ? 'Update' : 'Add' }} Challonge API Key</h2>
<button
@click="showGuide = true"
class="help-btn"
title="How to get a Challonge API key"
>
Need Help?
</button>
</div>
<div class="form-group">
<label for="api-key">Challonge API Key</label>
<div class="input-wrapper">
<input
id="api-key"
v-model="inputKey"
:type="showPassword ? 'text' : 'password'"
placeholder="Enter your Challonge API key"
class="form-input"
/>
<button
@click="showPassword = !showPassword"
class="toggle-password"
:title="showPassword ? 'Hide' : 'Show'"
>
{{ showPassword ? '👁️' : '👁️‍🗨️' }}
</button>
</div>
<p class="help-text">
Get your API key from
<a
href="https://challonge.com/settings/developer"
target="_blank"
rel="noopener"
>
Challonge Developer Settings
</a>
</p>
</div>
<div v-if="error" class="error-message">
{{ error }}
</div>
<button
@click="handleSaveKey"
:disabled="!inputKey || saving"
class="btn btn-primary"
>
{{ saving ? 'Saving...' : isKeyStored ? 'Update Key' : 'Save Key' }}
</button>
<div v-if="successMessage" class="success-message">
{{ successMessage }}
</div>
</div>
<!-- Information -->
<div class="section info-section">
<h2> How It Works</h2>
<ul>
<li>
<strong>Secure Storage:</strong> Your API key is stored locally in
your browser using localStorage. It never leaves your device.
</li>
<li>
<strong>Device Specific:</strong> Each device/browser has its own
storage. The key won't sync across devices.
</li>
<li>
<strong>Persistent:</strong> Your key will be available whenever you
use this app, even after closing the browser.
</li>
<li>
<strong>Clear Anytime:</strong> Use the "Clear Stored Key" button to
remove it whenever you want.
</li>
<li>
<strong>Works Everywhere:</strong> Compatible with desktop, mobile,
and tablet browsers.
</li>
</ul>
</div>
<!-- Security Notice -->
<div class="section warning-section">
<h2>🔒 Security Notice</h2>
<p>
⚠️ <strong>localStorage is not encrypted.</strong> Only use this on
trusted devices. If you're on a shared or public computer, clear your
API key when done.
</p>
<p>
For production use, consider using a backend proxy that handles API
keys server-side instead.
</p>
</div>
</div>
<!-- Delete Confirmation Modal -->
<div
v-if="showDeleteConfirm"
class="modal-overlay"
@click="showDeleteConfirm = false"
>
<div class="modal" @click.stop>
<h3>Delete API Key?</h3>
<p>
Are you sure you want to clear the stored API key? You'll need to
enter it again to use the tournament tools.
</p>
<div class="modal-buttons">
<button @click="showDeleteConfirm = false" class="btn btn-secondary">
Cancel
</button>
<button @click="handleDeleteKey" class="btn btn-danger">
Delete
</button>
</div>
</div>
</div>
<!-- Challonge API Key Guide Modal -->
<ChallongeApiKeyGuide v-if="showGuide" @close="showGuide = false" />
</div>
</template>
<script setup>
import { ref } from 'vue';
import ChallongeApiKeyGuide from '../components/ChallongeApiKeyGuide.vue';
import { useChallongeApiKey } from '../composables/useChallongeApiKey.js';
const { saveApiKey, clearApiKey, maskedKey, isKeyStored } =
useChallongeApiKey();
const inputKey = ref('');
const showPassword = ref(false);
const showDeleteConfirm = ref(false);
const saving = ref(false);
const error = ref('');
const successMessage = ref('');
const showGuide = ref(false);
async function handleSaveKey() {
error.value = '';
successMessage.value = '';
// Validate input
if (!inputKey.value.trim()) {
error.value = 'Please enter an API key';
return;
}
if (inputKey.value.length < 10) {
error.value = 'API key appears to be too short';
return;
}
saving.value = true;
try {
const success = saveApiKey(inputKey.value.trim());
if (success) {
successMessage.value = 'API key saved successfully!';
inputKey.value = '';
setTimeout(() => {
successMessage.value = '';
}, 3000);
} else {
error.value = 'Failed to save API key. Please try again.';
}
} finally {
saving.value = false;
}
}
function handleDeleteKey() {
const success = clearApiKey();
if (success) {
showDeleteConfirm.value = false;
inputKey.value = '';
successMessage.value = 'API key cleared successfully';
setTimeout(() => {
successMessage.value = '';
}, 3000);
} else {
error.value = 'Failed to clear API key';
}
}
</script>
<style scoped>
.api-key-manager {
min-height: 100vh;
padding: 2rem 1rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
border-radius: 12px;
padding: 2rem;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
}
.header {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 2rem;
flex-wrap: wrap;
}
.back-button {
padding: 0.5rem 1rem;
background: #667eea;
color: white;
text-decoration: none;
border-radius: 6px;
font-weight: 600;
transition: all 0.3s ease;
display: inline-block;
}
.back-button:hover {
background: #5568d3;
transform: translateX(-2px);
}
.help-btn {
padding: 0.5rem 1rem;
background: #f59e0b;
color: white;
border: none;
border-radius: 6px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: inline-block;
font-size: 0.95rem;
}
.help-btn:hover {
background: #d97706;
transform: translateY(-2px);
}
h1 {
color: #333;
margin: 0;
font-size: 2rem;
}
.section {
margin: 2rem 0;
padding: 1.5rem;
background: #f8f9fa;
border-radius: 8px;
border: 1px solid #e9ecef;
}
.section-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
flex-wrap: wrap;
margin-bottom: 1rem;
}
.section-header h2 {
margin: 0;
}
h2 {
color: #495057;
margin-top: 0;
font-size: 1.3rem;
}
.status {
display: flex;
gap: 1rem;
padding: 1.5rem;
border-radius: 8px;
align-items: flex-start;
}
.status.success {
background: #d1fae5;
border: 2px solid #10b981;
}
.status.warning {
background: #fef3c7;
border: 2px solid #f59e0b;
}
.status-icon {
font-size: 1.5rem;
min-width: 2rem;
}
.status-content p {
margin: 0.5rem 0;
color: #333;
}
.key-display {
font-family: 'Courier New', monospace;
font-weight: 600;
color: #10b981;
font-size: 1.1rem;
}
.form-group {
margin-bottom: 1.5rem;
}
label {
display: block;
font-weight: 600;
color: #495057;
margin-bottom: 0.5rem;
}
.input-wrapper {
position: relative;
display: flex;
align-items: center;
gap: 0.5rem;
}
.form-input {
flex: 1;
padding: 0.75rem;
border: 2px solid #dee2e6;
border-radius: 6px;
font-size: 1rem;
font-family: 'Courier New', monospace;
transition: border-color 0.3s ease;
}
.form-input:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.toggle-password {
padding: 0.75rem;
background: none;
border: none;
cursor: pointer;
font-size: 1.2rem;
transition: transform 0.2s ease;
}
.toggle-password:hover {
transform: scale(1.1);
}
.help-text {
margin-top: 0.5rem;
font-size: 0.875rem;
color: #666;
}
.help-text a {
color: #667eea;
text-decoration: none;
font-weight: 600;
}
.help-text a:hover {
text-decoration: underline;
}
.btn {
padding: 0.75rem 1.5rem;
font-size: 1rem;
font-weight: 600;
border: none;
border-radius: 6px;
cursor: pointer;
transition: all 0.3s ease;
margin-right: 0.5rem;
margin-bottom: 0.5rem;
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.btn-primary {
background: #667eea;
color: white;
}
.btn-primary:hover:not(:disabled) {
background: #5568d3;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
.btn-danger {
background: #ef4444;
color: white;
}
.btn-danger:hover:not(:disabled) {
background: #dc2626;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.4);
}
.btn-secondary {
background: #6b7280;
color: white;
}
.btn-secondary:hover {
background: #4b5563;
}
.error-message {
margin-top: 1rem;
padding: 1rem;
background: #fee;
color: #c33;
border-radius: 6px;
border-left: 4px solid #c33;
}
.success-message {
margin-top: 1rem;
padding: 1rem;
background: #d1fae5;
color: #065f46;
border-radius: 6px;
border-left: 4px solid #10b981;
font-weight: 500;
}
.info-section {
background: #e7f3ff;
border-color: #667eea;
}
.info-section h2 {
color: #667eea;
}
.info-section ul {
margin: 1rem 0;
padding-left: 1.5rem;
}
.info-section li {
margin: 0.75rem 0;
color: #495057;
line-height: 1.6;
}
.warning-section {
background: #fef3c7;
border-color: #f59e0b;
}
.warning-section h2 {
color: #d97706;
}
.warning-section p {
color: #92400e;
line-height: 1.6;
}
/* Modal Styles */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal {
background: white;
padding: 2rem;
border-radius: 12px;
max-width: 400px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
}
.modal h3 {
margin-top: 0;
color: #333;
font-size: 1.5rem;
}
.modal p {
color: #666;
line-height: 1.6;
}
.modal-buttons {
display: flex;
gap: 1rem;
justify-content: flex-end;
margin-top: 1.5rem;
}
.modal-buttons .btn {
margin: 0;
}
@media (max-width: 640px) {
.container {
padding: 1rem;
}
h1 {
font-size: 1.5rem;
}
.header {
flex-direction: column;
align-items: flex-start;
}
.modal-buttons {
flex-direction: column;
}
.modal-buttons .btn {
width: 100%;
}
}
</style>