Compare commits
4 Commits
v2.4.6
...
88d5d43448
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88d5d43448 | ||
|
|
4745af3639 | ||
|
|
4258699b73 | ||
|
|
bfd2c59ad3 |
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "app-ts",
|
||||
"version": "2.4.6",
|
||||
"version": "2.4.8",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "app-ts",
|
||||
"version": "2.4.6",
|
||||
"version": "2.4.8",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.102.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "app-ts",
|
||||
"version": "2.4.6",
|
||||
"version": "2.4.8",
|
||||
"description": "",
|
||||
"main": "dist/server.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -1163,7 +1163,16 @@ export default function InvoiceDetail() {
|
||||
mb: 3,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
|
||||
{/* flexWrap: long document titles must drop below the Zpět button
|
||||
on phones instead of overflowing the viewport. */}
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 2,
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
component={RouterLink}
|
||||
to="/invoices?tab=issued"
|
||||
@@ -1724,7 +1733,16 @@ export default function InvoiceDetail() {
|
||||
mb: 3,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
|
||||
{/* flexWrap: long document titles must drop below the Zpět button
|
||||
on phones instead of overflowing the viewport. */}
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 2,
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
component={RouterLink}
|
||||
to="/invoices?tab=issued"
|
||||
|
||||
@@ -573,7 +573,16 @@ export default function IssuedOrderDetail() {
|
||||
mb: 3,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
|
||||
{/* flexWrap: long document titles must drop below the Zpět button on
|
||||
phones instead of overflowing the viewport. */}
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 2,
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
component={RouterLink}
|
||||
to="/orders?tab=vydane"
|
||||
|
||||
@@ -620,7 +620,16 @@ export default function OfferDetail() {
|
||||
mb: 3,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
|
||||
{/* flexWrap: long document titles must drop below the Zpět button
|
||||
on phones instead of overflowing the viewport. */}
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 2,
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
component={RouterLink}
|
||||
to="/offers"
|
||||
|
||||
@@ -58,7 +58,13 @@ export const theme = createTheme({
|
||||
h1: { fontFamily: FONT_HEADING, fontWeight: 800 },
|
||||
h2: { fontFamily: FONT_HEADING, fontWeight: 800 },
|
||||
h3: { fontFamily: FONT_HEADING, fontWeight: 800 },
|
||||
h4: { fontFamily: FONT_HEADING, fontWeight: 700 },
|
||||
h4: {
|
||||
fontFamily: FONT_HEADING,
|
||||
fontWeight: 700,
|
||||
// Page/detail headlines: MUI's default 2.125rem overflows phone
|
||||
// viewports (long document titles + number). Scale down on xs only.
|
||||
"@media (max-width:600px)": { fontSize: "1.5rem" },
|
||||
},
|
||||
h5: { fontFamily: FONT_HEADING, fontWeight: 700 },
|
||||
h6: { fontFamily: FONT_HEADING, fontWeight: 700 },
|
||||
button: { textTransform: "none", fontWeight: 600 },
|
||||
|
||||
16
src/main.tsx
16
src/main.tsx
@@ -4,6 +4,22 @@ import { BrowserRouter } from "react-router-dom";
|
||||
import App from "./App";
|
||||
import { ThemeProvider } from "./context/ThemeContext";
|
||||
|
||||
// Deploy-skew recovery (official Vite mechanism): after a release the old
|
||||
// hashed chunks are deleted, so a tab still running the previous build fails
|
||||
// its next lazy-page import. Vite emits `vite:preloadError` for exactly this —
|
||||
// swallow the error and reload once, which loads the fresh index.html and new
|
||||
// chunk names. The timestamp guard prevents a reload loop when a chunk is
|
||||
// genuinely unloadable (e.g. offline): a second failure within 10 s falls
|
||||
// through to the normal error path.
|
||||
window.addEventListener("vite:preloadError", (event) => {
|
||||
const KEY = "vite-preload-reloaded-at";
|
||||
const last = Number(sessionStorage.getItem(KEY) || 0);
|
||||
if (Date.now() - last < 10_000) return;
|
||||
sessionStorage.setItem(KEY, String(Date.now()));
|
||||
event.preventDefault();
|
||||
window.location.reload();
|
||||
});
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
<React.StrictMode>
|
||||
<BrowserRouter
|
||||
|
||||
@@ -206,12 +206,30 @@ async function start() {
|
||||
root: path.join(__dirname, "..", "dist-client"),
|
||||
prefix: "/",
|
||||
wildcard: false,
|
||||
// Deploy-skew caching contract (per the Vite deploy guide):
|
||||
// - /assets/* names are content-hashed → safe to cache forever
|
||||
// (a changed file always gets a NEW name, so "immutable" is correct).
|
||||
// - index.html (and any non-hashed file) must always be revalidated,
|
||||
// otherwise a cached copy keeps referencing deleted old chunks.
|
||||
setHeaders: (res, filePath) => {
|
||||
if (filePath.includes(`${path.sep}assets${path.sep}`)) {
|
||||
res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
|
||||
} else {
|
||||
res.setHeader("Cache-Control", "no-cache");
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
app.setNotFoundHandler((request, reply) => {
|
||||
if (request.url.startsWith("/api/")) {
|
||||
return reply.status(404).send({ success: false, error: "Not found" });
|
||||
}
|
||||
// A missing hashed asset (old chunk after a deploy) must 404, NOT get
|
||||
// the SPA index.html fallback — HTML-as-JS masks the failure and breaks
|
||||
// the client's vite:preloadError reload recovery.
|
||||
if (request.url.startsWith("/assets/")) {
|
||||
return reply.status(404).send();
|
||||
}
|
||||
return reply.sendFile("index.html");
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user