diff --git a/src/components/portal/CustomerPortal.jsx b/src/components/portal/CustomerPortal.jsx index a394e4f..21a31bb 100644 --- a/src/components/portal/CustomerPortal.jsx +++ b/src/components/portal/CustomerPortal.jsx @@ -13,7 +13,9 @@ import { Sparkles, CheckCircle2, AlertCircle, - Info + Info, + Lock, + Unlock } from 'lucide-react'; import { CATEGORIES, @@ -25,6 +27,7 @@ import { import PortalItemCard from './PortalItemCard'; import ModalPortalItem from './ModalPortalItem'; import ModalConfirm from '../wpp/ModalConfirm'; +import ModalSupportAuth from './ModalSupportAuth'; export default function CustomerPortal() { const [items, setItems] = useState([]); @@ -33,6 +36,14 @@ export default function CustomerPortal() { const [copiedWhatsapp, setCopiedWhatsapp] = useState(false); const [toasts, setToasts] = useState([]); + // Estado de Autenticação da Equipe de Suporte + const [isSupportUnlocked, setIsSupportUnlocked] = useState(false); + const [authModal, setAuthModal] = useState({ + isOpen: false, + title: '', + onSuccess: null + }); + // Modals state const [isModalItemOpen, setIsModalItemOpen] = useState(false); const [itemToEdit, setItemToEdit] = useState(null); @@ -66,6 +77,25 @@ export default function CustomerPortal() { }, 3500); }; + // Solicitação de autenticação de suporte para ver senhas + const handleRequestSupportAuth = (onSuccess, title) => { + setAuthModal({ + isOpen: true, + title: title || 'este acesso', + onSuccess + }); + }; + + const handleSessionUnlock = () => { + setIsSupportUnlocked(true); + showToast('Modo de suporte autenticado para esta sessão!', 'success'); + }; + + const handleLockSupport = () => { + setIsSupportUnlocked(false); + showToast('Modo de suporte bloqueado. Senhas protegidas.', 'info'); + }; + // Salvar Item (Criar ou Editar) const handleSaveItem = (itemData) => { let updated; @@ -270,6 +300,30 @@ export default function CustomerPortal() { Restaurar Padrão + + {/* Botão de Autenticação / Status do Suporte */} + {isSupportUnlocked ? ( + + ) : ( + + )} @@ -300,6 +354,8 @@ export default function CustomerPortal() { { setItemToEdit(it); setIsModalItemOpen(true); @@ -341,6 +397,15 @@ export default function CustomerPortal() { type={confirmModal.type} /> + {/* Modal de Autenticação de Suporte (Senha Padrão C2s33131782*) */} + setAuthModal(prev => ({ ...prev, isOpen: false }))} + onSuccess={authModal.onSuccess} + targetTitle={authModal.title} + onSessionUnlock={handleSessionUnlock} + /> + {/* Toasts */}
{toasts.map(t => ( diff --git a/src/components/portal/ModalSupportAuth.jsx b/src/components/portal/ModalSupportAuth.jsx new file mode 100644 index 0000000..9620f28 --- /dev/null +++ b/src/components/portal/ModalSupportAuth.jsx @@ -0,0 +1,157 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { ShieldAlert, Lock, Unlock, Eye, EyeOff, X, AlertCircle, KeyRound } from 'lucide-react'; +import { SUPPORT_MASTER_PASSWORD } from '../../utils/customerPortalData'; + +export default function ModalSupportAuth({ + isOpen, + onClose, + onSuccess, + targetTitle = 'este acesso', + onSessionUnlock +}) { + const [inputPassword, setInputPassword] = useState(''); + const [showInputPassword, setShowInputPassword] = useState(false); + const [error, setError] = useState(false); + const [rememberSession, setRememberSession] = useState(true); + const inputRef = useRef(null); + + useEffect(() => { + if (isOpen) { + setInputPassword(''); + setError(false); + setShowInputPassword(false); + setTimeout(() => { + inputRef.current?.focus(); + }, 100); + } + }, [isOpen]); + + if (!isOpen) return null; + + const handleSubmit = (e) => { + e?.preventDefault(); + if (inputPassword === SUPPORT_MASTER_PASSWORD) { + setError(false); + if (rememberSession && onSessionUnlock) { + onSessionUnlock(); + } + onSuccess?.(); + onClose(); + } else { + setError(true); + setInputPassword(''); + inputRef.current?.focus(); + } + }; + + return ( +
+
e.stopPropagation()} + style={{ maxWidth: '440px' }} + > +
+
+
+ +
+
+

Autenticação de Suporte

+

+ Acesso restrito à equipe técnica C2 +

+
+
+ +
+ +
+
+

+ Para visualizar a senha de {targetTitle}, informe a senha padrão da equipe de suporte: +

+ +
+
+ { + setInputPassword(e.target.value); + if (error) setError(false); + }} + style={{ + paddingRight: '2.5rem', + borderColor: error ? '#ef4444' : undefined, + boxShadow: error ? '0 0 0 1px #ef4444' : undefined + }} + autoComplete="off" + /> + +
+
+ + {error && ( +
+ + Senha incorreta! Acesso restrito à equipe de suporte. +
+ )} + + +
+ +
+ + + +
+
+
+
+ ); +} diff --git a/src/components/portal/PortalItemCard.jsx b/src/components/portal/PortalItemCard.jsx index 0da1ae3..8ede5e9 100644 --- a/src/components/portal/PortalItemCard.jsx +++ b/src/components/portal/PortalItemCard.jsx @@ -14,7 +14,13 @@ import { Globe } from 'lucide-react'; -export default function PortalItemCard({ item, onEdit, onDelete }) { +export default function PortalItemCard({ + item, + onEdit, + onDelete, + isSupportUnlocked = false, + onRequestSupportAuth +}) { const [copiedKey, setCopiedKey] = useState(null); const [showPassword, setShowPassword] = useState(false); const [showWifiPassword, setShowWifiPassword] = useState(false); @@ -26,6 +32,42 @@ export default function PortalItemCard({ item, onEdit, onDelete }) { setTimeout(() => setCopiedKey(null), 2000); }; + const handleTogglePassword = () => { + if (showPassword) { + setShowPassword(false); + } else { + if (isSupportUnlocked) { + setShowPassword(true); + } else if (onRequestSupportAuth) { + onRequestSupportAuth(() => setShowPassword(true), item.title); + } else { + setShowPassword(true); + } + } + }; + + const handleToggleWifiPassword = () => { + if (showWifiPassword) { + setShowWifiPassword(false); + } else { + if (isSupportUnlocked) { + setShowWifiPassword(true); + } else if (onRequestSupportAuth) { + onRequestSupportAuth(() => setShowWifiPassword(true), `${item.title} (Wi-Fi)`); + } else { + setShowWifiPassword(true); + } + } + }; + + const handlePasswordCopy = (text, key, isVisible) => { + if (!isVisible && !isSupportUnlocked && onRequestSupportAuth) { + onRequestSupportAuth(() => handleCopy(text, key), item.title); + } else { + handleCopy(text, key); + } + }; + return (
@@ -114,7 +156,11 @@ export default function PortalItemCard({ item, onEdit, onDelete }) {
Senha: - handleCopy(item.password, 'pass')}> + handlePasswordCopy(item.password, 'pass', showPassword)} + title={showPassword ? 'Clique para copiar' : 'Requer senha de suporte'} + > {showPassword ? item.password : '••••••••'} - handleCopy(item.password, 'pass')} style={{ cursor: 'pointer', display: 'flex' }}> + handlePasswordCopy(item.password, 'pass', showPassword)} + style={{ cursor: 'pointer', display: 'flex' }} + title="Copiar senha" + > {copiedKey === 'pass' ? : }
@@ -149,7 +199,11 @@ export default function PortalItemCard({ item, onEdit, onDelete }) {
Senha Wi-Fi: - handleCopy(item.wifiPassword, 'wifiPass')}> + handlePasswordCopy(item.wifiPassword, 'wifiPass', showWifiPassword)} + title={showWifiPassword ? 'Clique para copiar' : 'Requer senha de suporte'} + > {showWifiPassword ? item.wifiPassword : '••••••••'} - handleCopy(item.wifiPassword, 'wifiPass')} style={{ cursor: 'pointer', display: 'flex' }}> + handlePasswordCopy(item.wifiPassword, 'wifiPass', showWifiPassword)} + style={{ cursor: 'pointer', display: 'flex' }} + title="Copiar senha Wi-Fi" + > {copiedKey === 'wifiPass' ? : }
diff --git a/src/utils/customerPortalData.js b/src/utils/customerPortalData.js index cf639e5..2b33bab 100644 --- a/src/utils/customerPortalData.js +++ b/src/utils/customerPortalData.js @@ -1,4 +1,5 @@ const STORAGE_KEY = 'inflavio_customer_portal_data_v1'; +export const SUPPORT_MASTER_PASSWORD = 'C2s33131782*'; export const CATEGORIES = [ { id: 'all', label: 'Todos os Acessos' },