fix: Applied fix for name, the oracle team sharing, sentinel client list visibility
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user