add by files
This commit is contained in:
68
src/App.jsx
68
src/App.jsx
@@ -707,6 +707,44 @@ export default function App() {
|
|||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleItemImageUpload = (e) => {
|
||||||
|
const file = e.target.files[0];
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (event) => {
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = () => {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const MAX_SIZE = 800; // Tamanho maior para roupas
|
||||||
|
let width = img.width;
|
||||||
|
let height = img.height;
|
||||||
|
|
||||||
|
if (width > height) {
|
||||||
|
if (width > MAX_SIZE) {
|
||||||
|
height *= MAX_SIZE / width;
|
||||||
|
width = MAX_SIZE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (height > MAX_SIZE) {
|
||||||
|
width *= MAX_SIZE / height;
|
||||||
|
height = MAX_SIZE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
ctx.drawImage(img, 0, 0, width, height);
|
||||||
|
const base64Data = canvas.toDataURL('image/jpeg', 0.8);
|
||||||
|
|
||||||
|
setImageUrlDraft(base64Data);
|
||||||
|
};
|
||||||
|
img.src = event.target.result;
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
};
|
||||||
|
|
||||||
const saveProfile = async (e) => {
|
const saveProfile = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setSavingProfile(true);
|
setSavingProfile(true);
|
||||||
@@ -1011,7 +1049,7 @@ export default function App() {
|
|||||||
|
|
||||||
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-transparent opacity-0 group-hover:opacity-100 transition-all duration-300 flex flex-col justify-end p-6 pb-[136px] text-white z-10 pointer-events-none">
|
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-transparent opacity-0 group-hover:opacity-100 transition-all duration-300 flex flex-col justify-end p-6 pb-[136px] text-white z-10 pointer-events-none">
|
||||||
<div className="grid grid-cols-2 gap-2 pointer-events-auto">
|
<div className="grid grid-cols-2 gap-2 pointer-events-auto">
|
||||||
<button onClick={() => { setEditingItem(item); setImageUrlDraft(''); setView('edit'); }} className="py-3 px-2 bg-white text-primary-600 rounded-xl font-black text-[9px] uppercase flex items-center justify-center gap-1.5 hover:bg-primary-50"><Edit2 size={14} /> {t('edit')}</button>
|
<button onClick={() => { setEditingItem(item); setImageUrlDraft(item.imageUrl || ''); setView('edit'); }} className="py-3 px-2 bg-white text-primary-600 rounded-xl font-black text-[9px] uppercase flex items-center justify-center gap-1.5 hover:bg-primary-50"><Edit2 size={14} /> {t('edit')}</button>
|
||||||
<button onClick={() => handleItemAction('laundry', item)} className="py-3 px-2 bg-blue-600 text-white rounded-xl font-black text-[9px] uppercase flex items-center justify-center gap-1.5 hover:bg-blue-700"><Droplets size={14} /> {t('makeDirty')}</button>
|
<button onClick={() => handleItemAction('laundry', item)} className="py-3 px-2 bg-blue-600 text-white rounded-xl font-black text-[9px] uppercase flex items-center justify-center gap-1.5 hover:bg-blue-700"><Droplets size={14} /> {t('makeDirty')}</button>
|
||||||
<button onClick={() => handleItemAction('trash', item)} className="py-3 px-2 bg-red-600/20 text-red-100 backdrop-blur-md rounded-xl font-black text-[9px] uppercase hover:bg-red-600 transition-colors col-span-2">{t('moveToTrash')}</button>
|
<button onClick={() => handleItemAction('trash', item)} className="py-3 px-2 bg-red-600/20 text-red-100 backdrop-blur-md rounded-xl font-black text-[9px] uppercase hover:bg-red-600 transition-colors col-span-2">{t('moveToTrash')}</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -1289,8 +1327,8 @@ export default function App() {
|
|||||||
<div className="space-y-8">
|
<div className="space-y-8">
|
||||||
<h3 className="text-5xl font-black tracking-tighter text-inherit">{editingItem ? t('edit') : t('newItem')}</h3>
|
<h3 className="text-5xl font-black tracking-tighter text-inherit">{editingItem ? t('edit') : t('newItem')}</h3>
|
||||||
<Card className="aspect-[3/4] overflow-hidden shadow-2xl relative" darkMode={darkMode}>
|
<Card className="aspect-[3/4] overflow-hidden shadow-2xl relative" darkMode={darkMode}>
|
||||||
{editingItem?.imageUrl || imageUrlDraft.startsWith('http') ? (
|
{imageUrlDraft ? (
|
||||||
<img src={imageUrlDraft || editingItem?.imageUrl} className="w-full h-full object-cover" alt="" />
|
<img src={imageUrlDraft} className="w-full h-full object-cover" alt="" />
|
||||||
) : (
|
) : (
|
||||||
<div className="h-full flex flex-col items-center justify-center opacity-10">
|
<div className="h-full flex flex-col items-center justify-center opacity-10">
|
||||||
<ImageIcon size={100} />
|
<ImageIcon size={100} />
|
||||||
@@ -1336,7 +1374,29 @@ export default function App() {
|
|||||||
<input type="hidden" name="color" value={itemColors.join(', ')} />
|
<input type="hidden" name="color" value={itemColors.join(', ')} />
|
||||||
{itemColors.length === 0 && <p className="text-[10px] text-red-500 uppercase tracking-widest font-black mt-2">Selecione pelo menos uma cor</p>}
|
{itemColors.length === 0 && <p className="text-[10px] text-red-500 uppercase tracking-widest font-black mt-2">Selecione pelo menos uma cor</p>}
|
||||||
</div>
|
</div>
|
||||||
<Input label={t('imageUrl')} name="imageUrl" defaultValue={editingItem?.imageUrl} onChange={(v) => setImageUrlDraft(v)} />
|
<div className="space-y-4">
|
||||||
|
<label className="text-[10px] font-black uppercase opacity-40 tracking-widest ml-1 text-inherit flex items-center gap-2"><ImageIcon size={12}/> {t('imageUrl')} ou Upload</label>
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
name="imageUrl"
|
||||||
|
value={imageUrlDraft}
|
||||||
|
onChange={(e) => setImageUrlDraft(e.target.value)}
|
||||||
|
placeholder="https://..."
|
||||||
|
className={`w-full p-5 rounded-2xl border-none outline-none focus:ring-4 focus:ring-primary-500/10 font-bold transition-all ${darkMode ? 'bg-gray-700 text-white' : 'bg-gray-100 text-gray-900'}`}
|
||||||
|
/>
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<div className="h-px bg-gray-200 dark:bg-gray-700 flex-1"></div>
|
||||||
|
<span className="text-[10px] font-black uppercase tracking-widest opacity-30">OU</span>
|
||||||
|
<div className="h-px bg-gray-200 dark:bg-gray-700 flex-1"></div>
|
||||||
|
</div>
|
||||||
|
<label className={`flex items-center justify-center gap-3 p-5 rounded-2xl cursor-pointer transition-all font-black text-[10px] uppercase tracking-widest border-2 border-dashed ${darkMode ? 'bg-gray-800 border-gray-700 hover:border-primary-500 hover:text-primary-400' : 'bg-gray-50 border-gray-200 hover:border-primary-400 hover:text-primary-600'}`}>
|
||||||
|
<ImageIcon size={16} />
|
||||||
|
<span>Upload da Galeria / Ficheiros</span>
|
||||||
|
<input type="file" accept="image/*" className="hidden" onChange={handleItemImageUpload} />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Campo de Secções */}
|
{/* Campo de Secções */}
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
|
|||||||
Reference in New Issue
Block a user