Replaces MUI autosize (no handle, mis-wraps in table cells) with a fixed 2-row textarea + resize:vertical so the box wraps and the user can drag it taller. Restores the dead '& input' sx as '& textarea'.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The editable unit-price number input was cramped (5.5rem, same as qty/unit); widened to 8rem to match the Celkem column and the read-only detail view, so real prices fit.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The list opened the PDF route via raw window.open, an unauthenticated top-level navigation that 401s on the token-guarded endpoint. Now uses the apiFetch -> blob -> window pattern from InvoiceDetail/IssuedOrderDetail.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses review of the issued-orders service: deleteIssuedOrder now derives the release year from created_at (cross-year deletes reclaim the right sequence); list sort direction is normalized to asc/desc; status cast uses $Enums instead of as-never. Adds a cross-year delete test.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
file-type 16 -> 22 fixes GHSA-5v7r-6r5c-r473 (infinite loop in the ASF parser on
malformed input). v22 is ESM-only with a strict `exports` map, so the CommonJS
server can no longer `require()` it (and tsc would down-level a normal dynamic
import() back to require()). Converted the single call site in
nas-file-manager.ts to a cached runtime dynamic ESM import via the
Function('return import("file-type")') indirection (the same pattern server.ts
already uses for Vite), and updated the API to the v22 named export
`fileTypeFromBuffer` (was the v16 default `FileType.fromBuffer`).
Verified at runtime that the dynamic import loads v22 from CJS and detects
PNG/JPEG/PDF correctly. `npm audit` no longer lists file-type (6 -> 5 vulns; the
rest are quill [Phase 4] and Prisma 7's dev-tooling @hono/node-server).
Gates: tsc 0 | build 0 | vitest 247/247 | eslint 0 errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
prisma / @prisma/client 6.19.2 -> 7.8.0; added @prisma/adapter-mariadb 7.8.0 and
mariadb 3.5.2 (the runtime driver).
Prisma 7 breaking changes handled (verified via Prisma's own tooling — the
official upgrade-guide summary had hallucinated package names, so I confirmed
everything against `prisma validate`/`generate` and the installed type defs):
- The Rust query engine is removed; the runtime connection is now made through a
driver adapter. src/config/database.ts wires PrismaMariaDb, parsing
DATABASE_URL into an explicit mariadb pool config so the literal `@` in our
password (which a naive connection-string parser mishandles) is correct.
- datasource `url` is no longer allowed in schema.prisma -> moved to a new
prisma.config.ts (schema + datasource.url + migrations.seed). Prisma 7 no
longer auto-loads .env, so the config does `import "dotenv/config"`.
- The deprecated package.json#prisma seed config was removed (now in the config).
- seed.ts and the two maintenance scripts now use the shared adapter-wired client.
The legacy `prisma-client-js` generator STILL works under v7 (generates to
@prisma/client as before), so NO import-path changes were needed across the 16
@prisma/client import sites — switching to the new prisma-client/output generator
is not required for us. No prisma.$use() middleware exists. Node 24 / TS 5.9
exceed the v7 minimums (20.19 / 5.4).
No migrations were run (your DBs are untouched); removing datasource.url is a
config change, not DDL, so this phase needs no new migration.
Gates: tsc 0 | build 0 | vitest 247/247 against app_test (proves the mariadb
adapter works at runtime, incl. raw SELECT...FOR UPDATE locking and Decimal/VAT)
| eslint 0 errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
react / react-dom 18.3.1 -> 19.2.7; @types/react 18.3.31 -> 19.2.17;
@types/react-dom 18.3.7 -> 19.2.3 (latest stable 19.2.x).
Only breaking change hitting our code: React 19 removed the no-arg useRef<T>()
overload. AppShell's logoutTimer -> useRef<T | undefined>(undefined) — the same
transform the official `types-react-codemod preset-19` (useRef-required-initial)
applies. No other preset-19 transforms were relevant (already on createRoot; no
ReactDOM.render, string refs, propTypes/defaultProps on function components).
No Next.js / RSC in this project (it's a Vite SPA served by Fastify), so those
React 19 concerns don't apply here. MUI v7, framer-motion 12, TanStack Query 5,
react-router-dom 6 and react-quill-new all installed cleanly against React 19
(no peer-dependency conflicts).
Gates: tsc 0 | build 0 | vitest 247/247 | eslint 0 errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Odin currently advertises general Q&A via the /chat path, which burns API
credits on open-ended questions. Guard it: a text-only message makes NO AI
call — it gets a canned reply telling the user to attach an invoice. The
invoice path (attachments → extract) is unchanged. Removing the guard
re-enables the general /chat path for a later phase.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ensureActive() created the conversation and invalidated the list before the
server-side auto-title was applied (that happens on the message append), so
the sidebar showed "Nová konverzace" until a refresh. Move the
["ai","conversations"] invalidation to persist()'s success path — after the
append (and thus the auto-title) lands.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Composer rebuilt claude-style: a rounded pill with a multiline auto-growing
textarea and attach/send ICON buttons (vertically centered), so there's room
to type on mobile (was squeezed between two text buttons).
- Conversation list collapses into a slide-in Drawer below md, with a menu
button in the chat header; chat is full-width on mobile.
- Each new message bubble animates in (fade + slide-up; fade-only under
prefers-reduced-motion) via framer-motion, so sent messages and Odin's
replies appear smoothly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Swap Fraunces → Newsreader (literary serif, full Czech, optical sizing).
- Split the greeting so the user's first name renders in brand-red italic.
- Add a subtle staggered fade-in (framer-motion), skipped under
prefers-reduced-motion.
- New description that names Odin and covers chat + invoice import.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the gradient "Odin" wordmark from the new-conversation hero and make the
personalized greeting the centerpiece in Fraunces (optical-sized serif), with
an italic Fraunces description — a refined, literary, claude-like treatment.
Loads Fraunces (upright + italic) via the existing Google Fonts link.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Time-based Czech greeting + the user's first name (e.g. "Dobrý večer, Admin")
above the big gradient "Odin" wordmark, with a stylish description line. Drop
the redundant mark from the hero (it lives on the nav item + thinking state).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The mark already lives on the nav menu item; the conversation sidebar's brand
no longer duplicates it — just a larger gradient "Odin" wordmark above the new
chat button.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the small empty-state with a large centred hero — the animated Odin
mark + a big gradient "Odin" wordmark (responsive clamp font) and the helper
line beneath.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the chat-bubble glyph on the "Odin" nav item with the OdinMark, and
make the mark's SVG sizing !important so the nav's `& svg { width:18 }` rule
can't shrink it (its `size` prop stays authoritative everywhere).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A self-contained animated SVG mark in BOHA red: a 4-point AI spark (Odin) that
slow-rotates and breathes, a data node orbiting a faint ring (the assistant at
the centre of the business system). An "idle" and a "thinking" state (faster
orbit + red glow); honours prefers-reduced-motion. Used as the sidebar brand,
the empty-state hero, and the busy/thinking indicator.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Replace top tabs with a left sidebar (New chat + scrollable conversation
list, per-row rename/delete menu, active highlight), chat on the right.
- Fix duplicate-empty-conversations bug: "Nová konverzace" now just opens an
empty composer; a DB row is created only on the first successful message
(and a failed AI call no longer leaves an orphan conversation). Repeated
clicks never spawn empties, and nothing empty survives a refresh.
- Land on a fresh new chat on mount; deleting the open conversation returns
to new chat.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the 1100px max-width cap and size the chat shell to fill the content
area (100dvh - 100px = top bar + main bottom padding), so Odin uses the whole
page now that it owns the route.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Final review caught two minor issues:
- saveInvoice invalidated ["received-invoices"], which matches no query (the
received list/stats are keyed ["invoices","received",…]); use ["invoices"]
so the list/stats actually refresh after an import.
- The Save button hit /received-invoices (requires invoices.create) while Odin
is gated only on ai.use; disable Uložit (with a hint) when the user lacks
invoices.create so the UI never offers a 403 action. Dormant today (ai.use is
admin-only and admin bypasses checks) but correct going forward.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wire OdinTabs/OdinThread/InvoiceReviewCard/OdinComposer into a single
OdinChat orchestrator; mount it on /odin (maxWidth 1100); delete the
old DashAssistant and its aiHistoryOptions query helper.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add four presentational components + shared types under
src/admin/components/odin/ as props-driven building blocks for the
multi-conversation Odin page (Task 6 wires state/queries).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove single-thread getChatHistory/appendChatMessages/clearChatHistory and the
three /history routes; add listConversations, createConversation, getConversation-
Messages, appendConversationMessages, renameConversation, deleteConversation with
ownership checks and auto-title; wire six /conversations[/:id] routes.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>