-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #97 from JimTheCat/CU-8697bp6d2_Add-possibility-to…
…-add-pictures-to-posts_Patryk-Kosiski feature: Add pictures to post
- Loading branch information
Showing
11 changed files
with
251 additions
and
61 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,60 +1,89 @@ | ||
import {IconPencil} from "@tabler/icons-react"; | ||
import {Button} from "@mantine/core"; | ||
import {ModificationModal} from "../shared/components/ModificationModal"; | ||
import {PostForm} from "./components/PostForm"; | ||
import {useRef, useState} from "react"; | ||
import api from "../shared/services/api.ts"; | ||
import {useAlert} from "../../Providers/AlertProvider.tsx"; | ||
import {ModificationModal} from "../shared/components/ModificationModal"; | ||
import {Button} from "@mantine/core"; | ||
import {ModalRichContent} from "../shared/consts"; | ||
import api from "../shared/services/api.ts"; | ||
|
||
export const CreatePost = () => { | ||
const [content, setContent] = useState(''); | ||
const [content, setContent] = useState(""); | ||
const contentRef = useRef(content); | ||
const [images, setImages] = useState<File[]>([]); | ||
const imagesRef = useRef(images); | ||
const alert = useAlert(); | ||
|
||
const handleContentChange = (html: string) => { | ||
setContent(html); | ||
contentRef.current = html; | ||
} | ||
}; | ||
|
||
const handleCreatePost = () => { | ||
const handleImagesChange = (files: File[]) => { | ||
setImages(files); | ||
imagesRef.current = files; | ||
}; | ||
|
||
const handleCreatePost = async () => { | ||
const contentToSave = contentRef.current; | ||
console.log('Create post: ', contentToSave); | ||
if (!contentToSave) { | ||
const images = imagesRef.current; | ||
|
||
// Walidacja: musi być treść lub zdjęcia | ||
if (!contentToSave && images.length === 0) { | ||
alert.showError({ | ||
title: 'Error', | ||
message: 'Content is empty', | ||
level: 'WARNING', | ||
timestamp: new Date().toISOString() | ||
title: "Error", | ||
message: "Post cannot be empty. Please add content or images.", | ||
level: "WARNING", | ||
timestamp: new Date().toISOString(), | ||
}); | ||
return; | ||
} | ||
|
||
api.post('/api/posts', null, {params: {content: contentToSave}}).then((response) => { | ||
if (response.status === 200) { | ||
close(); | ||
} | ||
// Prepare form data | ||
const formData = new FormData(); | ||
if (contentToSave) formData.append("content", contentToSave); | ||
images.forEach((image) => { | ||
formData.append(`pictures`, image); | ||
}); | ||
} | ||
|
||
const response = await api.post("/api/posts", formData); | ||
|
||
if (response.status === 200) { | ||
alert.showError({ | ||
title: "Success", | ||
message: "Post created successfully!", | ||
level: "INFO", | ||
timestamp: new Date().toISOString(), | ||
}); | ||
close(); | ||
} | ||
}; | ||
|
||
return ( | ||
<Button | ||
variant={"subtle"} | ||
size={"md"} | ||
variant="subtle" | ||
size="md" | ||
leftSection={<IconPencil/>} | ||
autoContrast | ||
fullWidth | ||
justify={"flex-start"} | ||
color={"gray"} | ||
onClick={() => ModificationModal({ | ||
handleAction: handleCreatePost, | ||
title: 'Create post', | ||
buttonConfirmText: 'Create', | ||
buttonConfirmColor: 'blue', | ||
childrenContent: <ModalRichContent handleContentChange={handleContentChange}/> | ||
})} | ||
justify="flex-start" | ||
color="gray" | ||
onClick={() => | ||
ModificationModal({ | ||
handleAction: () => { | ||
handleCreatePost().catch(); | ||
}, | ||
title: "Create post", | ||
buttonConfirmText: "Create", | ||
buttonConfirmColor: "blue", | ||
childrenContent: ( | ||
<PostForm | ||
handleContentChange={handleContentChange} | ||
setImages={handleImagesChange} | ||
/> | ||
), | ||
}) | ||
} | ||
> | ||
Create post | ||
</Button> | ||
|
||
); | ||
} | ||
}; |
110 changes: 110 additions & 0 deletions
110
frontend/src/Features/CreatePost/components/PostForm/PostForm.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import {useState} from "react"; | ||
import {Dropzone, MIME_TYPES} from "@mantine/dropzone"; | ||
import {Button, Group, Image, Paper, rem, SimpleGrid, Text} from "@mantine/core"; | ||
import {ModalRichContent} from "../../../shared/consts"; | ||
import {useAlert} from "../../../../Providers/AlertProvider.tsx"; | ||
import {IconPhoto, IconUpload, IconX} from "@tabler/icons-react"; | ||
|
||
type PostFormProps = { | ||
handleContentChange: (html: string) => void; | ||
setImages: (files: File[]) => void; | ||
}; | ||
|
||
export const PostForm = ({handleContentChange, setImages}: PostFormProps) => { | ||
const [localImages, setLocalImages] = useState<File[]>([]); | ||
const [previews, setPreviews] = useState<{ src: string; name: string }[]>([]); | ||
const alert = useAlert(); | ||
|
||
// Function to handle dropping images | ||
const handleDrop = (files: File[]) => { | ||
if (localImages.length + files.length > 5) { | ||
alert.showError({ | ||
title: "Too many images", | ||
message: "You can upload up to 5 images", | ||
level: "WARNING", | ||
timestamp: new Date().toISOString(), | ||
}); | ||
return; | ||
} | ||
|
||
// Add new images to the existing ones | ||
const updatedImages = [...localImages, ...files]; | ||
setLocalImages(updatedImages); | ||
setImages(updatedImages); // Passing to CreatePost | ||
|
||
setPreviews((prevPreviews) => [ | ||
...prevPreviews, | ||
...files.map((file) => ({src: URL.createObjectURL(file), name: file.name})), | ||
]); | ||
}; | ||
|
||
// Function to remove an image | ||
const handleRemoveImage = (index: number) => { | ||
const updatedImages = localImages.filter((_, i) => i !== index); | ||
setLocalImages(updatedImages); | ||
setImages(updatedImages); // Passing to CreatePost | ||
|
||
URL.revokeObjectURL(previews[index].src); | ||
setPreviews((prevPreviews) => prevPreviews.filter((_, i) => i !== index)); | ||
}; | ||
|
||
return ( | ||
<> | ||
<ModalRichContent handleContentChange={handleContentChange}/> | ||
|
||
<Dropzone | ||
onDrop={handleDrop} | ||
accept={[MIME_TYPES.jpeg, MIME_TYPES.png, MIME_TYPES.gif]} | ||
maxSize={3 * 1024 ** 2} | ||
multiple | ||
> | ||
<Group justify="center" gap="xl" mih={120} style={{pointerEvents: "none"}}> | ||
<Dropzone.Accept> | ||
<IconUpload | ||
style={{width: rem(52), height: rem(52), color: "var(--mantine-color-blue-6)"}} | ||
stroke={1.5} | ||
/> | ||
</Dropzone.Accept> | ||
<Dropzone.Reject> | ||
<IconX | ||
style={{width: rem(52), height: rem(52), color: "var(--mantine-color-red-6)"}} | ||
stroke={1.5} | ||
/> | ||
</Dropzone.Reject> | ||
<Dropzone.Idle> | ||
<IconPhoto | ||
style={{width: rem(52), height: rem(52), color: "var(--mantine-color-dimmed)"}} | ||
stroke={1.5} | ||
/> | ||
</Dropzone.Idle> | ||
|
||
<div> | ||
<Text size="xl" inline> | ||
Drag images here or click to select files | ||
</Text> | ||
<Text size="sm" c="dimmed" inline mt={7}> | ||
Attach up to 5 files, each file should not exceed 3MB | ||
</Text> | ||
</div> | ||
</Group> | ||
</Dropzone> | ||
|
||
{previews.length > 0 && ( | ||
<SimpleGrid cols={2} mt="md"> | ||
{previews.map((preview, index) => ( | ||
<Paper maw={"25vw"} withBorder p="sm" radius="md" key={index + 1}> | ||
<Image src={preview.src} alt={`preview-${index}`} width={100} height={100} radius="sm"/> | ||
|
||
<Text size="sm" fw={500} my={"sm"} truncate={"end"}> | ||
{preview.name} | ||
</Text> | ||
<Button fullWidth color="red" size="xs" onClick={() => handleRemoveImage(index)}> | ||
Remove | ||
</Button> | ||
</Paper> | ||
))} | ||
</SimpleGrid> | ||
)} | ||
</> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export {PostForm} from './PostForm'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.