feat(version): versão em runtime via /api/version (Passos 1/3/4)
- buildInfo.ts: hook useAppVersion() consome GET /api/version (fonte única = backend),
com fallback ao carimbo do bundle; deixa de depender do APP_VERSION manual.
- Versão exibida em Configurações ("Sobre o sistema") + telas públicas (Login/WaitingInvite);
removida do rodapé do Sidebar.
- compose.prod/deploy-prod.sh: injetam APP_VERSION (=tag) e APP_ENV=PROD em runtime,
desacoplando a versão semântica do build (commit/build seguem da imagem).
- constants.ts marcado legado + bump V1.0.15 (transitório até o Passo 5; evita colisão de tag).
- DEPLOY-PRODUCAO.md reescrito com status ✅/🧪/🎯 + nota de fase transitória.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,7 @@ import { GoogleConnectButton } from '../components/GoogleConnectButton.tsx';
|
||||
import { useToast } from '../contexts/ToastContext.tsx';
|
||||
import { useConfirm } from '../contexts/ConfirmContext.tsx';
|
||||
import { ChangePasswordModal } from '../components/ChangePasswordModal.tsx';
|
||||
import { APP_VERSION } from '../constants.ts';
|
||||
import { useAppVersion } from '../buildInfo.ts';
|
||||
import { buscarCep } from '../services/viacep.ts';
|
||||
|
||||
const FIELD_CLS = 'w-full border border-gray-200 rounded-xl px-3 py-2.5 text-sm font-medium text-gray-800 outline-none focus:border-teal-500 focus:ring-2 focus:ring-teal-100 transition-all';
|
||||
@@ -352,6 +352,8 @@ export const ConfiguracoesView: React.FC = () => {
|
||||
const toast = useToast();
|
||||
const confirm = useConfirm();
|
||||
const [showChangePw, setShowChangePw] = useState(false);
|
||||
// Versão/commit/ambiente vêm do backend em RUNTIME (fonte única). Fallback: carimbo do bundle.
|
||||
const ver = useAppVersion();
|
||||
|
||||
const authHeader = { 'Authorization': `Bearer ${localStorage.getItem('SCOREODONTO_AUTH_TOKEN')}`, 'Content-Type': 'application/json' };
|
||||
|
||||
@@ -417,7 +419,7 @@ export const ConfiguracoesView: React.FC = () => {
|
||||
<p className="text-sm text-gray-400 font-medium mt-1">Gerencie seu perfil e integrações</p>
|
||||
</div>
|
||||
<span className="bg-gray-100 text-gray-400 text-[11px] font-mono font-bold px-3 py-1 rounded-full border border-gray-200 mb-1">
|
||||
{APP_VERSION}
|
||||
{ver.version}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -567,6 +569,29 @@ export const ConfiguracoesView: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sobre o sistema — versão/commit/ambiente vindos do backend em runtime */}
|
||||
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm overflow-hidden">
|
||||
<div className="px-6 py-4 border-b border-gray-50">
|
||||
<h2 className="text-xs font-black text-gray-500 uppercase tracking-widest">Sobre o sistema</h2>
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<dl className="grid grid-cols-[auto,1fr] gap-x-6 gap-y-2 text-sm font-mono">
|
||||
<dt className="text-gray-400 font-bold uppercase text-[11px] tracking-widest self-center">Versão</dt>
|
||||
<dd className="text-gray-900 font-bold">{ver.version}</dd>
|
||||
<dt className="text-gray-400 font-bold uppercase text-[11px] tracking-widest self-center">Commit</dt>
|
||||
<dd className="text-gray-700">{ver.commit}</dd>
|
||||
<dt className="text-gray-400 font-bold uppercase text-[11px] tracking-widest self-center">Build</dt>
|
||||
<dd className="text-gray-700">{ver.build || '—'}</dd>
|
||||
<dt className="text-gray-400 font-bold uppercase text-[11px] tracking-widest self-center">Ambiente</dt>
|
||||
<dd>
|
||||
<span className={`font-black ${ver.environment === 'PRODUÇÃO' || ver.environment === 'PROD' ? 'text-emerald-600' : 'text-amber-600'}`}>
|
||||
{ver.environment}
|
||||
</span>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{showChangePw && <ChangePasswordModal onClose={() => setShowChangePw(false)} onDone={() => setShowChangePw(false)} />}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
UserSearch, DoorOpen, ScanLine, Sparkles, MapPin, Building2, Users, FlaskConical
|
||||
} from 'lucide-react';
|
||||
import { HybridBackend } from '../services/backend.ts';
|
||||
import { APP_VERSION } from '../constants.ts';
|
||||
import { useAppVersion } from '../buildInfo.ts';
|
||||
import { OnboardingChat } from './OnboardingChat.tsx';
|
||||
|
||||
interface LoginViewProps {
|
||||
@@ -57,6 +57,7 @@ const InputCls = 'w-full pl-10 pr-4 py-3.5 bg-gray-50 border border-gray-200 rou
|
||||
|
||||
// ── Component ─────────────────────────────────────────────────────────────────
|
||||
export const LoginView: React.FC<LoginViewProps> = ({ onLoginSuccess }) => {
|
||||
const ver = useAppVersion(); // versão oficial via /api/version (público), fallback bundle
|
||||
const [tab, setTab] = useState<'login' | 'cadastro'>('login');
|
||||
const [authMode, setAuthMode] = useState<'login' | 'forgot' | 'reset'>('login');
|
||||
|
||||
@@ -215,7 +216,7 @@ export const LoginView: React.FC<LoginViewProps> = ({ onLoginSuccess }) => {
|
||||
|
||||
{/* Footer */}
|
||||
<div className="relative z-10 flex items-center gap-2 text-teal-300/50 text-[10px] font-black uppercase tracking-widest">
|
||||
<ShieldCheck size={12} /> Dados criptografados · LGPD · {APP_VERSION}
|
||||
<ShieldCheck size={12} /> Dados criptografados · LGPD · {ver.version}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -335,7 +336,7 @@ export const LoginView: React.FC<LoginViewProps> = ({ onLoginSuccess }) => {
|
||||
</div>
|
||||
<div className="px-8 py-4 bg-gray-50 border-t border-gray-100 flex items-center justify-center gap-2">
|
||||
<ShieldCheck size={12} className="text-gray-400" />
|
||||
<span className="text-[10px] font-black text-gray-400 uppercase tracking-widest">Ambiente Seguro · {APP_VERSION}</span>
|
||||
<span className="text-[10px] font-black text-gray-400 uppercase tracking-widest">Ambiente Seguro · {ver.version}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Mail, LogOut, Stethoscope, Clock, CheckCircle2, ArrowRight, MapPin, Users, Wrench, ChevronDown, ChevronUp, Loader2 } from 'lucide-react';
|
||||
import { HybridBackend } from '../services/backend.ts';
|
||||
import { APP_VERSION } from '../constants.ts';
|
||||
import { useAppVersion } from '../buildInfo.ts';
|
||||
|
||||
const UF_LIST = ['AC','AL','AM','AP','BA','CE','DF','ES','GO','MA','MG','MS','MT','PA','PB','PE','PI','PR','RJ','RN','RO','RR','RS','SC','SE','SP','TO'];
|
||||
|
||||
@@ -13,6 +13,7 @@ const ROLE_CONFIG: Record<string, { label: string; icon: React.ElementType; colo
|
||||
};
|
||||
|
||||
export const WaitingInviteView: React.FC = () => {
|
||||
const ver = useAppVersion();
|
||||
const userData = localStorage.getItem('SCOREODONTO_USER_DATA');
|
||||
const user = userData ? JSON.parse(userData) : null;
|
||||
const role = user?.role ?? 'dentista';
|
||||
@@ -229,7 +230,7 @@ export const WaitingInviteView: React.FC = () => {
|
||||
</div>
|
||||
|
||||
<div className="px-8 py-4 bg-gray-50 border-t border-gray-100 flex items-center justify-center">
|
||||
<span className="text-[10px] font-black text-gray-400 uppercase tracking-widest">{APP_VERSION}</span>
|
||||
<span className="text-[10px] font-black text-gray-400 uppercase tracking-widest">{ver.version}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user