This commit is contained in:
Jakub Kaniecki
2025-05-18 16:23:03 +02:00
commit 12c76e3e5a
220 changed files with 31696 additions and 0 deletions

View File

@@ -0,0 +1,166 @@
import React, { useState, useEffect } from 'react';
import axios from 'axios';
import { Button, Input, Progress, message, Upload, Modal, List, Image as AntImage } from 'antd';
import { UploadOutlined, PictureOutlined } from '@ant-design/icons';
import { useAuth } from '../context/AuthContext';
interface ImageQuota {
daily_limit: number;
used: number;
remaining: number;
daily_limit_display: string;
used_display: string;
remaining_display: string;
}
interface UploadedImage {
id: number;
title: string;
url: string;
uploaded_at: string;
file_size_display: string;
}
interface ImageUploaderProps {
onImageSelect: (imageUrl: string) => void;
}
const ImageUploader: React.FC<ImageUploaderProps> = ({ onImageSelect }) => {
const [isModalVisible, setIsModalVisible] = useState(false);
const [images, setImages] = useState<UploadedImage[]>([]);
const [quota, setQuota] = useState<ImageQuota | null>(null);
const [uploading, setUploading] = useState(false);
const { authToken } = useAuth();
const fetchImages = async () => {
try {
const response = await axios.get('/api/content/images/', {
headers: { Authorization: `Bearer ${authToken}` }
});
setImages(response.data);
} catch (error) {
message.error('Failed to fetch images');
}
};
const fetchQuota = async () => {
try {
const response = await axios.get('/api/content/images/quota/', {
headers: { Authorization: `Bearer ${authToken}` }
});
setQuota(response.data);
} catch (error) {
message.error('Failed to fetch quota information');
}
};
useEffect(() => {
if (isModalVisible) {
fetchImages();
fetchQuota();
}
}, [isModalVisible]);
const handleUpload = async (file: File) => {
const formData = new FormData();
formData.append('image', file);
formData.append('title', file.name);
try {
setUploading(true);
await axios.post('/api/content/images/', formData, {
headers: {
'Content-Type': 'multipart/form-data',
Authorization: `Bearer ${authToken}`
}
});
message.success('Upload successful');
fetchImages();
fetchQuota();
} catch (error: any) {
if (error.response?.data?.image) {
message.error(error.response.data.image[0]);
} else {
message.error('Upload failed');
}
} finally {
setUploading(false);
}
};
const handleImageSelect = (url: string) => {
onImageSelect(url);
setIsModalVisible(false);
};
return (
<>
<Button
icon={<PictureOutlined />}
onClick={() => setIsModalVisible(true)}
style={{ marginRight: 8 }}
className="dark:text-white dark:hover:text-white dark:bg-gray-800 dark:hover:bg-gray-700 "
>
Insert Image
</Button>
<Modal
title="Image Manager"
open={isModalVisible}
onCancel={() => setIsModalVisible(false)}
footer={null}
width={800}
>
<div style={{ marginBottom: 16 }}>
<Upload
beforeUpload={(file) => {
handleUpload(file);
return false;
}}
showUploadList={false}
>
<Button icon={<UploadOutlined />} loading={uploading} className="dark:text-white dark:hover:text-white dark:bg-gray-800 dark:hover:bg-gray-700">
Upload Image
</Button>
</Upload>
</div>
{quota && (
<div style={{ marginBottom: 16 }}>
<p>Daily Upload Quota</p>
<Progress
percent={Math.round((quota.used / quota.daily_limit) * 100)}
format={() => `${quota.used_display} / ${quota.daily_limit_display}`}
/>
</div>
)}
<List
grid={{ gutter: 16, xs: 1, sm: 2, md: 3, lg: 3, xl: 4, xxl: 4 }}
dataSource={images}
renderItem={(item) => (
<List.Item>
<div style={{ textAlign: 'center' }}>
<div style={{ marginBottom: 8 }}>
<AntImage
src={item.url}
alt={item.title}
style={{ maxHeight: 100, width: 'auto' }}
/>
</div>
<Button type="link" onClick={() => handleImageSelect(item.url)}>
Insert
</Button>
<div style={{ fontSize: '12px', color: '#999' }}>
{item.file_size_display}
</div>
</div>
</List.Item>
)}
/>
</Modal>
</>
);
};
export default ImageUploader;