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

Create Next Actions & Allow Their Immediate Selection #95

Merged
merged 8 commits into from
Dec 17, 2024
124 changes: 65 additions & 59 deletions src/app/needs/next-actions/components/NextActionsDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export default function NextActionsDisplay() {
const [highlightedNeeds, setHighlightedNeeds] = useState<NeedDocument[]>([]);
const [relatedNextActions, setRelatedNextActions] = useState<NextActionDocument[]>([]);
const [chainEnd, setChainEnd] = useState(0);
const [actionState, setActionState] = useState(0);

useEffect(() => { /* Fetch Data */
async function fetchData() {
Expand Down Expand Up @@ -66,7 +67,7 @@ export default function NextActionsDisplay() {
}

fetchData();
}, [database, chainEnd]);
}, [database, chainEnd, actionState]);

const priorityGroups = useMemo(() => {
if (highlightedNeeds.length === 0) return [];
Expand Down Expand Up @@ -112,12 +113,9 @@ export default function NextActionsDisplay() {
const collectionName = "next_actions";

if (highlighted) {
// Un-highlight action:
// Remove last selectedTimestamp
const updatedTimestamps = [...action.selectedTimestamps];
updatedTimestamps.pop();

// Set selectedExpiry back to the original timestamp
await database.updateDocument(
collectionName,
action.id,
Expand Down Expand Up @@ -158,70 +156,78 @@ export default function NextActionsDisplay() {
);
}

// After toggling, increment chainEnd to re-fetch and update UI
setChainEnd(prev => prev + 1);
};

// Commented out until we agree on an answer to my question in the PR
// const saveAndExit = () => { router.push("/needs"); };
const handleAddAction = async (newAction: string, need: NeedDocument) => {
if (newAction.trim()) {
const newActionDocument = {
id: crypto.randomUUID(),
name: newAction.trim(),
need: need.id,
selectedTimestamps: [],
selectedExpiry: new Date().toISOString(),
timestamp: new Date().toISOString(),
}
try {
await database.addToDb("next_actions", newActionDocument);
console.log(`Action Created: ${newActionDocument.name}`);
} catch (error) {
console.error("Error creating Action:", error);
}

setActionState(prev => prev + 1);
}
};

return (
<>
<div className="w-11/12 m-auto">
{priorityGroups.length === 0
? (<p className="mb-5">
You have no unmet needs selected. Review which needs might be unmet before we can recommend next actions to meet them.
</p>)
: (priorityGroups.map((group, i) => (
<div key={i} className="mb-6">
<h3 className={clsx(
<div className="w-11/12 m-auto">
{ priorityGroups.length === 0 ?
(<p className="mb-5">
You have no unmet needs selected. Review which needs might be unmet before we can recommend next actions to meet them.
</p>) :
(priorityGroups.map((group, i) => (
<div key={i} className="mb-6">
<h3
className={clsx(
"text-xl font-bold mb-2",
{"text-twd-cube-red" : group.priority.order === 1 },
{"text-twd-cube-yellow" : group.priority.order === 2},
{"text-twd-cube-blue" : group.priority.order === 3},
{"text-twd-cube-green" : group.priority.order === 4}
)}>
{changeCase(group.priority.name, "sentence")}
</h3>

{group.needs.map((need) => {
const actions = getActionsForNeed(need.id);

return (
<div key={need.id} className="ml-4 mb-4">
<h4 className="font-semibold">
To meet a need for {changeCase(need.name, "lower")}, which actions can you take next?
</h4>

{ actions.length > 0
? (
<NextActionsSection
need={need}
actions={actions}
onToggleAction={onToggleAction}
/>
) : (
<p className="text-sm text-gray-500 ml-6">No next actions available for this need.</p>
)
}
</div>
);
})}
</div>
)))
}
</div>

{/* Commented out until we agree on an answer to my question in the PR
<Button
onClick={saveAndExit}
label="Save & Exit"
className={clsx(
"fixed right-4 bottom-24 text-white rounded",
"bg-twd-primary-purple shadow-twd-primary-purple"
)}
/>
*/}
</>
)}
>
{changeCase(group.priority.name, "sentence")}
</h3>

{group.needs.map((need) => {
const actions = getActionsForNeed(need.id);

return (
<div key={need.id} className="ml-4 mb-4">
<h4 className="font-semibold">
To meet a need for {changeCase(need.name, "lower")}, which actions can you take next?
</h4>

{ actions.length > 0 ? (
<NextActionsSection
need={need}
actions={actions}
onToggleAction={onToggleAction}
handleAddAction={handleAddAction}
/>
) : (
<p className="text-sm text-gray-500 ml-6">
No next actions available for this need.
</p>
)
}
</div>
);
})}
</div>
)))
}
</div>
);
}
98 changes: 81 additions & 17 deletions src/app/needs/next-actions/components/NextActionsSection.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,102 @@
"use client";

import React from "react";
import React, { useState } from "react";
import Button from "@/ui/shared/Button";
import Modal from "@/ui/shared/Modal";
import { NeedDocument } from "./NextActionsDisplay";
import { NextActionDocument } from "./NextActionsDisplay";
import { PlusCircleIcon } from "@heroicons/react/24/outline";

interface NextActionsSectionProps {
interface SectionProps {
need: NeedDocument;
actions: NextActionDocument[];
onToggleAction: (action: NextActionDocument) => Promise<void>;
handleAddAction: (newAction: string, need: NeedDocument) => Promise<void>;
}

export default function NextActionsSection({
need,
actions,
onToggleAction,
}: NextActionsSectionProps) {
return (
handleAddAction,
}: SectionProps) {
const [modalOpen, setModalOpen] = useState(false);
const [newAction, setNewAction] = useState<string>("");

const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setNewAction(e.target.value)
};

const handleSubmitAction = async () => {
await handleAddAction(newAction, need);
setModalOpen(false);
}

/* const handleAddAction = async () => {
if (newAction.trim()) {
const newActionDocument = {
id: crypto.randomUUID(),
name: newAction.trim(),
need: need.id,
selectedTimestamps: [],
selectedExpiry: new Date().toISOString(),
timestamp: new Date().toISOString(),
}

try {
await database.addToDb("next_actions", newActionDocument);

console.groupCollapsed("Action Created");
console.log(`Name: ${newActionDocument.name}`);
console.log(`Need: ${newActionDocument.need}`);
console.groupEnd();

setTriggerRerender(triggerRerender + 1);
} catch (error) {
console.error("Error creating Action:", error);
}

setModalOpen(false);
}
}; */

JasonWarrenUK marked this conversation as resolved.
Show resolved Hide resolved
return ( <>
<div className="ml-4 mb-6">
{actions.map((action) => {
const highlighted = new Date(action.selectedExpiry) > new Date();

return (
<Button
key={action.id}
label={action.name}
className={
highlighted
? "bg-twd-primary-purple text-black"
: "bg-gray-600 text-white"
}
onClick={() => onToggleAction(action)}
/>
);
return (<Button key={action.id}
label={action.name}
className={ highlighted ?
"bg-twd-primary-purple text-black" :
"bg-gray-600 text-white"
}
onClick={() => onToggleAction(action)}
/>);
})}

<button onClick={() => setModalOpen(true)}
className="flex justify-center items-center"
aria-label="Add Action"
>
<PlusCircleIcon className="w-7 m-auto" />
</button>
</div>
);

<Modal title="Add a New Action"
inputModal={true}
modalOpen={modalOpen}
placeholder="What action might help meet this need?"
handleInputChange={handleInputChange}
forwardButton={{ label: "Add",
action: handleSubmitAction,
}}
backButton={{ label: "Cancel",
action: () => {
setNewAction("");
setModalOpen(false);
},
}}
/>
</> );
}
36 changes: 12 additions & 24 deletions src/app/toolkit/add-tool/components/AddToolInputs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,10 @@ export default function Inputs() {
}
};

return (
<Button
label="Add Tool"
onClick={handleSubmit}
className="w-full mt-3 bg-twd-primary-purple"
/>
);
return ( <Button label="Add Tool"
onClick={handleSubmit}
className="w-full mt-3 bg-twd-primary-purple"
/> );
}

return (
Expand All @@ -125,10 +122,10 @@ export default function Inputs() {
<AddDescription />
<AddImageUrl />
<AddInfoUrl />

<SubmitButton />

<Modal
title="Tool Added"
<Modal title="Tool Added"
modalOpen={confirmationModalOpen}
forwardButton={{
label: "Continue",
Expand All @@ -139,18 +136,15 @@ export default function Inputs() {
}}
/>

{/* Modal for missing name */}
<Modal
title="Name is required"
<Modal title="Name is required"
modalOpen={nameErrorModalOpen}
forwardButton={{
label: "OK",
action: () => setNameErrorModalOpen(false),
}}
/>

<Modal
title="You created an unused tag. What would you like to save?"
<Modal title="You created an unused tag. What would you like to save?"
modalOpen={unusedCategoryModalOpen}
forwardButton={{
label: "Tool & Tag",
Expand All @@ -168,35 +162,29 @@ export default function Inputs() {
}}
/>

<Modal
title="Please select at least one tag"
<Modal title="Please select at least one tag"
modalOpen={categoryErrorModal}
forwardButton={{
label: "OK",
action: () => setCategoryErrorModal(false),
}}
/>

<Modal
title={submitErrorMessage}
modalOpen={infoUrlErrorModal}
<Modal title={submitErrorMessage} modalOpen={infoUrlErrorModal}
forwardButton={{
label: "OK",
action: () => setInfoUrlErrorModal(false),
}}
/>

<Modal
title={submitErrorMessage}
modalOpen={imageUrlErrorModal}
<Modal title={submitErrorMessage} modalOpen={imageUrlErrorModal}
forwardButton={{
label: "OK",
action: () => setImageUrlErrorModal(false),
}}
/>

<Modal
title={`Failed to save tool: ${submitErrorMessage}`}
<Modal title={`Failed to save tool: ${submitErrorMessage}`}
modalOpen={submitErrorModal}
forwardButton={{
label: "OK",
Expand Down
Loading
Loading