diff --git a/.prettierrc b/.prettierrc index 48e90e8..d27ac8e 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,7 +1,7 @@ { "endOfLine": "lf", "semi": false, - "singleQuote": false, + "singleQuote": true, "tabWidth": 2, "trailingComma": "es5" } diff --git a/app/app.js b/app/app.js index ecf5e8f..2649a5e 100644 --- a/app/app.js +++ b/app/app.js @@ -142,7 +142,10 @@ const App = () => { ( - + )} /> diff --git a/app/components/CbetDropzone.js b/app/components/CbetDropzone.js new file mode 100644 index 0000000..46395e1 --- /dev/null +++ b/app/components/CbetDropzone.js @@ -0,0 +1,72 @@ +import React, { useCallback, useState, useEffect } from "react" +import { Image } from "react-bootstrap" +import { useDropzone } from "react-dropzone" +import dropzone from "./images/DropZone.png" +import dropzone_Active from "./images/DropZone Active.png" +import dropzone_Complete from "./images/DropZone Complete.png" +import dropzone_Uploading from "./images/DropZone Uploading.png" +import styled from "styled-components" +import { FaSpinner } from "react-icons/fa/index" + +const SpinSpinner = styled(FaSpinner)` + @keyframes spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } + } + animation: spin 1.5s infinite; +` + +export default function CbetDropzone(props) { + const [isUploading, setIsUploading] = useState(false) + const onDrop = useCallback((acceptedFiles) => { + // Do something with the files + if (acceptedFiles.length === 0) { + return + } + + props.upload(acceptedFiles, setIsUploading(false)) + }, []) + const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop }) + + useEffect(() => { + console.log("cbetDropzone initial props", props) + }, []) + + function clickUpload(e) { + e.preventDefault() + setIsUploading(true) + } + + return ( +
+ + + {isDragActive ? ( + + ) : null} + + {isUploading ? ( + + + + + ) : null} + + {props.complete ? : null} + + {isUploading === false && + props.complete === false && + props.editImageUrl === "" ? ( + + ) : ( + + )} +
+ ) +} diff --git a/app/components/images/DropZone Active.png b/app/components/images/DropZone Active.png new file mode 100644 index 0000000..7d2cd35 Binary files /dev/null and b/app/components/images/DropZone Active.png differ diff --git a/app/components/images/DropZone Complete.png b/app/components/images/DropZone Complete.png new file mode 100644 index 0000000..175e797 Binary files /dev/null and b/app/components/images/DropZone Complete.png differ diff --git a/app/components/images/DropZone Uploading.png b/app/components/images/DropZone Uploading.png new file mode 100644 index 0000000..b4e70e1 Binary files /dev/null and b/app/components/images/DropZone Uploading.png differ diff --git a/app/components/images/DropZone.png b/app/components/images/DropZone.png new file mode 100644 index 0000000..12a2054 Binary files /dev/null and b/app/components/images/DropZone.png differ diff --git a/app/create-edit.js b/app/create-edit.js index e425bb1..dc0a9b9 100644 --- a/app/create-edit.js +++ b/app/create-edit.js @@ -1,10 +1,998 @@ -import React from 'react' +import React, { useEffect, useState } from "react" +import { useForm } from "react-hook-form" +import { + Button, + Container, + Row, + Col, + Form, + ListGroup, + OverlayTrigger, + Tooltip, +} from "react-bootstrap" +import SunEditor, { buttonList } from "suneditor-react" +import "suneditor/dist/css/suneditor.min.css" +import { FaCheck, FaSpinner } from "react-icons/fa" +import styled from "styled-components" +import CbetDatePicker from "./components/CbetDropzone" -export default function CreateEdit({ title }) { +const SpinSpinner = styled(FaSpinner)` + @keyframes spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } + } + animation: spin 1.5s infinite; +` + +const partnersList = [ + { + name: "Accet", + link: "https://cbet.blob.core.windows.net/cbetblobs/accet.svg", + }, + { + name: "Texas Workforce Commission", + link: "https://cbet.blob.core.windows.net/cbetblobs/twc-logo.jpg", + }, + { + name: "Texas Higher Education Board", + link: "https://cbet.blob.core.windows.net/cbetblobs/TXHigherEd.png", + }, + { + name: "Veteran Owned Business", + link: "https://cbet.blob.core.windows.net/cbetblobs/vet-owned.png", + }, + { + name: "MedWrench", + link: "https://cbet.blob.core.windows.net/cbetblobs/Medwrench-logo.png", + }, + { + name: "TechNation", + link: "https://cbet.blob.core.windows.net/cbetblobs/TechNation_logo.png", + }, + { + name: "Tuition Financing", + link: "https://cbet.blob.core.windows.net/cbetblobs/tfc_logo.png", + }, + { + name: "Stephens International Recruiting", + link: "https://cbet.blob.core.windows.net/cbetblobs/si-recruiting.png", + }, + { + name: "CER Technology", + link: "https://cbet.blob.core.windows.net/cbetblobs/cer-logo.png", + }, + { + name: "Summit Imaging", + link: "https://cbet.blob.core.windows.net/cbetblobs/summit-logo.jpg", + }, + { + name: "New Braunfels Chamber of Commerce", + link: "https://cbet.blob.core.windows.net/cbetblobs/NBChambers-logo.png", + }, + { + name: "Catholic Health Initiatives", + link: "https://cbet.blob.core.windows.net/cbetblobs/chi-health.jpg", + }, + { + name: "Citizens Medical Center", + link: "https://cbet.blob.core.windows.net/cbetblobs/citizens.png", + }, + { + name: "CYBERTEXAS Foundation", + link: "https://cbet.blob.core.windows.net/cbetblobs/logo-cyberTexas.png", + }, + { + name: "Charney & Associates Recruiting", + link: "https://cbet.blob.core.windows.net/cbetblobs/charney-logo.png", + }, + { + name: "Memorial Hermann", + link: + "https://cbet.blob.core.windows.net/cbetblobs/memorial-hermann-logo.jpg", + }, + { + name: "Vyaire", + link: "https://cbet.blob.core.windows.net/cbetblobs/Vyaire_Medical_Art.jpg", + }, + { + name: "SouthEastern Community College", + link: "https://cbet.blob.core.windows.net/cbetblobs/scc-logo.png", + }, + { + name: "iMed Biomedical", + link: "https://cbet.blob.core.windows.net/cbetblobs/iMed-Biomedical.png", + }, + { + name: "MiraCosta College", + link: "https://cbet.blob.core.windows.net/cbetblobs/mira-costa-college.png", + }, + { + name: "U.S. Army", + link: "https://cbet.blob.core.windows.net/cbetblobs/ArmyLogo.jpg", + }, + { + name: "Partnership for Youth Success", + link: "https://cbet.blob.core.windows.net/cbetblobs/REGISTEREDlogo.jpg", + }, + { + name: "Healthcare Technology Management Association of South Carolina", + link: "https://cbet.blob.core.windows.net/cbetblobs/HTMA-SC-logo.jpg", + }, + { + name: "Kentucky Association for Medical Instumentation", + link: "https://cbet.blob.core.windows.net/cbetblobs/kami-logo.png", + }, +] + +partnersList.sort((a, b) => { + if (a.name < b.name) { + return -1 + } + return 1 +}) + +const linkValidator = new RegExp( + /^((https?|ftp|smtp):\/\/)?(www.)?[a-z0-9]+\.[a-z]+(\/[a-zA-Z0-9#]+\/?)*$/ +) + +export default function CreateEdit(props) { + const { + register, + handleSubmit, + errors, + setValue, + reset, + unregister, + } = useForm() + const [htmlContent, setHtmlContent] = useState("") // html content for blog post + const [cbetContentCategory, setCbetContentCategory] = useState(1) // content Category + const [thumbnailUpload, setThumbnailUpload] = useState([]) // thumbnail image + const [cbetPartner, setCbetPartner] = useState("") // Partner dropdown list + const [cbetTitle, setCbetTitle] = useState("") // Title + const [featured, setFeatured] = useState(false) // is Featured? + const [status, setStatus] = useState("0") // Status: active/disabled + const [publishDate, setPublishDate] = useState("") // publish date + const [startDate, setStartDate] = useState("1/1/2020") // Event Start Date + const [endDate, setEndDate] = useState("1/1/2021") // Event End Date + const [author, setAuthor] = useState("") // Author text box + const [isSubmitting, setIsSubmitting] = useState(false) // is Submitting form + const [location, setLocation] = useState("") // Location text box + const [submitMessage, setSubmitMessage] = useState("") // Submit message label + const [isDone, setIsDone] = useState(false) // Done with submission + const [link, setLink] = useState("") // url link + const [cbetDescription, setCbetDescription] = useState("") // Description textarea + const [partnerLink, setPartnerLink] = useState("") // Partner link from array + const [initialHtmlContents, setInitialHtmlComments] = useState("") // initial HTML content + const [contentID, setContentID] = useState(0) // ID of cbet Content + // const authContent = useCbetAuth() // key for running azure function + const [editImageURL, setEditImageURL] = useState("") // link to Blog header image + + useEffect(() => { + register({ name: "cbetDropzone" }, { required: true }) + register( + { name: "publishDate" }, + { required: true, validate: (value) => Date.parse(value) !== isNaN } + ) + register({ name: "htmlContent" }, { required: true }) + setLocation("Unknown") + + // Set default dates for each date field + const dateF = new Date() + const day = dateF.getDate() + const monthIndex = dateF.getMonth() + const year = dateF.getFullYear() + + const month = + (monthIndex + 1).toString().length === 1 + ? `0${monthIndex + 1}` + : monthIndex + 1 + + const newDay = + day.toString().length === 1 ? `0${day.toString()}` : day.toString() + + console.log("date set in useEffect", `${month}/${newDay}/${year}`) + setValue("publishDate", `${month}/${newDay}/${year}`) + setPublishDate(`${month}/${newDay}/${year}`) + setStartDate(`${month}/${newDay}/${year}`) + setEndDate(`${month}/${newDay}/${year}`) + + if ( + props.location.state && + props.location.state.cbetContent !== undefined + ) { + const cbetContent = props.location.state.cbetContent + + // Blog header url + setEditImageURL(cbetContent.Thumbnail) + console.log("job,event,blog", cbetContent) + setContentID(cbetContent.Id) + + // job = 1, event = 2, blog = 3 + switch (cbetContent.Category) { + case 1: + setCbetContentCategory(cbetContent.CbetCategory_Id) + setAuthor(cbetContent.Author) + setCbetTitle(cbetContent.Title) + setCbetDescription(cbetContent.Description) + setFeatured(cbetContent.Featured) + setStatus(cbetContent.Status === true ? "1" : "0") + setLink(cbetContent.Link) + setPublishDate(cbetContent.StartDate) + setCbetPartner(cbetContent.PartnerName) + unregister("cbetDropzone") + unregister("htmlContent") + break + case 2: + unregister("cbetDropzone") + unregister("htmlContent") + setCbetContentCategory(cbetContent.CbetCategory_Id) + setCbetTitle(cbetContent.Title) + setCbetDescription(cbetContent.Description) + setLink(cbetContent.Link) + setStatus(cbetContent.Status === true ? "1" : "0") + setLocation(cbetContent.Location) + break + case 3: + setCbetContentCategory(cbetContent.CbetCategory_Id) + setCbetTitle(cbetContent.Title) + setStatus(cbetContent.Status === true ? "1" : "0") + setAuthor(cbetContent.Author) + setInitialHtmlComments(cbetContent.Description) + unregister("cbetDropzone") + break + default: + break + } + + if (props.location.state.create === true) { + clearFields() + } + } + }, []) + + function getTodaysDate() { + const dateF = new Date() + const day = dateF.getDate() + const monthIndex = dateF.getMonth() + const year = dateF.getFullYear() + + const month = monthIndex + 1 + return `${month}/${day}/${year}` + } + + const onSubmit = (data) => { + if (cbetContentCategory === 0) { + setSubmitMessage("Select a Cbet Category to save.") + return + } + + setIsSubmitting(true) + insertCbetContent(data) + } + + function getCategoryName(cbetCategoryNumber) { + switch (cbetCategoryNumber) { + case 1: + return "Job" + case 2: + return "Event" + case 3: + return "Blog" + default: + return "Category" + } + } + + function handleContentChange(content) { + console.log("html content is ", content) + setValue("htmlContent", content) + setHtmlContent(content) + } + + function handleTitleChange(e) { + e.preventDefault() + setCbetTitle(e.target.value) + } + + function handleCbetStatusChange(e) { + e.preventDefault() + setStatus(e.target.value === "1" ? "1" : "0") + } + + function uploadThumbnail(files) { + setValue("cbetDropzone", files) + setThumbnailUpload(files) + } + + function insertCbetContent(formData) { + // console.log("reached insert cbet content api call", formData) + + let cbetContent = {} + + switch (cbetContentCategory) { + case 1: // Job + cbetContent = { + ID: contentID, // number + ContentTitle: formData.title, // string + Description: cbetDescription, // string + Thumbnail: partnerLink, // string for url link from partner + PartnerName: cbetPartner, // string + Author: author, // string + ContentCreator: formData.signedInAuthor, // string + Status: status === "1" ? true : false, // string + CbetCategory: cbetContentCategory, // number + Link: link, // string - Event and Job only + StartDate: publishDate, // date + EndDate: publishDate, // date + Location: "", // string + Tags: "one,two", // string + Featured: featured, // bool + } + break + case 2: // Event + cbetContent = { + ID: contentID, // number + ContentTitle: formData.title, // string + Description: cbetDescription, // string + Thumbnail: "", + PartnerName: cbetPartner, // string + Author: author, // string + ContentCreator: formData.signedInAuthor, // string + Status: status === "1" ? true : false, // string + CbetCategory: cbetContentCategory, // number + Link: link, // string + StartDate: startDate, // date + EndDate: endDate, // date + Location: formData.location, // string - Event ONLY + Tags: "one,two", // string + Featured: featured, // bool + } + break + case 3: // Blog + cbetContent = { + ID: contentID, // number + ContentTitle: formData.title, // string + Description: htmlContent, // HTML for blog + Thumbnail: "", + PartnerName: cbetPartner, // string + Author: author, // string + ContentCreator: formData.signedInAuthor, // string + Status: status === "1" ? true : false, // string + CbetCategory: cbetContentCategory, // number + Link: link, // string + StartDate: publishDate, // date + EndDate: publishDate, // date + Location: "", // string + Tags: "one,two", // string + Featured: featured, // bool + } + break + default: + return "Cbet Category" + } + + console.log("payload", cbetContent) + + const payload = new FormData() + if (thumbnailUpload.length > 0) { + payload.append("file", thumbnailUpload[0]) + } else { + payload.append("file", null) + } + payload.append("cbetContent", JSON.stringify(cbetContent)) + + const myInit = { + method: "POST", + body: payload, + } + + try { + // const response = fetch("http://localhost:7071/api/GetCbetContent", myInit) + const response = fetch( + `https://cbetdata.azurewebsites.net/api/GetCbetContent?code=${authContent}`, + myInit + ) + + if (!response.ok) { + console.log("response not OK.") + } + + console.log("response is OK") + + setTimeout(() => { + setIsSubmitting(false) + clearFields() + setIsDone(true) + ClearDone() + }, 3000) + + // Adds Build hook fetch if any Blog is updated/created + // if (cbetContentCategory === 3) { + // const buildHookInit = { + // method: "POST", + // } + // fetch( + // "https://api.netlify.com/build_hooks/5ecebf26051d938410c0d4fc", + // buildHookInit + // ) + // } + } catch (e) { + console.log(`catch error: ${e}`) + } + } + + function ClearDone() { + setTimeout(() => { + setIsDone(false) + }, 5000) + } + + function handleCbetPartnerChange(e) { + e.preventDefault() + + const partner = partnersList.find( + (element) => element.name === e.target.value + ) + + setPartnerLink(partner.link) + setCbetPartner(e.target.value) + } + + function handleCbetCategoryChange(e) { + e.preventDefault() + + const CategorySelected = Number(e.target.value) + setCbetContentCategory(CategorySelected) + + if (CategorySelected !== 2) { + setLocation("Unknown") + } else { + setLocation("") + } + + if (CategorySelected === 1) { + unregister("cbetDropzone") + unregister("htmlContent") + unregister("startDate") + unregister("endDate") + // unregister("") + } else if (CategorySelected === 2) { + unregister("cbetDropzone") + unregister("htmlContent") + const newDate = getTodaysDate() + register( + { name: "startDate" }, + { required: true, validate: (value) => Date.parse(value) !== isNaN } + ) + register( + { name: "endDate" }, + { required: true, validate: (value) => Date.parse(value) !== isNaN } + ) + setValue("startDate", newDate) + setValue("endDate", newDate) + } else if (CategorySelected === 3) { + register({ name: "htmlContent" }, { required: true }) + } + } + + function handleLocation(e) { + setLocation(e.target.value) + } + + function handleAuthor(e) { + setAuthor(e.target.value) + } + + function handleLink(e) { + setLink(e.target.value) + } + + function handleCbetDescription(e) { + setCbetDescription(e.target.value) + } + + function getPublishDate(renderedDate) { + setValue("publishDate", renderedDate) + setPublishDate(renderedDate) + } + + function getStartDate(startDateCbet) { + setValue("startDate", startDateCbet) + setStartDate(startDateCbet) + } + + function getEndDate(endDateCbet) { + setValue("endDate", endDateCbet) + setEndDate(endDateCbet) + } + + function clearFields() { + reset() + setCbetTitle("") + setAuthor("") + setCbetPartner("0") + setHtmlContent("") + setLocation("") + setThumbnailUpload([]) + setLink("") + setCbetDescription("") + } + + console.log("props", props) return ( - <> -

I AM {title}

-

TODO: {title} go here

- + + + + {isDone ? ( + + + + {`${getCategoryName(cbetContentCategory)} is submitted`} + + + ) : null} + + + {getCategoryName(cbetContentCategory)} + + + + + + + + + {errors.category && "* Cbet Category is required"} + + + + {`Title of ${getCategoryName(cbetContentCategory)}`} + + + {errors.title && "* Title is required"} + + + + Status + + + + + + {errors.category && "* Cbet Category is required"} + + + + {/* Job only */} + {cbetContentCategory === 1 ? ( + + Partners + value !== "0", + })} + value={cbetPartner} + onChange={handleCbetPartnerChange} + > + + {partnersList.map((partner) => { + return + })} + + + {errors.author && "* Partner is required"} + + + ) : null} + + {/* Blog only */} + {cbetContentCategory !== 3 ? ( + + + Link + + IE: https://google.com + + } + > + linkValidator.test(value) === true, + })} + > + + + {errors.link && "* Valid Link is required"} + + + + ) : null} + + {cbetContentCategory !== 2 ? ( + + + Author + + + {errors.author && "* Author is required"} + + + + + + + Publish Date + + + + {errors.publishDate && "* Valid Date is required"} + + + + + ) : null} + + {/* Blog Header image */} + {cbetContentCategory === 3 ? ( + + + Blog Header image{" "} + + + + * Requires an image at 1440 x 300 pixels + + + 0} + editImageUrl={ + props.location.state && + props.location.state.cbetContent !== undefined + ? editImageURL + : null + } + > + + {errors.cbetDropzone && "* Blog Header image is required"} + + + + {thumbnailUpload ? ( +
    + {thumbnailUpload.map((file) => ( +
  • + + + {file.name} + +
  • + ))} +
+ ) : null} +
+ ) : null} + + {/* Submit Message here */} + {submitMessage.length > 1 ? ( + {submitMessage} + ) : null} + + {/* Save Cancel buttons */} + + + + + + + {isSubmitting === true ? ( + + ) : null} + + + + + + + + + {/* */} + + + + + {/* Job description */} + {cbetContentCategory === 1 ? ( + + + + Is Featured + + + { + setFeatured(true) + }} + active={featured === true} + href="link1" + style={{ width: "77px" }} + > + On + + { + setFeatured(false) + }} + style={{ width: "77px" }} + > + Off + + + + + + Job Description + + + + {errors.description && "* Description is required"} + + + + ) : null} + {/* Event */} + {cbetContentCategory === 2 ? ( + + + + + Start Date + + + + {errors.startDate && "* Start date must be a valid date."} + + + + + + + End Date + + + + {errors.endDate && "* End date must be a valid date."} + + + + + ) : null} + {/* Blog only */} + {cbetContentCategory === 3 ? ( + + + Blog content + + + + {errors.htmlContent && "* Content is required"} + + + + ) : null} + + {/* Event */} + + {cbetContentCategory === 2 ? ( + + {`Location of ${getCategoryName( + cbetContentCategory + )}`} + + + {errors.location && "* Location is required"} + + + ) : null} + + {/* + {({ accountInfo }) => { + return ( + + + User Logged in + + + + ) + }} + */} + + +
+
) } diff --git a/package-lock.json b/package-lock.json index 6bac7a1..b5581c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2078,8 +2078,7 @@ "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" }, "camelize": { "version": "1.0.0", @@ -3445,6 +3444,29 @@ "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", "dev": true }, + "file-loader": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.0.0.tgz", + "integrity": "sha512-/aMOAYEFXDdjG0wytpTL5YQLfZnnTmLNjn+AIrJ/6HVnTfDqLsVKUUwkDf4I4kgex36BvjuXEn/TX9B/1ESyqQ==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^2.6.5" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + } + } + }, "file-selector": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.1.12.tgz", @@ -5940,6 +5962,14 @@ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-5.7.2.tgz", "integrity": "sha512-bJvY348vayIvEUmSK7Fvea/NgqbT2racA2IbnJz/aPlQ3GBtaTeDITH6rtCa6y++obZzG6E3Q8VuoXPir7QYUg==" }, + "react-icons": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-3.10.0.tgz", + "integrity": "sha512-WsQ5n1JToG9VixWilSo1bHv842Cj5aZqTGiS3Ud47myF6aK7S/IUY2+dHcBdmkQcCFRuHsJ9OMUI0kTDfjyZXQ==", + "requires": { + "camelcase": "^5.0.0" + } + }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", diff --git a/package.json b/package.json index 073c8c5..941c611 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "react-dom": "^16.13.1", "react-dropzone": "^11.0.1", "react-hook-form": "^5.7.2", + "react-icons": "^3.10.0", "react-moment": "^0.9.7", "react-router-dom": "^5.2.0", "styled-components": "^5.1.1", @@ -50,6 +51,7 @@ "babel-loader": "^8.1.0", "css-loader": "^3.5.3", "dotenv-webpack": "^1.8.0", + "file-loader": "^6.0.0", "html-webpack-plugin": "^4.3.0", "style-loader": "^1.2.1", "webpack": "^4.43.0", diff --git a/webpack.config.js b/webpack.config.js index bc7aa77..0c0cd09 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -13,6 +13,11 @@ module.exports = { rules: [ { test: /\.(js)$/, use: "babel-loader" }, { test: /\.css$/, use: ["style-loader", "css-loader"] }, + { + test: /\.(png|jpg|gif|mp3|ico|svg)$/, + loader: 'file-loader', + options: {} + } ], }, mode: "development", diff --git a/webpack.production.config.js b/webpack.production.config.js index 1418979..ae8767a 100644 --- a/webpack.production.config.js +++ b/webpack.production.config.js @@ -12,6 +12,11 @@ module.exports = { rules: [ { test: /\.(js)$/, use: "babel-loader" }, { test: /\.css$/, use: ["style-loader", "css-loader"] }, + { + test: /\.(png|jpg|gif|mp3|ico|svg)$/, + loader: "file-loader", + options: {}, + }, ], }, mode: "development",