mirror of
https://github.com/ceratic/project_vollidioten_website.git
synced 2026-05-14 00:16:47 +02:00
Refactor CityProfile and PlayerProfile components for improved data fetching and error handling; add NPC management modals for banner, gallery, and logo with enhanced user experience and error feedback.
This commit is contained in:
@@ -21,6 +21,19 @@ const PlayerProfile: React.FC = () => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [activeTab, setActiveTab] = useState<'story' | 'stats' | 'projects'>('story');
|
||||
|
||||
const handleNavigateToOrg = (orgId: string) => {
|
||||
const org = dbService.getOrg(orgId);
|
||||
if (org?.type === 'City') {
|
||||
navigate(`/cities/${orgId}`);
|
||||
} else {
|
||||
navigate(`/organizations/${orgId}`);
|
||||
}
|
||||
};
|
||||
|
||||
const handleNavigateToProject = (projectId: string) => {
|
||||
navigate(`/projects/${projectId}`);
|
||||
};
|
||||
|
||||
// Is this the logged-in user's profile?
|
||||
const isOwner = currentUser?.linkedPlayerUuid === player?.uuid;
|
||||
const playerOrg = player ? dbService.getOrg(player.organizationId || '') : null;
|
||||
@@ -36,17 +49,37 @@ const PlayerProfile: React.FC = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load player data
|
||||
const playerData = dbService.getPlayer(id);
|
||||
if (playerData) {
|
||||
setPlayer(playerData);
|
||||
} else {
|
||||
navigate('/players');
|
||||
return;
|
||||
}
|
||||
// Load player data - try to fetch from API first, fallback to cache
|
||||
const loadPlayerData = async () => {
|
||||
try {
|
||||
const playerData = await dbService.fetchPlayer(id);
|
||||
if (playerData) {
|
||||
setPlayer(playerData);
|
||||
} else {
|
||||
// Fallback to cache if API fails
|
||||
const cachedPlayer = dbService.getPlayer(id);
|
||||
if (cachedPlayer) {
|
||||
setPlayer(cachedPlayer);
|
||||
} else {
|
||||
navigate('/players');
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading player data:', error);
|
||||
// Fallback to cache
|
||||
const cachedPlayer = dbService.getPlayer(id);
|
||||
if (cachedPlayer) {
|
||||
setPlayer(cachedPlayer);
|
||||
} else {
|
||||
navigate('/players');
|
||||
return;
|
||||
}
|
||||
}
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
console.log(playerData);
|
||||
setLoading(false);
|
||||
loadPlayerData();
|
||||
}, [id, navigate]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -252,27 +285,38 @@ const PlayerProfile: React.FC = () => {
|
||||
|
||||
<div className="bg-surface border border-border rounded-xl p-4 shadow-card">
|
||||
<h3 className="text-xs font-bold uppercase tracking-wider text-textMuted mb-3">Zugehörigkeit</h3>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`w-10 h-10 rounded flex items-center justify-center text-lg font-bold border border-white/5 ${
|
||||
playerOrg
|
||||
? (playerOrg.type === 'City' ? 'bg-blue-500/10 text-blue-400' :
|
||||
playerOrg.type === 'Guild' ? 'bg-amber-500/10 text-amber-400' :
|
||||
'bg-purple-500/10 text-purple-400')
|
||||
: 'bg-surfaceHighlight text-textMuted'
|
||||
}`}>
|
||||
{playerOrg ? playerOrg.name.charAt(0) : <Icons.Map className="w-5 h-5 opacity-50" />}
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-medium text-textMain">{player.minecraftStats?.role || 'Unbekannt'}</div>
|
||||
<div className="text-xs text-textMuted">
|
||||
{playerOrg ? (
|
||||
<span className="group-hover:text-accentInfo transition-colors">{playerOrg.name}</span>
|
||||
) : (
|
||||
'Freiberufler / Keine Zugehörigkeit'
|
||||
)}
|
||||
{playerOrg ? (
|
||||
<div
|
||||
onClick={() => handleNavigateToOrg(playerOrg.id)}
|
||||
className="flex items-center gap-3 cursor-pointer hover:bg-surfaceHighlight/50 -m-1 p-1 rounded transition-colors group"
|
||||
>
|
||||
<div className={`w-10 h-10 rounded flex items-center justify-center text-lg font-bold border border-white/5 ${
|
||||
playerOrg.type === 'City' ? 'bg-blue-500/10 text-blue-400' :
|
||||
playerOrg.type === 'Guild' ? 'bg-amber-500/10 text-amber-400' :
|
||||
'bg-purple-500/10 text-purple-400'
|
||||
}`}>
|
||||
{playerOrg.name.charAt(0)}
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-medium text-textMain group-hover:text-accentInfo transition-colors">
|
||||
{playerOrg.name}
|
||||
</div>
|
||||
<div className="text-xs text-textMuted">
|
||||
{player.minecraftStats?.role || 'Unbekannt'} • Klick zum Anzeigen
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded flex items-center justify-center text-lg font-bold border border-white/5 bg-surfaceHighlight text-textMuted">
|
||||
<Icons.Map className="w-5 h-5 opacity-50" />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-medium text-textMain">Freiberufler</div>
|
||||
<div className="text-xs text-textMuted">Keine Zugehörigkeit</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -437,9 +481,15 @@ const PlayerProfile: React.FC = () => {
|
||||
<div>
|
||||
<div className="grid grid-cols-1 gap-4">
|
||||
{ownedProjects.map((project) => (
|
||||
<div key={project.id} className="bg-surfaceHighlight/30 border border-border rounded-lg p-4 hover:border-accentInfo/50 transition-all">
|
||||
<div
|
||||
key={project.id}
|
||||
onClick={() => handleNavigateToProject(project.id)}
|
||||
className="bg-surfaceHighlight/30 border border-border rounded-lg p-4 hover:border-accentInfo/50 transition-all cursor-pointer group"
|
||||
>
|
||||
<div className="flex justify-between items-start mb-2">
|
||||
<h4 className="font-medium text-textMain">{project.title}</h4>
|
||||
<h4 className="font-medium text-textMain group-hover:text-accentInfo transition-colors">
|
||||
{project.title}
|
||||
</h4>
|
||||
<span className={`text-xs px-2 py-1 rounded border ${
|
||||
project.status === 'active' ? 'bg-green-500/10 text-green-400 border-green-500/20' :
|
||||
project.status === 'recruiting' ? 'bg-blue-500/10 text-blue-400 border-blue-500/20' :
|
||||
@@ -468,6 +518,9 @@ const PlayerProfile: React.FC = () => {
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs text-accentInfo mt-2 opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
Klick zum Anzeigen →
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user