QrRapido/Views/Developer/Index.cshtml

369 lines
18 KiB
Plaintext

@model QRRapidoApp.Models.User
@{
ViewData["Title"] = "Portal do Desenvolvedor";
Layout = "~/Views/Shared/_Layout.cshtml";
var newKey = TempData["NewKey"] as string;
var newKeyName = TempData["NewKeyName"] as string;
var errorMsg = TempData["Error"] as string;
var successMsg = TempData["Success"] as string;
var activeKeys = Model.ApiKeys.Where(k => k.IsActive).OrderByDescending(k => k.CreatedAt).ToList();
var revokedKeys = Model.ApiKeys.Where(k => !k.IsActive).OrderByDescending(k => k.CreatedAt).ToList();
var baseUrl = Context.Request.Scheme + "://" + Context.Request.Host;
var isEn = (ViewBag.Culture as string) == "en";
var isEs = (ViewBag.Culture as string) == "es-PY" || (ViewBag.Culture as string) == "es";
var docsBase = isEn ? "/en/Developer" : isEs ? "/es-PY/Developer" : "/Developer";
string T(string pt, string es, string en = null) => isEn && en != null ? en : isEs ? es : pt;
}
<div class="container mt-4 mb-5">
<!-- Cabeçalho -->
<div class="d-flex align-items-center justify-content-between mb-4 flex-wrap gap-2">
<div class="d-flex align-items-center">
<div class="me-3">
<i class="fas fa-code fa-2x text-primary"></i>
</div>
<div>
<h1 class="h3 mb-0">@T("Portal do Desenvolvedor", "Portal del Desarrollador", "Developer Portal")</h1>
<p class="text-muted mb-0 small">
@T("Gerencie suas chaves de API e integre o QR Rapido nas suas aplicações.",
"Gestioná tus claves de API ha integrá QR Rapido en tus aplicaciones.",
"Manage your API keys and integrate QR Rapido into your applications.")
@{
var tier = Model.ApiSubscription?.EffectiveTier ?? QRRapidoApp.Models.ApiPlanTier.Free;
var tierLabel = tier == QRRapidoApp.Models.ApiPlanTier.Free
? "<span class='badge bg-secondary ms-2'>Free</span>"
: $"<span class='badge bg-primary ms-2'>{tier}</span>";
}
@T("Plano atual:", "Plan actual:", "Current plan:") @Html.Raw(tierLabel)
</p>
</div>
</div>
<div class="d-flex gap-2">
<a href="@docsBase/docs" class="btn btn-outline-info btn-sm">
<i class="fas fa-book me-1"></i> Docs API
</a>
<a href="/api/docs" target="_blank" class="btn btn-outline-success btn-sm">
<i class="fas fa-code me-1"></i> Swagger
</a>
<a href="@docsBase/Pricing" class="btn btn-outline-primary btn-sm">
<i class="fas fa-arrow-up me-1"></i> @T("Ver Planos de API", "Ver Planes de API", "API Plans")
</a>
</div>
</div>
<!-- Alertas -->
@if (!string.IsNullOrEmpty(errorMsg))
{
<div class="alert alert-danger alert-dismissible fade show" role="alert">
<i class="fas fa-exclamation-circle me-2"></i>@errorMsg
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
}
@if (!string.IsNullOrEmpty(successMsg))
{
<div class="alert alert-success alert-dismissible fade show" role="alert">
<i class="fas fa-check-circle me-2"></i>@successMsg
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
}
<!-- Alerta de nova chave gerada (exibida UMA vez) -->
@if (!string.IsNullOrEmpty(newKey))
{
<div class="alert alert-warning border-warning shadow-sm" role="alert">
<h5 class="alert-heading">
<i class="fas fa-key me-2"></i>@T("Chave", "Clave") "<strong>@newKeyName</strong>" @T("criada com sucesso!", "¡creada porã!")
</h5>
<p class="mb-2 small">
<strong class="text-danger">@T("Copie agora.", "Copiá ko'aga.")</strong>
@T("Esta chave", "Esta clave") <strong>@T("não será exibida novamente", "no se mostrará de nuevo")</strong> @T("por segurança.", "por seguridad.")
</p>
<div class="input-group">
<input type="text" id="newApiKey" class="form-control font-monospace" value="@newKey" readonly>
<button class="btn btn-warning" type="button" onclick="copyKey('newApiKey', this)">
<i class="fas fa-copy me-1"></i> @T("Copiar", "Copiar")
</button>
</div>
</div>
}
<div class="row g-4">
<!-- Coluna esquerda: chaves + criar -->
<div class="col-lg-7">
<!-- Minhas Chaves Ativas -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-white d-flex justify-content-between align-items-center py-3">
<h5 class="mb-0">
<i class="fas fa-key me-2 text-primary"></i>@T("Chaves Ativas", "Claves Activas")
<span class="badge bg-primary ms-1">@activeKeys.Count / 5</span>
</h5>
</div>
<div class="card-body p-0">
@if (activeKeys.Any())
{
<div class="table-responsive">
<table class="table table-hover mb-0 align-middle">
<thead class="table-light">
<tr>
<th>@T("Nome", "Nombre")</th>
<th>@T("Prefixo", "Prefijo")</th>
<th>@T("Criada", "Creada")</th>
<th>@T("Último uso", "Último uso")</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var key in activeKeys)
{
<tr>
<td class="fw-semibold">@key.Name</td>
<td><code class="text-primary">@key.Prefix...</code></td>
<td class="small text-muted">@key.CreatedAt.ToString("dd/MM/yyyy")</td>
<td class="small text-muted">
@(key.LastUsedAt.HasValue
? key.LastUsedAt.Value.ToString("dd/MM/yyyy HH:mm")
: T("Nunca", "Nunca"))
</td>
<td class="text-end">
<form method="post" asp-action="RevokeKey" asp-controller="Developer"
onsubmit="return confirm('@T("Revogar", "Revocar") \'@key.Name\'?')">
<input type="hidden" name="prefix" value="@key.Prefix">
<button type="submit" class="btn btn-outline-danger btn-sm">
<i class="fas fa-ban me-1"></i>@T("Revogar", "Revocar")
</button>
</form>
</td>
</tr>
}
</tbody>
</table>
</div>
}
else
{
<div class="text-center py-5">
<i class="fas fa-key fa-3x text-muted opacity-25 mb-3"></i>
<p class="text-muted">@T("Nenhuma chave ativa. Crie a primeira abaixo.", "Ninguna clave activa. Creá la primera abajo.")</p>
</div>
}
</div>
</div>
<!-- Criar nova chave -->
@if (activeKeys.Count < 5)
{
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-white py-3">
<h5 class="mb-0">
<i class="fas fa-plus-circle me-2 text-success"></i>@T("Criar Nova Chave", "Crear Nueva Clave")
</h5>
</div>
<div class="card-body">
<form method="post" asp-action="GenerateKey" asp-controller="Developer">
<div class="mb-3">
<label for="keyName" class="form-label fw-semibold">@T("Nome da chave", "Nombre de la clave")</label>
<input type="text" id="keyName" name="keyName" class="form-control"
placeholder="@T("Ex: Meu App, Produção, Teste...", "Ej: Mi App, Producción, Prueba...")"
maxlength="50" required>
<div class="form-text">@T("Identifique para qual projeto ou ambiente esta chave será usada.", "Identificá para qué proyecto o ambiente se va a usar esta clave.")</div>
</div>
<button type="submit" class="btn btn-success">
<i class="fas fa-key me-2"></i>@T("Gerar Chave de API", "Generar Clave de API")
</button>
</form>
</div>
</div>
}
else
{
<div class="alert alert-info">
<i class="fas fa-info-circle me-2"></i>
@T("Você atingiu o limite de 5 chaves ativas. Revogue uma para criar outra.",
"Llegaste al límite de 5 claves activas. Revocá una para crear otra.")
</div>
}
<!-- Chaves revogadas -->
@if (revokedKeys.Any())
{
<div class="card border-0 shadow-sm">
<div class="card-header bg-white py-3">
<h6 class="mb-0 text-muted">
<i class="fas fa-ban me-2"></i>@T("Chaves Revogadas", "Claves Revocadas")
</h6>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-sm mb-0 align-middle text-muted">
<tbody>
@foreach (var key in revokedKeys)
{
<tr>
<td class="ps-3"><s>@key.Name</s></td>
<td><code>@key.Prefix...</code></td>
<td class="small pe-3">@T("Revogada", "Revocada")</td>
</tr>
}
</tbody>
</table>
</div>
</div>
</div>
}
</div>
<!-- Coluna direita: documentação rápida -->
<div class="col-lg-5">
<!-- Quickstart -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-primary text-white py-3">
<h5 class="mb-0"><i class="fas fa-rocket me-2"></i>Quickstart</h5>
</div>
<div class="card-body">
<p class="small text-muted mb-3">
@T("Envie suas requisições para o endpoint abaixo com o header",
"Enviá tus solicitudes al endpoint de abajo con el header") <code>X-API-Key</code>:
</p>
<div class="bg-dark rounded p-3 mb-3">
<code class="text-success small">POST @baseUrl/api/v1/QRManager/generate</code>
</div>
<h6 class="fw-bold mt-3">@T("Exemplo (cURL)", "Ejemplo (cURL)")</h6>
<div class="bg-dark rounded p-3 overflow-auto" style="max-height: 220px;">
<pre class="text-light mb-0 small"><code>curl -X POST \
@baseUrl/api/v1/QRManager/generate \
-H "X-API-Key: @T("SUA_CHAVE_AQUI", "TU_CLAVE_AQUI")" \
-H "Content-Type: application/json" \
-d '{
"content": "https://seusite.com",
"type": "url",
"size": 400,
"primaryColor": "#000000",
"backgroundColor": "#FFFFFF"
}'</code></pre>
</div>
<h6 class="fw-bold mt-3">@T("Resposta de sucesso", "Respuesta exitosa")</h6>
<div class="bg-dark rounded p-3 overflow-auto" style="max-height: 160px;">
<pre class="text-light mb-0 small"><code>{
"success": true,
"qrCodeBase64": "iVBORw0KGgo...",
"qrId": "abc123",
"generationTimeMs": 280,
"remainingCredits": 42,
"fromCache": false
}</code></pre>
</div>
<a href="/api/v1/QRManager/ping" target="_blank" class="btn btn-outline-primary btn-sm mt-3">
<i class="fas fa-heartbeat me-1"></i> @T("Testar Ping", "Probar Ping")
</a>
</div>
</div>
<!-- Limites e planos -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-white py-3">
<h5 class="mb-0"><i class="fas fa-tachometer-alt me-2 text-warning"></i>@T("Limites da API", "Límites de la API")</h5>
</div>
<div class="card-body">
<table class="table table-sm mb-0">
<thead class="table-light">
<tr>
<th>@T("Recurso", "Recurso")</th>
<th>@T("Gratuito", "Gratuito")</th>
<th>@T("Créditos", "Créditos")</th>
</tr>
</thead>
<tbody>
<tr>
<td>@T("QRs por chave", "QRs por clave")</td>
<td>@T("5 vitalícios", "5 de por vida")</td>
<td>1 @T("crédito/QR", "crédito/QR")</td>
</tr>
<tr>
<td>Rate limit</td>
<td colspan="2">600 req/min @T("por IP", "por IP")</td>
</tr>
<tr>
<td>@T("Chaves ativas", "Claves activas")</td>
<td colspan="2">@T("Até 5", "Hasta 5")</td>
</tr>
<tr>
<td>@T("Formatos", "Formatos")</td>
<td colspan="2">PNG (base64)</td>
</tr>
</tbody>
</table>
<div class="mt-3">
<a href="/Pagamento/SelecaoPlano" class="btn btn-warning btn-sm fw-bold">
<i class="fas fa-coins me-1"></i> @T("Comprar Créditos", "Comprar Créditos")
</a>
</div>
</div>
</div>
<!-- Tipos de QR suportados -->
<div class="card border-0 shadow-sm">
<div class="card-header bg-white py-3">
<h5 class="mb-0"><i class="fas fa-list me-2 text-info"></i>@T("Tipos de QR (campo", "Tipos de QR (campo") <code>type</code>)</h5>
</div>
<div class="card-body p-0">
<ul class="list-group list-group-flush small">
<li class="list-group-item d-flex justify-content-between">
<code>url</code><span class="text-muted">Link / URL</span>
</li>
<li class="list-group-item d-flex justify-content-between">
<code>pix</code><span class="text-muted">@T("Pagamento Pix", "Pago Pix")</span>
</li>
<li class="list-group-item d-flex justify-content-between">
<code>wifi</code><span class="text-muted">@T("Rede Wi-Fi", "Red Wi-Fi")</span>
</li>
<li class="list-group-item d-flex justify-content-between">
<code>vcard</code><span class="text-muted">@T("Cartão de Visita", "Tarjeta de Visita")</span>
</li>
<li class="list-group-item d-flex justify-content-between">
<code>whatsapp</code><span class="text-muted">Link WhatsApp</span>
</li>
<li class="list-group-item d-flex justify-content-between">
<code>email</code><span class="text-muted">E-mail</span>
</li>
<li class="list-group-item d-flex justify-content-between">
<code>sms</code><span class="text-muted">SMS</span>
</li>
<li class="list-group-item d-flex justify-content-between">
<code>texto</code><span class="text-muted">@T("Texto livre", "Texto libre")</span>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
@section Scripts {
<script>
function copyKey(inputId, btn) {
const input = document.getElementById(inputId);
navigator.clipboard.writeText(input.value).then(() => {
const original = btn.innerHTML;
btn.innerHTML = '<i class="fas fa-check me-1"></i> @T("Copiado!", "¡Copiado!")';
btn.classList.replace('btn-warning', 'btn-success');
setTimeout(() => {
btn.innerHTML = original;
btn.classList.replace('btn-success', 'btn-warning');
}, 2500);
});
}
</script>
}