Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Binary file removed src/frontend_react/public/favicon-16x16.png
Binary file not shown.
Binary file removed src/frontend_react/public/favicon-32x32.png
Binary file not shown.
Binary file added src/frontend_react/public/favicon-96x96.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src/frontend_react/public/favicon.ico
Binary file not shown.
Binary file modified src/frontend_react/public/logo192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src/frontend_react/public/logo512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
252 changes: 127 additions & 125 deletions src/frontend_react/src/components/content/HomeInput.tsx
Original file line number Diff line number Diff line change
@@ -1,152 +1,154 @@
import {
Body1,
Body1Strong,
Button,
Caption1,
Card,
Title2,
Body1Strong,
Button,
Caption1,
Title2,
} from "@fluentui/react-components";
import { FoodToast20Regular, Send20Regular } from "@fluentui/react-icons";
import { Send20Regular } from "@fluentui/react-icons";
import React, { useRef, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { useNavigate, useLocation } from "react-router-dom";

import "./../../styles/Chat.css";
import "../../styles/prism-material-oceanic.css";
import "./../../styles/HomeInput.css";

import { HomeInputProps, quickTasks, QuickTask } from "../../models/homeInput";
import { TaskService } from "../../services/TaskService";
import { NewTaskService } from "../../services/NewTaskService";

import ChatInput from "@/coral/modules/ChatInput";
import InlineToaster, { useInlineToaster } from "../toast/InlineToaster";
import PromptCard from "@/coral/components/PromptCard";
import { Send } from "@/coral/imports/bundleicons";

const HomeInput: React.FC<HomeInputProps> = ({
onInputSubmit,
onQuickTaskSelect,
onInputSubmit,
onQuickTaskSelect,
}) => {
const [submitting, setSubmitting] = useState(false);
const [input, setInput] = useState("");
const textareaRef = useRef<HTMLTextAreaElement>(null);
const navigate = useNavigate();
const { showToast, dismissToast } = useInlineToaster();

const resetTextarea = () => {
const [submitting, setSubmitting] = useState(false);
const [input, setInput] = useState("");

const textareaRef = useRef<HTMLTextAreaElement>(null);
const navigate = useNavigate();
const location = useLocation(); // ✅ location.state used to control focus
const { showToast, dismissToast } = useInlineToaster();

useEffect(() => {
if (location.state?.focusInput) {
textareaRef.current?.focus();
}
}, [location]);

const resetTextarea = () => {
setInput("");
if (textareaRef.current) {
textareaRef.current.style.height = "auto";
textareaRef.current.focus();
}
};

useEffect(() => {
const cleanup = NewTaskService.addResetListener(resetTextarea);
return cleanup;
}, []);

const handleSubmit = async () => {
if (input.trim()) {
setSubmitting(true);
let id = showToast("Creating a plan", "progress");

try {
const response = await TaskService.submitInputTask(input.trim());
setInput("");
if (textareaRef.current) {
textareaRef.current.style.height = "auto";
textareaRef.current.focus();
}
};

useEffect(() => {
const cleanup = NewTaskService.addResetListener(resetTextarea);
return cleanup;
}, []);

const handleSubmit = async () => {
if (input.trim()) {
setSubmitting(true);
let id = showToast("Creating a plan", "progress");
try {
const response = await TaskService.submitInputTask(input.trim());

setInput("");
if (textareaRef.current) {
textareaRef.current.style.height = "auto";
}


console.log('Task response', response);
if (response.plan_id && response.plan_id !== null) {
// plan_id is valid (not null or undefined)
showToast("Plan created!", "success");
dismissToast(id);
navigate(`/plan/${response.plan_id}`);
} else {
// plan_id is not valid, handle accordingly
console.log('Invalid plan:', response.status);

showToast("Failed to create plan", "error");
dismissToast(id);
}
} catch (error) {
console.error("Failed to create plan:", error);
dismissToast(id);
showToast("Something went wrong", "error");
} finally {
setInput("");
setSubmitting(false);
}
}
};

const handleQuickTaskClick = (task: QuickTask) => {
setInput(task.description);
if (textareaRef.current) {
textareaRef.current.focus();
textareaRef.current.style.height = "auto";
}
onQuickTaskSelect(task.description);
};

useEffect(() => {
if (textareaRef.current) {
textareaRef.current.style.height = "auto";
textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`;
if (response.plan_id && response.plan_id !== null) {
showToast("Plan created!", "success");
dismissToast(id);
navigate(`/plan/${response.plan_id}`);
} else {
console.log("Invalid plan:", response.status);
showToast("Failed to create plan", "error");
dismissToast(id);
}
}, [input]);



return (
<div className="home-input-container">
<div className="home-input-content">
<div className="home-input-center-content">
<div className="home-input-title-wrapper">
<Title2>How can I help?</Title2>
</div>

<ChatInput
value={input}
placeholder="Tell us what needs planning, building, or connecting—we'll handle the rest."
onChange={setInput}
onEnter={handleSubmit}
disabledChat={submitting}
>
<Button
appearance="subtle"
className="home-input-send-button"
onClick={handleSubmit}
disabled={submitting}
icon={<Send20Regular />}
/>

</ChatInput>

{/* Inline Toaster lives right under chat input */}
<InlineToaster />

<div className="home-input-quick-tasks-section">
<div className="home-input-quick-tasks-header">
<Body1Strong>Quick tasks</Body1Strong>
</div>
<div className="home-input-quick-tasks">
{quickTasks.map((task) => (
<PromptCard
key={task.id}
title={task.title}
icon={task.icon}
description={task.description}
onClick={() => handleQuickTaskClick(task)}
disabled={submitting}
/>
))}
</div>
</div>
</div>
} catch (error) {
console.error("Failed to create plan:", error);
dismissToast(id);
showToast("Something went wrong", "error");
} finally {
setInput("");
setSubmitting(false);
}
}
};

const handleQuickTaskClick = (task: QuickTask) => {
setInput(task.description);
if (textareaRef.current) {
textareaRef.current.focus();
}
onQuickTaskSelect(task.description);
};

useEffect(() => {
if (textareaRef.current) {
textareaRef.current.style.height = "auto";
textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`;
}
}, [input]);

return (
<div className="home-input-container">
<div className="home-input-content">
<div className="home-input-center-content">
<div className="home-input-title-wrapper">
<Title2>How can I help?</Title2>
</div>

<ChatInput
ref={textareaRef} // forwarding
value={input}
placeholder="Tell us what needs planning, building, or connecting—we'll handle the rest."
onChange={setInput}
onEnter={handleSubmit}
disabledChat={submitting}
>
<Button
appearance="subtle"
className="home-input-send-button"
onClick={handleSubmit}
disabled={submitting}
icon={<Send />}
/>
</ChatInput>

<InlineToaster />

<div className="home-input-quick-tasks-section">
<div className="home-input-quick-tasks-header">
<Body1Strong>Quick tasks</Body1Strong>
</div>

<div className="home-input-quick-tasks">
{quickTasks.map((task) => (
<PromptCard
key={task.id}
title={task.title}
icon={task.icon}
description={task.description}
onClick={() => handleQuickTaskClick(task)}
disabled={submitting}
/>
))}
</div>
</div>
</div>
);
</div>
</div>
);
};

export default HomeInput;

25 changes: 15 additions & 10 deletions src/frontend_react/src/components/content/PlanPanelLeft.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,7 @@ import PanelFooter from "@/coral/components/Panels/PanelFooter";
import PanelUserCard from "../../coral/components/Panels/UserCard";
import { getUserInfoGlobal } from "@/api/config";

const PlanPanelLeft: React.FC<PlanPanelLefProps> = ({
reloadTasks,
onNewTaskButton,
}) => {
const PlanPanelLeft: React.FC<PlanPanelLefProps> = ({ reloadTasks }) => {
const { dispatchToast } = useToastController("toast");
const navigate = useNavigate();
const { planId } = useParams<{ planId: string }>();
Expand Down Expand Up @@ -118,14 +115,22 @@ const PlanPanelLeft: React.FC<PlanPanelLefProps> = ({
</PanelLeftToolbar>

<br />
<div className="tab tab-new-task" onClick={onNewTaskButton}>
<div className="tab tab-new-task-icon"
>
<div
className="tab tab-new-task"
onClick={() => navigate("/", { state: { focusInput: true } })}
tabIndex={0} // ✅ allows tab focus
role="button" // ✅ announces as button
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
navigate("/", { state: { focusInput: true } });
}
}}
>
<div className="tab tab-new-task-icon">
<ChatAdd20Regular />

</div>
<Body1Strong>New task</Body1Strong>

</div>

<br />
Expand All @@ -139,7 +144,7 @@ const PlanPanelLeft: React.FC<PlanPanelLefProps> = ({

<PanelFooter>
<PanelUserCard
name={userInfo ? userInfo.user_first_last_name : "Guess"}
name={userInfo ? userInfo.user_first_last_name : "Guest"}
alias={userInfo ? userInfo.user_email : ""}
size={32}
/>
Expand Down
17 changes: 8 additions & 9 deletions src/frontend_react/src/components/content/TaskDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,11 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
</div>
<div className="task-details-subtask-content">
<Body1
className={`task-details-subtask-description ${step.human_approval_status === "rejected"
? "strikethrough"
: ""
}`}
className={`task-details-subtask-description ${
step.human_approval_status === "rejected"
? "strikethrough"
: ""
}`}
>
{description}{" "}
{functionOrDetails && (
Expand All @@ -167,10 +168,8 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
<div className="task-details-action-buttons">
{step.human_approval_status !== "accepted" &&
step.human_approval_status !== "rejected" && (
<div>
<Tooltip relationship="label" content="Approve">
<> <Tooltip relationship="label" content="Approve">
<Button

icon={<Checkmark20Regular />}
appearance="subtle"
onClick={
Expand Down Expand Up @@ -201,8 +200,8 @@ const TaskDetails: React.FC<TaskDetailsProps> = ({
: "task-details-action-button-disabled"
}
/>
</Tooltip>
</div>
</Tooltip></>

)}
</div>
</div>
Expand Down
Loading
Loading