feat: add some images to invoice
All checks were successful
Deploy to SFTP Server / build (push) Successful in 25m31s

This commit is contained in:
max_richter 2024-04-08 01:52:04 +02:00
parent 146c8d5279
commit e41ef2fceb
10 changed files with 72 additions and 41 deletions

BIN
src/content/projects/invoice/images/customers.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/content/projects/invoice/images/edit-customer.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/content/projects/invoice/images/edit-profile.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/content/projects/invoice/images/invoices.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/content/projects/invoice/images/overview.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -12,6 +12,18 @@ links:
] ]
--- ---
import bg from './images/bg.jpg'
import invoices from './images/invoices.png'
import customers from './images/customers.png'
import editCustomers from './images/edit-customer.png'
import editProfile from './images/edit-profile.png'
import overview from './images/overview.png'
import ImageSlider from '@components/ImageSlider.svelte'
import Image from '@components/Image.astro'
# Einleitung
In meiner Freizeit übernehme ich gerne kleinere Aufträge und erledige Botengänge, Aufbauten und Abholungen für andere. In meiner Freizeit übernehme ich gerne kleinere Aufträge und erledige Botengänge, Aufbauten und Abholungen für andere.
Ein unvermeidlicher Bestandteil dieser Tätigkeiten ist das Erstellen von Rechnungen im PDF-Format. Anfangs habe ich mich dem manuellen Prozess hingegeben und die ersten Rechnungen in Figma erstellt. Doch wie es unter Programmierer*innen oft heißt: Ein unvermeidlicher Bestandteil dieser Tätigkeiten ist das Erstellen von Rechnungen im PDF-Format. Anfangs habe ich mich dem manuellen Prozess hingegeben und die ersten Rechnungen in Figma erstellt. Doch wie es unter Programmierer*innen oft heißt:
@ -20,25 +32,32 @@ Ein unvermeidlicher Bestandteil dieser Tätigkeiten ist das Erstellen von Rechnu
Aus dieser Überlegung heraus entstand mein neuestes Hobbyprojekt **"Invoice."** Aus dieser Überlegung heraus entstand mein neuestes Hobbyprojekt **"Invoice."**
## Entwicklung <ImageSlider title="Invoice Screens" client:load>
<Image src={invoices} alt="Invoices" />
<Image src={customers} alt="Customers" />
<Image src={editCustomers} alt="Edit Customers" />
<Image src={editProfile} alt="Edit Profile" />
<Image src={overview} alt="Overview" />
</ImageSlider>
# Entwicklung
In der Entwicklung habe ich stets das Prinzip 'K.I.S.S.' im Hinterkopf behalten: Keep it simple, stupid. Für dieses Projekt bedeutete das die Auswahl von "langweiligen", aber mir bestens vertrauten Technologien: In der Entwicklung habe ich stets das Prinzip 'K.I.S.S.' im Hinterkopf behalten: Keep it simple, stupid. Für dieses Projekt bedeutete das die Auswahl von "langweiligen", aber mir bestens vertrauten Technologien:
[-> SvelteKit](https://kit.svelte.dev) ## [🚀 SvelteKit](https://kit.svelte.dev)
Für eine effiziente und reaktive Benutzeroberfläche. Für eine effiziente und reaktive Benutzeroberfläche.
[-> UNOcss](https://unocss.dev/) ## [🎨 UNOcss](https://unocss.dev/)
Die schnellere Tailwind Alternative. Die schnellere Tailwind Alternative.
[-> TypesafeI18n]() ## [🌍 TypesafeI18n](https://github.com/ivanhofer/typesafe-i18n)
Um mehrsprachige Unterstützung ohne komplizierte Logik zu integrieren. Um mehrsprachige Unterstützung ohne komplizierte Logik zu integrieren.
[-> Prisma](https://prisma.io) ## [🛠️ Prisma](https://prisma.io)
Als Datenbankzugriffslayer für eine reibungslose Datenverwaltung. Als Datenbankzugriffslayer für eine reibungslose Datenverwaltung.
[-> SQLite](https://www.sqlite.org/index.html) ## [🗃️ SQLite](https://www.sqlite.org/index.html)
Als zuverlässiges Backend, das sich ideal für kleinere Projekte eignet. Als zuverlässiges Backend, das sich ideal für kleinere Projekte eignet.
Diese bewährten Technologien bildeten das robuste Fundament, auf dem "Invoice" aufgebaut wurde. ## [📄Playwright ](https://playwright.dev)
Zum erstellen der PDFs.

View File

@ -5,20 +5,25 @@ draft: false
cover: ./images/main.png cover: ./images/main.png
icon: "/projects/isyncrasy/favicon.ico" icon: "/projects/isyncrasy/favicon.ico"
description: "A small fun virtual OS build with svelte" description: "A small fun virtual OS build with svelte"
links: [["live", "https://isyncrasy.com"]]
tags: ["svelte", "web", "os"] tags: ["svelte", "web", "os"]
--- ---
# Isyncrasy
Isyncrasy ist ein kleines virtuelles Betriebssystem, das mit Svelte erstellt wurde. Es ist ein kleines Projekt, das ich gemacht habe, um Svelte zu lernen. Es ist ein einfaches Betriebssystem, das eine E-Mail-Anwendung und eine Terminalanwendung enthält. Es ist ein einfaches Projekt, aber es war sehr lehrreich.
import Image from "@components/Image.astro" import Image from "@components/Image.astro"
import Main from "./images/main.png" import Main from "./images/main.png"
import Mail from "./images/mail.png" import Mail from "./images/mail.png"
import Terminal from "./images/terminal.png" import Terminal from "./images/terminal.png"
import ImageGallery from "@components/ImageGallery.svelte" import ImageGallery from "@components/ImageGallery.svelte"
import ImageSlider from "@components/ImageSlider.svelte"
<ImageGallery client:load/> <ImageGallery client:load/>
<Image src={Main} alt="Isyncrasy" /> <ImageSlider title="Design" client:load>
<Image src={Mail} alt="Isyncrasy" /> <Image src={Main} alt="Desktop Overview" />
<Image src={Terminal} alt="Isyncrasy" /> <Image src={Mail} alt="Mail Application" />
<Image src={Terminal} alt="Terminal Application" />
</ImageSlider>

View File

@ -26,7 +26,7 @@ import ImageGallery from "@components/ImageGallery.svelte"
> K.A.R.L ist eine WebApp die einem dabei hilft 360Grad Panoramas in Sektionen einzuteilen, (Himmel, Boden, Bäume usw...) und dann den Anteil der einzelnen Sektionen am Gesamtbild festzustellen. > K.A.R.L ist eine WebApp die einem dabei hilft 360Grad Panoramas in Sektionen einzuteilen, (Himmel, Boden, Bäume usw...) und dann den Anteil der einzelnen Sektionen am Gesamtbild festzustellen.
## Einleitung # Einleitung
Das Projekt ist aus der Zusammenarbeit mit zwei Freunden entstanden. Der eine steckt gerade mitten in der Konzeptionsphase seiner Bachelorarbeit (Geographie), die sich mit der Auswirkung von Vegetation auf das Stadtklima beschäftigt. Dazu hat er an verschiedenen Orten in Köln Albedo Messungen vorgenommen, also quasi "wieviel Licht kommt vom Himmel, und wieviel davon wird vom Boden reflektiert". Um diese Messungen in den richtigen Kontext zu setzen hat er von jedem Messort 360 Panoramas angelegt, diese sehen ungefähr so aus: Das Projekt ist aus der Zusammenarbeit mit zwei Freunden entstanden. Der eine steckt gerade mitten in der Konzeptionsphase seiner Bachelorarbeit (Geographie), die sich mit der Auswirkung von Vegetation auf das Stadtklima beschäftigt. Dazu hat er an verschiedenen Orten in Köln Albedo Messungen vorgenommen, also quasi "wieviel Licht kommt vom Himmel, und wieviel davon wird vom Boden reflektiert". Um diese Messungen in den richtigen Kontext zu setzen hat er von jedem Messort 360 Panoramas angelegt, diese sehen ungefähr so aus:
@ -36,7 +36,7 @@ Dazu brauchte er jetzt Angaben wieviel Prozent der Sicht zum Beispiel Vegetation
<Image src={CrosswalkMask} alt="Segmentationsmap" /> <Image src={CrosswalkMask} alt="Segmentationsmap" />
## Problemstellung # Problemstellung
Wenn wir jetzt einfach naiv hingehen und die Pixel der einzelnen Farben zählen und daraus eine prozentuale Verteilung machen kriegen wir das klassische Problem mit der Verzerrung das die Menschheit schon seit Jahrhunderten mit Karten hat. Undzwar lassen sich Kugeln nur sehr ungern zwei dimensional darstellen, dabei kommt es immer zu Verzerrungen, wie folgendes Bild visualisiert. Wenn wir jetzt einfach naiv hingehen und die Pixel der einzelnen Farben zählen und daraus eine prozentuale Verteilung machen kriegen wir das klassische Problem mit der Verzerrung das die Menschheit schon seit Jahrhunderten mit Karten hat. Undzwar lassen sich Kugeln nur sehr ungern zwei dimensional darstellen, dabei kommt es immer zu Verzerrungen, wie folgendes Bild visualisiert.
@ -63,31 +63,31 @@ Hier noch einiger der ersten Versuch in Desmos (fantastisches Tool btw):
<iframe src="https://www.desmos.com/calculator/52ph4thjah?embed"/> <iframe src="https://www.desmos.com/calculator/52ph4thjah?embed"/>
## Die Technologien # Die Technologien
Ich hatte den Wunsch eine WebApp zu bauen die reintheoretisch auch komplett offline funktioniert. Deswegen werden nach dem ersten laden der Website keine weiteren Daten mehr verschickt, sondern alles wird im Browser ausgeführt. Dies ist bei einer Anwendung die sehr viele Pixel bearbeiten und so sehr viel Performance braucht schwierig da die Ressourcen des Browsers begrenzt sind. Folgende Technologien haben mir dabei geholfen die User Experience trotzdem halbwegs okay zu gestalten: Ich hatte den Wunsch eine WebApp zu bauen die reintheoretisch auch komplett offline funktioniert. Deswegen werden nach dem ersten laden der Website keine weiteren Daten mehr verschickt, sondern alles wird im Browser ausgeführt. Dies ist bei einer Anwendung die sehr viele Pixel bearbeiten und so sehr viel Performance braucht schwierig da die Ressourcen des Browsers begrenzt sind. Folgende Technologien haben mir dabei geholfen die User Experience trotzdem halbwegs okay zu gestalten:
### Canvas ## Canvas
Wenn es um irgendwas mit Pixeln im Browser geht kommt man an Canvas2D eigentlich gar nicht vorbei. Canvas ist durch seine programmatische Schnittstelle optimal für diesen Job geeignet. Wenn es um irgendwas mit Pixeln im Browser geht kommt man an Canvas2D eigentlich gar nicht vorbei. Canvas ist durch seine programmatische Schnittstelle optimal für diesen Job geeignet.
### Web Workers ## Web Workers
Normalerweise werden im Browser alle Operationen einer Website in einem Thread ausgeführt. Dies führt dazu eine rechenintensive Aufgabe die komplette Website zum erliegen bringen kann. Dabei können WebWorker Abhilfe schaffen. WebWorker können beliebigen Code in einem seperaten Thread ausführen mit dem einzigen Hinderniss das die Kommunikation mit dem Haupt Thread etwas schwierig ist. Normalerweise werden im Browser alle Operationen einer Website in einem Thread ausgeführt. Dies führt dazu eine rechenintensive Aufgabe die komplette Website zum erliegen bringen kann. Dabei können WebWorker Abhilfe schaffen. WebWorker können beliebigen Code in einem seperaten Thread ausführen mit dem einzigen Hinderniss das die Kommunikation mit dem Haupt Thread etwas schwierig ist.
Bei K.A.R.L werden zwei WebWorker eingesetzt, der pixel-worker ist für das Analysieren der Segmentationsmap und für das Eimer Werkzeug verantwortlich und der ai-worker fürs ausführen des Tensorflow Codes. Bei K.A.R.L werden zwei WebWorker eingesetzt, der pixel-worker ist für das Analysieren der Segmentationsmap und für das Eimer Werkzeug verantwortlich und der ai-worker fürs ausführen des Tensorflow Codes.
### Tensorflow ## Tensorflow
Warum per Hand malen nach zahlen machen wenn der Rechner das ganz automagisch kann? Hab ich mir auch gedacht und Tensorflow mit dem [ade20k](https://groups.csail.mit.edu/vision/datasets/ADE20K/) eingebaut. Dieses Netwerk ist super darin Bäume, Boden, Beton und Himmel zu erkennen. In der Editor Ansicht versteckt sich diese Funktion rechts unter dem "AI" Knopf. Warum per Hand malen nach zahlen machen wenn der Rechner das ganz automagisch kann? Hab ich mir auch gedacht und Tensorflow mit dem [ade20k](https://groups.csail.mit.edu/vision/datasets/ADE20K/) eingebaut. Dieses Netwerk ist super darin Bäume, Boden, Beton und Himmel zu erkennen. In der Editor Ansicht versteckt sich diese Funktion rechts unter dem "AI" Knopf.
### IndexDB ## IndexDB
Wenn es darum geht größere Mengen an Daten (vorallem Bilder), lokal im Browser zu speichern geht das eigentlich nur richtig mit IndexDB *(ja, localStorage mit Base64 Bildern könnte auch gehen, ist aber in machen Browsern auf 5mb beschränkt)*. Da die IndexDB Api aber zu einer der [verwirrensten Browser Api's weit und breit gehört](https://nolanlawson.github.io/offlinefirst-2016-03/#/27) benutze ich [idb](npmjs.com/package/idb), eine fantastische kleine Wrapperlibrary um IndexDB rum die auch noch Promises unterstützt. Wenn es darum geht größere Mengen an Daten (vorallem Bilder), lokal im Browser zu speichern geht das eigentlich nur richtig mit IndexDB *(ja, localStorage mit Base64 Bildern könnte auch gehen, ist aber in machen Browsern auf 5mb beschränkt)*. Da die IndexDB Api aber zu einer der [verwirrensten Browser Api's weit und breit gehört](https://nolanlawson.github.io/offlinefirst-2016-03/#/27) benutze ich [idb](npmjs.com/package/idb), eine fantastische kleine Wrapperlibrary um IndexDB rum die auch noch Promises unterstützt.
## Interessantes... # Interessantes...
### FloodFill Allgorithmus ## FloodFill Allgorithmus
Nachdem ich einige Panoramas per Hand mit den Tools segmentiert habe ist mir aufgefallen das man sehr oft einfach Regionen die eine ähnliche Farbe haben anmalt. Das brachte mich auf die Idee ein Fill Tool ähnlich wie in Photoshop zu bauen, aber vieeel besser. Nachdem ich einige Panoramas per Hand mit den Tools segmentiert habe ist mir aufgefallen das man sehr oft einfach Regionen die eine ähnliche Farbe haben anmalt. Das brachte mich auf die Idee ein Fill Tool ähnlich wie in Photoshop zu bauen, aber vieeel besser.
@ -98,7 +98,7 @@ Das Tool funktioniert ungefähr so:
4. Das Bild wird zurückgeschickt 4. Das Bild wird zurückgeschickt
5. Im Canvas Code wird dann aufgrund des Wertes der einzelnen Pixel entschieden ob dies gefüllt werden oder nicht 5. Im Canvas Code wird dann aufgrund des Wertes der einzelnen Pixel entschieden ob dies gefüllt werden oder nicht
### Svelte Bindings ## Svelte Bindings
Ich fand es immer kompliziert State über mehrere Komponenten hinweg zu regeln. Ein Beispiel wäre der Editor, er besteht aus drei einzelnen Komponenten (Toolbar, Topbar, PaintArea) die alle wissen müssen welches Farbe und welches Werkzeug gerade aktiv sind. Man könnte diesen State in einen globalen Store schreiben und in jedem der einzelnen Komponenten darauf zugreifen, aber eigentlich ist das aktive Werkzeug nur in dem Editor Kontext wichtig. Also liegt dieser State jetzt in dem Editor Komponent der in per binding an seine Unterkomponenten weitergibt, das ganze sieht dan ungefähr so aus: Ich fand es immer kompliziert State über mehrere Komponenten hinweg zu regeln. Ein Beispiel wäre der Editor, er besteht aus drei einzelnen Komponenten (Toolbar, Topbar, PaintArea) die alle wissen müssen welches Farbe und welches Werkzeug gerade aktiv sind. Man könnte diesen State in einen globalen Store schreiben und in jedem der einzelnen Komponenten darauf zugreifen, aber eigentlich ist das aktive Werkzeug nur in dem Editor Kontext wichtig. Also liegt dieser State jetzt in dem Editor Komponent der in per binding an seine Unterkomponenten weitergibt, das ganze sieht dan ungefähr so aus:
@ -116,7 +116,6 @@ Ich fand es immer kompliziert State über mehrere Komponenten hinweg zu regeln.
<TopBar bind:activeColor/> <TopBar bind:activeColor/>
<!-- TopBar.svelte --> <!-- TopBar.svelte -->
<script> <script>
export let activeColor = "ff0000"; export let activeColor = "ff0000";
@ -125,16 +124,6 @@ Ich fand es immer kompliziert State über mehrere Komponenten hinweg zu regeln.
{{activeColor}} {{activeColor}}
``` ```
## Svelte Stores
### Svelte Stores
Für einige andere Sachen kann man hingegen super Stores verwenden, zum Beispiel für Toasts/Modals. So kann man den State für die Toasts (Dass sind die Nachrichten unten Links) super in einem Komponent packen und muss sie nicht über mehrere hinweg verteilen. Für einige andere Sachen kann man hingegen super Stores verwenden, zum Beispiel für Toasts/Modals. So kann man den State für die Toasts (Dass sind die Nachrichten unten Links) super in einem Komponent packen und muss sie nicht über mehrere hinweg verteilen.
### Wofür steht K.A.R.L?
W.I.P
### Wofür steht W.I.P
Work in Progress

View File

@ -49,7 +49,6 @@ import ImageGallery from "@components/ImageGallery.svelte"
<Image src={page01_6} alt="Data flow inside app"/> <Image src={page01_6} alt="Data flow inside app"/>
</ImageSlider> </ImageSlider>
Schon gar nicht schlecht, aber wie das mit Prototypen so ist gab es noch einiges zu verbessern. Also eine Kurze Historie der größten Änderungen bis heute: Schon gar nicht schlecht, aber wie das mit Prototypen so ist gab es noch einiges zu verbessern. Also eine Kurze Historie der größten Änderungen bis heute:
# Refactors # Refactors
@ -97,6 +96,13 @@ Das coole ist das man dieses System sehr generisch gestalten kann und zum beispi
# Architektur # Architektur
Nach einigen unterschiedlichen Versuchen habe ich mich für eine eventbasierte Architektur entschieden.
Irgendwie passt das sehr gut zu Node-Systemen da die einzelnen Komponenten über Events miteinander kommunizieren können.
<div class="center my-4">
![Architecture](./images/architecture.svg)
</div>
# Design # Design
# Zukunft # Zukunft
@ -105,7 +111,4 @@ Das coole ist das man dieses System sehr generisch gestalten kann und zum beispi
Aktuell sieht die Architectur in etwa so aus: Aktuell sieht die Architectur in etwa so aus:
<div class="center my-4">
![Architecture](./images/architecture.svg)
</div>

View File

@ -87,7 +87,7 @@ import "./global.css";
</style> </style>
</head> </head>
<body class="text-neutral flex flex-col"> <body class="text-neutral flex flex-col">
<header class="sticky top-0 z-2"> <header class="sticky bottom-0 sm:top-0 z-2 order-last sm:order-first">
<Nav /> <Nav />
</header> </header>
<main id="main-content" class="flex flex-col gap-4"> <main id="main-content" class="flex flex-col gap-4">