diff --git a/package.json b/package.json index 0169589..b9eac10 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "react-datepicker": "^4.8.0", "react-dom": "18.2.0", "react-icons": "^4.7.1", + "react-image-picker-editor": "^1.2.0", "react-loader-spinner": "^5.3.4", "react-quill": "^2.0.0", "react-range": "^1.8.14", diff --git a/src/components/announcements/add-announcement/AddAnnouncementIndex.tsx b/src/components/announcements/add-announcement/AddAnnouncementIndex.tsx index a336bc7..e66ddb9 100644 --- a/src/components/announcements/add-announcement/AddAnnouncementIndex.tsx +++ b/src/components/announcements/add-announcement/AddAnnouncementIndex.tsx @@ -1,22 +1,49 @@ import dynamic from "next/dynamic"; -import { useState } from "react"; +import { useRouter } from "next/router"; +import React, { useState } from "react"; +import { ImagePickerConf } from "react-image-picker-editor"; +import "react-image-picker-editor/dist/index.css"; import "react-quill/dist/quill.snow.css"; import { ICONS } from "../../../constants/icons"; import { INFO } from "../../../constants/info"; +import { ROUTES } from "../../../constants/routes"; + import { WILAYAS_FR } from "../../../constants/wilaya_algeria"; +import AnnouncementService from "../../../services/annoucement.service"; import LocationService from "../../../services/locations.service"; import { Announcement } from "../../../typings/announcement"; import Select from "../../shared/Select"; import TextInput from "../../shared/TextInput"; const ReactQuill = dynamic(() => import("react-quill"), { ssr: false }); +const ReactImagePickerEditor = dynamic( + () => import("react-image-picker-editor"), + { ssr: false } +); const locationService = LocationService.getInstance(); +const announcementService = AnnouncementService.getInstance(); + +const config2: ImagePickerConf = { + borderRadius: "8px", + language: "en", + width: "100%", + height: "8rem", + objectFit: "cover", + compressInitial: null, + hideEditBtn: true, + hideDownloadBtn: true, + hideAddBtn: true, +}; const AddAnnouncementIndex = () => { + const router = useRouter(); + const [announcement, setAnnouncement] = useState({}); + const [photos, setPhotos] = useState<(Blob | null)[]>([]); const [communes, setCommunes] = useState([]); + const [loading, setLoading] = useState(false); const onChange = (e: React.ChangeEvent) => { setAnnouncement((prev) => ({ ...prev, [e.target.name]: e.target.value })); @@ -37,6 +64,49 @@ const AddAnnouncementIndex = () => { } }; + const onSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if ( + !announcement.titre || + !announcement.description || + !announcement.adresse || + !announcement.categorie || + !announcement.type || + !announcement.commune || + !announcement.wilaya || + !announcement.surface || + !announcement.prix + ) { + return; + } + setLoading(true); + const bodyFormData = new FormData(); + bodyFormData.append("titre", announcement.titre); + bodyFormData.append("description", announcement.description); + bodyFormData.append("adresse", announcement.adresse); + bodyFormData.append("categorie", announcement.categorie); + bodyFormData.append("type", announcement.type); + bodyFormData.append("wilaya", announcement.wilaya); + bodyFormData.append("commune", announcement.commune); + bodyFormData.append("surface", announcement.surface.toString()); + bodyFormData.append("prix", announcement.prix.toString()); + photos.forEach(async (photo, index) => { + if (photo) { + bodyFormData.append("photos", photo, `image${index}.jpg`); + } + }); + try { + const response = await announcementService.createAnnouncement( + bodyFormData + ); + router.push(ROUTES.POSTED_ANNOUNCEMENTS.path); + } catch (e) { + console.log(e); + } finally { + setLoading(false); + } + }; + return (
@@ -45,7 +115,7 @@ const AddAnnouncementIndex = () => { Création d'une annonce
-
+

@@ -137,6 +207,34 @@ const AddAnnouncementIndex = () => { } />

+
+ +
+ {[0, 1, 2, 3].map((item) => ( +
+ { + const blob = newDataUri + ? await (await fetch(newDataUri)).blob() + : null; + setPhotos((prev) => { + let newPhotos = [...prev]; + newPhotos[item] = blob; + return newPhotos; + }); + }} + /> +
+ ))} +
+

@@ -178,7 +276,29 @@ const AddAnnouncementIndex = () => { Icon={ICONS.Map} />

-
+
+
+ +
+ ); diff --git a/src/services/annoucement.service.ts b/src/services/annoucement.service.ts index 79b070b..51f45ef 100644 --- a/src/services/annoucement.service.ts +++ b/src/services/annoucement.service.ts @@ -33,6 +33,19 @@ class AnnouncementService { } } + public async createAnnouncement(data: FormData) { + try { + const response = await axios.postForm("/announcements/create", data, { + headers: { + "Content-Type": "multipart/form-data", + }, + }); + return response; + } catch (e) { + throw e; + } + } + public async getAnnouncementById(id: number) { try { const response = await axios.get(`/announcements/${id}`); diff --git a/src/typings/announcement.d.ts b/src/typings/announcement.d.ts index aef130d..2eed15b 100644 --- a/src/typings/announcement.d.ts +++ b/src/typings/announcement.d.ts @@ -23,7 +23,16 @@ declare module Announcement { type AnnouncementPart = Omit; type AnnouncementNew = Partial< - Omit & + Omit< + Announcement, + | "id" + | "messages" + | "localisation" + | "photos" + | "fans" + | "auteur" + | "date_publication" + > & Omit >; diff --git a/src/utils/lib.ts b/src/utils/lib.ts index 2f3ef6b..7688d4f 100644 --- a/src/utils/lib.ts +++ b/src/utils/lib.ts @@ -65,3 +65,22 @@ export const formatDate = (date: Date) => { return [year, month, day].join("-"); }; + +export function dataURItoBlob(dataURI: string) { + // convert base64/URLEncoded data component to raw binary data held in a string + var byteString; + if (dataURI.split(",")[0].indexOf("base64") >= 0) + byteString = atob(dataURI.split(",")[1]); + else byteString = unescape(dataURI.split(",")[1]); + + // separate out the mime component + var mimeString = dataURI.split(",")[0].split(":")[1].split(";")[0]; + + // write the bytes of the string to a typed array + var ia = new Uint8Array(byteString.length); + for (var i = 0; i < byteString.length; i++) { + ia[i] = byteString.charCodeAt(i); + } + + return new Blob([ia], { type: mimeString }); +} diff --git a/yarn.lock b/yarn.lock index bdbed2f..f020d14 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2315,6 +2315,11 @@ react-icons@^4.7.1: resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.7.1.tgz#0f4b25a5694e6972677cb189d2a72eabea7a8345" integrity sha512-yHd3oKGMgm7zxo3EA7H2n7vxSoiGmHk5t6Ou4bXsfcgWyhfDKMpyKfhHR6Bjnn63c+YXBLBPUql9H4wPJM6sXw== +react-image-picker-editor@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/react-image-picker-editor/-/react-image-picker-editor-1.2.0.tgz#5185145f79a8f0b6c5b26751ecf59fff4707baff" + integrity sha512-ASl8QFQRPPepB7vA6phe8lTzNYrVysgNJiWuNHiMQAVBh7I0qacJsNG9ByBolywLqM3ofcFksYLhj/yGks/nxg== + react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"