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>
New issued-purchase-order document type (schema, numbering, service, routes, PDF, frontend Vydané tab) plus the orders/invoices consistency pass (Vydané-first tabs, shared month filter, shared red-accent PDF template). Dependency upgrades (React 19, Prisma 7, file-type 22) were landed separately on master first.
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>
Pre-existing quotations.project_code + ai_chat_messages FK drift is captured in its own reconcile_quotations_aichat_fk migration; the issued_orders migration is purely additive.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Brainstormed design for a new 'objednávky vydané' document type — purchase
orders you author with line items and export to PDF, alongside the existing
customer orders (which become the 'přijaté' tab).
Key decisions: dedicated issued_orders/issued_order_items models mirroring
invoices; customers reused as supplier/partner; NET+VAT-on-top; dedicated
numbering sequence; reuse of orders.* permissions; standalone v1 (no
warehouse/received-invoice links).
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>
CLAUDE.md: new "2026-06-09 audit" enforced-rules section (mandatory coercion
helpers, deterministic id-tiebreak ordering, $queryRaw FOR-UPDATE locking
discipline, uniqueness-in-transaction, guard reads, Rules-of-Hooks, seed prod
guard), lint/typecheck/format commands, and the app_test/.env.test testing
section with the hard-throw guard.
README rewritten from the placeholder stub (setup/run/build/test/gates/migrations).
REVIEW.md / REVIEW_FINDINGS.md / REVIEW_FIXES.md: the full audit record — 277
files reviewed, ~362 findings, every one fixed and verified.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
From the 2026-06-09 full-codebase audit. Warehouse line->header relations
CASCADE on delete; line->master-data and the financial FKs are explicit
RESTRICT; UNIQUE on receipt/issue/session numbers; new indexes on hot FK
columns (sklad_issues/receipts, bank_accounts); dropped a duplicate
audit_logs created_at index. Applied to dev `app` and `app_test`.
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>