Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/needs UI #73

Merged
merged 49 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
780ca18
feat: ui for needs
jackcasstlesjones Dec 10, 2024
088fed8
feat: add subtitle and remove needssection
jackcasstlesjones Dec 10, 2024
34d7e6e
Merge branch 'main' into feature/needs-ui
jackcasstlesjones Dec 10, 2024
87ad6ea
feat: add mock data
jackcasstlesjones Dec 10, 2024
7139537
chore: change div to fragment
jackcasstlesjones Dec 10, 2024
e9079b3
feat: get data but not working properly
jackcasstlesjones Dec 10, 2024
57b2e06
chore: add key"
jackcasstlesjones Dec 10, 2024
3bab35b
feat: add pages for needs questions
jackcasstlesjones Dec 10, 2024
513602e
refactor: use question page"
jackcasstlesjones Dec 10, 2024
4a0e701
Merge branch 'main' into feature/needs-ui
jackcasstlesjones Dec 10, 2024
10d7578
feat: populate page with category names from db
jackcasstlesjones Dec 10, 2024
546801a
feat: fetch needs
jackcasstlesjones Dec 10, 2024
dbedb74
feat: display needs from db on page
jackcasstlesjones Dec 10, 2024
88d9f1a
feat: display needs better
jackcasstlesjones Dec 11, 2024
c562300
feat: add console log
jackcasstlesjones Dec 11, 2024
47c675c
feat: add questionsmodal
jackcasstlesjones Dec 11, 2024
9568c81
refactor: create needs section component
jackcasstlesjones Dec 11, 2024
be844c7
feat: modal wip
jackcasstlesjones Dec 11, 2024
0b8e17f
feat: modal wip 2
jackcasstlesjones Dec 11, 2024
5a9166b
feat: modal ui behaviour is working
jackcasstlesjones Dec 11, 2024
6a6411c
feat: chevron only shows on second and third steps
jackcasstlesjones Dec 11, 2024
72a3c78
feat: chevron moves forward and back
jackcasstlesjones Dec 11, 2024
d062af5
feat: add close button
jackcasstlesjones Dec 11, 2024
7268843
style: modal now appears in centre of screen and disables scrolling
jackcasstlesjones Dec 11, 2024
9b0fc6c
chore: fix type errors
jackcasstlesjones Dec 11, 2024
de5928a
chore: fix more type errors, retrievedataobject now takes generic types
jackcasstlesjones Dec 11, 2024
3299b50
chore: remove console.logs
jackcasstlesjones Dec 11, 2024
94bc34a
chore: console.logs to track urgency, effort, worthdoing
jackcasstlesjones Dec 11, 2024
e8cee94
feat: back button functionality works
jackcasstlesjones Dec 11, 2024
a9bedb6
chore: change function names
jackcasstlesjones Dec 11, 2024
e5aedef
feat: finishing modal dialogue resets to 0
jackcasstlesjones Dec 11, 2024
264a6b5
chore: rename functions
jackcasstlesjones Dec 11, 2024
d428d23
feat: logic to update db
jackcasstlesjones Dec 11, 2024
9513ac8
feat: update schema and seed
jackcasstlesjones Dec 11, 2024
19dd29c
feat: use patch, now it updates
jackcasstlesjones Dec 11, 2024
467d004
fix: console log now shows updated doc
jackcasstlesjones Dec 11, 2024
9f6e170
chore: add consolelogs
jackcasstlesjones Dec 11, 2024
d6301c0
feat: submission to db working without reset
jackcasstlesjones Dec 11, 2024
fd33ecc
feat: needs submission finished
jackcasstlesjones Dec 11, 2024
db063e8
chore: remove comments
jackcasstlesjones Dec 11, 2024
66f39dc
feat: close click now resets
jackcasstlesjones Dec 11, 2024
7f9fb4f
chore: remove unused imports
jackcasstlesjones Dec 11, 2024
8a231a4
feat: expiry pushed to db
jackcasstlesjones Dec 11, 2024
487e283
chore: remove unused imports and variables for build
jackcasstlesjones Dec 11, 2024
65b75c2
chore: delete mock data
jackcasstlesjones Dec 11, 2024
22bf805
remove comment in database manager
jackcasstlesjones Dec 11, 2024
9b8294d
delete another comment
jackcasstlesjones Dec 11, 2024
f36d434
chore: delete unused file
jackcasstlesjones Dec 11, 2024
020619a
Merge branch 'feature/needs-ui' of github.com:fac30/things-we-do into…
jackcasstlesjones Dec 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading