Enjoy the detours!

100daystooffload

I don’t do it deliberately, but over the years I used some tools as to-do list without declaring them as such. For example, my open tabs in Firefox on mobile and desktop. Both are basically different to-do lists because I use them on different topics.

As next is my mail client. The inbox is also like a to-do list. Every mail I keep in here needs work. Not immediately, but I should do it every so often.

Messengers are also used as a to-do list. But more of people I should write some day.

My calendar is another one.

And the one I realized that I’m doing this, was Claude. I use the app on my phone and saw that I only kept old conversations, where I think I will later come back to them and continue my research.

On the last weekend, I thought about unifying them into one. I do some agile with my wife in the apple reminders app. We are having once a week after breakfast a short conversation on what we want to accomplish in this week. And then we put the to-dos into a list. We also have one list for each quarter with stuff that, we think, needs to be done in this period. So I already use the reminder's app for family stuff and some small to-dos that don’t fit in my to-do apps above. 😅

While I write all this, I don’t know whether I should unify all in the reminders app or just keep it as it is. Because, if everything is in one app, it could be a bit overwhelming? 🤔


19 of #100DaysToOffload
#log
Thoughts? Discuss...

Wow, I wrote this post nearly 2 months ago. Why didn't I publish this one?

Currently, I'm on a path to improve the UX a bit by giving more feedback on actions.

Last time I brought a CSV import error to the frontend, which otherwise failed silently and had been a requirement from the start.

The next step is to show a success message after the user imported CSV Data successfully. The difficulty here was, that I didn't want to convert my import component into a client component. So I was unable to use useActionState. My solution now is, that I created a Catch-all Segments route. Now, after a successful import, I can simply redirect to this page and show a success message. Which is not ideal, and I would prefer something like useActionState for server components, but it is what it is.


18 of #100DaysToOffload
#pelletyze
Thoughts? Discuss...

After procrastinating this for far too long, I sat down and did a new setup of Paperless-NGX on my NAS.

For this, I followed the setup guide for docker-compose: https://docs.paperless-ngx.com/setup/#docker

This guide requires you to have Container Manager installed on your NAS. Under /volume1 I have created a folder called docker which has another folder called paperless in it. So the path will be /volume1/docker/paperless.

My adjustments to the docker-compose.yml file:

    volumes:
      - /volume1/docker/paperless/data:/usr/src/paperless/data
      - /volume1/docker/paperless/media:/usr/src/paperless/media
      - /volume1/docker/paperless/export:/usr/src/paperless/export
      - /volume1/docker/paperless/consume:/usr/src/paperless/consume

And a different port: someportnumber:8000.

My changes to the docker-compose.env:

PAPERLESS_TIME_ZONE=Europe/Berlin
PAPERLESS_OCR_LANGUAGE=deu

And the correct IDs for USERMAP_UID= and USERMAP_GID= which you can get from id -u and id -g. The guide will tell you this as well.

Notes

The guide uses docker compose ... to run docker-compose, without the dash. Synology uses docker-compose ... with dash, to run it. I was searching, why this was not working on my side. So, this could be one reason. 😅

Database Error

I've got this error: PostgreSQL Database directory appears to contain a database; Skipping initialization. Which resulted from an old installation. Because I wanted to a fresh start, I needed to remove the old data and found this command: 🚨 docker-compose down --volumes 🚨 Be careful, this will erase all related data.

Webserver Error

Because I'm stupid, I used the wrong timezone and got an error. ValueError: Incorrect timezone setting: Germany/Berlin The correct timezone I needed to set in docker-compose.env was: PAPERLESS_TIME_ZONE=Europe/Berlin.

Conclusion?

I've used docker-compose because I broke my installation for too often with the Container Manager. And in the end, it's just a UI wrapper which confuses me. So I go with docker-compose, which I'm used in the past for client projects. The setup was straightforward, excluding the errors I had, which were self-made in the end.


17 of #100DaysToOffload
#log #toFutureMe #paperless
Thoughts? Discuss...

Hui, it surprises me every time how fast time can fly. I prepared some post, but the needed some finish and because there was nearly no time or motivation to write, so I avoided looking into my backlog of posts.

In the last month, I was sick and also focused a lot on the work with my clients. Besides that, there is life. The spring arrived and we have lovely weather here. Maybe a bit to dry, to get the garden ready but still nice and manageable.

Plenty of topics in the last month arrived, which didn't involve sitting on my laptop. We are still working in our basement. So I did lie out more rooms with new a floor and prepared the stairs. They required a new color. At the moment I painted the stairs 3 times. The next step is to sand them and add one more layer of paint. Another topic is, that I bought a lawn mower robot, which is really nice and saves some time eventually. Another thing is that we lease one part of our property to an elderly woman. Sadly, she has issues staying there or taking care of the garden. So she will stop leasing, and we have to take care of the garden.

I also realized that I have too many client projects and I need to stop at least one. It's taking too much energy from me. It's hard for me to stay in a good mood, and I quickly react irritably when something is wrong.

Tomorrow we will finally install our new solar power system on the roof. Which is something I'm waiting for a long time now. We can finally use the sun for our home to produce energy. And we have a lot of sun here. 🌞😎 We both work from home most of the time, so our benefit will be huge. Installing the system will be a challenge because I have acrophobia. The good thing is, it takes some time, and then I'm used to the height and have less fear then. Let's see how tomorrow will work out. Maybe I can share some pictures.

Another thing this month, I've tried Cursor AI and wrote some toots on mastodon. I will plan to write more about my journey with Cursor. Because I've learned a lot.

That's it. Just some words that poured out of me before going to bet. :D


16 of #100DaysToOffload
#log
Thoughts? Discuss...

We had a nice family weekend trip. The purpose was to escape a little from our normal routine and to explore the rural land not far away from our hometown.

The first trip since I can remember being with my wife where I did not bring more technical gadgets than my phone and kindle. Usually, I take my notebook with me, on every trip. Newly, also the steam deck and iPad, just in case. Just to avoid possible boredom. (Writing this down and reading it again sounds as if I'm addicted to my gadgets. BuT i cAn stOp, iF i WaNT!)

But don't have these gadgets with me, it was refreshing just having the phone with me. I carry the notebook just in case if I'm bored and can use this time to be productive. Which is somehow good but sounds like an excuse. Without it, I was able to focus on other things. I was able to go through a lot of notes in my notion. Clean up many of my to-do lists. (Yes, there are multiples) And then there were my +99 open tabs in Firefox. I was capable of transforming 50% of them into new to-dos. The rest were mostly articles I want to read some time. I did it with a bunch of them.

At the beginning of the year, I've created 6 to-do lists in the Reminders App. 4 for each quarter. 1 for the current week and 1 for the backlog. Together with my wife, we try to go through the open to-dos and sort them every Sunday. What needs to be done in the ongoing quarter? Which tasks of this quarter can be done in the next week? These are questions we tackle on a Sunday after breakfast. (Or we try it) And it works. We both have a better overview of the things that need to be done. Before, everyone had their one agenda and these agendas collided very often. On this weekend, we thought about this approach and edited many tasks. And we were happy with the result. I'm excited about the next quarter. :)

That's it. 👋


15 of #100DaysToOffload
#log
Thoughts? Discuss...

Displays the error message: “Looks like your CSV is not formatted correctly”

Finally, I found a solution to throw an error at the frontend, while importing wrong CSV Data on the server. For Fast-CSV, I needed a solution to parse the data and put the data into the database. While this part was easy, the error message from Supabase was swallowed, and I had no practical option to present the error to the frontend. Or, there was no uncomplicated way, since now. :D

const stream = csv
  .parse(options)
  .on("error", (error) => {
    console.log(error);
  })
  .on("data", (row) => {
    result.push(row);
  })
  .on("end", async () => {
    const supabase = await createClient();
    const { error, statusText } = await supabase
      .from("fillings_import")
      .upsert(completeFillings(result))
      .select();

    if (error) {
      throw new Error(`${error.message}, ${statusText}`);
    }
  });

stream.write(fileData);
stream.end();

This was my initial solution, and the error at the end was never shown. 🤷

The new version is wrapped with a new Promise and calls reject if adding data to Supabase produces an error. Easy, right? I'm so happy that I don't need to wrap an <ErrorBoundary /> or something else.

await new Promise<void>((resolve, reject) => {
  const stream = csv
    .parse(options)
    .on("error", (error) => {
      console.log(error);
    })
    .on("data", (row) => {
      result.push(row);
    })
    .on("end", async () => {
      const supabase = await createClient();
      const { error, statusText } = await supabase
        .from("fillings_import")
        .upsert(completeFillings(result))
        .select();

      if (error) {
        console.error({ error, statusText, result });
        reject(new Error("Looks like your CSV is not formatted correctly"));
      }

      resolve();
    });

  stream.write(fileData);
  stream.end();
});

This little fix makes me happier than it perhaps should. But in the end, that it. I thought much more about a solution than it took time to write the code. I thought at least on and off for 3 days about it. And today, the “a-ha” moment under the shower. 😎


14 of #100DaysToOffload
#pelletyze
Thoughts? Discuss...

In the last few months, there were some big releases. ESLint v9 was one of them. Sadly, do to their new config approach, I was unable to upgrade Next.js. While waiting for Vercel to fix it, Tailwind CSS v4 and next-intl v4 were released. So I faced 4 big releases.

The first one I did was next-intl v4. They wrote a nice blog post before v4 was released. So you were able to prepare your codebase before the switch, which was great and worked very well and without problems. 👍

Next in line was Tailwind CSS v4. And with it, Tailwind-Merge v3. You need to upgrade them together. Because twMerge dropped tw v3 support. Basically, this worked flawlessly. But some adjustments needed to be done. I use the reset.css from Josh W Comeau which needs special treatment when used with Tailwind CSS v4. Furthermore, I've added some custom styling to the html, body and h1...h6 tags. The result was that I had to wrap them to the correct layers for, so they will not collide with TWv4.

@import "./reset.css" layer(base);
@import "./custom.css" layer(base);

While using the codemod from TWv4 to migrate everything, it swallowed my custom classes. Which I had to add back manually. The new solution is with CSS only and is straightforward.

@utility border-image-highlight-b {
  border-width: (--tw-border-width);
  border-image: linear-gradient(
      to right,
      var(--tw-gradient-from),
      var(--tw-gradient-to)
    )
    1;
}

That's it. The rest was done via the codemod. :)

After the TWv4 upgrade was done, I continued with the Next.js v15 upgrade. Next.js also brought a codemod with it which did its job also very code. But it had an easy job here because I did nothing special. It changed some code in the code, which I had to clean up a bit afterward. Because it looked ugly and was too verbose.

Let's continue with the biggest pain, while I did the update on my #pelletyze codebase. ESLint v9. I was tried and erroring my way through the process but the documentation on both sides, Next.js and ESLInt were not so user-friendly then for the other codebases. Sure, if you just use ESLint, you are fine. But the combination with Next.js was a bit of a pain in the ass. Therefore, I don't like the look of the new flat config, especially the fix Next.js needed to bring in, to get everything to work. I hope this will improve in the future.

Before or after you upgrade, delete the .next folder. I had seen it much earlier to save some sanity. Because every lint run failed, and because there was some old code in it. Which gave me this error: a.getScope is not a function. After this issue was solved, I needed to add some packages because Next.js was not installing them by itself, thous it depends on them for linting. 🤷 These packages are: eslint-plugin-react and eslint-plugin-react-hook. This all happened on my second try. On the first one, a stopped after 2hrs and watched Severance. On the next day, I stared from scratch. I've installed a new test project with create-next-app and copied the important ESLint config stuff. Then I stepped my way through my old config. After I solved the major issues, the rest followed effortless. I had eslint-prettier installed, which was easy to put into the new config. And then there were some rules for next-intl, which was also just copy pasta into the new config.

Yesterday evening, I got to bed with a good feeling. Because I was done with the big update and I can now continue on my roadmap to the first public release of the #pelletyze app.

Man, what a huge update post. :) And the first one, completely written while traveling by train. I hope that someone finds it, and it will help to solve some issues on their side.

Now I've earned me some episode of Severance. 😎


13 of #100DaysToOffload
#pelletyze
Thoughts? Discuss...

Disclaimer: The Title was generated by GPT-4o mini in Raycast because I had no Idea what to put in there.

So, I arrived at the Hotel and I had around 5.5 hours on a train. A lot of time to do something and make progress. What I'm ended up doing was watching Severance and reading through some articles I had put on my to-read list.

Three reasons why I did not put out my notebook or steam deck:

  1. The person who was sitting next to me made me uncomfortable, I didn't want him to watch me, while I code or played on my deck.
  2. I wanted to continue to watch Severance because it is so captivating.
  3. My initial plan was to watch a Severance episode and then start coding, but I was so demotivated, I just wanted to watch. :D So, I think, I needed the break. Because I was coding a lot in the last week.

This Hotel room is nice. Now I know why it was so expensive. It has this enjoyable little corner with an armchair and ottoman. Here I can sit with the notebook on my lap and the iPad next to me, to watch some Videos. Something I NEED in my home office. Never thought I would need this, but this is now a must-have. 😎

That's it for today. Tomorrow I will spend nearly the whole day at my client. I hope that we as a team will go out for a soda or bear. 🤞


12 of #100DaysToOffload
#log
Thoughts? Discuss...´

Another Sunday with some time to work on my App.

Today I made good progress. Mostly for code in the background, so I don't really have screenshots besides one.


This will add the current version number to the frontend. First, I thought about a complex idea by adding the version number to an .env file or to a constant hardcoded inside the codebase. Then I thought, why not use the package.json version number? I maintain them with every release, so it is the single of truth. The code is also simple and just works.

import packageJson from "@/package.json";

export function AppVersionNumber() {
  return (
    <p className="flex gap-2 justify-center items-center">
      <small
        className="opacity-50 align-middle"
        title={`App Version: packageJson.version`}
        aria-label={`App Version: packageJson.version`}
      >
        {packageJson.version}
      </small>
    </p>
  );
}

Next on my plate, there were some bugs to fix. One, I saw at the turn of the year and I had it on my list since then. I couldn't really explain how this came together. Luckily, this one resolved itself after I removed the old data and put the new one in. Because the old data was corrupted.


Another bug was a simple fix because it was just a key prop warning. I've added the key prop and everything was fine. 😎


The last Bug was related to the data export to CSV. First I've string.replaced() my way through, but later I found out, this isn't an ideal solution. So I sat down and rewrote some parts. Supabase has its own csv export, right after you fetch your data. Which is nice, but the result was not importable for fast-csv.

Now I run csv.writeToString on my formatted data. The result cuts the file size by half. Which is not relevant because it's under 1kb but still, a good achievement.

  const formattedData = [
    ["bags", "filled_at"],
    ...(data || []).map((row) => [
      row.bags,
      format(row.filled_at, "yyyy-MM-dd"),
    ]),
  ];

  const exportReadyCSV = await csv.writeToString(formattedData, {
    delimiter: ";",
  });

And the last to-do for today was, to update all packages with the patch and minor version updates. If clicked through the application and everything worked, so this was a quick one too. :)

I would say that this was a productive Sunday. :)

I will visit my client from Tuesday to Thursday, and I'm sitting around 12hrs on the Train, so if I'm lazy, I will be watching some series or playing on my Steam Deck. OR I will be productive and work more on my Application. It depends on the mood. So lets see. :)


11 of #100DaysToOffload
#pelletyze
Thoughts? Discuss...

Finally, I was able to work again on my Pelletyze project. 🥳

The Bug

There was a bug, where the checkbox was not recognized and the formData I've sent to the server was empty. This feature worked in the past and I don't understand why it broke.

The Fix

Because I use the @radix-ui/react-checkbox and don't handle the state manually, I need to wrap with a @radix-ui/react-form. That's it. 😎

While I was working on the component, I also transformed it into a server component. Which was easily done, by using getTranslation() from next-intl instead of useTranslation and making the component asynchronous.

The bug was the reason I've implemented the delete-all Button. To save me a trip to the Supabase admin page and clear the table.

This was my Sunday contribution to Pelletyze.


10 of #100DaysToOffload
#pelletyze
Thoughts? Discuss...