diff --git a/src/components/collapsible/index.tsx b/src/components/collapsible/index.tsx index 6eea44c..2c11a98 100644 --- a/src/components/collapsible/index.tsx +++ b/src/components/collapsible/index.tsx @@ -5,13 +5,15 @@ import {FaAngleDown, FaAngleUp } from "react-icons/fa"; //This is collapsible component, use it as if you are using any pre-designed component //Specify the Style of collapsible component as if you were styling a div using style prompt -const Collapsible = ({style = {}, label, children}) => { +const Collapsible = ({style = {}, titleStyle={}, label, children}) => { const [isOpen, setOpen] = useState(false) const contentRef = useRef(null) const toogle = () => {setOpen(!isOpen)} return (
-
- +
diff --git a/src/components/dashboard/ContentForm.tsx b/src/components/dashboard/ContentForm.tsx index 303c1c8..68ebc58 100644 --- a/src/components/dashboard/ContentForm.tsx +++ b/src/components/dashboard/ContentForm.tsx @@ -1,13 +1,16 @@ import "@fontsource/inter"; import React, { useEffect, useRef, useState } from "react"; -import { Button, Col, Container, Form, Row } from "react-bootstrap"; +import { Button, Col, Container, Dropdown, Form, Row } from "react-bootstrap"; // import grabberIcon from "../../assets/svg/grabber.svg"; import { ReactComponent as GrabberIcon } from "../../assets/svg/grabber.svg"; import { ReactComponent as PencilIcon } from "../../assets/svg/pencil.svg"; import { ReactComponent as CheckmarkIcon } from "../../assets/svg/checkmark.svg"; import styles from "./ContentForm.module.css"; +import ChartComponentForm from "components/subcomponents/chartcomponents/ChartComponentForm"; +import Collapsible from "components/collapsible"; +import ProviderGallery from "./ProviderGallery"; const EditableText = ({ text, setText, isEditing, setIsEditing }) => { const inputRef = useRef(null); @@ -97,8 +100,9 @@ const SectionCard = ({ isEditing, setIsEditing, }) => { + const [components, setComponents] = useState([]); return ( - + + {components.map((v, i) => + + {v} + + )} - {index} + + + + Add Filter + + + + + setComponents([...components, + + + + ])}>Chart + + setComponents([...components, + + + + ])}>Gallery + + ); @@ -179,9 +229,8 @@ const SectionButton = ({ maxWidth: "12px", borderTopLeftRadius: "8px", borderBottomLeftRadius: "8px", - backgroundColor: `${ - isSelected ? "#226DFF" : "transparent" - }`, + backgroundColor: `${isSelected ? "#226DFF" : "transparent" + }`, }} > { style={{ backgroundColor: "#E3E9F5", padding: "16px 16px 28px 16px", + width: "calc(100% - 264px)" }} > {selectedSection === null ? null : ( diff --git a/src/components/dashboard/ProviderGallery.tsx b/src/components/dashboard/ProviderGallery.tsx new file mode 100644 index 0000000..a444f20 --- /dev/null +++ b/src/components/dashboard/ProviderGallery.tsx @@ -0,0 +1,97 @@ +import React, { useState, useEffect } from "react"; +import ProviderGallerySlide from "./ProviderGallerySlide"; +import { storage } from "../../store"; + +interface GallerySlide { + title: string; + description: string; + imgLink: string; +} + +export default function ProviderGallery({ + slidesArray = [], +}: { + slidesArray?: GallerySlide[]; +}) { + const [slides, setSlides] = useState(slidesArray); + + const defaultSlide: GallerySlide = { + title: "", + description: "", + imgLink: "", + }; + + useEffect(() => { + if (!slides || slides.length === 0) { + setSlides([{ ...defaultSlide }]); + } + }, [slides]); + + const handleSlideDataChange = ( + index: number, + field: keyof GallerySlide, + e: React.ChangeEvent + ) => { + const newSlides = slides.map((slide, i) => + i === index ? { ...slide, [field]: e.target.value } : slide + ); + setSlides(newSlides); + }; + + const handleDelete = (index: number) => { + if (slides.length > 1) { + const newSlides = slides.filter((_, i) => i !== index); + setSlides(newSlides); + } + }; + + const handleAdd = (index: number) => { + const newSlides = [...slides]; + const newSlide = { ...defaultSlide }; + + if (index === newSlides.length - 1) { + newSlides.push(newSlide); + } else { + newSlides.splice(index + 1, 0, newSlide); + } + setSlides(newSlides); + }; + + const handleUpload = async (file, index: number) => { + const filename = file.name; + const fileRef = storage.ref("images").child(filename); + + await fileRef.put(file); + const url = await fileRef.getDownloadURL(); + + const newSlides = slides.map((slide, i) => + i === index ? { ...slide, imgLink: url } : slide + ); + setSlides(newSlides); + }; + + const renderSlides = () => { + return slides.map((slide, i) => ( + + )); + }; + + return ( +
+ {renderSlides()} + {/*TO BE DELETED */} +
+

Current Data:

+
{JSON.stringify(slides, null, 2)}
+
+
+ ); +} diff --git a/src/components/dashboard/ProviderGalleryCarousel.tsx b/src/components/dashboard/ProviderGalleryCarousel.tsx new file mode 100644 index 0000000..dd0f1f5 --- /dev/null +++ b/src/components/dashboard/ProviderGalleryCarousel.tsx @@ -0,0 +1,181 @@ +import React, { useState } from "react"; + +interface GallerySlide { + title: string; + description: string; + imgLink: string; +} + +export default function ProviderGalleryCarousel({ + slidesArray, +}: { + slidesArray: GallerySlide[]; +}) { + const [currentIndex, setCurrentIndex] = useState(0); + + const goToPrevious = () => { + setCurrentIndex((prevIndex) => + prevIndex === 0 ? slidesArray.length - 1 : prevIndex - 1 + ); + }; + + const goToNext = () => { + setCurrentIndex((prevIndex) => + prevIndex === slidesArray.length - 1 ? 0 : prevIndex + 1 + ); + }; + + const isActive = (index: number) => currentIndex === index; + + return ( +
+
+ + + {/* Card */} +
+ {/*Left-side */} +
+ {/* Card Title */} +
+

+ {slidesArray[currentIndex].title} +

+
+ + {/* Card Description */} +
+

+ {slidesArray[currentIndex].description} +

+
+
+ {/*Right-side*/} +
+ {slidesArray[currentIndex].title} +
+
+ +
+ + {/* bubbles */} +
+ {slidesArray.map((_, index) => ( +
+ ))} +
+
+ ); +} diff --git a/src/components/dashboard/ProviderGallerySlide.tsx b/src/components/dashboard/ProviderGallerySlide.tsx new file mode 100644 index 0000000..a79440a --- /dev/null +++ b/src/components/dashboard/ProviderGallerySlide.tsx @@ -0,0 +1,176 @@ +import React, { useState } from "react"; +import Form from "react-bootstrap/Form"; +import { Button } from "react-bootstrap"; +import Dropzone from "react-dropzone"; + +export default function ProviderGallerySlide({ + title, + description, + imgLink, // Opens use of imgLink in slide for editing maybe? + index, + handleSlideDataChange, + handleDelete, + handleAdd, + handleUpload, +}) { + return ( +
+
+ {`Slide ${index + 1}`} + + + Title * + + + handleSlideDataChange(index, "title", e) + } + style={{ borderColor: "#D9D9D9" }} + required + /> + + + Description + + handleSlideDataChange(index, "description", e) + } + style={{ borderColor: "#D9D9D9" }} + /> + + + Upload file + + +
+
+
+ +
+ +
+
+ ); +} + +const ImageModal = ({ handleSuccess, index }) => { + const [uploaded, setUploaded] = useState(false); + const [image, setImage] = useState(null); + + const handleDrop = (img) => { + setImage(img[0]); + setUploaded(true); + }; + + return ( +
+ {!uploaded && ( + + {({ getRootProps, getInputProps }) => ( +
+ +

+ Drop your image here, or{" "} + browse +

+

Supports JPG, JPEG2000, PNG

+
+ )} +
+ )} + {uploaded && ( +
+
+ description +
+ + +
+ )} +
+ ); +}; diff --git a/src/components/dashboard/RowForm.tsx b/src/components/dashboard/RowForm.tsx index c279b47..5a970ec 100644 --- a/src/components/dashboard/RowForm.tsx +++ b/src/components/dashboard/RowForm.tsx @@ -19,6 +19,33 @@ import Button from "react-bootstrap/Button"; import ActionForm from "./ActionForm"; import ContentForm from "./ContentForm"; +const galleryData = [ + { + title: "testVal1", + description: "testing testing", + imgLink: + "https://images.squarespace-cdn.com/content/v1/54822a56e4b0b30bd821480c/45ed8ecf-0bb2-4e34-8fcf-624db47c43c8/Golden+Retrievers+dans+pet+care.jpeg", + }, + { + title: "testVal2", + description: "testing testing", + imgLink: + "https://images.squarespace-cdn.com/content/v1/54822a56e4b0b30bd821480c/45ed8ecf-0bb2-4e34-8fcf-624db47c43c8/Golden+Retrievers+dans+pet+care.jpeg", + }, + { + title: "testVal3", + description: "testing testing", + imgLink: + "https://images.squarespace-cdn.com/content/v1/54822a56e4b0b30bd821480c/45ed8ecf-0bb2-4e34-8fcf-624db47c43c8/Golden+Retrievers+dans+pet+care.jpeg", + }, + { + title: "testVal4", + description: "testing testing", + imgLink: + "https://images.squarespace-cdn.com/content/v1/54822a56e4b0b30bd821480c/45ed8ecf-0bb2-4e34-8fcf-624db47c43c8/Golden+Retrievers+dans+pet+care.jpeg", + }, +]; + function validURL(str) { const pattern = new RegExp( "^(https?:\\/\\/)?" + // protocol @@ -27,7 +54,7 @@ function validURL(str) { "(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*" + // port and path "(\\?[;&a-z\\d%_.~+=-]*)?" + // query string "(\\#[-a-z\\d_]*)?$", - "i", + "i" ); // fragment locator return !!pattern.test(str); } @@ -248,9 +275,9 @@ const RowForm = (props) => { style={{ color: isValidNumberForRegion( parseIncompletePhoneNumber( - item.phoneNum[0], + item.phoneNum[0] ), - "US", + "US" ) ? "green" : "red", @@ -258,9 +285,9 @@ const RowForm = (props) => { > {isValidNumberForRegion( parseIncompletePhoneNumber( - item.phoneNum[0], + item.phoneNum[0] ), - "US", + "US" ) ? "Valid number" : "Invalid number"} @@ -387,7 +414,7 @@ const RowForm = (props) => { }} /> - ), + ) )} ); @@ -411,7 +438,7 @@ const RowForm = (props) => { }} /> - ), + ) )} ); @@ -437,7 +464,7 @@ const RowForm = (props) => { }} /> - ), + ) )} ); diff --git a/src/components/subcomponents/ProviderInfo.tsx b/src/components/subcomponents/ProviderInfo.tsx index 0f86a87..d9f2479 100644 --- a/src/components/subcomponents/ProviderInfo.tsx +++ b/src/components/subcomponents/ProviderInfo.tsx @@ -11,6 +11,64 @@ import ReadMoreAndLess from "react-read-more-less"; import LazyLoad from "react-lazy-load"; import { GOOGLE_API_KEY } from "../../config/keys"; import Linkify from "react-linkify"; +import ProviderGalleryCarousel from "components/dashboard/ProviderGalleryCarousel"; +import Collapsible from "components/collapsible"; + +{ + /*TO BE DELETED */ +} +const galleryData = [ + { + title: "Urban Tree Fundraiser", + description: + "Last Friday, we gathered for food, fun, and giving back at Urban Tree cidery. All proceeds from the evening went to our Bereavement fund. Everyone remembered to bring a sweater because the back deck got cold. We enjoyed drinks, games, and more!", + imgLink: + "https://images.squarespace-cdn.com/content/v1/54822a56e4b0b30bd821480c/45ed8ecf-0bb2-4e34-8fcf-624db47c43c8/Golden+Retrievers+dans+pet+care.jpeg", + }, + { + title: "testVal2", + description: "testing testing", + imgLink: + "https://images.squarespace-cdn.com/content/v1/54822a56e4b0b30bd821480c/45ed8ecf-0bb2-4e34-8fcf-624db47c43c8/Golden+Retrievers+dans+pet+care.jpeg", + }, + { + title: "testVal3", + description: "testing testing", + imgLink: + "https://static.vecteezy.com/system/resources/thumbnails/005/857/332/small_2x/funny-portrait-of-cute-corgi-dog-outdoors-free-photo.jpg", + }, + { + title: "testVal4", + description: "testing testing", + imgLink: + "https://images.squarespace-cdn.com/content/v1/54822a56e4b0b30bd821480c/45ed8ecf-0bb2-4e34-8fcf-624db47c43c8/Golden+Retrievers+dans+pet+care.jpeg", + }, + { + title: "testVal1", + description: "testing testing", + imgLink: + "https://images.squarespace-cdn.com/content/v1/54822a56e4b0b30bd821480c/45ed8ecf-0bb2-4e34-8fcf-624db47c43c8/Golden+Retrievers+dans+pet+care.jpeg", + }, + { + title: "testVal2", + description: "testing testing", + imgLink: + "https://static.vecteezy.com/system/resources/thumbnails/005/857/332/small_2x/funny-portrait-of-cute-corgi-dog-outdoors-free-photo.jpg", + }, + { + title: "testVal3", + description: "testing testing", + imgLink: + "https://images.squarespace-cdn.com/content/v1/54822a56e4b0b30bd821480c/45ed8ecf-0bb2-4e34-8fcf-624db47c43c8/Golden+Retrievers+dans+pet+care.jpeg", + }, + { + title: "testVal4", + description: "testing testing", + imgLink: + "https://images.squarespace-cdn.com/content/v1/54822a56e4b0b30bd821480c/45ed8ecf-0bb2-4e34-8fcf-624db47c43c8/Golden+Retrievers+dans+pet+care.jpeg", + }, +]; + const ProviderInfo = (props) => { const [image, setImage] = useState("bog"); @@ -23,7 +81,7 @@ const ProviderInfo = (props) => { try { const res2 = await fetch( `https://maps.googleapis.com/maps/api/staticmap?center=${props.item.latitude},${props.item.longitude}&zoom=16&scale=2&size=335x250&maptype=roadmap&key=${GOOGLE_API_KEY}&format=png&visual_refresh=true` + - `&markers=${props.item.latitude},${props.item.longitude}`, + `&markers=${props.item.latitude},${props.item.longitude}`, ); setStreetView(res2.url); setImage(props.item.imageURL); @@ -101,7 +159,7 @@ const ProviderInfo = (props) => { index === props.item.address.toString().split(",") .length - - 1 + 1 ) { return (
@@ -163,6 +221,14 @@ const ProviderInfo = (props) => { + + + + {/*TO BE DELETED */} + + + +
{categoriesToUse .filter( @@ -181,7 +247,7 @@ const ProviderInfo = (props) => { if ( index !== props.item[category.id].length - - 1 + 1 ) { return (
@@ -230,9 +296,9 @@ function calculateHours(props) { !props.item.hours[days[i]] || !props.item.hours[days[i - 1]] || props.item.hours[days[i]][0] !== - props.item.hours[days[i - 1]][0] || + props.item.hours[days[i - 1]][0] || props.item.hours[days[i]][1] !== - props.item.hours[days[i - 1]][1] + props.item.hours[days[i - 1]][1] ) { startandFinish.push(i - 1); startandFinish.push(i); @@ -267,13 +333,13 @@ function calculateHours(props) { {props.item.hours[days[startandFinish[i]]] ? props.item.hours[days[startandFinish[i]]].map( - (time, index) => - formatTime( - props.item.hours[days[startandFinish[i]]], - time, - index, - ), - ) + (time, index) => + formatTime( + props.item.hours[days[startandFinish[i]]], + time, + index, + ), + ) : "CLOSED"} , ); diff --git a/src/components/subcomponents/chartcomponents/ChartComponentForm.tsx b/src/components/subcomponents/chartcomponents/ChartComponentForm.tsx index 344b308..0249fd3 100644 --- a/src/components/subcomponents/chartcomponents/ChartComponentForm.tsx +++ b/src/components/subcomponents/chartcomponents/ChartComponentForm.tsx @@ -500,7 +500,7 @@ const ChartComponentForm = () => { return (
-

Chart

+ {/*

Chart

*/}