| Server IP : 54.37.205.81 / Your IP : 216.73.216.76 Web Server : nginx/1.22.1 System : Linux vps-249481fa 6.1.0-50-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.176-1 (2026-07-02) x86_64 User : debian ( 1000) PHP Version : 8.2.32 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/app.copyrightchain.it/copyrightchain_app/components/ |
Upload File : |
import React, { useState } from 'react';
import { User, Mail, Wallet, ShieldAlert, UserCheck, Building2, Phone, MapPin, Save, IdCard } from 'lucide-react';
import { Language } from '../types';
interface UserProfileProps {
user: any;
lang: Language;
onUpdateUser?: (user: any) => void;
clientToken?: string | null;
}
const splitName = (value: string) => {
const parts = String(value || '').trim().split(/\s+/).filter(Boolean);
return {
nome: parts[0] || '',
cognome: parts.slice(1).join(' ') || ''
};
};
const UserProfile: React.FC<UserProfileProps> = ({ user, lang, onUpdateUser, clientToken }) => {
const isIt = lang === 'it';
const verified = Boolean(user?.identity?.verified);
let saved: any = {};
try {
saved = JSON.parse(localStorage.getItem('ccp_profile_postal') || '{}');
} catch (_) {
saved = {};
}
const nameParts = splitName(user?.name || '');
const [form, setForm] = useState({
ragione_sociale: saved.ragione_sociale || user?.identity?.postal?.ragione_sociale || '',
nome: saved.nome || user?.identity?.postal?.nome || nameParts.nome || '',
cognome: saved.cognome || user?.identity?.postal?.cognome || nameParts.cognome || '',
email: saved.email || user?.identity?.postal?.email || user?.email || '',
telefono: saved.telefono || user?.identity?.postal?.telefono || '',
codice_fiscale: saved.codice_fiscale || user?.identity?.postal?.codice_fiscale || '',
partita_iva: saved.partita_iva || user?.identity?.postal?.partita_iva || '',
dug: saved.dug || user?.identity?.postal?.dug || 'via',
indirizzo: saved.indirizzo || user?.identity?.postal?.indirizzo || '',
civico: saved.civico || user?.identity?.postal?.civico || '',
comune: saved.comune || user?.identity?.postal?.comune || '',
cap: saved.cap || user?.identity?.postal?.cap || '',
provincia: saved.provincia || user?.identity?.postal?.provincia || '',
nazione: saved.nazione || user?.identity?.postal?.nazione || 'IT'
});
const update = (key: string, value: string) => {
let next = value;
if (key === 'provincia' || key === 'nazione') {
next = value.toUpperCase().replace(/[^A-Z]/g, '').slice(0, 2);
}
if (key === 'cap') {
next = value.replace(/\D/g, '').slice(0, 5);
}
setForm(prev => ({ ...prev, [key]: next }));
};
const saveProfile = async () => {
try {
const postal = { ...form };
localStorage.setItem('ccp_profile_postal', JSON.stringify(postal));
let backendUser: any = null;
if (clientToken) {
const res = await fetch('/api/ai/client/profile', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CCP-Token': clientToken
},
body: JSON.stringify({ postal })
});
const data = await res.json().catch(() => null);
if (!res.ok || !data?.ok) {
throw new Error(data?.error || 'profile_save_failed');
}
backendUser = data.user || null;
if (data.postal) {
localStorage.setItem('ccp_profile_postal', JSON.stringify(data.postal));
}
}
const displayName = [postal.nome, postal.cognome].filter(Boolean).join(' ') || user?.name || 'User';
const nextUser = {
...user,
...(backendUser || {}),
name: backendUser?.name || displayName,
username: backendUser?.name || displayName,
email: user?.email,
identity: {
...(user?.identity || {}),
postal: backendUser?.postal_profile || backendUser?.profile?.postal || postal
}
};
if (onUpdateUser) onUpdateUser(nextUser);
alert(isIt ? 'Profilo salvato.' : 'Profile saved.');
} catch (e: any) {
alert((isIt ? 'Errore salvataggio profilo: ' : 'Profile save error: ') + (e?.message || 'unknown_error'));
}
};
return (
<div className="space-y-8">
<div>
<h2 className="text-5xl font-black text-slate-900 uppercase tracking-tight leading-none">
{isIt ? 'PROFILO UTENTE' : 'USER PROFILE'}
</h2>
<p className="text-slate-500 font-medium text-sm mt-3">
{isIt ? 'Dati account, azienda e indirizzo per invii documentali' : 'Account, company and postal data'}
</p>
</div>
<div className="grid grid-cols-1 xl:grid-cols-2 gap-8">
<div className="bg-white rounded-[2.5rem] border border-slate-100 shadow-sm overflow-hidden">
<div className="h-40 bg-slate-950" />
<div className="-mt-14 px-8 pb-8">
<div className="w-40 h-40 rounded-[2rem] bg-slate-100 border-4 border-white flex items-center justify-center text-slate-400 shadow-sm mx-auto">
<User size={64} />
</div>
<div className="text-center mt-6">
<h3 className="text-3xl font-black text-slate-900 uppercase tracking-tight">
{String(user?.name || 'User')}
</h3>
<p className="text-blue-600 font-black uppercase tracking-widest text-sm mt-2">
{isIt ? 'Client account' : 'Client account'}
</p>
</div>
</div>
</div>
<div className="bg-white rounded-[2.5rem] border border-slate-100 shadow-sm overflow-hidden">
<div className="px-8 py-8 border-b border-slate-100 flex items-start gap-4">
<div className={`w-16 h-16 rounded-[1.5rem] flex items-center justify-center ${verified ? 'bg-emerald-50 text-emerald-600' : 'bg-orange-50 text-orange-500'}`}>
{verified ? <UserCheck size={30} /> : <ShieldAlert size={30} />}
</div>
<div>
<h3 className="text-2xl font-black text-slate-900 uppercase tracking-tight">
{isIt ? 'Informazioni account' : 'Account information'}
</h3>
<p className={`font-black uppercase tracking-widest text-sm mt-2 ${verified ? 'text-emerald-600' : 'text-orange-500'}`}>
{verified ? (isIt ? 'Identità verificata' : 'Identity verified') : (isIt ? 'Identità non verificata' : 'Identity not verified')}
</p>
</div>
</div>
<div className="px-8 py-8 space-y-6">
<InfoRow icon={<User size={20} />} label={isIt ? 'Nome profilo' : 'Profile name'} value={String(user?.name || '-')} />
<InfoRow icon={<Mail size={20} />} label="Email" value={String(user?.email || '-')} />
<InfoRow icon={<Wallet size={20} />} label="ABO Wallet" value={`${Number(user?.credits?.balance || 0)} ${isIt ? 'coin' : 'coins'}`} />
<div className="rounded-[2rem] bg-slate-50 border border-slate-100 p-5">
<p className="text-[10px] font-black uppercase tracking-widest text-slate-400">
{isIt ? 'Ruolo accesso' : 'Access role'}
</p>
<p className="text-lg font-black text-slate-900 mt-1">
{isIt ? 'Cliente' : 'Client'}
</p>
</div>
</div>
</div>
</div>
<div className="bg-white rounded-[2.5rem] border border-slate-100 shadow-sm overflow-hidden">
<div className="px-8 py-8 border-b border-slate-100 flex items-start gap-4">
<div className="w-16 h-16 rounded-[1.5rem] bg-blue-50 text-blue-600 flex items-center justify-center">
<Building2 size={30} />
</div>
<div>
<h3 className="text-2xl font-black text-slate-900 uppercase tracking-tight">
{isIt ? 'Dati cliente e mittente' : 'Client and sender data'}
</h3>
<p className="text-slate-500 font-bold text-sm mt-2">
{isIt ? 'Questi dati saranno usati per invio documenti, raccomandate e comunicazioni tracciate. L’email di accesso non viene modificata da questa sezione.' : 'These data will be used for document delivery and tracked mail. The login email is not changed here.'}
</p>
</div>
</div>
<div className="p-8 grid grid-cols-1 md:grid-cols-2 gap-5">
<Field label="Azienda / Organizzazione" value={form.ragione_sociale} onChange={v => update('ragione_sociale', v)} placeholder="Es. CopyrightChain, LampoVert, Studio Rossi" />
<Field label="Email per invii/ricevute" value={form.email} onChange={v => update('email', v)} placeholder="email@dominio.it" icon={<Mail size={16} />} />
<Field label="Nome" value={form.nome} onChange={v => update('nome', v)} placeholder="Es. Marcello" />
<Field label="Cognome" value={form.cognome} onChange={v => update('cognome', v)} placeholder="Es. Orsini" />
<Field label="Telefono" value={form.telefono} onChange={v => update('telefono', v)} placeholder="+39 ..." icon={<Phone size={16} />} />
<Field label="Codice fiscale" value={form.codice_fiscale} onChange={v => update('codice_fiscale', v.toUpperCase())} placeholder="Codice fiscale" icon={<IdCard size={16} />} />
<Field label="Partita IVA" value={form.partita_iva} onChange={v => update('partita_iva', v)} placeholder="Partita IVA" />
<Field label="Tipo strada" value={form.dug} onChange={v => update('dug', v)} placeholder="via, viale, piazza, corso" />
<Field label="Nome strada" value={form.indirizzo} onChange={v => update('indirizzo', v)} placeholder="Es. degli Armeni" icon={<MapPin size={16} />} />
<Field label="Numero civico" value={form.civico} onChange={v => update('civico', v)} placeholder="Es. 14" />
<Field label="Comune / città" value={form.comune} onChange={v => update('comune', v)} placeholder="Es. Pistoia" />
<Field label="CAP" value={form.cap} onChange={v => update('cap', v)} placeholder="51100" maxLength={5} />
<Field label="Provincia" value={form.provincia} onChange={v => update('provincia', v)} placeholder="PT" maxLength={2} />
<Field label="Nazione" value={form.nazione} onChange={v => update('nazione', v)} placeholder="IT" maxLength={2} />
</div>
<div className="px-8 pb-8">
<button
onClick={saveProfile}
className="w-full md:w-auto px-8 py-4 rounded-2xl bg-slate-900 text-white font-black uppercase tracking-[0.2em] flex items-center justify-center gap-3 shadow-xl"
>
<Save size={18} />
{isIt ? 'Salva dati profilo' : 'Save profile data'}
</button>
</div>
</div>
</div>
);
};
const InfoRow: React.FC<{ icon: React.ReactNode; label: string; value: string }> = ({ icon, label, value }) => (
<div className="flex items-start gap-4">
<div className="w-12 h-12 rounded-2xl bg-slate-50 flex items-center justify-center text-slate-500">
{icon}
</div>
<div>
<p className="text-[10px] font-black uppercase tracking-widest text-slate-400">{label}</p>
<p className="text-lg font-black text-slate-900 break-all">{value}</p>
</div>
</div>
);
const Field: React.FC<{ label: string; value: string; onChange: (value: string) => void; placeholder?: string; maxLength?: number; icon?: React.ReactNode }> = ({ label, value, onChange, placeholder, maxLength, icon }) => (
<div className="space-y-2">
<label className="text-[10px] font-black text-slate-400 uppercase tracking-widest">{label}</label>
<div className="relative">
{icon && <div className="absolute left-4 top-1/2 -translate-y-1/2 text-slate-400">{icon}</div>}
<input
value={value}
onChange={e => onChange(e.target.value)}
placeholder={placeholder}
maxLength={maxLength}
className={`w-full px-5 py-4 rounded-2xl bg-slate-50 border border-slate-100 outline-none font-black text-sm focus:ring-4 focus:ring-blue-500/10 focus:border-blue-200 ${icon ? 'pl-11' : ''}`}
/>
</div>
</div>
);
export default UserProfile;