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:
Lars Behrends
2025-12-30 13:56:00 +01:00
parent 5eb2eca110
commit c6ad8a92ec
14 changed files with 2539 additions and 102 deletions

View File

@@ -1,6 +1,9 @@
import React, { useState, useEffect } from 'react';
import { Icons } from '../components/IconSet';
import { authService } from '../services/AuthService';
import NpcBannerManagementModal from '../components/NpcBannerManagementModal';
import NpcLogoManagementModal from '../components/NpcLogoManagementModal';
import NpcGalleryManagementModal from '../components/NpcGalleryManagementModal';
interface AdminPageProps {
onBack: () => void;
@@ -453,6 +456,9 @@ const EditNpcCompanyCard: React.FC<{ company: any; npcCitizens: any[]; onUpdate:
const [isEditingShop, setIsEditingShop] = useState(false);
const [isManaging, setIsManaging] = useState(false);
const [shopItems, setShopItems] = useState<any[]>(company.shopCatalog || []);
const [bannerModalOpen, setBannerModalOpen] = useState(false);
const [logoModalOpen, setLogoModalOpen] = useState(false);
const [galleryModalOpen, setGalleryModalOpen] = useState(false);
const [formData, setFormData] = useState({
title: company.title,
description: company.description || '',
@@ -606,42 +612,89 @@ const EditNpcCompanyCard: React.FC<{ company: any; npcCitizens: any[]; onUpdate:
}
return (
<div className="bg-surfaceHighlight/30 border border-border rounded-lg p-4">
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-purple-500/20 rounded flex items-center justify-center text-xs font-bold text-purple-400">
NPC
<>
<div className="bg-surfaceHighlight/30 border border-border rounded-lg p-4">
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-purple-500/20 rounded flex items-center justify-center text-xs font-bold text-purple-400">
NPC
</div>
<div>
<h4 className="font-medium text-white">{company.title}</h4>
<p className="text-xs text-textMuted">{company.category}</p>
</div>
</div>
<div>
<h4 className="font-medium text-white">{company.title}</h4>
<p className="text-xs text-textMuted">{company.category}</p>
<div className="flex gap-2">
<button
onClick={() => setBannerModalOpen(true)}
className="text-blue-400 hover:text-blue-300 text-sm"
title="Banner bearbeiten"
>
<Icons.Layers className="w-4 h-4" />
</button>
<button
onClick={() => setLogoModalOpen(true)}
className="text-green-400 hover:text-green-300 text-sm"
title="Logo bearbeiten"
>
<Icons.Shield className="w-4 h-4" />
</button>
<button
onClick={() => setGalleryModalOpen(true)}
className="text-orange-400 hover:text-orange-300 text-sm"
title="Portfolio verwalten"
>
<Icons.Box className="w-4 h-4" />
</button>
<button
onClick={() => onOpenShopModal(company.id, company.shopCatalog || [])}
className="text-accentInfo hover:text-accentInfo/80 text-sm"
title="Shop verwalten"
>
<Icons.ShoppingBag className="w-4 h-4" />
</button>
<button
onClick={() => setIsEditing(true)}
className="text-purple-400 hover:text-purple-300 text-sm"
>
<Icons.Edit className="w-4 h-4" />
</button>
</div>
</div>
<div className="flex gap-2">
<button
onClick={() => onOpenShopModal(company.id, company.shopCatalog || [])}
className="text-accentInfo hover:text-accentInfo/80 text-sm"
title="Shop verwalten"
>
<Icons.ShoppingBag className="w-4 h-4" />
</button>
<button
onClick={() => setIsEditing(true)}
className="text-purple-400 hover:text-purple-300 text-sm"
>
<Icons.Edit className="w-4 h-4" />
</button>
</div>
<div className="text-xs text-textMuted">
Eigentümer: {company.owner}
</div>
<div className="text-xs text-textMuted">
Eigentümer: {company.owner}
{company.shopCatalog && company.shopCatalog.length > 0 && (
<div className="text-xs text-accentInfo mt-1">
Shop: {company.shopCatalog.length} Artikel
</div>
)}
</div>
{company.shopCatalog && company.shopCatalog.length > 0 && (
<div className="text-xs text-accentInfo mt-1">
Shop: {company.shopCatalog.length} Artikel
</div>
)}
</div>
{/* Image Management Modals */}
<NpcBannerManagementModal
isOpen={bannerModalOpen}
onClose={() => setBannerModalOpen(false)}
projectId={company.id}
currentBannerUrl={company.bannerUrl}
onUpdate={() => onUpdate()}
/>
<NpcLogoManagementModal
isOpen={logoModalOpen}
onClose={() => setLogoModalOpen(false)}
projectId={company.id}
currentLogoUrl={company.logoUrl}
onUpdate={() => onUpdate()}
/>
<NpcGalleryManagementModal
isOpen={galleryModalOpen}
onClose={() => setGalleryModalOpen(false)}
projectId={company.id}
onUpdate={() => onUpdate()}
/>
</>
);
};
@@ -666,13 +719,24 @@ const AdminPage: React.FC<AdminPageProps> = ({ onBack }) => {
return unsub;
}, []);
useEffect(() => {
if (activeTab === 'npcs' && isAdmin) {
useEffect(() => {
loadNpcs();
}
if (activeTab === 'cities' && isAdmin) {
loadCities();
}
}, [isAdmin]);
// Auto-refresh data every 30 seconds when on relevant tabs
useEffect(() => {
if (!isAdmin) return;
const interval = setInterval(() => {
if (activeTab === 'edit-npcs' || activeTab === 'create-npc') {
loadNpcs();
} else if (activeTab === 'cities' || activeTab === 'create-city') {
loadCities();
}
}, 30000); // 30 seconds
return () => clearInterval(interval);
}, [activeTab, isAdmin]);
const loadNpcs = async () => {
@@ -1677,7 +1741,6 @@ const AdminPage: React.FC<AdminPageProps> = ({ onBack }) => {
});
if (response.ok) {
alert('Banner erfolgreich aktualisiert!');
loadCities(); // Reload to show updated image
} else {
setError('Fehler beim Hochladen des Banners');
@@ -1727,7 +1790,6 @@ const AdminPage: React.FC<AdminPageProps> = ({ onBack }) => {
});
if (response.ok) {
alert('Logo erfolgreich aktualisiert!');
loadCities(); // Reload to show updated image
} else {
setError('Fehler beim Hochladen des Logos');
@@ -1781,7 +1843,6 @@ const AdminPage: React.FC<AdminPageProps> = ({ onBack }) => {
});
if (response.ok) {
alert('Stadt erfolgreich aktualisiert!');
setEditingCity(null);
loadCities();
} else {