mirror of
https://github.com/jacobtread/obsidian-timekeep.git
synced 2026-07-22 10:10:27 +00:00
feat: show path to running entry
This commit is contained in:
parent
1093d23725
commit
8214cd0448
5 changed files with 131 additions and 1 deletions
|
|
@ -7,6 +7,7 @@ import { useTimekeepStore } from "@/contexts/use-timekeep-store";
|
|||
import {
|
||||
withEntry,
|
||||
isKeepRunning,
|
||||
getPathToEntry,
|
||||
getRunningEntry,
|
||||
stopRunningEntries,
|
||||
} from "@/timekeep";
|
||||
|
|
@ -29,6 +30,14 @@ export default function TimekeepStart() {
|
|||
[timekeep]
|
||||
);
|
||||
|
||||
const pathToEntry = useMemo(
|
||||
() =>
|
||||
currentEntry
|
||||
? getPathToEntry(timekeep.entries, currentEntry)
|
||||
: null,
|
||||
[timekeep, currentEntry]
|
||||
);
|
||||
|
||||
const isTimekeepRunning = currentEntry !== null;
|
||||
|
||||
const onStart = (event: FormEvent) => {
|
||||
|
|
@ -86,7 +95,20 @@ export default function TimekeepStart() {
|
|||
</span>
|
||||
<div className="active-entry__details">
|
||||
<span className="active-entry__name">
|
||||
<b>Name: </b> {currentEntry.name}
|
||||
<b>Name: </b>{" "}
|
||||
{pathToEntry && pathToEntry.length > 0 && (
|
||||
<span className="timekeep-path-to-entry">
|
||||
{pathToEntry.map((path, index) => (
|
||||
<span key={path.id}>
|
||||
{path.name}
|
||||
|
||||
{index <
|
||||
pathToEntry.length - 1 &&
|
||||
" >"}
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
<span className="active-entry__time">
|
||||
<b>{"Started at: "}</b>
|
||||
|
|
|
|||
|
|
@ -224,3 +224,8 @@
|
|||
color: #777;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.timekeep-path-to-entry {
|
||||
display: inline-flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
|
|
|||
51
src/timekeep/__fixtures__/checking/findRunningEntryPath.ts
Normal file
51
src/timekeep/__fixtures__/checking/findRunningEntryPath.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import moment from "moment";
|
||||
|
||||
export const currentTime = moment();
|
||||
|
||||
export const runningEntry = {
|
||||
id: "9054dee3-8c15-493b-ad31-f070e08c2699",
|
||||
name: "Running Entry",
|
||||
startTime: currentTime,
|
||||
endTime: null,
|
||||
subEntries: null,
|
||||
};
|
||||
|
||||
export const input = [
|
||||
{
|
||||
id: "8054dee3-8c15-493b-ad31-f070e08c2699",
|
||||
name: "Block 1",
|
||||
startTime: null,
|
||||
endTime: null,
|
||||
subEntries: [
|
||||
{
|
||||
id: "7054dee3-8c15-493b-ad31-f070e08c2699",
|
||||
name: "Part 1",
|
||||
startTime: currentTime,
|
||||
endTime: currentTime,
|
||||
subEntries: null,
|
||||
},
|
||||
{
|
||||
id: "7054dee3-8c15-493b-ad31-f070e08c269a",
|
||||
name: "Part 2",
|
||||
startTime: null,
|
||||
endTime: null,
|
||||
subEntries: [runningEntry],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export const path = [
|
||||
{
|
||||
id: "8054dee3-8c15-493b-ad31-f070e08c2699",
|
||||
name: "Block 1",
|
||||
},
|
||||
{
|
||||
id: "7054dee3-8c15-493b-ad31-f070e08c269a",
|
||||
name: "Part 2",
|
||||
},
|
||||
{
|
||||
id: "9054dee3-8c15-493b-ad31-f070e08c2699",
|
||||
name: "Running Entry",
|
||||
},
|
||||
];
|
||||
|
|
@ -23,6 +23,7 @@ import {
|
|||
isKeepRunning,
|
||||
isEntryRunning,
|
||||
removeSubEntry,
|
||||
getPathToEntry,
|
||||
getRunningEntry,
|
||||
getEntryDuration,
|
||||
getTotalDuration,
|
||||
|
|
@ -300,6 +301,16 @@ describe("checking entries", () => {
|
|||
expect(output).toBe(runningEntry);
|
||||
});
|
||||
|
||||
it("should find running entry path", async () => {
|
||||
const { input, runningEntry, path } = await import(
|
||||
"./__fixtures__/checking/findRunningEntryPath"
|
||||
);
|
||||
|
||||
const output = getPathToEntry(input, runningEntry);
|
||||
|
||||
expect(output).toEqual(path);
|
||||
});
|
||||
|
||||
it("should not find running entry", async () => {
|
||||
const { input } = await import(
|
||||
"./__fixtures__/checking/shouldNotFindRunningEntry"
|
||||
|
|
|
|||
|
|
@ -288,6 +288,47 @@ export function isEntryRunning(entry: TimeEntry) {
|
|||
return entry.startTime !== null && entry.endTime === null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to the provided target entry
|
||||
*
|
||||
* @param entries The collection of entries
|
||||
* @param target The target entry
|
||||
* @returns The path to the entry
|
||||
*/
|
||||
export function getPathToEntry(
|
||||
entries: TimeEntry[],
|
||||
target: TimeEntry
|
||||
): { id: string; name: string }[] {
|
||||
const path: { id: string; name: string }[] = [];
|
||||
|
||||
function dfs(entry: TimeEntry): boolean {
|
||||
path.push({ id: entry.id, name: entry.name });
|
||||
|
||||
if (entry.id === target.id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (entry.subEntries) {
|
||||
for (const sub of entry.subEntries) {
|
||||
if (dfs(sub)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
path.pop();
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const entry of entries) {
|
||||
if (dfs(entry)) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches through the nested list of time entries using
|
||||
* a "stack" depth-first search approach attempting to
|
||||
|
|
|
|||
Loading…
Reference in a new issue