Skip to content

Commit

Permalink
Gantt chart and minor fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Dan Nadler committed Mar 27, 2024
1 parent b5ce5c9 commit 678d0f4
Show file tree
Hide file tree
Showing 11 changed files with 309 additions and 56 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
# Project Journal
## Development Tasks / TODO
### v0.2.0
- [ ] Allow the user to modify the System Prompt
- [ ] Allow the user to select the OpenAI language model
- [ ] Allow the user to provide examples of output style
- [ ] Generate all project summaries and compile into a top-level summary
- [ ] Constrain summary to a specific date range
### Future
- [ ] Ingest and sync with notes on the file system (eg. Obsidian vault)
- [ ] Show summary streaming results from LLM
- [ ] Export notes as text files

# Tauri + React + Typescript

This template should help get you started developing with Tauri, React and Typescript in Vite.
Expand Down
12 changes: 12 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"@tauri-apps/plugin-sql": "^2.0.0-beta.2",
"@uiw/react-md-editor": "^4.0.4",
"dayjs": "^1.11.10",
"gantt-task-react": "^0.3.9",
"openai": "^4.29.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
35 changes: 33 additions & 2 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,26 @@ fn migrations() -> Vec<Migration> {
kind: MigrationKind::Up,
};

return vec![migration, m2, m3, m4];
let m5 = Migration {
version: 5,
description: "add_status_table",
sql: "create table status
(
id integer not null
constraint status_pk
primary key autoincrement,
project integer
constraint status_projects_id_fk
references projects
on delete cascade,
progress integer,
start_date text not null,
end_date text not null
);",
kind: MigrationKind::Up,
};

return vec![migration, m2, m3, m4, m5];
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
Expand All @@ -115,12 +134,24 @@ pub fn run() {
Menu::with_items(
handle,
&[
&PredefinedMenuItem::separator(handle)?,
#[cfg(target_os = "macos")]
&Submenu::with_items(
handle,
"Project Journal",
true,
&[&PredefinedMenuItem::quit(handle, None)?],
&[
#[cfg(target_os = "macos")]
&MenuItem::with_id(
handle,
"settings",
"&Settings",
true,
Some("CmdOrCtrl+,"),
)?,
&PredefinedMenuItem::separator(handle)?,
&PredefinedMenuItem::quit(handle, None)?,
],
)?,
&Submenu::with_items(
handle,
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"productName": "Project Journal",
"version": "0.1.1",
"version": "0.1.2",
"identifier": "io.tortoise.project-journal",
"build": {
"beforeDevCommand": "npm run dev",
Expand Down
154 changes: 131 additions & 23 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ import { PencilSquareIcon } from "@heroicons/react/24/solid";
import { PlusIcon, TrashIcon } from "@heroicons/react/24/outline";
import dayjs from "dayjs";
import { listen } from "@tauri-apps/api/event";
import { WebviewWindow } from "@tauri-apps/api/webviewWindow";
import { summarizeEntries } from "./generation";
import { create } from "zustand";
import MDEditor from "@uiw/react-md-editor";
import { confirm } from "@tauri-apps/plugin-dialog";
import { GENERATED_NOTES_WEBVIEW, SETTINGS_WEBVIEW } from "./globals";
import { Gantt, ViewMode } from "gantt-task-react";
import "gantt-task-react/dist/index.css";

// Zustand store for entries
interface IEntryState {
Expand Down Expand Up @@ -49,7 +51,104 @@ const useProjects = create<IProjectState>((set) => ({
set({ activeProject }),
}));

// const EXAMPLE_TASKS = [
// {
// start: dayjs("2024-01-01").toDate(),
// end: dayjs("2024-01-30").toDate(),
// name: "Project 1",
// id: "P1",
// type: "project",
// progress: 15,
// },
// {
// start: dayjs("2024-01-01").toDate(),
// end: dayjs("2024-01-10").toDate(),
// name: "Task 1",
// id: "1",
// type: "task",
// progress: 50,
// project: "P1",
// },
// {
// start: dayjs("2024-01-10").toDate(),
// end: dayjs("2024-01-20").toDate(),
// name: "Task 2",
// id: "2",
// type: "task",
// progress: 0,
// project: "P1",
// dependencies: ["1"],
// },
// {
// start: dayjs("2024-01-30").toDate(),
// end: dayjs("2024-03-30").toDate(),
// name: "Project 2",
// id: "P2",
// type: "project",
// progress: 15,
// dependencies: ["P1"],
// },
// {
// start: dayjs("2024-01-30").toDate(),
// end: dayjs("2024-02-10").toDate(),
// name: "Task 1",
// id: "2-1",
// type: "task",
// progress: 50,
// project: "P2",
// dependencies: [],
// },
// {
// start: dayjs("2024-02-10").toDate(),
// end: dayjs("2024-02-20").toDate(),
// name: "Task 2",
// id: "2-2",
// type: "task",
// progress: 0,
// project: "P2",
// dependencies: ["2-1"],
// },
// ];

// Main UI components
const GanttChart: React.FC = () => {
const { projects } = useProjects((s) => s);

return (
projects.length > 0 && (
<Gantt
viewMode={ViewMode.Week}
TaskListHeader={({ headerHeight }) => (
<div style={{ height: headerHeight }} className="border p-2">
<div>Name</div>
</div>
)}
TaskListTable={({ tasks, rowHeight }) => (
<div className="border border-t-0">
{tasks.map((task) => (
<div
key={task.id}
style={{ height: rowHeight }}
className={`flex flex-col justify-center border p-2 pr-4 ${task.type == "project" ? "" : "pl-6 font-light"}`}
>
<div>{task.name}</div>
</div>
))}
</div>
)}
tasks={projects.map((p) => ({
start: dayjs("2024-01-01").toDate(),
end: dayjs("2024-03-31").toDate(),
name: p.name,
id: p.id.toString(),
type: "project",
progress: 0,
}))}
/>
)
);
};

const Entry: React.FC<{ entry: IEntry; updateEntries: () => void }> = ({
entry,
updateEntries,
Expand Down Expand Up @@ -122,13 +221,13 @@ const Entries: React.FC<{ projectId: number }> = ({ projectId }) => {
};

React.useEffect(() => {
console.log(`bound listener ${projectId}`)
console.log(`bound listener ${projectId}`);
document.addEventListener("keydown", keydownHandler);
return () => {
console.log(`unbound listener ${projectId}`)
console.log(`unbound listener ${projectId}`);
document.removeEventListener("keydown", keydownHandler);
};
}, []);
}, [projectId]);

return (
<>
Expand Down Expand Up @@ -212,26 +311,16 @@ function App() {
getProjects().then(setProjects);
}, []);

const handleSettings = useCallback(() => {
SETTINGS_WEBVIEW();
}, []);

const handleGenerate = useCallback(() => {
summarizeEntries(useEntries.getState().entries).then(
(result: string | null) => {
const data = new URLSearchParams();
data.append(
"project",
useProjects.getState().activeProject?.name || "Untitled Project",
);
data.append("notes", result || "No notes were generated.");
console.log(`/notes?${data.toString()}`);

new WebviewWindow("generated-notes", {
title: "Generated Notes",
width: 600,
height: 720,
url: `/notes?${data.toString()}`,
resizable: true,
visible: true,
focus: true,
});
const project =
useProjects.getState().activeProject?.name || "Untitled Project";
GENERATED_NOTES_WEBVIEW(result || "No notes were generated.", project);
},
);
}, []);
Expand All @@ -251,6 +340,9 @@ function App() {
case "generate":
handleGenerate();
break;
case "settings":
handleSettings();
break;
}
});
}, []);
Expand All @@ -263,6 +355,16 @@ function App() {
</div>
<hr />
<ul className="flex cursor-pointer flex-col gap-2 text-sm">
<li
key={"gantt"}
onClick={() => {
setActiveProject(undefined);
}}
className={`${activeProject === undefined && "bg-zinc-600"} px-2 hover:bg-zinc-700`}
>
Gantt Chart
</li>
<hr />
{projects.map((project) => (
<li
key={project.id}
Expand All @@ -276,7 +378,7 @@ function App() {
))}
</ul>
</div>
<div className="flex h-[100vh] w-full flex-col gap-0 bg-zinc-100 p-2">
<div className="flex h-[100vh] w-full flex-col gap-0 bg-zinc-100 p-2 overflow-auto">
{activeProject && (
<>
<ProjectTitle
Expand All @@ -287,7 +389,13 @@ function App() {
</>
)}
<div className="flex flex-grow flex-col overflow-y-hidden">
{activeProject && <Entries projectId={activeProject.id} />}
{activeProject ? (
<Entries projectId={activeProject.id} />
) : (
<div className={`h-[100vh]`}>
<GanttChart />
</div>
)}
</div>
</div>
</div>
Expand Down
40 changes: 34 additions & 6 deletions src/Notes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,46 @@ import Markdown from "react-markdown";
import { useSearchParams } from "react-router-dom";

export const Notes: React.FC = () => {
console.log("load")
console.log("load");
// get the data from URL param "notes" via React Router DOM
const [searchParams, _] = useSearchParams();
console.log(searchParams)
console.log(searchParams);
const notes = searchParams.get("notes");
const project = searchParams.get("project");
console.log(notes, project)
console.log(notes, project);
return (
<div className="flex-start flex flex-col gap-1 px-4 py-2">
<h1 className="text-2xl font-extralight">Generated Notes for {project}</h1>
<hr />
<Markdown>{notes}</Markdown>
<h1 className="text-2xl font-extralight">
Generated Notes for {project}
</h1>
<hr className="my-2"/>
<Markdown
components={{
h1: ({ node, ...props }) => {
return <h1 className="text-xl font-light" {...props} />;
},
h2: ({ node, ...props }) => {
return <h2 className="text-xl font-extralight" {...props} />;
},
h3: ({ node, ...props }) => {
return <h3 className="text-lg font-extralight" {...props} />;
},
p: ({ node, ...props }) => {
return <p className="text-base" {...props} />;
},
ol: ({ node, ...props }) => {
return <ol className="list-inside list-decimal" {...props} />;
},
ul: ({ node, ...props }) => {
return <ul className="list-inside list-disc" {...props} />;
},
hr: ({ node, ...props }) => {
return <hr className="my-2" {...props} />;
}
}}
>
{notes}
</Markdown>
</div>
);
};
Loading

0 comments on commit 678d0f4

Please sign in to comment.