Skip to content

Commit

Permalink
Org dashboard geochart (#730)
Browse files Browse the repository at this point in the history
* Org Dashboard Geo Chart

* Add geo chart for countries
* Rework chart responsiveness
* Add eduction pie chart
* Dashboard cards arrangement

* Remove commented code

* Fix type error

* Refactor skills & line chart legends

* Remove id prop from Line chart

* Carousel Pagination

* Carousel dynamic loading
* Carousel total count
* Org admin Opportunity mobile card styling
* Remove hover from Opps and Marketplace cards
* Add hover to Opp card in grid view

* Remove hover from Opp cards on grid view

* Remove hover effect from all card components
  • Loading branch information
Matthew-Baird authored Apr 17, 2024
1 parent c01eb11 commit c4abc15
Show file tree
Hide file tree
Showing 16 changed files with 652 additions and 635 deletions.
File renamed without changes
1 change: 1 addition & 0 deletions src/web/src/api/models/organizationDashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export interface OrganizationDemographic {
countries: Demographic;
genders: Demographic;
ages: Demographic;
education: Demographic;
}

export interface Demographic {
Expand Down
4 changes: 4 additions & 0 deletions src/web/src/api/services/organizationDashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ export const searchOrganizationEngagement = async (
legend: "ages",
items: { item1: 100, item2: 200 },
},
education: {
legend: "education",
items: { item1: 100, item2: 200 },
},
},
dateStamp: "2021-12-01",
};
Expand Down
2 changes: 1 addition & 1 deletion src/web/src/components/Marketplace/ItemCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const ItemCardComponent: React.FC<InputProps> = ({
return (
<Link
key={id}
className="relative ml-1 flex aspect-square h-56 w-full transform-gpu flex-col flex-wrap items-start justify-start gap-4 overflow-hidden rounded-lg bg-white p-4 shadow-sm transition-transform hover:scale-[1.01] hover:shadow-md md:ml-0 md:w-[340px]"
className="relative ml-1 flex aspect-square h-56 w-full flex-col flex-wrap items-start justify-start gap-4 overflow-hidden rounded-lg bg-white p-4 shadow-sm md:ml-0 md:w-[340px]"
href={href ?? "/"}
onClick={onClick2}
onAuxClick={onClick2}
Expand Down
2 changes: 1 addition & 1 deletion src/web/src/components/Marketplace/TransactionItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const TransactionItemComponent: React.FC<{
return (
<button
key={key}
className="relative flex aspect-square h-64 w-[300px] transform-gpu flex-col gap-1 rounded-lg bg-white p-5 shadow-sm transition-all duration-300 hover:scale-[1.01] hover:shadow-md"
className="relative flex aspect-square h-64 w-[300px] flex-col gap-1 rounded-lg bg-white p-5 shadow-sm"
onClick={onClick}
>
<div className="flex flex-row">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const OpportunityListItem: React.FC<{
return (
<Link
href={`/opportunities/${data.opportunityId}`}
className="flex cursor-pointer flex-col gap-1 rounded-lg border-none border-gray bg-white p-4 shadow-custom duration-300 hover:scale-[1.005] hover:shadow-lg"
className="flex cursor-pointer flex-col gap-1 rounded-lg border-none border-gray bg-white p-4 shadow-custom"
>
<div className="mb-2 flex flex-row gap-2">
<AvatarImage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import Moment from "react-moment";
import { DATE_FORMAT_HUMAN } from "~/lib/constants";
import { IoMdPause, IoMdPlay, IoMdClose } from "react-icons/io";
import { AvatarImage } from "../AvatarImage";

interface InputProps {
data: OpportunityInfo;
[key: string]: any;
Expand All @@ -18,7 +17,7 @@ const OpportunityPublicSmallComponent: React.FC<InputProps> = ({ data }) => {
return (
<Link
href={`/opportunities/${data.id}`}
className="relative flex aspect-square h-[19rem] w-max flex-col gap-1 rounded-lg bg-white p-4 shadow-sm transition-all duration-300 md:ml-0 md:hover:scale-[1.01] md:hover:shadow-md"
className="relative flex aspect-square h-[19rem] w-max flex-col gap-1 rounded-lg bg-white p-4 shadow-sm md:ml-0"
>
<div className="flex flex-row">
<div className="flex flex-row">
Expand Down
132 changes: 127 additions & 5 deletions src/web/src/components/Organisation/Dashboard/DashboardCarousel.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from "react";
import type { EmblaOptionsType } from "embla-carousel";
import React, { useState, useRef, useEffect, useCallback } from "react";
import type { EmblaCarouselType, EmblaOptionsType } from "embla-carousel";
import type { EngineType } from "embla-carousel/components/Engine";
import {
PrevButton,
NextButton,
Expand All @@ -20,6 +21,8 @@ import { YouthCompletedCard } from "./YouthCompletedCard";
interface PropType {
slides: OpportunityInfoAnalytics[] | YouthInfo[];
orgId: string;
loadData: (startRow: number) => Promise<any>;
totalSildes: number;
}

const OPTIONS: EmblaOptionsType = {
Expand All @@ -30,8 +33,56 @@ const OPTIONS: EmblaOptionsType = {
};

const DashboardCarousel: React.FC<PropType> = (props: PropType) => {
const { slides, orgId } = props;
const [emblaRef, emblaApi] = useEmblaCarousel(OPTIONS);
const { orgId, loadData, totalSildes } = props;
const scrollListenerRef = useRef<() => void>(() => undefined);
const listenForScrollRef = useRef(true);
const hasMoreToLoadRef = useRef(true);
const [slides, setSlides] = useState(props.slides);
const [hasMoreToLoad, setHasMoreToLoad] = useState(true);
const [loadingMore, setLoadingMore] = useState(false);

const [emblaRef, emblaApi] = useEmblaCarousel({
...OPTIONS,
watchSlides: (emblaApi) => {
const reloadEmbla = (): void => {
const oldEngine = emblaApi.internalEngine();

emblaApi.reInit();
const newEngine = emblaApi.internalEngine();
const copyEngineModules: (keyof EngineType)[] = [
"location",
"target",
"scrollBody",
];
copyEngineModules.forEach((engineModule) => {
Object.assign(newEngine[engineModule], oldEngine[engineModule]);
});

newEngine.translate.to(oldEngine.location.get());
const { index } = newEngine.scrollTarget.byDistance(0, false);
newEngine.index.set(index);
newEngine.animation.start();

setLoadingMore(false);
listenForScrollRef.current = true;
};

const reloadAfterPointerUp = (): void => {
emblaApi.off("pointerUp", reloadAfterPointerUp);
reloadEmbla();
};

const engine = emblaApi.internalEngine();

if (hasMoreToLoadRef.current && engine.dragHandler.pointerDown()) {
const boundsActive = engine.limit.reachedMax(engine.target.get());
engine.scrollBounds.toggleActive(boundsActive);
emblaApi.on("pointerUp", reloadAfterPointerUp);
} else {
reloadEmbla();
}
},
});

const {
prevBtnDisabled,
Expand All @@ -42,6 +93,68 @@ const DashboardCarousel: React.FC<PropType> = (props: PropType) => {

const { selectedSnap, snapCount } = useSelectedSnapDisplay(emblaApi);

const onScroll = useCallback(
(emblaApi: EmblaCarouselType) => {
if (!listenForScrollRef.current) return;

setLoadingMore((loadingMore) => {
const lastSlide = emblaApi.slideNodes().length - 1;
const lastSlideInView = emblaApi.slidesInView().includes(lastSlide);
let loadMore = !loadingMore && lastSlideInView;

if (emblaApi.slideNodes().length < 1) {
loadMore = false;
}

if (loadMore) {
listenForScrollRef.current = false;

console.warn(
`Loading more data... ${lastSlide} lastSlideInView: ${lastSlideInView} nextStartRow: ${
emblaApi.slideNodes().length + 1
}`,
);

loadData(emblaApi.slideNodes().length + 1).then((data) => {
// debugger;
if (data.items.length == 0) {
setHasMoreToLoad(false);
emblaApi.off("scroll", scrollListenerRef.current);
}

setSlides((prevSlides) => [...prevSlides, ...data.items]);
});
}

return loadingMore || lastSlideInView;
});
},
[loadData],
);

const addScrollListener = useCallback(
(emblaApi: EmblaCarouselType) => {
scrollListenerRef.current = () => onScroll(emblaApi);
emblaApi.on("scroll", scrollListenerRef.current);
},
[onScroll],
);

useEffect(() => {
if (!emblaApi) return;
addScrollListener(emblaApi);

const onResize = () => emblaApi.reInit();
window.addEventListener("resize", onResize);
emblaApi.on("destroy", () =>
window.removeEventListener("resize", onResize),
);
}, [emblaApi, addScrollListener]);

useEffect(() => {
hasMoreToLoadRef.current = hasMoreToLoad;
}, [hasMoreToLoad]);

return (
<div>
<div className="embla">
Expand All @@ -67,6 +180,15 @@ const DashboardCarousel: React.FC<PropType> = (props: PropType) => {
</div>
</div>
))}
{hasMoreToLoad && (
<div
className={"embla-infinite-scroll".concat(
loadingMore ? " embla-infinite-scroll--loading-more" : "",
)}
>
<span className="embla-infinite-scroll__spinner" />
</div>
)}
</div>
</div>

Expand All @@ -75,7 +197,7 @@ const DashboardCarousel: React.FC<PropType> = (props: PropType) => {
<div className="flex justify-center gap-4">
<SelectedSnapDisplay
selectedSnap={selectedSnap}
snapCount={slides.length ?? snapCount}
snapCount={totalSildes ?? slides.length}
/>
<PrevButton
onClick={onPrevButtonClick}
Expand Down
Loading

0 comments on commit c4abc15

Please sign in to comment.