fix: Applied fix for name, the oracle team sharing, sentinel client list visibility

This commit is contained in:
Sagnik
2026-04-19 17:07:12 +05:30
parent 269591a3cc
commit d886e4a669
20 changed files with 940 additions and 109 deletions

View File

@@ -6,7 +6,7 @@ import {
clearVelocityToken,
loginVelocity,
normalizeVelocityRole,
type VelocityUserProfile,
resolveVelocityFirstName,
} from '@/lib/velocityPlatformClient';
export function LoginScreen() {
@@ -22,9 +22,18 @@ export function LoginScreen() {
setError('');
try {
const me = await loginVelocity(email.trim(), password);
const fallbackEmail = me.email?.trim() || email.trim();
const fallbackFullName = me.full_name?.trim() || undefined;
login({
id: me.user_id,
name: resolveVelocityDisplayName(me),
name: resolveVelocityFirstName({
...me,
email: fallbackEmail || null,
full_name: fallbackFullName || null,
}),
fullName: fallbackFullName,
email: fallbackEmail || undefined,
avatar: me.avatar_url?.trim() || undefined,
role: normalizeVelocityRole(me.role),
});
} catch (err) {
@@ -172,17 +181,3 @@ export function LoginScreen() {
</div>
);
}
function resolveVelocityDisplayName(profile: VelocityUserProfile) {
const fullName = profile.full_name?.trim();
if (fullName) {
return fullName;
}
const email = profile.email?.trim();
if (email) {
return email;
}
return profile.user_id;
}

View File

@@ -1,4 +1,4 @@
import { useState } from 'react';
import { useRef, useState, type ChangeEvent } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import {
User,
@@ -15,13 +15,20 @@ import {
Check,
ChevronDown,
LogOut,
Pencil,
type LucideIcon,
} from 'lucide-react';
import { useStore } from '@/store/useStore';
import { useCurrency, CURRENCY_OPTIONS } from '@/store/useCurrencyStore';
import type { CurrencyCode } from '@/store/useCurrencyStore';
import { API_URL } from '@/lib/api';
import { clearVelocityToken, getVelocityToken, normalizeVelocityRole } from '@/lib/velocityPlatformClient';
import {
clearVelocityToken,
getVelocityToken,
normalizeVelocityRole,
resolveVelocityFirstName,
uploadVelocityAvatar,
} from '@/lib/velocityPlatformClient';
// ── Design tokens (matching inventory glassmorphism) ─────────────────────────
const GLASS = {
@@ -361,35 +368,104 @@ function CompanionSurfacesCard() {
// ── Profile ──────────────────────────────────────────────────────────────────
function ProfileSettings() {
const { user } = useStore();
const initials = user?.name.split(' ').map((n) => n[0]).join('') ?? 'AU';
const { user, login } = useStore();
const fileInputRef = useRef<HTMLInputElement | null>(null);
const [avatarError, setAvatarError] = useState<string | null>(null);
const [isUploadingAvatar, setIsUploadingAvatar] = useState(false);
const displayName = user?.fullName?.trim() || user?.name?.trim() || user?.email?.trim() || user?.id || 'Authenticated User';
const firstName = resolveVelocityFirstName({
user_id: user?.id ?? '',
full_name: user?.fullName ?? user?.name ?? null,
email: user?.email ?? null,
});
const initials = (user?.fullName || user?.name || user?.email || 'AU')
.split(/[\s@._-]+/)
.filter(Boolean)
.slice(0, 2)
.map((n) => n[0]?.toUpperCase() ?? '')
.join('') || 'AU';
const roleLabel = normalizeVelocityRole(user?.role)
.toLowerCase()
.split('_')
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
.join(' ') || 'Authenticated User';
const handleAvatarUpload = async (event: ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (!file || !user) {
return;
}
setAvatarError(null);
setIsUploadingAvatar(true);
try {
const { avatar_url } = await uploadVelocityAvatar(file);
login({
...user,
avatar: avatar_url,
});
} catch (error) {
setAvatarError(error instanceof Error ? error.message : 'Failed to upload profile picture.');
} finally {
setIsUploadingAvatar(false);
if (event.target) {
event.target.value = '';
}
}
};
return (
<GlassCard delay={0.1}>
<SectionHeader icon={User} title="Profile" />
<div className="px-6 pb-6">
<div className="flex items-center gap-4 mb-5 p-4 rounded-xl" style={INNER_SURFACE}>
<div
className="w-14 h-14 rounded-full flex items-center justify-center flex-shrink-0 text-white text-lg font-bold"
style={{ background: 'hsl(var(--accent))', boxShadow: '0 0 20px hsl(var(--accent) / 0.3)' }}
>
{initials}
<div className="relative flex-shrink-0">
{user?.avatar ? (
<img
src={user.avatar}
alt={displayName}
className="w-14 h-14 rounded-full object-cover border border-white/10 shadow-[0_0_20px_rgba(59,130,246,0.2)]"
/>
) : (
<div
className="w-14 h-14 rounded-full flex items-center justify-center text-white text-lg font-bold"
style={{ background: 'hsl(var(--accent))', boxShadow: '0 0 20px hsl(var(--accent) / 0.3)' }}
>
{initials}
</div>
)}
<button
type="button"
onClick={() => fileInputRef.current?.click()}
className="absolute -right-1 -top-1 w-6 h-6 rounded-full flex items-center justify-center border border-white/10 bg-zinc-900/90 text-zinc-200 hover:text-white hover:bg-zinc-800 transition-colors"
title="Update profile picture"
>
<Pencil className="w-3 h-3" />
</button>
</div>
<div>
<p className="text-white font-semibold">{user?.name}</p>
<div className="min-w-0 flex-1">
<p className="text-white font-semibold truncate">{displayName}</p>
<p className="text-xs mt-0.5" style={{ color: 'hsl(var(--muted-fg))' }}>
{roleLabel}
</p>
{isUploadingAvatar && (
<p className="mt-3 text-xs text-zinc-400">Uploading profile picture...</p>
)}
</div>
<input
ref={fileInputRef}
type="file"
accept="image/png,image/jpeg,image/webp,image/jpg"
className="hidden"
onChange={handleAvatarUpload}
/>
</div>
{avatarError && (
<p className="mb-4 text-xs text-red-400">{avatarError}</p>
)}
<div className="space-y-0 -mx-6">
<SettingsRow label="Authenticated Name" description="Resolved from the active Velocity session">
<span className="text-sm text-white">{user?.name ?? 'Unavailable'}</span>
<span className="text-sm text-white">{firstName || 'Unavailable'}</span>
</SettingsRow>
<SettingsRow label="User ID" description="Backend principal identifier">
<span className="text-sm font-mono text-white">{user?.id ?? 'Unavailable'}</span>