init
This commit is contained in:
166
frontend/src/components/ImageUploader.tsx
Normal file
166
frontend/src/components/ImageUploader.tsx
Normal 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;
|
||||
Reference in New Issue
Block a user