Skip to content

Commit

Permalink
Merge pull request #73 from fac30/feature/needs-ui
Browse files Browse the repository at this point in the history
Feature/needs UI
  • Loading branch information
maxitect authored Dec 11, 2024
2 parents f1f112d + 020619a commit 7195ac6
Show file tree
Hide file tree
Showing 8 changed files with 454 additions and 3 deletions.
278 changes: 278 additions & 0 deletions src/app/needs/components/NeedsDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
"use client";

import { useState, useEffect } from "react";
import { useDatabase } from "@/context/DatabaseContext";
import retrieveDataObject from "@/lib/utils/retrieveDataObject";
import { RxDocumentData } from "rxdb";
import NeedsSection from "./NeedsSection";
import NeedsModal from "./NeedsModal";

export interface Category {
id: string;
name: string;
}

export interface Need {
id: string;
name: string;
category: string;
}

interface CategorizedNeed {
category: string;
needs: Need[];
}

export default function NeedsDisplay() {
const database = useDatabase();
const [categories, setCategories] = useState<RxDocumentData<Category>[]>([]);
const [needs, setNeeds] = useState<RxDocumentData<Need>[]>([]);
const [urgent, setUrgent] = useState<number>(0);
const [effortful, setEffortful] = useState<number>(0);
const [worthDoing, setWorthDoing] = useState<number>(0);
const [positiveLabel, setPositiveLabel] = useState<string>("urgent");
const [negativeLabel, setNegativeLabel] = useState<string>("not urgent");

const fetchCategories = async () => {
const response = await database.getFromDb("needs_categories");
const categories = retrieveDataObject(response);

setCategories(categories);
};

const fetchNeeds = async () => {
const response = await database.getFromDb("needs");
const needs = retrieveDataObject(response);
setNeeds(needs);
};

useEffect(() => {
fetchCategories();
fetchNeeds();
}, []);

const categorisedNeeds: CategorizedNeed[] = categories.map((category) => {
const categoryNeeds = needs.filter((need) => need.category === category.id);
return { category: category.name, needs: categoryNeeds };
});

const [modalOpen, setModalOpen] = useState(false);
const [selectedNeed, setSelectedNeed] = useState("");
const [needsStep, setNeedsStep] = useState(1);

const handleOpen = (need: string) => {
setModalOpen(true);
setSelectedNeed(need);
};

const handleStepIncrease = () => {
setNeedsStep((prevStep) => prevStep + 1);
};
const handleBackClick = () => {
setNeedsStep((prevStep) => prevStep - 1);
{
if (needsStep === 2 && urgent > 0) {
handleDecrease(setUrgent);
} else if (needsStep === 2 && urgent < 0) {
handleIncrease(setUrgent);
} else if (needsStep === 3 && effortful > 0) {
handleDecrease(setEffortful);
} else if (needsStep === 3 && effortful < 0) {
handleIncrease(setEffortful);
}
}
};

const handleIncrease = (
setter: React.Dispatch<React.SetStateAction<number>>
) => {
setter((prev) => prev + 1);
};
const handleDecrease = (
setter: React.Dispatch<React.SetStateAction<number>>
) => {
setter((prev) => prev - 1);
};

const updateNeedWithAction = async (needName: string, action: string) => {
try {
const selectedNeed = needs.find((need) => need.name === needName);

if (selectedNeed) {
const docId = selectedNeed.id;

await database.updateDocument("needs", docId, "mood", action);
await database.updateDocument(
"needs",
docId,
"selectedExpiry",
new Date(Date.now() + 6 * 60 * 60 * 1000).toISOString()
);
console.log(`Updated ${needName} with action: ${action}`);
} else {
console.log(`Need ${needName} not found`);
}
} catch (error) {
console.error(`Failed to update need: ${needName}`, error);
}
};

const handlePositiveClick = () => {
if (needsStep === 1) {
handleIncrease(setUrgent);
} else if (needsStep === 2) {
handleIncrease(setEffortful);
} else if (needsStep === 3) {
handleIncrease(setWorthDoing);

setModalOpen(false);
setNeedsStep(1);
}
};

const handleNegativeClick = () => {
if (needsStep === 1) {
handleDecrease(setUrgent);
} else if (needsStep === 2) {
handleDecrease(setEffortful);
} else if (needsStep === 3) {
handleDecrease(setWorthDoing);

setModalOpen(false);
setNeedsStep(1);
}
};

const resetNeuros = () => {
setUrgent(0);
setEffortful(0);
setWorthDoing(0);
};

const handleLabelChange = () => {
switch (needsStep) {
case 1:
setPositiveLabel("urgent");
setNegativeLabel("not urgent");
break;
case 2:
setPositiveLabel("A lot of effort");
setNegativeLabel("A little effort");
break;
case 3:
setPositiveLabel("worth doing");
setNegativeLabel("not worth doing");
break;
}
};

const handleStepAction = () => {
handleStepIncrease();
};

useEffect(() => {
handleLabelChange();
console.log(`selected need: ${selectedNeed}`);
console.log(`urgency: ${urgent}`);
console.log(`effort: ${effortful}`);
console.log(`worthDoing: ${worthDoing}`);
}, [needsStep]);

useEffect(() => {
if (modalOpen) {
document.body.style.overflow = "hidden";
} else {
document.body.style.overflow = "auto";
}

return () => {
document.body.style.overflow = "auto";
};
}, [modalOpen]);

useEffect(() => {
if (urgent !== 0 && effortful !== 0 && worthDoing !== 0) {
const action = determineAction(urgent, effortful, worthDoing);
updateNeedWithAction(selectedNeed, action);
}
resetNeuros();
}, [worthDoing]);

function determineAction(
urgent: number,
effortful: number,
worthDoing: number
): string {
switch (true) {
case urgent === 1 && effortful === 1 && worthDoing === 1:
console.log("interest");
return "interest";
case urgent === -1 && effortful === -1 && worthDoing === -1:
console.log("guilt");
return "guilt";
case urgent === 1 && effortful === -1 && worthDoing === -1:
console.log("freeze");
return "freeze";
case urgent === 1 && effortful === 1 && worthDoing === -1:
console.log("fight/flight");
return "fight/flight";
case urgent === 1 && effortful === -1 && worthDoing === 1:
console.log("joy");
return "joy";
case urgent === -1 && effortful === -1 && worthDoing === 1:
console.log("content");
return "content";
case urgent === -1 && effortful === 1 && worthDoing === 1:
console.log("relief");
return "relief";
case urgent === -1 && effortful === 1 && worthDoing === -1:
console.log("distress");
return "distress";
default:
return "Invalid input";
}
}

const handleCloseClick = () => {
resetNeuros();
setModalOpen(false);
setNeedsStep(1);
};

return (
<>
<div className="w-11/12 m-auto">
{categorisedNeeds.map((categoryData, index) => {
return (
<NeedsSection
key={index}
categoryData={categoryData}
handleOpen={handleOpen}
/>
);
})}
</div>
<NeedsModal
modalOpen={modalOpen}
forwardButton={{
label: positiveLabel,
action: () => {
handleStepAction();
handlePositiveClick();
},
}}
backButton={{
label: negativeLabel,
action: () => {
handleStepAction();
handleNegativeClick();
},
}}
title={`You have selected ~${selectedNeed}~`}
needsStep={needsStep}
handleBackClick={handleBackClick}
handleCloseClick={handleCloseClick}
/>
</>
);
}
84 changes: 84 additions & 0 deletions src/app/needs/components/NeedsModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import Button from "@/ui/shared/Button";
import { ChevronLeftIcon } from "@heroicons/react/24/outline";
import { XMarkIcon } from "@heroicons/react/24/outline";

interface ModalProps {
inputModal?: boolean;
placeholder?: string;
modalOpen: boolean;
title?: string;
forwardButton?: {
label: string;
action: () => void;
};
backButton?: {
label: string;
action: () => void;
};
handleInputChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
needsStep: number;
handleBackClick: () => void;
handleCloseClick: () => void;
}

export default function NeedsModal({
modalOpen,
title,
forwardButton,
backButton,
needsStep,
handleBackClick,
handleCloseClick,
}: ModalProps) {
return (
<>
{modalOpen && (
<div
className="w-11/12 absolute top-1/2 left-1/2 bg-gray-800 border-[1.5px] rounded-lg -translate-x-1/2 -translate-y-1/2"
style={{
position: "fixed",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
zIndex: 1000,
overflow: "hidden",
}}
>
<p className="text-center">step {needsStep} of 3</p>
{needsStep > 1 && (
<button className="absolute left-2 top-3" onClick={handleBackClick}>
<ChevronLeftIcon className="h-10 w-10" />
</button>
)}
<button className="absolute right-2 top-3" onClick={handleCloseClick}>
<XMarkIcon className="h-10 w-10" />
</button>

<div className="flex flex-col w-full items-center py-10 justify-between h-full ">
<p className="text-xl w-10/12 text-center mb-5">{title}</p>
<p className="text-md w-10/12 text-center mb-10">
Select the button that best describes meeting this need right now.
</p>

<div className="flex justify-center gap-10 w-2/3">
{backButton && (
<Button
onClick={backButton.action}
label={backButton.label}
className="text-xl font-normal w-36 h-48 bg-twd-secondary-purple text-balance rounded-none"
/>
)}
{forwardButton && (
<Button
onClick={forwardButton.action}
label={forwardButton.label}
className="bg-twd-primary-purple text-xl font-normal w-36 text-balance rounded-none"
/>
)}
</div>
</div>
</div>
)}
</>
);
}
33 changes: 33 additions & 0 deletions src/app/needs/components/NeedsSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Button from "@/ui/shared/Button";
import { Need } from "./NeedsDisplay";

interface NeedsSectionProps {
categoryData: {
category: string;
needs: Need[];
};
handleOpen: (needName: string) => void;
}

export default function NeedsSection({
categoryData,
handleOpen,
}: NeedsSectionProps) {
return (
<div>
<h2 className="text-xl mb-5 font-semibold">{categoryData.category}</h2>
<div className="flex gap-5 flex-wrap mb-10">
{categoryData.needs.map((need: Need, needIndex: number) => {
return (
<Button
key={needIndex}
label={need.name}
className="bg-gray-600 font-normal text-nowrap"
onClick={() => handleOpen(need.name)}
/>
);
})}
</div>
</div>
);
}
Loading

0 comments on commit 7195ac6

Please sign in to comment.