Skip to content

Commit

Permalink
Merge pull request #95 from fac30/feature/create-and-delete-actions
Browse files Browse the repository at this point in the history
Create Next Actions & Allow Their Immediate Selection
  • Loading branch information
maxitect authored Dec 17, 2024
2 parents a95466d + 2a30ce8 commit 5627786
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 139 deletions.
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>
);
}
70 changes: 53 additions & 17 deletions src/app/needs/next-actions/components/NextActionsSection.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,74 @@
"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);
}

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

0 comments on commit 5627786

Please sign in to comment.