Commit Graph
36 Commits
Author SHA1 Message Date
thomasandClaude 811188f492 Modal-Scroll: Backdrop scrollt, Panel wächst natürlich (margin:auto)
Die max-height/overflow-Variante griff beim Bewerbung-hinzufügen-Formular
nicht zuverlässig. Stattdessen scrollt jetzt das Overlay selbst wie eine
Seite; das Panel wächst auf natürliche Höhe und wird über margin:auto
zentriert (kurzer Inhalt) bzw. ohne Top-Clipping erreichbar (langer Inhalt).
Close- und Speichern-Button bleiben beide erreichbar.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-20 08:55:20 +02:00
thomasandClaude 1ba615fa80 UI fixes: modal scroll, Bewerbungen-Aktionen, E-Mail-Uhrzeit Berlin
- Modal „Bewerbung hinzufügen" lässt sich auf allen Bildschirmgrößen scrollen
  (nicht nur ≤640px), sonst war Speichern abgeschnitten
- Aktionen-Spalte der Bewerbungen von 4.5rem auf 5.5rem (passt zur
  Cluster-Breite, Icons wandern nicht mehr nach rechts)
- Merken-Stern im Compact-Modus ohne Rahmen
- E-Mail-Uhrzeiten in Postfach, Bewerbung und Reply-Quote in Europe/Berlin

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-20 08:02:37 +02:00
thomasandClaude d9ba8698e0 Termine: alle anstehenden auf Startseite + Bearbeiten; Status Telefonat
- Startseite zeigt alle anstehenden Termine (vorher auf 6 begrenzt)
- Termine lassen sich inline bearbeiten (CalDAV-Update, Neu-Anlage bei 404/412)
- Neuer Bewerbungsstatus Telefonat (Listen, Farben, API, PDF, Statistik)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-16 15:27:19 +02:00
thomasandClaude Opus 4.8 67b8e5735f KI-Chat: Thread-Titel vom Modell formulieren lassen
Der Titel in der Seitenleiste waren bisher die ersten 60 Zeichen der
ersten Nachricht - abgeschnitten mitten im Wort ("Ich habe mich bei der
Hetzner Online GmbH bewor..."). Jetzt formuliert das Modell daraus einen
kurzen Titel, wie man es aus ChatGPT kennt.

- Der Aufruf laeuft NACH dem done-Event: die Antwort wartet nicht auf die
  Titelgenerierung (1-3 s), der Titel kommt als eigenes Event nach und die
  Seitenleiste zieht live nach.
- Der abgeschnittene Titel bleibt als Platzhalter, solange die Antwort
  streamt - die Seitenleiste ist also nie leer, und faellt die
  Titelgenerierung aus, bleibt es schlicht dabei.
- Nur beim ersten Schlagabtausch und nur, wenn der Thread noch keinen
  Titel hatte: ein selbst vergebener Name wird nicht ueberschrieben.
- Eigener Mini-Systemprompt statt des Chat-Prompts, der will beraten,
  nicht betiteln. Anfuehrungszeichen/"Titel:"-Praefixe werden abgeraeumt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 12:19:27 +02:00
thomasandClaude Opus 4.8 4b3e66b4bd KI-Chat: Markdown-Tabellen und Trennlinien werden gerendert
Der Mini-Renderer kannte weder Tabellen noch ---, also landeten beide als
roher Text in einem Absatz: aus einer Anforderung/Erfahrung-Tabelle wurde
eine einzeilige Pipe-Wueste, aus --- blieb ---. Das Modell nutzt aber
gerade fuer Passungs-Vergleiche gern Tabellen.

- GFM-Tabellen inkl. Ausrichtung (:--:), auch ohne Rand-Pipes.
- Krumme Zeilen werden auf die Kopfbreite gezogen (fehlende Zellen leer,
  ueberzaehlige raus), sonst zerreisst eine Zeile das Raster.
- Wrapper scrollt horizontal, damit eine breite Tabelle die Chat-Blase
  auf dem Handy nicht auseinanderzieht.
- Zellinhalte laufen durch dieselbe Escape-/inlineFmt-Kette wie der Rest,
  HTML aus dem Modell kann also weiterhin nichts injizieren.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 12:15:25 +02:00
thomasandClaude Opus 4.8 4a036fe215 KI-Chat: Web-Recherche ueber Ollama Web Search
Der Assistent kannte bisher nur die Datenbank. Fragen wie "Was muss ich
ueber die Firma wissen?" konnte er damit nicht beantworten - Groesse,
Produkte, News, Kultur stehen nirgends in der Bewerbung.

Zwei neue Werkzeuge (web_suche, web_seite_lesen) ueber die Web-Search-API
von ollama.com. Sie authentifizieren sich mit demselben OLLAMA_API_KEY,
den der Benutzer fuer Chat und Dokumente ohnehin hinterlegt hat: keine
zusaetzliche Konfiguration. Anders als /api/chat haengt die Websuche
nicht an OLLAMA_HOST - wer rein lokal chattet, hat schlicht keine
Websuche, der Rest laeuft unveraendert weiter.

- Tool-Runden 4 -> 8: Bewerbung holen, suchen, Seiten lesen, antworten
  lief vorher ins Limit, statt zu antworten.
- Antworten der Websuche hart gedeckelt, sie wandern sonst in jeder
  weiteren Runde erneut ins Kontextfenster.
- Status-Label zeigt, wonach gesucht wird - sonst sieht der Nutzer nicht,
  ob die richtige Firma erwischt wurde.
- Nackte URLs im Chat sind klickbar: Belege nennt das Modell meist als
  reine URL, nicht als Markdown-Link.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 12:02:29 +02:00
thomasandClaude Opus 4.8 ab62399f6f Mobile: Glocken-Panel und Bewerbungsliste liefen aus dem Bild
Benachrichtigungen: Das Panel haengt per right:0 am Wrapper der Glocke, rechts
von ihr stehen aber noch Konto und Burger - die 20rem Breite wuchsen dadurch
nach links aus dem Viewport (gemessen -51px bei 375px Breite). Unter 768px
spannt es sich jetzt ueber den Header: `position: static` auf dem Anker macht
den Header zum Bezugsrahmen, was auch die vertikale Lage inklusive
Impersonation-Banner richtig haelt.

Bewerbungsliste: Das gestapelte Karten-Layout griff erst unter 640px, die
minmax()-Spalten des Rasters brauchen aber ~764px und die Karte bekommt ihre
min-w-[760px] erst ab md (768px). Im Band dazwischen - Handy quer, grosse
Phones - galt also das Desktop-Raster, und Stelle, Art und Status lagen
ausserhalb (132px Ueberhang bei 667px). Grenze auf 767px gezogen, damit sie an
md anschliesst.

Nachgemessen im Headless-Browser bei 375/390/412/667/740/820px: nichts ragt
mehr heraus, die Liste scrollt nicht mehr horizontal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 11:27:22 +02:00
thomasandClaude Opus 4.8 5ce835ff3a Bewerbungsstatus "Vertragsverhandlung" ergaenzt
Neue Station zwischen Vorstellungsgespraech und Einstellung - in der
Statusliste, der Gruppierung auf der Startseite, den Farbskalen (Lime,
zwischen dem Gelb des Gespraechs und dem Gruen der Einstellung), im
PDF-Export, in der Statistik und in der REST-API/OpenAPI.

Der Statistik-Funnel zaehlte bisher ever('Vorstellungsgespraech') +
ever('Einstellung') und damit eine Bewerbung, die beide Stationen
durchlaufen hat, doppelt. Mit einer weiteren Stufe waere das schlimmer
geworden, deshalb zaehlt der Funnel jetzt pro Bewerbung statt pro
Statuszeile: jede Stufe zaehlt eine Bewerbung genau einmal, ein spaeterer
Status impliziert die frueheren.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 11:10:02 +02:00
thomasandClaude 3c7f5d7c74 Mobile-Support: Smartphones für die ganze Seite
- Dashboard-Bewerbungsliste auf Handy als gestapelte Karten statt horizontal gescrolltem 760px-Raster
- Modals auf Handy scrollbar (Inhalt wurde bisher unten abgeschnitten)
- Chat: Thread-Aktionen auf Touch sichtbar, Sidebar/Chat-Höhen für mobil
- Bewerbungs-Antwort-Toolbar & Admin-Stat-Boxen umbruchfähig

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-14 10:24:49 +02:00
thomasandClaude 295c447d58 PDF-Export: /api/export monats/jahresbezogen gab 500 (ungueltiges SQL)
Die gefilterten Export-Zweige bauten `SELECT * FROM (<subquery>) AND
strftime(...)` — ohne WHERE nach der abgeleiteten Tabelle. SQLite
quittierte das mit "near \"AND\": syntax error" => HTTP 500, der Client
erhielt {error:'Serverfehler'} statt des Bewerbungs-Arrays und
generatePdfDocument crashte mit "applications.map is not a function".

Fix: `AND` => `WHERE` in den drei gefilterten Zweigen (month+year,
month, year). Die Per-User-Isolation bleibt unberuehrt (user_id-Filter
steht weiterhin in der Subquery). Zusaetzlich prueft generatePDF jetzt
Array.isArray(applications) und zeigt eine klare Fehlermeldung statt
des .map-Crashs.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-14 02:38:47 +02:00
thomasandClaude 69e6a29d87 PDF-Export: Autotable-Plugin zuverlaessig laden vor der Generierung
loadPdfLibraries() hat im "bereits ladend"-Zweig aufgeloest, sobald jsPDF
vorhanden war — das Autotable-Plugin (zweites Script) war dann aber noch
nicht angehaengt, sodass doc.autoTable undefined war und die Generierung
mit "doc.autoTable is not a function" fehlschlug. Die Polling-Bedingung
prueft jetzt auch jsPDF.API.autoTable, und generatePDF wartet zusaetzlich
auf loadPdfLibraries(), bevor das Dokument gebaut wird.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-14 02:35:37 +02:00
thomasandClaude fd1db3970f Sicherheitscheck: Schwachstellen behoben
- Pfad-Traversal: safeFilename/containedPath-Helper, storeAnhang sanitizes
  filename, alle Download/Delete/Mail-Send-Routen pruefen Containment
- Stored XSS: serveInline entscheidet Viewable-Typ nur nach Extension,
  nicht nach client/seitigem MIME; nicht viewbare Typen werden als Download
  erzwungen. Upload fileFilter (Basis/Interne) + Extension-Validierung
  (Signatur/Foto leiten Ext aus MIME, blockieren .html)
- URL-Scheme-Allowlist (safeUrl) fuer quelle_url-hrefs gegen javascript:-XSS
- E-Mail-Iframe: Sandbox auf allow-same-only (kein allow-popups-to-escape)
- Sicherheits-Header: CSP, X-Content-Type-Options, X-Frame-Options,
  Referrer-Policy, COOP; x-powered-by aus; jsPDF self-hosted unter /vendor
- Session: Secure-Flag bei TLS, serverseitige absoluteexpiry, Scrypt async
  + Dummy-Verify gegen Timing/Enumerate + Login-Rate-Limit
- Open Redirect: /email/fetch nur same-origin Redirects
- SSRF: Validierung von OLLAMA_HOST/CALDAV_URL/MAIL_HOST gegen
  Metadata/Link-Local-BLock (localhost/LAN bleibt erlaubt)
- Globaler Error-Handler ohne Interna-Leak, env=production

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-14 02:09:17 +02:00
thomasandClaude bf789af62a Rebranding zu NextJobs (nextjobs.cc)
- Markenname 'Bewerbungs-Tracker' -> 'NextJobs' im gesamten UI,
  Footer (incl. nextjobs.cc Copyright), Login, Wortmarke in der
  Top-Navi (Next + Jobs-Gradient), Favicon, Swagger/OpenAPI-Titel,
  CalDAV-PRODID, Browser-Erweiterung (manifest, content, background),
  Runner-Log und Doku.
- Funktionale Begriffe (Bewerbungsunterlagen, -datum etc.) bleiben.
- Docker-Registry (git.hackner.dev) als Infrastruktur unberuehrt.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-14 01:40:26 +02:00
thomasandClaude cc2df90653 Favicon und moderner Schriftzug fuer die Top-Navi
- Passendes SVG-Favicon (Gradient-Mark wie in der Top-Navi) +
  theme-color, registriert im <head>
- 'Bewerbungs-Tracker' als zweifarbiger Schriftzug: Bewerbungs in
  Slate, Tracker im Sky->Blue-Gradient

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-14 01:33:26 +02:00
thomasandClaude 9f000e2719 Filterbereich entfernt, Suche + Aktionen als Toolbar zusammengefasst
- Monat/Jahr-Filter geloescht (Server-Filter bleibt via URL nutzbar)
- Suchfeld und die Buttons 'PDF Export' und 'Bewerbung hinzufuegen'
  in einer gemeinsamen Toolbar zusammengefasst, als Pill-Buttons
- Tote filterForm-CSS und -JS entfernt

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-14 00:56:24 +02:00
thomasandClaude 4fd843318f Header/Navigation-Styling ueberarbeitet
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-14 00:34:49 +02:00
thomasandClaude Opus 4.8 1aa902bdce Top-Navigation: neu strukturiert, mit Aktiv-Zustand und Dropdowns
Die Leiste war eine flache Reihe aus sieben gleichrangigen Pillen. Zwei
Probleme: sie zeigte nie an, wo man gerade ist (es gab schlicht keinen
Aktiv-Zustand), und auf dem Handy fielen nur die Beschriftungen weg - uebrig
blieben sieben unbeschriftete Icons.

Struktur folgt jetzt dem tatsaechlichen Ablauf (finden -> bewerben ->
antworten) statt der Reihenfolge, in der die Seiten entstanden sind:

  Bewerbungen | Jobangebote | Stellensuche v | Postfach | Vorlagen

"Stellensuche" buendelt als Dropdown, was Angebote erzeugt und filtert:
Suchprofil & Laeufe, Uebernommene Angebote, Blacklist - jeweils mit einer Zeile,
die erklaert, wozu der Punkt gut ist. Einstellungen, Benutzerverwaltung und
Abmelden wandern ins Konto-Menue rechts, wo Systemkram hingehoert. Die
taeglichen Wege (Bewerbungen, Jobangebote, Postfach) bleiben ein Klick weit weg.

Gestaltung: Die Leiste behaelt in beiden Themes ihr dunkles Navy - sie ist der
Rahmen des Werkzeugs und die einzige Flaeche, die sich beim Navigieren nie
aendert. Alles darin ist bewusst still (Slate auf Navy); der einzige Akzent ist
der aktive Punkt, markiert durch eine schmale leuchtende Schiene an der
Unterkante. Beim Hover deutet sich dieselbe Schiene gedaempft an.

Zaehler (ungelesene Post, offene Angebote) haengen jetzt an Klassen statt IDs,
weil sie in Leiste UND Mobilmenue erscheinen. Ein Controller bedient alle
Dropdowns: Klick oeffnet, andere schliessen, Escape und Klick daneben schliessen,
aria-expanded bleibt ehrlich. Fokus ist sichtbar, prefers-reduced-motion wird
respektiert, das Mobilmenue zeigt alle Punkte ausgeschrieben und gruppiert.

Der aktive Pfad kommt aus res.locals.pfad (Auth-Middleware). Die Navigation liegt
weiterhin in genau einem Partial - sie ist damit auf allen Seiten identisch;
nur die Login-Seite hat bewusst keine.

Geprueft mit echtem Browser (Screenshots): hell/dunkel, Dropdowns, Mobilmenue,
und der Aktiv-Zustand je Seite - Unterseiten wie /blacklist oder /jobsuche heben
korrekt "Stellensuche" hervor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 23:55:58 +02:00
thomasandClaude 41887dd56c Status "Absage von meiner Seite" ergaenzt
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-13 12:51:37 +02:00
thomasandClaude 0c866805d3 Dark Mode folgt Browser-Einstellung (prefers-color-scheme), Toggle entfernt
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-13 12:34:56 +02:00
thomasandClaude e97d1b3687 Persoenliche Angaben unter Vorlagen statt hardcoded/Settings-Modal
- settings-Tabelle um email/telefon/ort/webseite/geburtsdatum erweitert
  (CREATE + ALTER-Migration + Default-INSERT)
- Vorlagen-Seite: neuer Bereich "Persoenliche Angaben" (8 Felder),
  POST /vorlagen/persoenlich; Settings-Modal + Zahnrad-Button entfernt
- Generierung: Kontakt kommt verbindlich aus den Settings (buildHeader
  laesst gespeicherte Werte ueber die KI-Extraktion gewinnen), bewerber-
  Fakten + Prompt angepasst; Design-Vorschau zeigt echte Kontaktdaten
- main.js: Settings-Modal-Handling entfernt; /api/settings bleibt bestehen

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-13 11:41:32 +02:00
thomasandClaude 0c7502cfd7 KI-Chat: Bewerbungsdaten per Tool-Calling nachschlagen statt in Prompt stopfen
Der Systemprompt enthielt früher nur die 12 jüngsten Bewerbungen (LIMIT 12),
daher fand die KI ältere Firmen wie QBS KLIMTAX nicht. Alle Bewerbungen in den
Prompt zu laden machte den Chat sehr langsam, da der Prompt bei jedem Turn
voll neu verarbeitet wird.

Jetzt nutzt der Assistent Ollama-Tool-Calling, um Datenbankdaten on demand
nachzuschlagen. Der Systemprompt bleibt klein und konstant (nur Profil +
Datum), unabhängig von der Anzahl der Bewerbungen.

- lib/chat.js: streamChat mit tools + runChat-Tool-Schleife (max 4 Runden)
- server.js: Tools suche_bewerbungen/list_bewerbungen/bewerbung_detail/
  kommende_termine mit SQL-Queries; gatherChatContext auf Kern reduziert
- public/js/chat.js: Tool-Indikator ("durchsucht Bewerbungen…") im UI

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-09 05:41:14 +02:00
thomasandClaude 40b4de12e1 KI-Chat: Verlaufs-Titel live aktualisiert + sauberes Floating-Icon
- Der automatisch generierte Verlaufs-Titel (aus der ersten Nachricht) erscheint
  jetzt sofort in der Sidebar, nicht erst nach Reload. Der titel wird im
  done-Event mitgeschickt und clientseitig gesetzt.
- Floating-Button-Icon durch eine saubere Chat-Bubble ersetzt.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-07 14:57:35 +00:00
thomasandClaude 26a61aa302 KI-Chat: Verlaufs-Titel inline umbenennbar
Pro Verlauf ein Stift-Button (neben dem Löschen-Button). Klick macht den
Titel inline editierbar; Enter/Blur speichert via PATCH /chat/api/threads/:id,
Escape bricht ab. Auch für client-seitig neu angelegte Threads.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-07 14:55:06 +00:00
thomasandClaude 61b67e0843 KI-Chat: Verlauf wird beim Laden angezeigt + einzelne Chats löschbar
- initialMessages-JSON wurde mit <%= %> HTML-escaped, wodurch JSON.parse
  fehlschlug und die Nachrichtenfläche beim Öffnen eines Verlaufs leer blieb.
  Jetzt <%- %> mit < -> < escapet, JSON bleibt parsebar.
- Pro Verlauf ein Trash-Button in der Sidebar (DELETE /chat/api/threads/:id).
  Löscht den Verlauf, entfernt die Zeile, setzt bei aktivem Verlauf auf
  neuen Chat zurück. Auch für client-seitig neu angelegte Threads.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-07 14:42:29 +00:00
thomasandClaude cc397e8f71 KI-Chat: Markdown-Rendering, Left-Alignment, kompaktere UI, "Neuer Chat" fix
- Markdown-Renderer (Headings/Listen/**bold*/*italic*/`code`/Links) für
  Assistenten-Antworten; HTML wird vorher escaped (keine Injektion).
- Antworten explizit linksbündig, Inhalt getrimmt (keine Leerzeilen oben/unten).
- Initiale Nachrichten als JSON-Blob, einheitlich via chat.js gerendert.
- Chat-Container kompakter (Höhe/Spacing reduziert).
- "Neuer Chat" setzt clientseitig auf leeren Zustand zurück (vorher lud der
  Button nur den letzten Thread neu).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-07 14:28:54 +00:00
thomasandClaude ead70efa49 KI-Chat: interaktiver Bewerbungs-Assistent (Ollama-Streaming)
Eigenes Chat-Interface mit SSE-Streaming gegen das hinterlegte Ollama-Modell,
gegroundet in den Bewerbungs-/E-Mail-/Termindaten des Nutzers.

- lib/chat.js: streamChat (Ollama stream:true, NDJSON-Token) + buildContextPrompt
- chat_threads/chat_messages Tabellen (CASCADE, Index)
- Routen: GET /chat, Thread-CRUD, POST /messages (SSE, AbortController)
- views/chat.ejs + public/js/chat.js + Floating-Button im Footer
- hasApiKey-Gating (503 ohne OLLAMA_API_KEY)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-07 14:07:38 +00:00
thomasandClaude Opus 4.8 c2c51f9fca Neue Bewerbungs-Status: In Bearbeitung, Interessiert, Warten auf Rückmeldung/meine Antwort
Vier mittlere Pipeline-Status ergänzt (nach Eingangsbestätigung, vor
Vorstellungsgespräch), inkl. Farben/Reihenfolge in Übersicht, Detailseite
und PDF-Export sowie REST-API-Validierung und Swagger-Enum.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-06 10:06:56 +02:00
thomasandClaude Opus 4.8 03760d293f Labels für Stellen (Bewerbungen + Jobangebote), inkl. API & Swagger
Mehrere Labels pro Stelle (Regional, Remote-Deutschlandweit,
Homeoffice-Deutschlandweit), gespeichert als JSON-Array in einer neuen
labels-Spalte beider Tabellen (Migration). Geteiltes lib/labels.js mit
parse/serialize; wiederverwendbare Partials fuer Chips + Mehrfachauswahl.

- Web: setzen im Hinzufuegen-Modal, auf der Bearbeiten-Seite und im
  Jobangebot-Bearbeiten-Formular; Anzeige als Chips in den Listen.
- Uebernahme eines Angebots traegt dessen Labels in die neue Bewerbung.
- REST-API: labels[] in /applications und /joboffers (GET/POST/PUT),
  Filter ?label=…; OpenAPI/Swagger-Schemas + Enums erweitert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 15:23:44 +02:00
thomasandClaude Opus 4.8 d1c6743d2f PDF-Export: keine zerrissenen Zeilen mehr + HTML-Entities dekodieren
rowPageBreak 'avoid' verhindert das Splitten einer Zeile am Seitenumbruch,
das bisher Datum/Art/Status auf der Folgeseite leer liess. Firma/Stelle/Art
werden HTML-dekodiert (z.B. "&amp;" -> "&"), leere Werte zeigen "—".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 14:20:27 +02:00
thomasandClaude Opus 4.8 78b1ea78d0 PDF-Export: kompakte Tabelle, Filter nach letztem Status-Datum
Verbose Blöcke (Notizen + kompletter Verlauf) durch eine autoTable ersetzt:
eine Zeile pro Bewerbung mit Datum, Firma, Stelle, Art und letztem Status
(farbiges Badge). Export filtert/datiert nun nach dem effektiven Datum, also
der letzten Statusaenderung – eine im Juni gesendete, im Juli zum Gespraech
gewordene Bewerbung erscheint dadurch im Juli-Export. Modal-Auswahl nutzt
dieselben effektiven Monate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 14:12:24 +02:00
thomasandClaude Opus 4.8 b1c92dd77b Guard against duplicate applications + capture jobs on any website
Duplicate safeguard: before creating an application (manual add and
browser import) the server checks for an existing one for the same job —
matched on a normalised source URL (job-id params like Indeed's jk pin
the posting across paths/tracking) or an identical company + role
(case/umlaut/whitespace-insensitive). On a match it returns 409 with the
matches; the web form and the extension show the existing entry and
re-submit with force=true only if the user confirms. Not a hard block, so
legitimate re-applications stay possible.

Universal capture: the extension popup becomes an editable capture form
that works on any site. It extracts the active page on demand (schema.org
JobPosting JSON-LD -> OpenGraph/meta -> h1/title/selection -> canonical
URL), lets the user review/correct, and sends. The import route is now
source-agnostic and derives the application source (art) from the URL
instead of hardcoding Indeed; the Indeed on-page button remains as a fast
path. Adds scripting/activeTab permissions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 18:32:17 +02:00
thomasandClaude Opus 4.8 05357acddb Fix monthly PDF export to include only the selected month
The month dropdown carried only the month (e.g. "07") while the year was
a separate select; the export required month AND year, so a month-only
selection fell through to exporting every application. Encode the year in
the month option value ("YYYY-MM"), parse it client-side to always send
the exact month+year, and harden /api/export so a month can never fall
through to "export all".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 16:39:46 +02:00
thomasandClaude Opus 4.8 fd20ca0daf Add AI application assistant: Indeed import + Ollama document generation
- Browser extension (Chromium MV3) injecting a "send to tracker" button next
  to the Indeed job description; scrapes job info and posts it to a new
  /api/indeed-import endpoint (CORS-enabled), configurable tracker URL via popup.
- New "Entwurf" status. Imports create a draft and trigger background AI
  generation of tailored Anschreiben + Lebenslauf (PDF attachments) via the
  Ollama Cloud API, grounded strictly in user-provided base documents.
- Vorlagen page to manage base documents; attachments UI, generation status
  polling, regenerate and download routes on the application page.
- Schema: ort/stellenbeschreibung/quelle_url/generierung_* columns, plus
  basis_dokumente and anhaenge tables (with migrations).
- Config via .env (OLLAMA_API_KEY/OLLAMA_MODEL/OLLAMA_HOST); dependency-free
  .env loader. Dockerfile copies lib/, .dockerignore added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 22:21:28 +02:00
thomas 6952309de9 update 2026-06-19 04:32:02 +02:00
thomasandClaude Opus 4.8 c2a629e2c0 Refactor UI/views, rework Docker build, untrack local data
- Views umstrukturiert: einstellungen.ejs -> bewerbung.ejs, neues
  partials/head.ejs, header/footer/index angepasst
- CSS umbenannt: style.css -> styles.css
- server.js und public/js/main.js ueberarbeitet
- Dockerfile auf schlankes Multi-Stage-Setup umgestellt;
  docker-compose.yml und .dockerignore entfernt
- npm-Scripts docker:build/push/deploy ergaenzt
- SQLite-DB und .idea aus Git entfernt und via .gitignore ignoriert

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 04:01:37 +02:00
thomasandClaude Sonnet 4.6 c65c9f1751 Initial commit
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 18:15:11 +02:00