Project tracking(Metaedit, Dataview and Kanban)

By using metaedit, dataview and Kanban, now we could track all us projects with a progress-bar. Watch video here: https://youtu.be/TbWdI-trdvo

First, you should set metaedit plugin like (for make metaedit autoupdate variable):

Then, put these into your Kanban’s YAML Frontmatter:

Title: /*(Set anything if you want)*/
kanban-plugin: basic
tag: projects
Total: /*(Metaedit will auto-update these variable)*/
Incomplete: 
Completed: 

Finally, put code below into your project tracking note:

```dataviewjs
function projectTracker(dv, query) {
    let searchPagePaths = dv.pages(query).file.path
    
    for(let i=0; i < searchPagePaths.length; i++){
        if(dv.page(searchPagePaths[i]).Total){
                    let title = dv.page(searchPagePaths[i]).Title;
                    console
                    let total = dv.page(searchPagePaths[i]).Total;
                    let status = ((dv.page(searchPagePaths[i]).Completed / dv.page(searchPagePaths[i]).Total) * 100).toFixed();
                    const progress = "![pb|500](https://progress-bar.dev/" + status + "/?scale=" + "100" + "&title=" + title + "&width=400)"; //you could set any width if you need
                    dv.paragraph(progress);
                    dv.paragraph("<br>"); //use this if you have many projects to track.
        }
    }
} 

projectTracker(
    dv,
    "#projects" //change tag if you need
)
```

Yep, you did it. You will get something like this below:

20 Likes

Hi! This really cool! Can you walk me trough implementation? I’m not a programmer and my skills are basic…

Hey, how can i help you?

@Aonto Help needed. I am trying to do something similar but without Kanban.
I intend have Project > Sub-Project > Sub-Projects.
And have a count auto-updated and a small progressbar on each project page (autoupdated). Possible?

Yep, it will only count how many completed tasks and how many incomplete tasks.

1 Like

Hi @Aonto, thanks for the set up! I like it!

I am not comfortable with using YAML at all, and I initially had a problem with the title - I found that titles with more than one word would break the code. And I see that all your projects are one word.

  • is there a way to format the YAML Title field with more than one word?

thanks :+1:

Hi @dryice,
not sure whether you’re still looking for a solution, but I thought I’d post it anyway, as it took me a while to figure out a solution for the same question. I am not a programmer and fairly new to obsidian and dataview. What worked for me though, was to convert the YAML title using encodeURIComponent(). This works for me with YAML titles like “My perfect project”.

Here’s how I changed the code snippet from @Aonto

function projectTracker(dv, query) {
    let searchPagePaths = dv.pages(query).file.path
    
    for(let i=0; i < searchPagePaths.length; i++){
        if(dv.page(searchPagePaths[i]).Total){
                    let title = dv.page(searchPagePaths[i]).Title;
                    console
                    let total = dv.page(searchPagePaths[i]).Total;
                    let status = ((dv.page(searchPagePaths[i]).Completed / dv.page(searchPagePaths[i]).Total) * 100).toFixed();
                    const progress = "![pb|500](https://progress-bar.dev/" + status + "/?scale=" + "100" + "&title=" + encodeURIComponent(title) + "&width=400)"; 
                    dv.paragraph(progress);
                    // dv.paragraph("<br>"); 
        }
    }
} 

projectTracker(
    dv,
    "#projects" //change tag if you need
)

The Kanban YAML section looks like

---
Title: My perfect project
kanban-plugin: basic
tag: projects
Total: 2
Incomplete: 0
Completed: 2
---

The result looks like this
grafik

Hope this helps :slight_smile:

4 Likes

Hi!
really interested on this cause I have a big database with the same problem (2 words or more as a title), but unfortunately, I don’t know how it works this “encodeURIComponent()”. I have put it everywhere and change it many times but I don’t find the way to make it work. Could you give more details about the process? I’d be eternally grateful.

That is so neat! I have now implemented this in my projects.

If you want a quick progress bar for putting directly on an individual project’s page, you can use this, which makes the progress from the un/completed tasks directly on that page:

const tasks = dv.current().file.tasks
const percent = Math.round(tasks.filter(x => x.completed).length / tasks.length * 100)
dv.paragraph(`![](https://progress-bar.dev/${percent}/?width=200)`)

In my case I put it in a separate project-progress.js file and call it with:

```dataviewjs
dv.view('project-progress')
```

That way you can update the style at any time and it will change across all your projects.

1 Like

Hi! The progress-bar server did not renew the domain. I made this adaptation to view it offline. It is still in the development phase. I hope it will be useful to you. :upside_down_face:

function renderProgressBar(title) {
    const container = dv.el("div", "", { attr: { style: `
            width: 100%;
            margin: 0 auto;
            padding: 1rem;
            display: flex;
            flex-direction: column;
            gap: 16px;
            align-items: center;
            justify-content: center;
            align: center; 
            background: rgba(255, 255, 255, 0.25);
            border-radius: 4px;
        ` 
    }});

    const titleLabel = dv.el("div", title, { attr: { style: `
            font-size: 1.2rem;
            font-weight: bold;
            margin-bottom: 0.5rem;
        `
    }});

    const progressText = dv.el("text");

    const progressBarContainer = dv.el("div", "", { attr: { style: `
            width: 95%;
            height: 24px;
            background-color: #e0e0e0;
            border-radius: 12px;
            overflow: hidden;
        `
    }});

    const progressBar = dv.el("div", "", { attr: { style: `
            height: 100%;
            width: 0%;
            border-radius: 12px;
            transition: width 0.2s, background-color 0.3s;
        `
    }});

    progressBarContainer.appendChild(progressBar);
    container.appendChild(titleLabel);
    container.appendChild(progressText);
    container.appendChild(progressBarContainer);
    
    return { container, titleLabel, progressText, progressBar };
}

function calculateProgress() {
    const tasks = (dv.current()?.file?.lists || []).filter(({task}) => task);
    return Math.round([...tasks].reduce((acc, { checked }) => acc + 
        Number(checked), 0) / tasks.length * 100 || 0);
}

function updateProgressBar({ progressText, progressBar }, progress) {
    if (typeof progress === 'number') {
        progressBar.style.width = `${progress}%`;
        progressText.innerHTML = `${progress}%`;

        // Change the color of the bar based on the percentage
        if (progress <= 20) {
            progressBar.style.backgroundColor = '#ff4d4d'; // Red
        } else if (progress <= 40) {
            progressBar.style.backgroundColor = '#ff7f0e'; // Orange
        } else if (progress <= 60) {
            progressBar.style.backgroundColor = '#ffcc00'; // Yellow
        } else if (progress <= 80) {
            progressBar.style.backgroundColor = '#4caf50'; // Green
        } else {
            progressBar.style.backgroundColor = '#1e90ff'; // Blue
        }
    }
}

// Get the "Title" property of the project
const title = dv.current().Title;

const progressBar = renderProgressBar(title);
const progress = calculateProgress();
updateProgressBar(progressBar, progress);