Add components for Act management and integrate Electron setup
This commit is contained in:
327
components/book/settings/objects/page.tsx
Normal file
327
components/book/settings/objects/page.tsx
Normal file
@@ -0,0 +1,327 @@
|
||||
'use client';
|
||||
import React, {useState} from 'react';
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
|
||||
import {faArrowLeft} from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
interface RelatedItem {
|
||||
name: string;
|
||||
type: string;
|
||||
description: string;
|
||||
history: string;
|
||||
}
|
||||
|
||||
interface Item {
|
||||
id: number | null;
|
||||
name: string;
|
||||
description: string;
|
||||
history: string;
|
||||
location: string;
|
||||
ownedBy: string;
|
||||
functionality: string;
|
||||
image: string;
|
||||
relatedItems: RelatedItem[];
|
||||
}
|
||||
|
||||
const initialItemState: Item = {
|
||||
id: null,
|
||||
name: '',
|
||||
description: '',
|
||||
history: '',
|
||||
location: '',
|
||||
ownedBy: '',
|
||||
functionality: '',
|
||||
image: '',
|
||||
relatedItems: [],
|
||||
};
|
||||
|
||||
export default function Items() {
|
||||
const [items, setItems] = useState<Item[]>([
|
||||
{
|
||||
id: 1,
|
||||
name: 'Sword of Destiny',
|
||||
description: 'A powerful sword',
|
||||
history: 'Forged in the ancient times...',
|
||||
location: 'Castle',
|
||||
ownedBy: 'John Doe',
|
||||
functionality: 'Cuts through anything',
|
||||
image: 'https://via.placeholder.com/150',
|
||||
relatedItems: []
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Shield of Valor',
|
||||
description: 'An unbreakable shield',
|
||||
history: 'Used by the legendary hero...',
|
||||
location: 'Fortress',
|
||||
ownedBy: 'Jane Doe',
|
||||
functionality: 'Deflects any attack',
|
||||
image: 'https://via.placeholder.com/150',
|
||||
relatedItems: []
|
||||
}
|
||||
]);
|
||||
|
||||
const [selectedItem, setSelectedItem] = useState<Item | null>(null);
|
||||
const [searchQuery, setSearchQuery] = useState<string>('');
|
||||
const [newItem, setNewItem] = useState<Item>(initialItemState);
|
||||
const [newRelatedItem, setNewRelatedItem] = useState<RelatedItem>({
|
||||
name: '',
|
||||
type: '',
|
||||
description: '',
|
||||
history: ''
|
||||
});
|
||||
|
||||
const filteredItems = items.filter(
|
||||
(item) =>
|
||||
item.name.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
);
|
||||
|
||||
const handleItemClick = (item: Item) => {
|
||||
setSelectedItem(item);
|
||||
};
|
||||
|
||||
const handleAddItem = () => {
|
||||
setSelectedItem(newItem);
|
||||
};
|
||||
|
||||
const handleSaveItem = () => {
|
||||
if (selectedItem) {
|
||||
if (selectedItem.id === null) {
|
||||
setItems([...items, {...selectedItem, id: items.length + 1}]);
|
||||
} else {
|
||||
setItems(items.map((item) => (item.id === selectedItem.id ? selectedItem : item)));
|
||||
}
|
||||
setSelectedItem(null);
|
||||
setNewItem(initialItemState);
|
||||
}
|
||||
};
|
||||
|
||||
const handleItemChange = (key: keyof Item, value: string) => {
|
||||
if (selectedItem) {
|
||||
setSelectedItem({...selectedItem, [key]: value});
|
||||
}
|
||||
};
|
||||
|
||||
const handleElementChange = (section: keyof Item, index: number, key: keyof RelatedItem, value: string) => {
|
||||
if (selectedItem) {
|
||||
const updatedSection = [...(selectedItem[section] as RelatedItem[])];
|
||||
updatedSection[index][key] = value;
|
||||
setSelectedItem({...selectedItem, [section]: updatedSection});
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddElement = (section: keyof Item, value: RelatedItem) => {
|
||||
if (selectedItem) {
|
||||
const updatedSection = [...(selectedItem[section] as RelatedItem[]), value];
|
||||
setSelectedItem({...selectedItem, [section]: updatedSection});
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveElement = (section: keyof Item, index: number) => {
|
||||
if (selectedItem) {
|
||||
const updatedSection = (selectedItem[section] as RelatedItem[]).filter((_, i) => i !== index);
|
||||
setSelectedItem({...selectedItem, [section]: updatedSection});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<main className="flex-grow p-8 overflow-y-auto">
|
||||
{selectedItem ? (
|
||||
<div>
|
||||
<div className="flex justify-between sticky top-0 z-10 bg-gray-900 py-4">
|
||||
<button onClick={() => setSelectedItem(null)}
|
||||
className="flex items-center gap-2 text-text-primary bg-secondary/50 hover:bg-secondary px-4 py-2 rounded-xl transition-all duration-200 hover:scale-105 shadow-md">
|
||||
<FontAwesomeIcon icon={faArrowLeft} className="mr-2 w-5 h-5"/> Back
|
||||
</button>
|
||||
<h2 className="text-3xl font-['ADLaM_Display'] text-center text-text-primary">{selectedItem.name}</h2>
|
||||
<button onClick={handleSaveItem}
|
||||
className="bg-primary hover:bg-primary-dark text-text-primary font-semibold py-2.5 px-6 rounded-xl shadow-md hover:shadow-lg hover:scale-105 transition-all duration-200">
|
||||
Save Item
|
||||
</button>
|
||||
</div>
|
||||
<div className="bg-gray-700 rounded-lg p-8 shadow-lg space-y-4">
|
||||
<div>
|
||||
<label className="block text-white mb-2" htmlFor="name">Name</label>
|
||||
<input
|
||||
type="text"
|
||||
id="name"
|
||||
value={selectedItem.name}
|
||||
onChange={(e) => handleItemChange('name', e.target.value)}
|
||||
className="w-full px-4 py-2.5 rounded-xl bg-secondary/50 text-text-primary border border-secondary/50 outline-none hover:bg-secondary hover:border-secondary focus:border-primary focus:ring-4 focus:ring-primary/20 transition-all duration-200"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-white mb-2" htmlFor="description">Description</label>
|
||||
<textarea
|
||||
id="description"
|
||||
rows={4}
|
||||
value={selectedItem.description}
|
||||
onChange={(e) => handleItemChange('description', e.target.value)}
|
||||
className="w-full px-4 py-2.5 rounded-xl bg-secondary/50 text-text-primary border border-secondary/50 outline-none hover:bg-secondary hover:border-secondary focus:border-primary focus:ring-4 focus:ring-primary/20 transition-all duration-200"
|
||||
></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-white mb-2" htmlFor="history">History</label>
|
||||
<textarea
|
||||
id="history"
|
||||
rows={4}
|
||||
value={selectedItem.history}
|
||||
onChange={(e) => handleItemChange('history', e.target.value)}
|
||||
className="w-full px-4 py-2.5 rounded-xl bg-secondary/50 text-text-primary border border-secondary/50 outline-none hover:bg-secondary hover:border-secondary focus:border-primary focus:ring-4 focus:ring-primary/20 transition-all duration-200"
|
||||
></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-white mb-2" htmlFor="location">Location</label>
|
||||
<select
|
||||
id="location"
|
||||
value={selectedItem.location}
|
||||
onChange={(e) => handleItemChange('location', e.target.value)}
|
||||
className="w-full px-4 py-2.5 rounded-xl bg-secondary/50 text-text-primary border border-secondary/50 outline-none hover:bg-secondary hover:border-secondary focus:border-primary focus:ring-4 focus:ring-primary/20 transition-all duration-200"
|
||||
>
|
||||
<option value="">Select Location</option>
|
||||
<option value="Castle">Castle</option>
|
||||
<option value="Fortress">Fortress</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-white mb-2" htmlFor="ownedBy">Owned By</label>
|
||||
<select
|
||||
id="ownedBy"
|
||||
value={selectedItem.ownedBy}
|
||||
onChange={(e) => handleItemChange('ownedBy', e.target.value)}
|
||||
className="w-full px-4 py-2.5 rounded-xl bg-secondary/50 text-text-primary border border-secondary/50 outline-none hover:bg-secondary hover:border-secondary focus:border-primary focus:ring-4 focus:ring-primary/20 transition-all duration-200"
|
||||
>
|
||||
<option value="">Select Owner</option>
|
||||
{items.map((item) => (
|
||||
<option key={item.id} value={item.name}>{item.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-white mb-2" htmlFor="functionality">Functionality</label>
|
||||
<textarea
|
||||
id="functionality"
|
||||
rows={4}
|
||||
value={selectedItem.functionality}
|
||||
onChange={(e) => handleItemChange('functionality', e.target.value)}
|
||||
className="w-full px-4 py-2.5 rounded-xl bg-secondary/50 text-text-primary border border-secondary/50 outline-none hover:bg-secondary hover:border-secondary focus:border-primary focus:ring-4 focus:ring-primary/20 transition-all duration-200"
|
||||
></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-white mb-2" htmlFor="image">Image URL</label>
|
||||
<input
|
||||
type="text"
|
||||
id="image"
|
||||
value={selectedItem.image}
|
||||
onChange={(e) => handleItemChange('image', e.target.value)}
|
||||
className="w-full px-4 py-2.5 rounded-xl bg-secondary/50 text-text-primary border border-secondary/50 outline-none hover:bg-secondary hover:border-secondary focus:border-primary focus:ring-4 focus:ring-primary/20 transition-all duration-200"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-gray-700 rounded-lg p-8 shadow-lg space-y-4 mt-4">
|
||||
<h3 className="text-2xl font-['ADLaM_Display'] text-text-primary">Related Items</h3>
|
||||
<div className="space-y-2">
|
||||
{selectedItem.relatedItems.map((relatedItem, index) => (
|
||||
<details key={index}
|
||||
className="bg-secondary/30 rounded-xl mb-4 p-4 shadow-sm hover:shadow-md transition-all duration-200">
|
||||
<summary className="text-lg text-white cursor-pointer">{relatedItem.name}</summary>
|
||||
<div className="mt-2">
|
||||
<label className="block text-white mb-2"
|
||||
htmlFor={`related-item-description-${relatedItem.name}`}>Description</label>
|
||||
<textarea
|
||||
id={`related-item-description-${relatedItem.name}`}
|
||||
rows={3}
|
||||
value={relatedItem.description}
|
||||
onChange={(e) => handleElementChange('relatedItems', index, 'description', e.target.value)}
|
||||
className="w-full px-4 py-2.5 rounded-xl bg-secondary/50 text-text-primary border border-secondary/50 outline-none hover:bg-secondary hover:border-secondary focus:border-primary focus:ring-4 focus:ring-primary/20 transition-all duration-200"
|
||||
></textarea>
|
||||
<label className="block text-white mb-2 mt-4"
|
||||
htmlFor={`related-item-history-${relatedItem.name}`}>History</label>
|
||||
<textarea
|
||||
id={`related-item-history-${relatedItem.name}`}
|
||||
rows={3}
|
||||
value={relatedItem.history}
|
||||
onChange={(e) => handleElementChange('relatedItems', index, 'history', e.target.value)}
|
||||
className="w-full px-4 py-2.5 rounded-xl bg-secondary/50 text-text-primary border border-secondary/50 outline-none hover:bg-secondary hover:border-secondary focus:border-primary focus:ring-4 focus:ring-primary/20 transition-all duration-200"
|
||||
></textarea>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemoveElement('relatedItems', index)}
|
||||
className="bg-error/90 hover:bg-error text-text-primary font-semibold py-2 px-4 rounded-xl shadow-md hover:shadow-lg hover:scale-105 transition-all duration-200 mt-2"
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
</details>
|
||||
))}
|
||||
<div className="flex space-x-2 items-center mt-2">
|
||||
<select
|
||||
className="w-full px-4 py-2.5 rounded-xl bg-secondary/50 text-text-primary border border-secondary/50 outline-none hover:bg-secondary hover:border-secondary focus:border-primary focus:ring-4 focus:ring-primary/20 transition-all duration-200"
|
||||
onChange={(e) => setNewRelatedItem({...newRelatedItem, name: e.target.value})}
|
||||
value={newRelatedItem.name}
|
||||
>
|
||||
<option value="">Select Related Item</option>
|
||||
{items.map((item) => (
|
||||
<option key={item.id} value={item.name}>{item.name}</option>
|
||||
))}
|
||||
</select>
|
||||
<select
|
||||
className="w-full px-4 py-2.5 rounded-xl bg-secondary/50 text-text-primary border border-secondary/50 outline-none hover:bg-secondary hover:border-secondary focus:border-primary focus:ring-4 focus:ring-primary/20 transition-all duration-200"
|
||||
onChange={(e) => setNewRelatedItem({...newRelatedItem, type: e.target.value})}
|
||||
value={newRelatedItem.type}
|
||||
>
|
||||
<option value="">Relation Type</option>
|
||||
<option value="Related">Related</option>
|
||||
<option value="Similar">Similar</option>
|
||||
{/* Add more relation types as needed */}
|
||||
</select>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
handleAddElement('relatedItems', {...newRelatedItem});
|
||||
setNewRelatedItem({name: '', type: '', description: '', history: ''});
|
||||
}}
|
||||
className="bg-primary hover:bg-primary-dark text-text-primary font-semibold py-2.5 px-5 rounded-xl shadow-md hover:shadow-lg hover:scale-105 transition-all duration-200"
|
||||
>
|
||||
Add Related Item
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<div className="flex justify-between sticky top-0 z-10 bg-gray-900 py-4">
|
||||
<input
|
||||
type="text"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="w-full px-4 py-2.5 rounded-xl bg-secondary/50 text-text-primary border border-secondary/50 outline-none hover:bg-secondary hover:border-secondary focus:border-primary focus:ring-4 focus:ring-primary/20 transition-all duration-200"
|
||||
placeholder="Search Items"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleAddItem}
|
||||
className="bg-primary hover:bg-primary-dark text-text-primary font-semibold py-2.5 px-5 rounded-xl ml-4 shadow-md hover:shadow-lg hover:scale-105 transition-all duration-200"
|
||||
>
|
||||
Add New Item
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<h2 className="text-4xl font-['ADLaM_Display'] text-text-primary mb-6">Items</h2>
|
||||
<div className="flex flex-wrap space-x-4">
|
||||
{filteredItems.map((item) => (
|
||||
<div key={item.id} onClick={() => handleItemClick(item)}
|
||||
className="cursor-pointer bg-tertiary/90 backdrop-blur-sm p-4 rounded-xl shadow-lg hover:shadow-xl hover:scale-105 transition-all duration-200 border border-secondary/50">
|
||||
<img src={item.image || 'https://via.placeholder.com/150'} alt={item.name}
|
||||
className="w-full h-32 object-cover rounded-lg mb-2"/>
|
||||
<h3 className="text-lg font-bold text-text-primary">{item.name}</h3>
|
||||
<p className="text-muted">{item.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</main>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user