import React, { useState, useEffect } from 'react';
import { Icons } from '../components/IconSet';
import { authService } from '../services/AuthService';
interface AdminPageProps {
onBack: () => void;
}
const EditNpcCitizenCard: React.FC<{ citizen: any; onUpdate: () => void; onError: (error: string) => void }> = ({ citizen, onUpdate, onError }) => {
const [isEditing, setIsEditing] = useState(false);
const [formData, setFormData] = useState({
username: citizen.username,
tags: citizen.tags.join(', '),
role: citizen.stats.role,
organizationId: citizen.stats.organizationId || '',
storyMarkdown: citizen.storyMarkdown || ''
});
const [loading, setLoading] = useState(false);
const handleSave = async () => {
try {
setLoading(true);
const response = await fetch(`https://vollidioten.ceraticsoft.de/api/players/${citizen.uuid}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({
tags: formData.tags.split(',').map(tag => tag.trim()).filter(tag => tag.length > 0),
organizationId: formData.organizationId || null,
storyMarkdown: formData.storyMarkdown.trim() || null
})
});
if (response.ok) {
setIsEditing(false);
onUpdate();
} else {
const errorData = await response.json();
onError(errorData.error || response.json());
}
} catch (err) {
console.error('Error updating NPC citizen:', err);
onError('Netzwerkfehler');
} finally {
setLoading(false);
}
};
if (isEditing) {
return (
{citizen.username} bearbeiten
setFormData({...formData, tags: e.target.value})}
className="w-full bg-[#0b0b0d] border border-border rounded p-2 text-xs"
placeholder="#Bürger, #Händler"
/>
);
}
return (
NPC
{citizen.username}
{citizen.stats.role}
Level {citizen.stats.level} • {citizen.stats.playtimeHours}h Spielzeit
Tags: {citizen.tags.join(', ') || 'Keine'}
);
};
const ShopManagementModal: React.FC<{
isOpen: boolean;
onClose: () => void;
shopItems: any[];
onUpdate: (items: any[]) => void;
projectId: string;
onError: (error: string) => void;
}> = ({ isOpen, onClose, shopItems, onUpdate, projectId, onError }) => {
const [localItems, setLocalItems] = useState(shopItems || []);
const [editingItem, setEditingItem] = useState(null);
const [isAdding, setIsAdding] = useState(false);
const [loading, setLoading] = useState(false);
const resetForm = () => {
setEditingItem(null);
setIsAdding(false);
};
const handleSaveItem = async (itemData: any) => {
try {
setLoading(true);
let updatedItems = [...localItems];
if (editingItem) {
// Update existing item
const response = await fetch(`https://vollidioten.ceraticsoft.de/api/projects/${projectId}/shop/${editingItem.id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify(itemData)
});
if (response.ok) {
const index = updatedItems.findIndex(item => item.id === editingItem.id);
if (index !== -1) {
updatedItems[index] = itemData;
}
} else {
throw new Error('Fehler beim Aktualisieren');
}
} else {
// Add new item
const response = await fetch(`https://vollidioten.ceraticsoft.de/api/projects/${projectId}/shop`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify(itemData)
});
if (response.ok) {
const newItem = await response.json();
updatedItems.push(newItem.item);
} else {
throw new Error('Fehler beim Hinzufügen');
}
}
setLocalItems(updatedItems);
onUpdate(updatedItems);
resetForm();
} catch (err) {
console.error('Error saving shop item:', err);
onError('Fehler beim Speichern des Artikels');
} finally {
setLoading(false);
}
};
const handleDeleteItem = async (itemId: string) => {
if (!confirm('Artikel wirklich löschen?')) return;
try {
setLoading(true);
const response = await fetch(`https://vollidioten.ceraticsoft.de/api/projects/${projectId}/shop/${itemId}`, {
method: 'DELETE',
credentials: 'include'
});
if (response.ok) {
const updatedItems = localItems.filter(item => item.id !== itemId);
setLocalItems(updatedItems);
onUpdate(updatedItems);
} else {
throw new Error('Fehler beim Löschen');
}
} catch (err) {
console.error('Error deleting shop item:', err);
onError('Fehler beim Löschen des Artikels');
} finally {
setLoading(false);
}
};
if (!isOpen) return null;
return (
Shop-Artikel ({localItems.length})
Verwalte die Produkte dieses Shops
{localItems.length === 0 ? (
Noch keine Artikel im Shop.
) : (
{localItems.map((item) => (
{item.name}
{item.description}
{item.price} {item.currency}
Lager: {item.stock}
))}
)}
{(editingItem || isAdding) && (
)}
);
};
const ShopItemForm: React.FC<{
item?: any;
onSave: (itemData: any) => void;
onCancel: () => void;
loading: boolean;
}> = ({ item, onSave, onCancel, loading }) => {
const [formData, setFormData] = useState({
name: item?.name || '',
description: item?.description || '',
price: item?.price || '',
currency: item?.currency || 'Gold',
stock: item?.stock || '',
type: item?.type || 'item'
});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
onSave({
...formData,
price: parseFloat(formData.price),
stock: parseInt(formData.stock),
id: item?.id // Keep existing ID for updates
});
};
return (
{item ? 'Artikel bearbeiten' : 'Neuer Artikel'}
);
};
const EditNpcCompanyCard: React.FC<{ company: any; npcCitizens: any[]; onUpdate: () => void; onError: (error: string) => void; onOpenShopModal: (projectId: string, shopItems: any[]) => void }> = ({ company, npcCitizens, onUpdate, onError, onOpenShopModal }) => {
const [isEditing, setIsEditing] = useState(false);
const [isEditingShop, setIsEditingShop] = useState(false);
const [isManaging, setIsManaging] = useState(false);
const [shopItems, setShopItems] = useState(company.shopCatalog || []);
const [formData, setFormData] = useState({
title: company.title,
description: company.description || '',
category: company.category,
owner: company.owner,
associatedOrgId: company.associatedOrgId || '',
shopItems: JSON.stringify(company.shopCatalog || [], null, 2)
});
const [loading, setLoading] = useState(false);
const handleShopUpdate = (updatedItems: any[]) => {
setShopItems(updatedItems);
setFormData(prev => ({
...prev,
shopItems: JSON.stringify(updatedItems, null, 2)
}));
};
const handleSave = async () => {
try {
setLoading(true);
// Parse shop items
let shopCatalog = [];
if (formData.shopItems.trim()) {
try {
shopCatalog = JSON.parse(formData.shopItems);
} catch (e) {
onError('Ungültiges JSON-Format für Shop-Artikel');
setLoading(false);
return;
}
}
const response = await fetch(`https://vollidioten.ceraticsoft.de/api/projects/${company.id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({
title: formData.title,
description: formData.description,
category: formData.category,
associatedOrgId: formData.associatedOrgId || null,
shopCatalog: shopCatalog
})
});
if (response.ok) {
setIsEditing(false);
onUpdate();
} else {
const errorData = await response.json();
onError(errorData.error || 'Fehler beim Aktualisieren');
}
} catch (err) {
console.error('Error updating NPC company:', err);
onError('Netzwerkfehler');
} finally {
setLoading(false);
}
};
if (isEditing) {
return (
{company.title} bearbeiten
setFormData({...formData, title: e.target.value})}
className="w-full bg-[#0b0b0d] border border-border rounded p-2 text-xs"
/>
);
}
return (
NPC
{company.title}
{company.category}
Eigentümer: {company.owner}
{company.shopCatalog && company.shopCatalog.length > 0 && (
Shop: {company.shopCatalog.length} Artikel
)}
);
};
const AdminPage: React.FC = ({ onBack }) => {
const [user, setUser] = useState(authService.getUser());
const [activeTab, setActiveTab] = useState<'overview' | 'npcs' | 'create-npc' | 'edit-npcs' | 'manage-admins'>('overview');
const [npcs, setNpcs] = useState({ citizens: [], companies: [] });
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [shopModalState, setShopModalState] = useState<{
isOpen: boolean;
projectId: string;
shopItems: any[];
}>({ isOpen: false, projectId: '', shopItems: [] });
// Check if user is admin
const isAdmin = user?.isAdmin;
useEffect(() => {
const unsub = authService.subscribe(setUser);
return unsub;
}, []);
useEffect(() => {
if (activeTab === 'npcs' && isAdmin) {
loadNpcs();
}
}, [activeTab, isAdmin]);
const loadNpcs = async () => {
try {
setLoading(true);
const response = await fetch('https://vollidioten.ceraticsoft.de/api/admin/npcs', {
credentials: 'include'
});
if (response.ok) {
const data = await response.json();
setNpcs(data);
} else {
setError('Fehler beim Laden der NPCs');
}
} catch (err) {
console.error('Error loading NPCs:', err);
setError('Netzwerkfehler');
} finally {
setLoading(false);
}
};
// NPC Creation Forms
const [citizenForm, setCitizenForm] = useState({
username: '',
tags: '',
role: 'Bürger',
organizationId: '',
storyMarkdown: ''
});
const [companyForm, setCompanyForm] = useState({
title: '',
description: '',
category: 'Enterprise',
owner: '',
associatedOrgId: '',
shopItems: ''
});
const createNpcCitizen = async () => {
if (!citizenForm.username.trim()) {
setError('NPC-Name ist erforderlich');
return;
}
try {
setLoading(true);
setError(null);
const response = await fetch('https://vollidioten.ceraticsoft.de/api/admin/npc-citizen', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({
username: citizenForm.username.trim(),
tags: citizenForm.tags.split(',').map(tag => tag.trim()).filter(tag => tag.length > 0),
role: citizenForm.role,
organizationId: citizenForm.organizationId || null,
storyMarkdown: citizenForm.storyMarkdown.trim() || undefined
})
});
if (response.ok) {
alert('NPC-Bürger erfolgreich erstellt!');
setCitizenForm({
username: '',
tags: '',
role: 'Bürger',
organizationId: '',
storyMarkdown: ''
});
if (activeTab === 'npcs') loadNpcs();
} else {
const errorData = await response.json();
setError(errorData.error || 'Fehler beim Erstellen');
}
} catch (err) {
console.error('Error creating NPC citizen:', err);
setError('Netzwerkfehler');
} finally {
setLoading(false);
}
};
const createNpcCompany = async () => {
if (!companyForm.title.trim() || !companyForm.owner.trim()) {
setError('Titel und NPC-Eigentümer sind erforderlich');
return;
}
try {
setLoading(true);
setError(null);
// Parse shop items if provided
let shopCatalog = [];
if (companyForm.shopItems.trim()) {
try {
shopCatalog = JSON.parse(companyForm.shopItems);
} catch (e) {
setError('Ungültiges JSON-Format für Shop-Artikel');
setLoading(false);
return;
}
}
const response = await fetch('https://vollidioten.ceraticsoft.de/api/admin/npc-company', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({
title: companyForm.title.trim(),
description: companyForm.description.trim() || undefined,
category: companyForm.category,
owner: companyForm.owner.trim(),
associatedOrgId: companyForm.associatedOrgId || null,
shopCatalog: shopCatalog
})
});
if (response.ok) {
alert('NPC-Firma erfolgreich erstellt!');
setCompanyForm({
title: '',
description: '',
category: 'Enterprise',
owner: '',
associatedOrgId: '',
shopItems: ''
});
if (activeTab === 'npcs') loadNpcs();
} else {
const errorData = await response.json();
setError(errorData.error || 'Fehler beim Erstellen');
}
} catch (err) {
console.error('Error creating NPC company:', err);
setError('Netzwerkfehler');
} finally {
setLoading(false);
}
};
if (!isAdmin) {
return (
Admin-Berechtigung erforderlich
Sie haben keine Berechtigung, auf die Admin-Funktionen zuzugreifen.
);
}
return (
Admin-Panel
Verwaltung von NPCs und System-Einstellungen
{/* Navigation Tabs */}
{/* Error Display */}
{error && (
)}
{/* Content */}
{activeTab === 'overview' && (
NPC-Bürger
Erstellte NPCs
{npcs.citizens.length}
NPC-Firmen
Erstellte Unternehmen
{npcs.companies.length}
System-Status
Admin-Berechtigung
Aktiv
)}
{activeTab === 'npcs' && (
{/* NPC Citizens */}
NPC-Bürger ({npcs.citizens.length})
{loading ? (
) : npcs.citizens.length === 0 ? (
Keine NPC-Bürger vorhanden.
) : (
{npcs.citizens.map((citizen: any) => (
NPC
{citizen.username}
{citizen.stats.role}
Level {citizen.stats.level} • {citizen.stats.playtimeHours}h Spielzeit
))}
)}
{/* NPC Companies */}
NPC-Firmen ({npcs.companies.length})
{npcs.companies.length === 0 ? (
Keine NPC-Firmen vorhanden.
) : (
{npcs.companies.map((company: any) => (
{company.title}
{company.category}
{company.description}
Eigentümer: {company.owner} • Gegründet: {company.foundedDate}
{company.shopCatalog && company.shopCatalog.length > 0 && (
Shop: {company.shopCatalog.length} Artikel
)}
))}
)}
)}
{activeTab === 'edit-npcs' && (
{/* Edit NPC Citizens */}
NPC-Bürger bearbeiten
{npcs.citizens.length === 0 ? (
Keine NPC-Bürger vorhanden.
) : (
{npcs.citizens.map((citizen: any) => (
loadNpcs()}
onError={setError}
/>
))}
)}
{/* Edit NPC Companies */}
NPC-Firmen bearbeiten
{npcs.companies.length === 0 ? (
Keine NPC-Firmen vorhanden.
) : (
{npcs.companies.map((company: any) => (
loadNpcs()}
onError={setError}
onOpenShopModal={(projectId, shopItems) =>
setShopModalState({ isOpen: true, projectId, shopItems })
}
/>
))}
)}
)}
{activeTab === 'create-npc' && (
{/* Create NPC Citizen */}
NPC-Bürger erstellen
setCitizenForm({...citizenForm, username: e.target.value})}
className="w-full bg-[#0b0b0d] border border-border rounded p-2 text-sm"
placeholder="z.B. Händler Karl"
/>
setCitizenForm({...citizenForm, tags: e.target.value})}
className="w-full bg-[#0b0b0d] border border-border rounded p-2 text-sm"
placeholder="z.B. #Bürger, #Händler, #Fleischer"
/>
{/* Create NPC Company */}
NPC-Firma erstellen
setCompanyForm({...companyForm, title: e.target.value})}
className="w-full bg-[#0b0b0d] border border-border rounded p-2 text-sm"
placeholder="z.B. Karls Fleischerei"
/>
)}
{/* Shop Management Modal */}
setShopModalState({ isOpen: false, projectId: '', shopItems: [] })}
shopItems={shopModalState.shopItems}
onUpdate={(updatedItems) => {
setShopModalState(prev => ({ ...prev, shopItems: updatedItems }));
// Trigger a reload to show updated data
loadNpcs();
}}
projectId={shopModalState.projectId}
onError={setError}
/>
);
};
export default AdminPage;