166 lines
4.6 KiB
TypeScript
166 lines
4.6 KiB
TypeScript
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;
|