Skip to content

Commit

Permalink
Merge pull request #32 from kscalelabs/second_milestone_serhii
Browse files Browse the repository at this point in the history
features_toasty_and_delete_items
  • Loading branch information
Serhii Ofii authored Sep 16, 2024
2 parents e3e21bc + c1922df commit 7ba1415
Show file tree
Hide file tree
Showing 14 changed files with 285 additions and 45 deletions.
10 changes: 9 additions & 1 deletion frontend/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,18 @@ export class Api {
await this.api.post(`/edit_collection`, data);
return null;
}
public async getAllCollections(): Promise<Array<Collection> | null> {
public async getAllCollections(): Promise<Array<Collection> | []> {
const response = await this.api.get(`/get_collections`);
return response.data;
}
public async deleteCollection(collection_id: string): Promise<null> {
await this.api.get(`/delete_collection?id=${collection_id}`);
return null;
}
public async deleteImage(image_id: string): Promise<null> {
await this.api.get(`/delete_image?id=${image_id}`);
return null;
}
public async uploadImage(file: File, collection_id: string): Promise<Image> {
const response = await this.api.post("/upload", {
file,
Expand Down
1 change: 0 additions & 1 deletion frontend/src/api/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export const read_me = async (token: string): Promise<Response | null> => {
};
export const signin = async (data: SigninData): Promise<Response> => {
const response = await axios.post(`${API_URL}/signin`, data);
console.log(response);
return response.data;
};
export const social_facebook_login = async (
Expand Down
10 changes: 6 additions & 4 deletions frontend/src/components/UploadContent.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { useAlertQueue } from "hooks/alerts";
import { FC, useState } from "react";
import { XCircleFill } from "react-bootstrap-icons";
import ImageUploading, { ImageListType } from "react-images-uploading";

interface UploadContentProps {
onUpload: (file: File) => Promise<void>; // Updated to handle a single file
}

const UploadContent: FC<UploadContentProps> = ({ onUpload }) => {
const [images, setImages] = useState<ImageListType>([]);
const [uploading, setUploading] = useState<boolean>(false);
// const [uploadIndex, setUploadIndex] = useState<number>(0);
const { addAlert } = useAlertQueue();

const maxNumber = 10; // Set the maximum number of files allowed

Expand All @@ -21,9 +21,11 @@ const UploadContent: FC<UploadContentProps> = ({ onUpload }) => {
const file = images[i].file as File;
try {
await onUpload(file); // Upload each file one by one
// Optionally, handle success feedback here
if (i == images.length - 1)
addAlert(`${i + 1} images has been uploaded!`, "success");
} catch (error) {
console.error(`Failed to upload file ${file.name}:`, error);
addAlert(`${file.name} has been failed to upload. ${error}`, "error");
break;
// Optionally, handle failure feedback here
}
}
Expand Down
19 changes: 16 additions & 3 deletions frontend/src/components/card.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { TrashFill } from "react-bootstrap-icons";
import { useNavigate } from "react-router-dom";
import { Collection } from "types/model";

const CardItem: React.FC<Collection> = (collectionProps) => {
const { id, title, description, images } = collectionProps;
interface CardItemProps extends Collection {
onDelete: (id: string) => void;
}
const CardItem: React.FC<CardItemProps> = (cardprops) => {
const { id, title, description, images, onDelete } = cardprops;
const navigate = useNavigate();

const handleOpen = () => {
Expand All @@ -12,6 +15,9 @@ const CardItem: React.FC<Collection> = (collectionProps) => {
const handleEdit = () => {
navigate(`/collection/${id}?Action=edit`);
};
const handleDelete = () => {
onDelete(id);
};

return (
<div className="relative m-8 group">
Expand All @@ -23,6 +29,7 @@ const CardItem: React.FC<Collection> = (collectionProps) => {
<p className="text-sm text-gray-800 dark:text-gray-200 grow">
{images.length} Images
</p>

<div className="absolute inset-0 flex items-end pb-3 justify-center group-hover:opacity-100 opacity-0 transition-opacity">
<div className="flex gap-2">
<button
Expand All @@ -37,6 +44,12 @@ const CardItem: React.FC<Collection> = (collectionProps) => {
>
Edit
</button>
<button
className="bg-red-700 text-white flex justify-content-center items-center w-8 h-8 rounded"
onClick={handleDelete}
>
<TrashFill size={18} />
</button>
</div>
</div>
</div>
Expand Down
51 changes: 40 additions & 11 deletions frontend/src/components/image.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import React from "react";
import { CheckCircleFill, LockFill, PencilFill } from "react-bootstrap-icons";
import {
CheckCircleFill,
LockFill,
PencilFill,
TrashFill,
} from "react-bootstrap-icons";
import { Image } from "types/model";
// Extend the existing Image interface to include the new function
interface ImageWithFunction extends Image {
handleTranslateOneImage: (image_id: string) => void;
showDeleteModal: (id: string) => void;
}
const ImageComponent: React.FC<ImageWithFunction> = ({
id,
is_translated,
image_url,
transcriptions,
handleTranslateOneImage,
showDeleteModal,
}) => {
return (
<div
Expand Down Expand Up @@ -41,17 +48,39 @@ const ImageComponent: React.FC<ImageWithFunction> = ({
{/* Centered Edit Button */}
<div className="absolute inset-x-0 bottom-1/2 transform translate-y-1/2 flex items-center justify-center gap-2">
{is_translated ? (
<button className="bg-yellow-500 text-white py-1 px-3 rounded flex items-center">
<PencilFill className="mr-2" />
Edit
</button>
<div className="flex gap-2">
<button className="bg-yellow-500 text-white py-1 px-3 rounded flex items-center">
<PencilFill className="mr-2" />
Edit
</button>
<button
className="bg-red-500 text-white py-1 px-2 rounded flex items-center"
onClick={() => {
showDeleteModal(id);
}}
>
<TrashFill className="mr-2" />
Delete
</button>
</div>
) : (
<button
className="bg-blue-500 text-white py-1 px-3 rounded"
onClick={() => handleTranslateOneImage(id)}
>
Translate
</button>
<div className="flex gap-2">
<button
className="bg-blue-500 text-white py-1 px-3 rounded"
onClick={() => handleTranslateOneImage(id)}
>
Translate
</button>
<button
className="bg-red-500 text-white py-1 px-2 rounded flex items-center"
onClick={() => {
showDeleteModal(id);
}}
>
<TrashFill className="mr-2" />
Delete
</button>
</div>
)}
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/hooks/alerts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,9 @@ export const AlertQueue = (props: AlertQueueProps) => {
<>
{children}
<ToastContainer
className="p-3"
className="p-3 mb-8"
position="bottom-center"
style={{ zIndex: 1000, position: "fixed", marginBottom: 50 }}
style={{ zIndex: 1000, position: "fixed" }}
>
{Array.from(alerts).map(([alertId, [alert, kind]]) => {
return (
Expand Down
68 changes: 60 additions & 8 deletions frontend/src/pages/Collection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Modal from "components/modal";
import UploadContent from "components/UploadContent";
import { useAuth } from "contexts/AuthContext";
import { useLoading } from "contexts/LoadingContext";
import { useAlertQueue } from "hooks/alerts";
import React, { useEffect, useMemo, useRef, useState } from "react";
import { Col, Row } from "react-bootstrap";
import {
Expand All @@ -29,10 +30,12 @@ const CollectionPage: React.FC = () => {
const [collection, setCollection] = useState<Collection | null>(null);
const { auth, is_auth } = useAuth();
const { startLoading, stopLoading } = useLoading();
const [showModal, setShowModal] = useState(false);
const [showUploadModal, setShowUploadModal] = useState(false);
const [showDeleteImageModal, setShowDeleteImageModal] = useState(false);
const [images, setImages] = useState<Array<Image> | null>([]);
const audioRef = useRef<HTMLAudioElement | null>(null);

const { addAlert } = useAlertQueue();
const [deleteImageIndex, setDeleteImageIndex] = useState<string>("");
const apiClient: AxiosInstance = useMemo(
() =>
axios.create({
Expand Down Expand Up @@ -101,7 +104,7 @@ const CollectionPage: React.FC = () => {
};
asyncfunction();
}
}, [collection]);
}, [collection?.id]);
useEffect(() => {
if (audioRef.current) {
audioRef.current.load();
Expand All @@ -111,8 +114,10 @@ const CollectionPage: React.FC = () => {
e.preventDefault();
startLoading();
const collection = await API.createCollection({ title, description });
if (collection != null)
if (collection != null) {
navigate(`/collection/${collection.id}?Action=edit`);
addAlert("New collection has been created successfully!", "success");
} else addAlert("The process has gone wrong!", "error");
stopLoading();
};
// Navigate between images
Expand Down Expand Up @@ -155,6 +160,7 @@ const CollectionPage: React.FC = () => {
const asyncfunction = async () => {
startLoading();
await API.editCollection(collection);
addAlert("The collection has been updated successfully!", "success");
stopLoading();
};
asyncfunction();
Expand All @@ -175,10 +181,32 @@ const CollectionPage: React.FC = () => {
const handleTranslateOneImage = async (image_id: string) => {
if (images) {
startLoading();
addAlert(
"The image is being tranlated. Please wait a moment.",
"primary",
);
const image_response = await API.translateImages([image_id]);
const i = images?.findIndex((image) => image.id == image_id);
images[i] = image_response[0];
setImages([...images]);
addAlert("The image has been tranlated!", "success");
stopLoading();
}
};
const onShowDeleteImageModal = (id: string) => {
setDeleteImageIndex(id);
setShowDeleteImageModal(true);
};
const onDeleteImage = async () => {
if (deleteImageIndex) {
startLoading();
await API.deleteImage(deleteImageIndex);
if (images) {
const filter = images.filter((image) => image.id != deleteImageIndex);
setImages(filter);
}
setShowDeleteImageModal(false);
addAlert("The image has been deleted!", "success");
stopLoading();
}
};
Expand All @@ -198,7 +226,7 @@ const CollectionPage: React.FC = () => {
<div className="flex flex-col items-center pt-20 gap-8">
<h1>New Collection</h1>
<form
className="flex flex-col items-end gap-4 w-96"
className="flex flex-col items-end gap-4 w-full"
onSubmit={handleCreate}
>
<input
Expand Down Expand Up @@ -271,22 +299,45 @@ const CollectionPage: React.FC = () => {
</form>
<button
className="bg-blue-500 text-white w-30 p-2 rounded hover:bg-blue-600"
onClick={() => setShowModal(true)}
onClick={() => setShowUploadModal(true)}
>
Add Images
</button>
{/* Upload Modal */}
<Modal isOpen={showModal} onClose={() => setShowModal(false)}>
<Modal
isOpen={showUploadModal}
onClose={() => setShowUploadModal(false)}
>
<UploadContent onUpload={handleUpload} />
<div className="mt-5 flex justify-end space-x-2">
<button
className="px-4 py-2 bg-gray-300 text-gray-700 rounded hover:bg-gray-400"
onClick={() => setShowModal(false)}
onClick={() => setShowUploadModal(false)}
>
Close
</button>
</div>
</Modal>
<Modal
isOpen={showDeleteImageModal}
onClose={() => setShowDeleteImageModal(false)}
>
<div className="mt-5 flex justify-end space-x-2 gap-4 items-center">
<span>Are you sure you want to delete the collection?</span>
<button
className="px-4 py-2 bg-red-700 text-gray-300 rounded hover:bg-red-800"
onClick={onDeleteImage}
>
Delete
</button>
<button
className="px-4 py-2 bg-gray-300 text-gray-700 rounded hover:bg-gray-400"
onClick={() => setShowDeleteImageModal(false)}
>
Cancel
</button>
</div>
</Modal>
<Row className="align-items-center w-full">
{images ? (
images.map((image) => {
Expand All @@ -295,6 +346,7 @@ const CollectionPage: React.FC = () => {
<ImageComponent
{...image}
handleTranslateOneImage={handleTranslateOneImage}
showDeleteModal={onShowDeleteImageModal}
/>
</Col>
);
Expand Down
Loading

0 comments on commit 7ba1415

Please sign in to comment.