From de388422d0f779f5d04d4fa348f29374039c8d4b Mon Sep 17 00:00:00 2001 From: Bianca Roberta Ianosel Date: Thu, 5 Nov 2026 11:10:01 +0000 Subject: [PATCH 1/2] feat(documents): add document catalog with mock PDF preview and download --- AGENTS.md | 2 +- public/documents/mock/bilancio-2022.pdf | Bin 0 -> 882 bytes public/documents/mock/bilancio-2023.pdf | Bin 0 -> 882 bytes public/documents/mock/bilancio-2024.pdf | Bin 0 -> 882 bytes .../documents/mock/polinetwork-in-breve.pdf | Bin 0 -> 889 bytes public/documents/mock/privacy-app.pdf | Bin 0 -> 884 bytes public/documents/mock/privacy-ccs.pdf | Bin 0 -> 898 bytes public/documents/mock/privacy-soci.pdf | Bin 0 -> 895 bytes public/documents/mock/regolamento-interno.pdf | Bin 0 -> 906 bytes public/documents/mock/regolamento-utenti.pdf | Bin 0 -> 895 bytes public/documents/mock/statuto.pdf | Bin 0 -> 894 bytes src/app/documents/page.tsx | 43 +++++++++++ .../documents/document-category-card.tsx | 61 +++++++++++++++ src/components/documents/mock-data.ts | 71 ++++++++++++++++++ src/components/documents/types.ts | 12 +++ src/components/header/constants.ts | 1 + src/components/ui/hero.tsx | 9 ++- 17 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 public/documents/mock/bilancio-2022.pdf create mode 100644 public/documents/mock/bilancio-2023.pdf create mode 100644 public/documents/mock/bilancio-2024.pdf create mode 100644 public/documents/mock/polinetwork-in-breve.pdf create mode 100644 public/documents/mock/privacy-app.pdf create mode 100644 public/documents/mock/privacy-ccs.pdf create mode 100644 public/documents/mock/privacy-soci.pdf create mode 100644 public/documents/mock/regolamento-interno.pdf create mode 100644 public/documents/mock/regolamento-utenti.pdf create mode 100644 public/documents/mock/statuto.pdf create mode 100644 src/app/documents/page.tsx create mode 100644 src/components/documents/document-category-card.tsx create mode 100644 src/components/documents/mock-data.ts create mode 100644 src/components/documents/types.ts diff --git a/AGENTS.md b/AGENTS.md index abd1342a..96a620fd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,7 +14,7 @@ breaking changes with `!` or a `BREAKING CHANGE:` footer. -# This is NOT the Next.js you know +## This is NOT the Next.js you know This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices. diff --git a/public/documents/mock/bilancio-2022.pdf b/public/documents/mock/bilancio-2022.pdf new file mode 100644 index 0000000000000000000000000000000000000000..bb3166c66c05062c09e87ba8e46b485f25cf355a GIT binary patch literal 882 zcmZ`&U2obj6n)RHI4?+@)HZQQ7)VtmK7xi;NCoNkQ0>7ahPeuMMi_xz7;h>A}n!x>j_mjqWga@ z*z^3avZ(Rcb@U%pMK{WVoeAB_N1?MZf~b2Re=Z8iNBR|y0rBHOjD8eSy``viV{&T9 zbvo6gXg?St=SV*8C0k{wT8;RC9@}=T_wH>Cf7PsQJGL91!5{Hj>{q^s@>y7!bNGqM zbs-gH@?EN-s^uGS%EcKT4eo^6+MnpMz|CJO8a!C?H1)B zIX>r}dynnT{djWaUq_A)4{G)7+}yy;epejsh#6L@4cvRS;WYvxEO2}4a9LQQ{eLgm z^W5*EsPPn9`cJB&8foC43GK>fp|#PRsCyrG#tXrQ>J?8O@#B7sUKCKh1+P`3b85(T zI#Hx(KYT>ik$mh+w$f0w8u0-=wryGO-P;=OvRRpSY&*OUcgSkKU-`z%9XBFpa1)uU zLP*NwhmZqVi#Onui#0qN+;O=vzYzMKoxe7kv#m4CsBa&BfNYJ}!`GgNtZ*zAwAjT^ zluYJAA@G9m3$ka&!&THfvi!&@FUINUVV2CZ6yxbEU1p0xHhoOtVxG?NFquq8)4^Tx z5Bc6Csrj}dkBVzlTJ0!(Fbb{GV#~l$s25o(RzUKyWT@CiNQSY>n=J(^uK!zjS|V@L zZ;XOe5}H+&<~6ApYQs!p#3eM}sFJm%E+OPv=`Ay2$Dy#=k53d<9(l2y ZJe9Ji{cu%iT5Z}syZye?=_KjI`3pBN^wt0X literal 0 HcmV?d00001 diff --git a/public/documents/mock/bilancio-2024.pdf b/public/documents/mock/bilancio-2024.pdf new file mode 100644 index 0000000000000000000000000000000000000000..de40a7edaef351590a4b3b6aea1a07bf64abc241 GIT binary patch literal 882 zcmZ`&U2obj6n)RHI4?+@)HZQQ7)VtmK7xi;NCoNkQ0>7ahPeuMMi_xz7;h>A}n!x>j_mjqWga@ z*z^3avZ(Rcb@U%pMK{WVoeAB_N1?MZf~b2Re=Z8iNBR|y0rBHOjD8eSy``viV{&T9 zbvo6gXg?St=SV*8C0k{wT8;RC9@}=T_wH>Cf7PsQJGL91!5{Hj>{q^s@>y7!bNGqM zbs-gH@?EN-s^uGS%EcKT4eo^6+MnpMz|CJO8*VH?>Rae$j%fUNKw)^4zi zxe|Ov=0)kB;1VlMJNks|*$Z$L^^W8)ase(z>F{xy%(4`t$uwPNi+(own!?8{o#Amj zo(w1bhvXmfy;oAvw&V&WX_Q*+>=3|g?@GjnoyUv*0s`GX84Yvu;PNroLdSL2cIN! zV?6Zna1UQKt!-nvUhuZijpI&?HBQ(qnp4OV7WY12VQ8o4))}uI*{AUr*6EySjb>bs hZeGrk|0ZFde z_B{WKC@MU49Q`|0(Y3M&&xCH}qtMwHPSm}RKjnpB1O1ApfcWukj9%2HdJA6Z+T_%b z>vXJ1(R_r6oFnq02Xr+Wh>4?AZ%&74?q1Fml?9Q968_CbKNXXfjQg*`l9KzNYXoOJ{f-k0--P z{~`H@d~cIfd|L{Qk{gsp?SXmlgk&2=3M d;x|W^4ye}dydN$LO>1n^=S>iLtyYqby+1`v_J05X literal 0 HcmV?d00001 diff --git a/public/documents/mock/privacy-ccs.pdf b/public/documents/mock/privacy-ccs.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f4407050c0d274dc15c2cd3180eae8f9b4d2573c GIT binary patch literal 898 zcmZ`&-EP`27`@L^oExN0YMXybrK%EyprI8~LAqU3yD-TIa~15!ak{P#vge&0pwM-* zauFY&^PTU9CR&4dj~>#ttoQC+40qYCbXT@5-iJG44ed@oQ}s-m7YuGH z3R!Ytdh(5np=kIUaO{gUJQ&=O*ytY!gTAf5(TdpC8RaH#AAW#*jo8E0z(Za-77IG; zEMcPLCDR;9vczvtMXy{6j=&4TPspDf4;Rtkpz1>=O53_WhXBbas*)m@Y^XbnGWyLf5>;HX+ztZOVp%LEBS1u0cM(5E52nQl=7#jBr74Psu*gv;euf- zi*{=o%`g8Ocv_+8rqOB%Aq5nxYekJwF*KU#R`YWx+Q^D^q0Yg@TB@ySz%xN&#VaNX zZYZqU`=q&@=f0u)3;CjGZ5q>eytjpZ9EUO1IAYi6P9ct1Jote5z8RxkC%hmsZ{#m5 o3{SLLFW#x$O|Gi+( z^S_A7;5l~m?-WICrA2rqbSEE$&PH>h?tT0zuLK*YS3C#APj6H7l8EBXxlvnPQbI1% zu_8sg36VHQ@^K&8N=wlU(gS)-+p*rew=w+1c4^zPUGX9O0W*Am@|o8=Zbix9XR=h4 zkkpfJLiVK*Z@{T9&hTjPz~$QhKpe%ceq%Lf8*flh-XX#W#R_SFn_hsT@*Ebm*hSwM zRSISWI7%ifQMtXtKB0K=0$e4%qa;jRn2S+9e41vnBFAVl%@@VIUrfH{@G;A0cp8r< z!%6=k`-gn*m>IrlghInL8m)FT3NY$mqs4|%7M1!bYsD%^Ue^o_TMNlBQss6-T^85> z4Lqq)wxifu1*s%7YZ}cBsTho9c5B5YG+(QlwV^H{_>>I)u#1%9fVG&lcZzk53`>CzW@LL literal 0 HcmV?d00001 diff --git a/public/documents/mock/regolamento-utenti.pdf b/public/documents/mock/regolamento-utenti.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f2a5a32f802b083a0376aad33962bd914d90b7eb GIT binary patch literal 895 zcmZ`&TW{Jh7=6#LI4?+@v^IB2rK%E_prI8~LApIudoYQ~Tm?IF9NO*I?{lC)*Uid9 ze01|9At~GB+#n2dLS|dI~9}A|9kQ=49%!nO_!dg^J=7K1!I`|}+9p<5% zhuiq7`Gy+Pcf8|5KaRr~8=SBkbf=IfEFOHoe4l1$*BLK}=z;u&h2fdjXeKJIyZ5sa dzd8DJSCz6?|L|_8T5Y;Kul>O3^^$bz`~ff5`n>=E literal 0 HcmV?d00001 diff --git a/public/documents/mock/statuto.pdf b/public/documents/mock/statuto.pdf new file mode 100644 index 0000000000000000000000000000000000000000..75181ec08077a143580fc5a2b930799ae7fe109e GIT binary patch literal 894 zcmZ`&-EP`27`@L^oExM~YMVF^N~Nk2KS4t)q=Iz2sCHoz!(0VBvQ69dG4{m!9H7v3 zvvLt1pYxsX$8Oz?#^>y!6EH;3%g5mQ8e#sc76^yj@k*}|-to1th>0-A%}pRw>4|>- zUa;eZUu9|W(Dw8n6h&{913MA=laE4gV+2umKH*f9k`MF?9wOo=R|$HZnBvWa)mu|g zLO#>6CPnkZ(D9Ds<3F-hj-pv4d-RaDXT5iCVuXwB(lurK;u*pLw_MJYXfMZ|0;lbcmsI~ivb{zZqtuumef3Q2^^UWB|1JVrdi zJ}n$~Tc=Tqs`|~X)rI7*QY-Np`C|~_ywf` + +
+ +

+ Anteprima dimostrativa: tutti i PDF presenti sono esempi e non sono documenti ufficiali. +

+ {mockDocumentCategories.map((category) => ( + + ))} +
+ + ) +} diff --git a/src/components/documents/document-category-card.tsx b/src/components/documents/document-category-card.tsx new file mode 100644 index 00000000..7b0313a6 --- /dev/null +++ b/src/components/documents/document-category-card.tsx @@ -0,0 +1,61 @@ +import { FiDownload, FiEye, FiFile } from "react-icons/fi" +import { Glass } from "@/components/glass" +import { Button } from "@/components/ui/button" +import type { DocumentCategory } from "./types" + +export function DocumentCategoryCard({ category }: { category: DocumentCategory }) { + return ( +
+ +
+

+ {category.title} +

+

{category.description}

+
+
    + {category.documents.map((document) => ( +
  • +
    +
    +
    + + +
    +
  • + ))} +
+
+
+ ) +} diff --git a/src/components/documents/mock-data.ts b/src/components/documents/mock-data.ts new file mode 100644 index 00000000..86e66f6d --- /dev/null +++ b/src/components/documents/mock-data.ts @@ -0,0 +1,71 @@ +import type { DocumentCategory } from "./types" + +// Demonstration content only. Replace this catalog when official documents are available. +export const mockDocumentCategories: DocumentCategory[] = [ + { + id: "associazione", + title: "Associazione", + description: "Chi siamo e su quali principi ci fondiamo.", + documents: [ + { + title: "PoliNetwork in breve", + description: "Una panoramica dell’associazione e delle sue attività.", + href: "/documents/mock/polinetwork-in-breve.pdf", + }, + { + title: "Statuto dell’associazione", + description: "Finalità, organizzazione e principi associativi.", + href: "/documents/mock/statuto.pdf", + }, + ], + }, + { + id: "regolamenti", + title: "Regolamenti", + description: "Le regole dell’associazione e della community.", + documents: [ + { + title: "Regolamento interno dell’associazione", + description: "Le regole di organizzazione e funzionamento dell’associazione.", + href: "/documents/mock/regolamento-interno.pdf", + }, + { + title: "Regolamento per gli utenti", + description: "Le regole di partecipazione ai gruppi e alle piattaforme PoliNetwork.", + href: "/documents/mock/regolamento-utenti.pdf", + }, + ], + }, + { + id: "privacy", + title: "Privacy e Informative", + description: "Come trattiamo e proteggiamo i dati personali.", + documents: [ + { + title: "Associati e candidati soci", + description: "Il trattamento dei dati personali di chi è socio o richiede di diventarlo.", + href: "/documents/mock/privacy-soci.pdf", + }, + { + title: "App PoliNetwork", + description: "Il trattamento dei dati personali nell’app PoliNetwork.", + href: "/documents/mock/privacy-app.pdf", + }, + { + title: "Candidati CCS – elezioni 2023", + description: "Il trattamento dei dati personali dei candidati alle elezioni CCS di maggio 2023.", + href: "/documents/mock/privacy-ccs.pdf", + }, + ], + }, + { + id: "bilanci", + title: "Bilanci", + description: "Come gestiamo le risorse dell’associazione.", + documents: [ + { title: "Bilancio 2024", href: "/documents/mock/bilancio-2024.pdf" }, + { title: "Bilancio 2023", href: "/documents/mock/bilancio-2023.pdf" }, + { title: "Bilancio 2022", href: "/documents/mock/bilancio-2022.pdf" }, + ], + }, +] diff --git a/src/components/documents/types.ts b/src/components/documents/types.ts new file mode 100644 index 00000000..d265e037 --- /dev/null +++ b/src/components/documents/types.ts @@ -0,0 +1,12 @@ +export type DocumentItem = { + title: string + description?: string + href: string +} + +export type DocumentCategory = { + id: string + title: string + description: string + documents: DocumentItem[] +} diff --git a/src/components/header/constants.ts b/src/components/header/constants.ts index 7cb314e9..c97f7672 100644 --- a/src/components/header/constants.ts +++ b/src/components/header/constants.ts @@ -8,6 +8,7 @@ export const headerMenuItems: HeaderMenuItem[] = [ { title: "Progetti", href: "/projects", icon: FiChevronRight }, { title: "Appunti", href: "https://polinetwork.org/it/materials/view/", icon: FiChevronRight }, { title: "Guide", href: "/guides", icon: FiChevronRight }, + { title: "Documenti", href: "/documents", icon: FiChevronRight }, { title: "FAQs", href: "/faqs", icon: FiChevronRight }, ], }, diff --git a/src/components/ui/hero.tsx b/src/components/ui/hero.tsx index bcd563e3..a254ceef 100644 --- a/src/components/ui/hero.tsx +++ b/src/components/ui/hero.tsx @@ -22,17 +22,24 @@ export function Hero({ description, gradientDescription = false, className, + titleClassName, }: { title: string description: string gradientDescription?: boolean className?: string + titleClassName?: string }) { return (
{/* Figma "lightheme-XLarge text effect": #020618 → #6e6e6e at 82.69% */} {/* The gradient is drawn per line (box-decoration-clone), so a title that wraps keeps the full effect on each line */} -

+

{title} From d9de97e928eca79c05bb3f49cbde194dc868d59a Mon Sep 17 00:00:00 2001 From: Bianca Roberta Ianosel Date: Thu, 5 Nov 2026 11:17:13 +0000 Subject: [PATCH 2/2] feat(documents): add internal PDF viewer with zoom and page controls --- .gitignore | 5 +- package.json | 5 +- pnpm-lock.yaml | 218 ++++++++++++++++++ .../documents/mock/polinetwork-in-breve.pdf | Bin 889 -> 1234 bytes scripts/copy-pdf-assets.mjs | 14 ++ src/app/documents/view/[slug]/page.tsx | 33 +++ src/components/conditional-footer.tsx | 2 +- .../documents/document-category-card.tsx | 12 +- .../documents/pdf-viewer-loader.tsx | 13 ++ src/components/documents/pdf-viewer.tsx | 159 +++++++++++++ src/components/documents/utils.ts | 9 + tsconfig.json | 3 +- 12 files changed, 462 insertions(+), 11 deletions(-) create mode 100644 scripts/copy-pdf-assets.mjs create mode 100644 src/app/documents/view/[slug]/page.tsx create mode 100644 src/components/documents/pdf-viewer-loader.tsx create mode 100644 src/components/documents/pdf-viewer.tsx create mode 100644 src/components/documents/utils.ts diff --git a/.gitignore b/.gitignore index c2e19a5b..dd586ac7 100644 --- a/.gitignore +++ b/.gitignore @@ -50,4 +50,7 @@ yarn-error.log* .idea # codex -.codex \ No newline at end of file +.codex + +# PDF.js assets copied from the installed dependency during dev/build +/public/pdfjs/ diff --git a/package.json b/package.json index 0aeb6177..9aec8a0d 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,8 @@ "type": "module", "scripts": { "preinstall": "npx -y only-allow pnpm", - "build": "cross-env SKIP_ENV_VALIDATION=true next build", - "dev": "cross-env PORT=3002 next dev", + "build": "node scripts/copy-pdf-assets.mjs && cross-env SKIP_ENV_VALIDATION=true next build", + "dev": "node scripts/copy-pdf-assets.mjs && cross-env PORT=3002 next dev", "preview": "pnpm build && pnpm start", "start": "node .next/standalone/server.js", "typecheck": "tsc --noEmit", @@ -40,6 +40,7 @@ "react-dom": "19.3.0", "react-hook-form": "^7.68.0", "react-icons": "^5.6.0", + "react-pdf": "^11.0.0", "superjson": "^2.2.6", "tailwind-merge": "^3.4.0", "tailwindcss-animate": "^1.0.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 94f4f76b..82a821d4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -80,6 +80,9 @@ importers: react-icons: specifier: ^5.6.0 version: 5.6.0(react@19.3.0) + react-pdf: + specifier: ^11.0.0 + version: 11.0.0(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) superjson: specifier: ^2.2.6 version: 2.2.6 @@ -401,6 +404,81 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@napi-rs/canvas-android-arm64@1.1.0': + resolution: {integrity: sha512-mKrSmRyhWK4q699Q1xthbZIZi7JBvLtsRNnOZAeFaLPOFT1yo8y82aRC2nNZbXTGJXU7fVDONgGQPr0vo3kerw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@napi-rs/canvas-darwin-arm64@1.1.0': + resolution: {integrity: sha512-iqwST4g0oH+x8VCpBIYh+MYokv/mVvkM+v9oJGB0d9A3P2qBh42ogJlVyWq9SpPCrAjS7tplMb1D3OIfxl+utw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@napi-rs/canvas-darwin-x64@1.1.0': + resolution: {integrity: sha512-jkeA8eVTxDum03LTvc32YJ28F6KB1U0gYwnWMfOAUQZvWWEh/kvONby4iWGJx3+Tw45UqMGMXjtw2GKNVZDhVg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@napi-rs/canvas-linux-arm-gnueabihf@1.1.0': + resolution: {integrity: sha512-Y4YkKG1ijoqkgUdTEEBNG4k+2BK5bR5jGL6S56YxCXYPwGfUd5nED8/74xyc7z+bpixPasCjSYoP3xYPOIGNxw==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@napi-rs/canvas-linux-arm64-gnu@1.1.0': + resolution: {integrity: sha512-dE/XthqYkiWcBVtKFlZbUXwAYfDuQGqDWixznipJNmevLGpprNnTVQZyftm1RZJudkIOn2EkzYZDMMETM08Row==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@napi-rs/canvas-linux-arm64-musl@1.1.0': + resolution: {integrity: sha512-DqZ/CMcHlhU56pPjv9AP0h3U7MFQ5LzC1HzMyRMotL+PFsJmZJA/ziApPYVyyz78DaVLhI8t3So3ztqtijbYpg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@napi-rs/canvas-linux-riscv64-gnu@1.1.0': + resolution: {integrity: sha512-kJ7m5n2VffxWXM5QGa5lfq8ZN4LdOJEoz3aAJAmIqYvKiZw1gRdXqpucnRXsF6D9nLYiiIlevaAtAclR7c5ilg==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@napi-rs/canvas-linux-x64-gnu@1.1.0': + resolution: {integrity: sha512-uQ9JuJlRhQdHyttcMWOiwLkfIqkqaB0YFUVe835LUe12THt2b51HXkTItq0fxWT1jdhUPX2ylWZ1walkJyD6GQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@napi-rs/canvas-linux-x64-musl@1.1.0': + resolution: {integrity: sha512-has4CPPAkkCac/LZ13VAw5p+MJLdaKVoyrhumN/A7uJd6960xYkJNDZgUebyxiha82AFzXx6RV+QJg5qGG079g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@napi-rs/canvas-win32-arm64-msvc@1.1.0': + resolution: {integrity: sha512-MJnktLKNijmXqdjTughHDZ9TDLZ9uUm2hvFXqaOqn0+w7lphXFnft6bpSgQAc9U1exc20UGIBlzGu9+kiCj9Ag==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@napi-rs/canvas-win32-x64-msvc@1.1.0': + resolution: {integrity: sha512-08575bFk7iiadxap4h+JxgGut9xDCHAGum6wh0eInN53zEMImkmEHi5MushX2FOmNO7qRNX7BPiDC5ZYBwHeEg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@napi-rs/canvas@1.1.0': + resolution: {integrity: sha512-k+ph/D8H9GS8ZZ48okvHrKeJynkqAA1kMUZRuzRWNlQUDHXngq7oRaWMZBdmSfd9ZhVy9bwNKei/7wMboI/uMg==} + engines: {node: '>= 10'} + '@next/env@16.4.1': resolution: {integrity: sha512-5sUUrrSvs/2/2LnLoesybMGx12EtPB46fdZ6RhAw0XDGSPxU2LFyQBB7yuEiOFFJPKmBY6+q2jTr1/AMbnp0/g==} @@ -1559,6 +1637,10 @@ packages: csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -1583,6 +1665,9 @@ packages: resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==} engines: {node: '>=10.13.0'} + es-toolkit@1.53.1: + resolution: {integrity: sha512-VhMc6IXmexu8iYFfL2aaAww6IvJTdHlqBLruLoEUs33YD4IcP5DdZcLOybMsMmmLsiy1c8PsiLsrM0DdIuuRUA==} + geist@1.5.1: resolution: {integrity: sha512-mAHZxIsL2o3ZITFaBVFBnwyDOw+zNLYum6A6nIjpzCGIO8QtC3V76XF2RnZTyLx1wlDTmMDy8jg3Ib52MIjGvQ==} peerDependencies: @@ -1609,6 +1694,9 @@ packages: jose@6.2.12: resolution: {integrity: sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw==} + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + lightningcss-android-arm64@1.30.2: resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==} engines: {node: '>= 12.0.0'} @@ -1683,9 +1771,27 @@ packages: resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} engines: {node: '>= 12.0.0'} + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + make-cancellable-promise@2.0.0: + resolution: {integrity: sha512-3SEQqTpV9oqVsIWqAcmDuaNeo7yBO3tqPtqGRcKkEo0lrzD3wqbKG9mkxO65KoOgXqj+zH2phJ2LiAsdzlogSw==} + + make-event-props@2.0.0: + resolution: {integrity: sha512-G/hncXrl4Qt7mauJEXSg3AcdYzmpkIITTNl5I+rH9sog5Yw0kK6vseJjCaPfOXqOqQuPUP89Rkhfz5kPS8ijtw==} + + merge-refs@2.0.0: + resolution: {integrity: sha512-3+B21mYK2IqUWnd2EivABLT7ueDhb0b8/dGK8LoFQPrU61YITeCMn14F7y7qZafWNZhUEKb24cJdiT5Wxs3prg==} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + nanoid@3.3.18: resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -1722,6 +1828,10 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + pdfjs-dist@6.3.289: + resolution: {integrity: sha512-ZHjSVpDa3D6izMq8/04lvkhkATUmL9px6ChPaXc1k6nU2Mrhlg1/7F0bdUqCwUjw3NsPTfPZsMDUU6ZIcRaeQw==} + engines: {node: '>=22.13.0 || >=24'} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1758,6 +1868,16 @@ packages: peerDependencies: react: '*' + react-pdf@11.0.0: + resolution: {integrity: sha512-x4Ierg3Sl+gOoqd0pVOQndiuFKeGcXQmUNtaSH0D/cs2nUW1HBDlFBT2Z8jDsDJv0Kh//jlOJT21ERyQtlRe7A==} + peerDependencies: + '@types/react': ^19.0.0 + react: ^19.0.0 + react-dom: ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + react-remove-scroll-bar@2.3.8: resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} engines: {node: '>=10'} @@ -1853,6 +1973,9 @@ packages: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -1892,6 +2015,9 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + warning@4.0.3: + resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -2101,6 +2227,54 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@napi-rs/canvas-android-arm64@1.1.0': + optional: true + + '@napi-rs/canvas-darwin-arm64@1.1.0': + optional: true + + '@napi-rs/canvas-darwin-x64@1.1.0': + optional: true + + '@napi-rs/canvas-linux-arm-gnueabihf@1.1.0': + optional: true + + '@napi-rs/canvas-linux-arm64-gnu@1.1.0': + optional: true + + '@napi-rs/canvas-linux-arm64-musl@1.1.0': + optional: true + + '@napi-rs/canvas-linux-riscv64-gnu@1.1.0': + optional: true + + '@napi-rs/canvas-linux-x64-gnu@1.1.0': + optional: true + + '@napi-rs/canvas-linux-x64-musl@1.1.0': + optional: true + + '@napi-rs/canvas-win32-arm64-msvc@1.1.0': + optional: true + + '@napi-rs/canvas-win32-x64-msvc@1.1.0': + optional: true + + '@napi-rs/canvas@1.1.0': + optionalDependencies: + '@napi-rs/canvas-android-arm64': 1.1.0 + '@napi-rs/canvas-darwin-arm64': 1.1.0 + '@napi-rs/canvas-darwin-x64': 1.1.0 + '@napi-rs/canvas-linux-arm-gnueabihf': 1.1.0 + '@napi-rs/canvas-linux-arm64-gnu': 1.1.0 + '@napi-rs/canvas-linux-arm64-musl': 1.1.0 + '@napi-rs/canvas-linux-riscv64-gnu': 1.1.0 + '@napi-rs/canvas-linux-x64-gnu': 1.1.0 + '@napi-rs/canvas-linux-x64-musl': 1.1.0 + '@napi-rs/canvas-win32-arm64-msvc': 1.1.0 + '@napi-rs/canvas-win32-x64-msvc': 1.1.0 + optional: true + '@next/env@16.4.1': {} '@next/swc-darwin-arm64@16.4.1': @@ -3162,6 +3336,8 @@ snapshots: csstype@3.2.3: {} + dequal@2.0.3: {} + detect-libc@2.1.2: {} detect-node-es@1.1.0: {} @@ -3183,6 +3359,8 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.0 + es-toolkit@1.53.1: {} + geist@1.5.1(next@16.4.1(@types/node@24.10.4)(babel-plugin-react-compiler@1.0.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0)): dependencies: next: 16.4.1(@types/node@24.10.4)(babel-plugin-react-compiler@1.0.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) @@ -3199,6 +3377,8 @@ snapshots: jose@6.2.12: {} + js-tokens@4.0.0: {} + lightningcss-android-arm64@1.30.2: optional: true @@ -3248,10 +3428,22 @@ snapshots: lightningcss-win32-arm64-msvc: 1.30.2 lightningcss-win32-x64-msvc: 1.30.2 + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + make-cancellable-promise@2.0.0: {} + + make-event-props@2.0.0: {} + + merge-refs@2.0.0(@types/react@19.3.0): + optionalDependencies: + '@types/react': 19.3.0 + nanoid@3.3.18: {} next-themes@0.4.6(react-dom@19.3.0(react@19.3.0))(react@19.3.0): @@ -3287,6 +3479,10 @@ snapshots: path-key@3.1.1: {} + pdfjs-dist@6.3.289: + optionalDependencies: + '@napi-rs/canvas': 1.1.0 + picocolors@1.1.1: {} postcss@8.5.23: @@ -3371,6 +3567,22 @@ snapshots: dependencies: react: 19.3.0 + react-pdf@11.0.0(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0): + dependencies: + clsx: 2.1.1 + dequal: 2.0.3 + es-toolkit: 1.53.1 + make-cancellable-promise: 2.0.0 + make-event-props: 2.0.0 + merge-refs: 2.0.0(@types/react@19.3.0) + pdfjs-dist: 6.3.289 + react: 19.3.0 + react-dom: 19.3.0(react@19.3.0) + tiny-invariant: 1.3.3 + warning: 4.0.3 + optionalDependencies: + '@types/react': 19.3.0 + react-remove-scroll-bar@2.3.8(@types/react@19.3.0)(react@19.3.0): dependencies: react: 19.3.0 @@ -3466,6 +3678,8 @@ snapshots: tapable@2.3.0: {} + tiny-invariant@1.3.3: {} + tslib@2.8.1: {} tw-animate-css@1.4.0: {} @@ -3514,6 +3728,10 @@ snapshots: dependencies: react: 19.3.0 + warning@4.0.3: + dependencies: + loose-envify: 1.4.0 + which@2.0.2: dependencies: isexe: 2.0.0 diff --git a/public/documents/mock/polinetwork-in-breve.pdf b/public/documents/mock/polinetwork-in-breve.pdf index ead94b9f32aa68a1eb9f95a69ad223334acbe3a6..bc2a9009baa74a0328e36dda1daf770a99c8bac8 100644 GIT binary patch delta 746 zcmey#c8POBq^OC4fkKdi8HkKk(09%+%_~tbnwTLwnVHd_(PUytIHU2zGpdt=7=`N% zEi4uE-7<4ZQj36ePGU)_OKNg{N~(gL9anKlQEFl?SH+ytGaK^`8Hl(&oV)zT@iGP` zrh{IdN|8-F7A^aoTNJ#r^xak;ruFwdE=>G8=l$8lxrv+4KA3fKPfWtjmh6w8)*TGL zbKm6eA?|%b@jpf1rB6!Sb?e{p?&>+W|Ne}q4>_k~?*99spLM#O?z0s+t;LbE_TH53 zT3%iFP_N4MNz05cUY(AYihcGe8%XW-49HTDVN$%bUEFEi*9#lJ)O`~%W8B&|foHE% z$OMgJN2Y&V6j=L5;?}k6^A0&{-H~^_5cGvBH7^C~5g;o+DT@mfE+E>*MnQjKy-&R{ zIJE5SfL;a#R$fVJUP-ZnIaCy+AEX+@hw1W3%}X!IP%ttuCnD@7oCb!Sfa~{~qp{n9 zqM5Qnw}}X83LUAH_?DNm%CU^C{^vy3ef!+Pu8N+nc7J{*K)}D1*9=5a1xpk&b zP``8a;0|s>6UXBDb0n1xt4hCIRF@I(eZup|*5l8w%J0y45SGLDd)H*9+hzLuo;;7* z=H6&&b@AZ^lef!a-@lsu!`(AZPR;Yp0urvLgHucH1jxt)o(p1grc zlF?%FDJIt-BSQrP5Kzcd-~uxY42%pdFvN^afXNX}otc3thM2jr;bco@X*CN4{h<8( g5(S{?z(fpAhKWTb6-B9OT!sdQ23)GDuKsRZ0P#XC2mk;8 delta 453 zcmZ{f&rZTX7{r4T6MHeylNyt`kf1?nY58*^L1>dE(i$vA4;~g+$ZEUWY!^840A3`0 z4qwKjCtpKP7SxmaoxV(Fev^6Ge&2e!-7%q~cZYIYOu2%wuGrbXU^m22WFX$1DgiDyB#C25 zlPpG%D59o{l_2Rvgk;SsWoySmMnth0g}{J?gb!I}A>ntC9i(rY`?*^Bv1y+eRp_e& ghqiuny category.documents) + +type Props = { params: Promise<{ slug: string }> } + +function findDocument(slug: string) { + return documents.find((document) => getDocumentSlug(document) === slug) +} + +export function generateStaticParams() { + return documents.map((document) => ({ slug: getDocumentSlug(document) })) +} + +export async function generateMetadata({ params }: Props): Promise { + const document = findDocument((await params).slug) + return { title: document?.title ?? "Documento non trovato" } +} + +export default async function DocumentViewerPage({ params }: Props) { + const document = findDocument((await params).slug) + if (!document) notFound() + + return ( +
+ +
+ ) +} diff --git a/src/components/conditional-footer.tsx b/src/components/conditional-footer.tsx index 35ce8eb5..8d52c38e 100644 --- a/src/components/conditional-footer.tsx +++ b/src/components/conditional-footer.tsx @@ -5,7 +5,7 @@ import type { ReactNode } from "react" import { matchesAnyRoutePrefix } from "@/utils/route-match" const HIDDEN_EXACT = ["/matricole/guida"] -const HIDDEN_PREFIXES = ["/groups"] +const HIDDEN_PREFIXES = ["/groups", "/documents/view"] /** * Hides the footer on full-screen routes. The footer itself is passed as `children` so it stays a server component diff --git a/src/components/documents/document-category-card.tsx b/src/components/documents/document-category-card.tsx index 7b0313a6..8d6406b7 100644 --- a/src/components/documents/document-category-card.tsx +++ b/src/components/documents/document-category-card.tsx @@ -1,7 +1,9 @@ +import Link from "next/link" import { FiDownload, FiEye, FiFile } from "react-icons/fi" import { Glass } from "@/components/glass" import { Button } from "@/components/ui/button" import type { DocumentCategory } from "./types" +import { getDocumentViewerHref } from "./utils" export function DocumentCategoryCard({ category }: { category: DocumentCategory }) { return ( @@ -32,14 +34,12 @@ export function DocumentCategoryCard({ category }: { category: DocumentCategory size="icon-lg" className="size-11 bg-transparent hover:bg-blue-tertiary/30" > - + +

{document.title}

+

+
+ + + + +
+ +

PDF dimostrativo, non ufficiale.

+
+ {width > 0 && ( + { + setPageCount(numPages) + setFailed(false) + }} + onLoadError={() => setFailed(true)} + loading={Caricamento del PDF…} + error={ +

+ Impossibile visualizzare il PDF. Puoi scaricarlo con il pulsante in alto. +

+ } + className="flex min-w-full justify-start p-4" + > + setFailed(true)} + loading={Caricamento della pagina…} + error={ +

+ Impossibile visualizzare questa pagina. Puoi scaricare il PDF con il pulsante in alto. +

+ } + className="mx-auto shrink-0 shadow-sm" + /> +
+ )} +
+ {pageCount > 0 && !failed && ( +
+ + + Pagina {page} di {pageCount} + + +
+ )} + + ) +} diff --git a/src/components/documents/utils.ts b/src/components/documents/utils.ts new file mode 100644 index 00000000..77dd3e10 --- /dev/null +++ b/src/components/documents/utils.ts @@ -0,0 +1,9 @@ +import type { DocumentItem } from "./types" + +export function getDocumentSlug(document: DocumentItem) { + return document.href.slice(document.href.lastIndexOf("/") + 1).replace(/\.pdf$/, "") +} + +export function getDocumentViewerHref(document: DocumentItem) { + return `/documents/view/${getDocumentSlug(document)}` +} diff --git a/tsconfig.json b/tsconfig.json index ca124295..326e81a0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -46,6 +46,7 @@ ".next/dev/types/**/*.ts" ], "exclude": [ - "node_modules" + "node_modules", + "public/pdfjs" ] }