eletrotupi / tcc/ commit / 4e5ac82

api: introduce a report endpoint, and build a pdf with a very simple report for now

Pedro Lucas Porcellis porcellis@eletrotupi.com 29 days ago 4e5ac8210eca36b8c2a4073c8eaac1d4998d8039
Parents: 78d5ebc
8 file(s) changed
  • api/package-lock.json +522 -14
  • api/package.json +3 -0
  • api/src/createApp.ts +2 -0
  • api/src/reports/ReportDocument.tsx +519 -0
  • api/src/reports/logo.ts +8 -0
  • api/src/reports/report.data.ts +136 -0
  • api/src/reports/report.router.ts +60 -0
  • api/tsconfig.json +2 -1
api/package-lock.json
1 1
{
2 2
  "name": "orbit-api",
3 3
  "version": "0.0.0",
4 4
  "lockfileVersion": 3,
5 5
  "requires": true,
▸ 5 unchanged lines
11 11
      "dependencies": {
12 12
        "@aws-sdk/client-s3": "^3.1048.0",
13 13
        "@prisma/adapter-better-sqlite3": "^7.5.0",
14 14
        "@prisma/adapter-pg": "^7.7.0",
15 15
        "@prisma/client": "^7.5.0",
16 +
        "@react-pdf/renderer": "^4.5.1",
16 17
        "@types/cors": "^2.8.19",
17 18
        "@types/jsonwebtoken": "^9.0.10",
18 19
        "bcryptjs": "^3.0.3",
19 20
        "better-sqlite3": "^12.5.0",
20 21
        "body-parser": "^2.2.2",
▸ 6 unchanged lines
27 28
        "multer": "^2.1.1",
28 29
        "multer-s3": "^3.0.1",
29 30
        "pg": "^8.20.0",
30 31
        "pino": "^10.3.1",
31 32
        "pino-http": "^11.0.0",
33 +
        "react": "^19.2.7",
32 34
        "resend": "^6.12.3",
33 35
        "zod": "^4.4.3",
34 36
        "zod-validation-error": "^5.0.0"
35 37
      },
36 38
      "devDependencies": {
▸ 1 unchanged lines
38 40
        "@types/express": "^5.0.6",
39 41
        "@types/morgan": "^1.9.10",
40 42
        "@types/multer": "^2.1.0",
41 43
        "@types/multer-s3": "^3.0.3",
42 44
        "@types/node": "^25.5.2",
45 +
        "@types/react": "^19.2.17",
43 46
        "@types/supertest": "^6.0.3",
44 47
        "cross-var": "^1.1.0",
45 48
        "dotenv-cli": "^11.0.0",
46 49
        "nodemon": "^3.1.14",
47 50
        "pino-pretty": "^13.1.3",
▸ 529 unchanged lines
577 580
      "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.4.tgz",
578 581
      "integrity": "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==",
579 582
      "license": "Apache-2.0",
580 583
      "engines": {
581 584
        "node": ">=18.0.0"
585 +
      }
586 +
    },
587 +
    "node_modules/@babel/runtime": {
588 +
      "version": "7.29.7",
589 +
      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz",
590 +
      "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==",
591 +
      "license": "MIT",
592 +
      "engines": {
593 +
        "node": ">=6.9.0"
582 594
      }
583 595
    },
584 596
    "node_modules/@chevrotain/cst-dts-gen": {
585 597
      "version": "10.5.0",
586 598
      "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.5.0.tgz",
▸ 619 unchanged lines
1206 1218
      "optional": true,
1207 1219
      "os": [
1208 1220
        "win32"
1209 1221
      ]
1210 1222
    },
1223 +
    "node_modules/@noble/ciphers": {
1224 +
      "version": "1.3.0",
1225 +
      "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz",
1226 +
      "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==",
1227 +
      "license": "MIT",
1228 +
      "engines": {
1229 +
        "node": "^14.21.3 || >=16"
1230 +
      },
1231 +
      "funding": {
1232 +
        "url": "https://paulmillr.com/funding/"
1233 +
      }
1234 +
    },
1211 1235
    "node_modules/@noble/hashes": {
1212 1236
      "version": "1.8.0",
1213 1237
      "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
1214 1238
      "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
1215 -
      "dev": true,
1216 1239
      "license": "MIT",
1217 1240
      "engines": {
1218 1241
        "node": "^14.21.3 || >=16"
1219 1242
      },
1220 1243
      "funding": {
▸ 278 unchanged lines
1499 1522
        "@types/react": "^18.0.0 || ^19.0.0",
1500 1523
        "react": "^18.0.0 || ^19.0.0",
1501 1524
        "react-dom": "^18.0.0 || ^19.0.0"
1502 1525
      }
1503 1526
    },
1527 +
    "node_modules/@react-pdf/fns": {
1528 +
      "version": "3.1.3",
1529 +
      "resolved": "https://registry.npmjs.org/@react-pdf/fns/-/fns-3.1.3.tgz",
1530 +
      "integrity": "sha512-0I7pApDr1/RLAKbizuLy/IHTEa93LSPy/bEwYniboC3Xqnp6Od8xFJKbKEzGw2wh/5zKFFwl00g4t9RwgIMc3w==",
1531 +
      "license": "MIT"
1532 +
    },
1533 +
    "node_modules/@react-pdf/font": {
1534 +
      "version": "4.0.8",
1535 +
      "resolved": "https://registry.npmjs.org/@react-pdf/font/-/font-4.0.8.tgz",
1536 +
      "integrity": "sha512-deNd+emtZAJho1IlzKL9bRoLAGv/6oXOIKO2oZfs4RuXUrK1onLHbJO7e2YoVLPFP/sQxisRTnzdJFtd35iKwA==",
1537 +
      "license": "MIT",
1538 +
      "dependencies": {
1539 +
        "@react-pdf/pdfkit": "^5.1.1",
1540 +
        "@react-pdf/types": "^2.11.1",
1541 +
        "fontkit": "^2.0.2",
1542 +
        "is-url": "^1.2.4"
1543 +
      }
1544 +
    },
1545 +
    "node_modules/@react-pdf/image": {
1546 +
      "version": "3.1.0",
1547 +
      "resolved": "https://registry.npmjs.org/@react-pdf/image/-/image-3.1.0.tgz",
1548 +
      "integrity": "sha512-ks7Ry8v711r8NvKWSELehj0BXBNPRihSnWsM09nDD8Ur175zbWBCK217LLwQMKDNYDVpkZaipdoJPom1LGaE9g==",
1549 +
      "license": "MIT",
1550 +
      "dependencies": {
1551 +
        "@react-pdf/svg": "^1.1.0",
1552 +
        "jay-peg": "^1.1.1",
1553 +
        "png-js": "^2.0.0"
1554 +
      }
1555 +
    },
1556 +
    "node_modules/@react-pdf/layout": {
1557 +
      "version": "4.6.1",
1558 +
      "resolved": "https://registry.npmjs.org/@react-pdf/layout/-/layout-4.6.1.tgz",
1559 +
      "integrity": "sha512-gN6PmWoEffvlIkifLfEhMsVucRywVMyH3rnxdyOVOhGy0nWJKKGpHyPc4plbDdpP6EfZ0r8prHXujDSkIG2nSA==",
1560 +
      "license": "MIT",
1561 +
      "dependencies": {
1562 +
        "@react-pdf/fns": "3.1.3",
1563 +
        "@react-pdf/image": "^3.1.0",
1564 +
        "@react-pdf/primitives": "^4.3.0",
1565 +
        "@react-pdf/stylesheet": "^6.2.1",
1566 +
        "@react-pdf/textkit": "^6.3.0",
1567 +
        "@react-pdf/types": "^2.11.1",
1568 +
        "emoji-regex-xs": "^1.0.0",
1569 +
        "queue": "^6.0.1",
1570 +
        "yoga-layout": "^3.2.1"
1571 +
      }
1572 +
    },
1573 +
    "node_modules/@react-pdf/pdfkit": {
1574 +
      "version": "5.1.1",
1575 +
      "resolved": "https://registry.npmjs.org/@react-pdf/pdfkit/-/pdfkit-5.1.1.tgz",
1576 +
      "integrity": "sha512-wNcdSsNlNYyGHGAgIdt453egBF7fiF9UxpRlklUfVvu8OWCrUppG9xiUrPLVoKiqWet5tMi0w6LmuFUJuYqjEg==",
1577 +
      "license": "MIT",
1578 +
      "dependencies": {
1579 +
        "@babel/runtime": "^7.20.13",
1580 +
        "@noble/ciphers": "^1.0.0",
1581 +
        "@noble/hashes": "^1.6.0",
1582 +
        "browserify-zlib": "^0.2.0",
1583 +
        "fontkit": "^2.0.2",
1584 +
        "jay-peg": "^1.1.1",
1585 +
        "js-md5": "^0.8.3",
1586 +
        "linebreak": "^1.1.0",
1587 +
        "png-js": "^2.0.0",
1588 +
        "vite-compatible-readable-stream": "^3.6.1"
1589 +
      }
1590 +
    },
1591 +
    "node_modules/@react-pdf/primitives": {
1592 +
      "version": "4.3.0",
1593 +
      "resolved": "https://registry.npmjs.org/@react-pdf/primitives/-/primitives-4.3.0.tgz",
1594 +
      "integrity": "sha512-nYXoZ36pvwNzbc54+DbL8RCn15jU7woJ9D/svnh5tpUXekJ+CbI4mZLo6boSv24CvJgychOu6h7gxX03B4ps0A==",
1595 +
      "license": "MIT"
1596 +
    },
1597 +
    "node_modules/@react-pdf/reconciler": {
1598 +
      "version": "2.0.0",
1599 +
      "resolved": "https://registry.npmjs.org/@react-pdf/reconciler/-/reconciler-2.0.0.tgz",
1600 +
      "integrity": "sha512-7zaPRujpbHSmCpIrZ+b9HSTJHthcVZzX0Wx7RzvQGsGBUbHP4p6s5itXrAIOuQuPvDepoHGNOvf6xUuMVvdoyw==",
1601 +
      "license": "MIT",
1602 +
      "dependencies": {
1603 +
        "object-assign": "^4.1.1",
1604 +
        "scheduler": "0.25.0-rc-603e6108-20241029"
1605 +
      },
1606 +
      "peerDependencies": {
1607 +
        "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
1608 +
      }
1609 +
    },
1610 +
    "node_modules/@react-pdf/reconciler/node_modules/scheduler": {
1611 +
      "version": "0.25.0-rc-603e6108-20241029",
1612 +
      "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0-rc-603e6108-20241029.tgz",
1613 +
      "integrity": "sha512-pFwF6H1XrSdYYNLfOcGlM28/j8CGLu8IvdrxqhjWULe2bPcKiKW4CV+OWqR/9fT52mywx65l7ysNkjLKBda7eA==",
1614 +
      "license": "MIT"
1615 +
    },
1616 +
    "node_modules/@react-pdf/render": {
1617 +
      "version": "4.5.1",
1618 +
      "resolved": "https://registry.npmjs.org/@react-pdf/render/-/render-4.5.1.tgz",
1619 +
      "integrity": "sha512-IW/N4HWJWtioBXCf7n02IR24VJJ8gbdS3jGypf+vW/rSErEx3/URRzh9UK6Ma8Fpog9+T/W6GE2NHJ5AAKHhVA==",
1620 +
      "license": "MIT",
1621 +
      "dependencies": {
1622 +
        "@babel/runtime": "^7.20.13",
1623 +
        "@react-pdf/fns": "3.1.3",
1624 +
        "@react-pdf/primitives": "^4.3.0",
1625 +
        "@react-pdf/textkit": "^6.3.0",
1626 +
        "@react-pdf/types": "^2.11.1",
1627 +
        "abs-svg-path": "^0.1.1",
1628 +
        "color-string": "^2.1.4",
1629 +
        "normalize-svg-path": "^1.1.0",
1630 +
        "parse-svg-path": "^0.1.2",
1631 +
        "svg-arc-to-cubic-bezier": "^3.2.0"
1632 +
      }
1633 +
    },
1634 +
    "node_modules/@react-pdf/renderer": {
1635 +
      "version": "4.5.1",
1636 +
      "resolved": "https://registry.npmjs.org/@react-pdf/renderer/-/renderer-4.5.1.tgz",
1637 +
      "integrity": "sha512-5r1VQrE6FRLXX5wWUxwZzM24E2BJMo6g8AQWuS8WyPs9ugu5yMnb2g8/RpPYka/Z6J+RUEWc32wty2NoUJF42Q==",
1638 +
      "license": "MIT",
1639 +
      "dependencies": {
1640 +
        "@babel/runtime": "^7.20.13",
1641 +
        "@react-pdf/fns": "3.1.3",
1642 +
        "@react-pdf/font": "^4.0.8",
1643 +
        "@react-pdf/layout": "^4.6.1",
1644 +
        "@react-pdf/pdfkit": "^5.1.1",
1645 +
        "@react-pdf/primitives": "^4.3.0",
1646 +
        "@react-pdf/reconciler": "^2.0.0",
1647 +
        "@react-pdf/render": "^4.5.1",
1648 +
        "@react-pdf/types": "^2.11.1",
1649 +
        "events": "^3.3.0",
1650 +
        "object-assign": "^4.1.1",
1651 +
        "prop-types": "^15.6.2",
1652 +
        "queue": "^6.0.1"
1653 +
      },
1654 +
      "peerDependencies": {
1655 +
        "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
1656 +
      }
1657 +
    },
1658 +
    "node_modules/@react-pdf/stylesheet": {
1659 +
      "version": "6.2.1",
1660 +
      "resolved": "https://registry.npmjs.org/@react-pdf/stylesheet/-/stylesheet-6.2.1.tgz",
1661 +
      "integrity": "sha512-2+UEk+7e+z8baaWi2l5kPLWmwtJeOI+T5wW9GGeN3iDH7vd3kbTqOpN1yt9mmfNVZFxQsnDHpznFb5v5UF983A==",
1662 +
      "license": "MIT",
1663 +
      "dependencies": {
1664 +
        "@react-pdf/fns": "3.1.3",
1665 +
        "@react-pdf/types": "^2.11.1",
1666 +
        "color-string": "^2.1.4",
1667 +
        "hsl-to-hex": "^1.0.0",
1668 +
        "media-engine": "^1.0.3",
1669 +
        "postcss-value-parser": "^4.1.0"
1670 +
      }
1671 +
    },
1672 +
    "node_modules/@react-pdf/svg": {
1673 +
      "version": "1.1.0",
1674 +
      "resolved": "https://registry.npmjs.org/@react-pdf/svg/-/svg-1.1.0.tgz",
1675 +
      "integrity": "sha512-cTIHXiz9x1HrbfqzfxfZP3FRdDwUXG77QWF6Fb5MP/lV3ONxR+g0Z3hwtBatCS9HeGBQCpxX/Lzb8wHE+co1PA==",
1676 +
      "license": "MIT",
1677 +
      "dependencies": {
1678 +
        "@react-pdf/primitives": "^4.3.0"
1679 +
      }
1680 +
    },
1681 +
    "node_modules/@react-pdf/textkit": {
1682 +
      "version": "6.3.0",
1683 +
      "resolved": "https://registry.npmjs.org/@react-pdf/textkit/-/textkit-6.3.0.tgz",
1684 +
      "integrity": "sha512-v6+V8nAcVwm7s2s1jIG2MD3Iw//x/k+XrH1foWOELBE4b32pyDgKyPXN/6KJE0dnX7+fVy27uctLNCLNMvzKzQ==",
1685 +
      "license": "MIT",
1686 +
      "dependencies": {
1687 +
        "@react-pdf/fns": "3.1.3",
1688 +
        "bidi-js": "^1.0.2",
1689 +
        "hyphen": "^1.6.4",
1690 +
        "unicode-properties": "^1.4.1"
1691 +
      }
1692 +
    },
1693 +
    "node_modules/@react-pdf/types": {
1694 +
      "version": "2.11.1",
1695 +
      "resolved": "https://registry.npmjs.org/@react-pdf/types/-/types-2.11.1.tgz",
1696 +
      "integrity": "sha512-i9xQgfaDU9QoeNnbp6rltXCWg1huEh195rpOuN8cE4BZ2FuLdQrsIcb2dhFF9aOxXf+XBA6LOSpIW051MDD/bw==",
1697 +
      "license": "MIT",
1698 +
      "dependencies": {
1699 +
        "@react-pdf/font": "^4.0.8",
1700 +
        "@react-pdf/primitives": "^4.3.0",
1701 +
        "@react-pdf/stylesheet": "^6.2.1"
1702 +
      }
1703 +
    },
1504 1704
    "node_modules/@rollup/rollup-android-arm-eabi": {
1505 1705
      "version": "4.62.2",
1506 1706
      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz",
1507 1707
      "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==",
1508 1708
      "cpu": [
▸ 512 unchanged lines
2021 2221
      "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
2022 2222
      "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
2023 2223
      "devOptional": true,
2024 2224
      "license": "MIT"
2025 2225
    },
2226 +
    "node_modules/@swc/helpers": {
2227 +
      "version": "0.5.23",
2228 +
      "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz",
2229 +
      "integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==",
2230 +
      "license": "Apache-2.0",
2231 +
      "dependencies": {
2232 +
        "tslib": "^2.8.0"
2233 +
      }
2234 +
    },
2026 2235
    "node_modules/@types/better-sqlite3": {
2027 2236
      "version": "7.6.13",
2028 2237
      "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz",
2029 2238
      "integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==",
2030 2239
      "dev": true,
▸ 184 unchanged lines
2215 2424
      "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
2216 2425
      "dev": true,
2217 2426
      "license": "MIT"
2218 2427
    },
2219 2428
    "node_modules/@types/react": {
2220 -
      "version": "19.2.14",
2221 -
      "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz",
2222 -
      "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==",
2429 +
      "version": "19.2.17",
2430 +
      "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz",
2431 +
      "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==",
2223 2432
      "devOptional": true,
2224 2433
      "license": "MIT",
2225 -
      "peer": true,
2226 2434
      "dependencies": {
2227 2435
        "csstype": "^3.2.2"
2228 2436
      }
2229 2437
    },
2230 2438
    "node_modules/@types/send": {
▸ 163 unchanged lines
2394 2602
        "tinyrainbow": "^2.0.0"
2395 2603
      },
2396 2604
      "funding": {
2397 2605
        "url": "https://opencollective.com/vitest"
2398 2606
      }
2607 +
    },
2608 +
    "node_modules/abs-svg-path": {
2609 +
      "version": "0.1.1",
2610 +
      "resolved": "https://registry.npmjs.org/abs-svg-path/-/abs-svg-path-0.1.1.tgz",
2611 +
      "integrity": "sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA==",
2612 +
      "license": "MIT"
2399 2613
    },
2400 2614
    "node_modules/accepts": {
2401 2615
      "version": "2.0.0",
2402 2616
      "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
2403 2617
      "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
▸ 1194 unchanged lines
3598 3812
      },
3599 3813
      "engines": {
3600 3814
        "node": "20.x || 22.x || 23.x || 24.x || 25.x"
3601 3815
      }
3602 3816
    },
3817 +
    "node_modules/bidi-js": {
3818 +
      "version": "1.0.3",
3819 +
      "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz",
3820 +
      "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==",
3821 +
      "license": "MIT",
3822 +
      "dependencies": {
3823 +
        "require-from-string": "^2.0.2"
3824 +
      }
3825 +
    },
3603 3826
    "node_modules/binary-extensions": {
3604 3827
      "version": "2.3.0",
3605 3828
      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
3606 3829
      "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
3607 3830
      "dev": true,
▸ 66 unchanged lines
3674 3897
      },
3675 3898
      "engines": {
3676 3899
        "node": ">=8"
3677 3900
      }
3678 3901
    },
3902 +
    "node_modules/brotli": {
3903 +
      "version": "1.3.3",
3904 +
      "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz",
3905 +
      "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==",
3906 +
      "license": "MIT",
3907 +
      "dependencies": {
3908 +
        "base64-js": "^1.1.2"
3909 +
      }
3910 +
    },
3911 +
    "node_modules/browserify-zlib": {
3912 +
      "version": "0.2.0",
3913 +
      "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
3914 +
      "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
3915 +
      "license": "MIT",
3916 +
      "dependencies": {
3917 +
        "pako": "~1.0.5"
3918 +
      }
3919 +
    },
3679 3920
    "node_modules/buffer": {
3680 3921
      "version": "5.7.1",
3681 3922
      "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
3682 3923
      "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
3683 3924
      "funding": [
▸ 205 unchanged lines
3889 4130
      "license": "MIT",
3890 4131
      "dependencies": {
3891 4132
        "consola": "^3.2.3"
3892 4133
      }
3893 4134
    },
4135 +
    "node_modules/clone": {
4136 +
      "version": "2.1.2",
4137 +
      "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
4138 +
      "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
4139 +
      "license": "MIT",
4140 +
      "engines": {
4141 +
        "node": ">=0.8"
4142 +
      }
4143 +
    },
3894 4144
    "node_modules/cluster-key-slot": {
3895 4145
      "version": "1.1.2",
3896 4146
      "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
3897 4147
      "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
3898 4148
      "license": "Apache-2.0",
3899 4149
      "engines": {
3900 4150
        "node": ">=0.10.0"
3901 4151
      }
3902 4152
    },
4153 +
    "node_modules/color-name": {
4154 +
      "version": "2.1.0",
4155 +
      "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
4156 +
      "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
4157 +
      "license": "MIT",
4158 +
      "engines": {
4159 +
        "node": ">=12.20"
4160 +
      }
4161 +
    },
4162 +
    "node_modules/color-string": {
4163 +
      "version": "2.1.4",
4164 +
      "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz",
4165 +
      "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==",
4166 +
      "license": "MIT",
4167 +
      "dependencies": {
4168 +
        "color-name": "^2.0.0"
4169 +
      },
4170 +
      "engines": {
4171 +
        "node": ">=18"
4172 +
      }
4173 +
    },
3903 4174
    "node_modules/colorette": {
3904 4175
      "version": "2.0.20",
3905 4176
      "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
3906 4177
      "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
3907 4178
      "dev": true,
▸ 264 unchanged lines
4172 4443
    "node_modules/csstype": {
4173 4444
      "version": "3.2.3",
4174 4445
      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
4175 4446
      "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
4176 4447
      "devOptional": true,
4177 -
      "license": "MIT",
4178 -
      "peer": true
4448 +
      "license": "MIT"
4179 4449
    },
4180 4450
    "node_modules/date-fns": {
4181 4451
      "version": "4.1.0",
4182 4452
      "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
4183 4453
      "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
▸ 147 unchanged lines
4331 4601
      "dependencies": {
4332 4602
        "asap": "^2.0.0",
4333 4603
        "wrappy": "1"
4334 4604
      }
4335 4605
    },
4606 +
    "node_modules/dfa": {
4607 +
      "version": "1.2.0",
4608 +
      "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz",
4609 +
      "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==",
4610 +
      "license": "MIT"
4611 +
    },
4336 4612
    "node_modules/dir-glob": {
4337 4613
      "version": "3.0.1",
4338 4614
      "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
4339 4615
      "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
4340 4616
      "dev": true,
▸ 110 unchanged lines
4451 4727
      "license": "MIT",
4452 4728
      "dependencies": {
4453 4729
        "@standard-schema/spec": "^1.0.0",
4454 4730
        "fast-check": "^3.23.1"
4455 4731
      }
4732 +
    },
4733 +
    "node_modules/emoji-regex-xs": {
4734 +
      "version": "1.0.0",
4735 +
      "resolved": "https://registry.npmjs.org/emoji-regex-xs/-/emoji-regex-xs-1.0.0.tgz",
4736 +
      "integrity": "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==",
4737 +
      "license": "MIT"
4456 4738
    },
4457 4739
    "node_modules/empathic": {
4458 4740
      "version": "2.0.0",
4459 4741
      "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz",
4460 4742
      "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==",
▸ 254 unchanged lines
4715 4997
    "node_modules/fast-copy": {
4716 4998
      "version": "4.0.3",
4717 4999
      "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-4.0.3.tgz",
4718 5000
      "integrity": "sha512-58apWr0GUiDFM8+3afrO6eYwJBn9ZAhDOzG3L+/9llab/haCARS2UIfffmOurYLwbgDRs8n0rfr6qAAPEAuAQw==",
4719 5001
      "dev": true,
5002 +
      "license": "MIT"
5003 +
    },
5004 +
    "node_modules/fast-deep-equal": {
5005 +
      "version": "3.1.3",
5006 +
      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
5007 +
      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
4720 5008
      "license": "MIT"
4721 5009
    },
4722 5010
    "node_modules/fast-glob": {
4723 5011
      "version": "3.3.3",
4724 5012
      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
▸ 87 unchanged lines
4812 5100
        "picomatch": {
4813 5101
          "optional": true
4814 5102
        }
4815 5103
      }
4816 5104
    },
5105 +
    "node_modules/fflate": {
5106 +
      "version": "0.8.3",
5107 +
      "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz",
5108 +
      "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==",
5109 +
      "license": "MIT"
5110 +
    },
4817 5111
    "node_modules/file-type": {
4818 5112
      "version": "3.9.0",
4819 5113
      "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz",
4820 5114
      "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==",
4821 5115
      "license": "MIT",
▸ 37 unchanged lines
4859 5153
        "node": ">= 18.0.0"
4860 5154
      },
4861 5155
      "funding": {
4862 5156
        "type": "opencollective",
4863 5157
        "url": "https://opencollective.com/express"
5158 +
      }
5159 +
    },
5160 +
    "node_modules/fontkit": {
5161 +
      "version": "2.0.4",
5162 +
      "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz",
5163 +
      "integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==",
5164 +
      "license": "MIT",
5165 +
      "dependencies": {
5166 +
        "@swc/helpers": "^0.5.12",
5167 +
        "brotli": "^1.3.2",
5168 +
        "clone": "^2.1.2",
5169 +
        "dfa": "^1.2.0",
5170 +
        "fast-deep-equal": "^3.1.3",
5171 +
        "restructure": "^3.0.0",
5172 +
        "tiny-inflate": "^1.0.3",
5173 +
        "unicode-properties": "^1.4.0",
5174 +
        "unicode-trie": "^2.0.0"
4864 5175
      }
4865 5176
    },
4866 5177
    "node_modules/foreground-child": {
4867 5178
      "version": "3.3.1",
4868 5179
      "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
▸ 382 unchanged lines
5251 5562
      "license": "MIT",
5252 5563
      "engines": {
5253 5564
        "node": ">=16.9.0"
5254 5565
      }
5255 5566
    },
5567 +
    "node_modules/hsl-to-hex": {
5568 +
      "version": "1.0.0",
5569 +
      "resolved": "https://registry.npmjs.org/hsl-to-hex/-/hsl-to-hex-1.0.0.tgz",
5570 +
      "integrity": "sha512-K6GVpucS5wFf44X0h2bLVRDsycgJmf9FF2elg+CrqD8GcFU8c6vYhgXn8NjUkFCwj+xDFb70qgLbTUm6sxwPmA==",
5571 +
      "license": "MIT",
5572 +
      "dependencies": {
5573 +
        "hsl-to-rgb-for-reals": "^1.1.0"
5574 +
      }
5575 +
    },
5576 +
    "node_modules/hsl-to-rgb-for-reals": {
5577 +
      "version": "1.1.1",
5578 +
      "resolved": "https://registry.npmjs.org/hsl-to-rgb-for-reals/-/hsl-to-rgb-for-reals-1.1.1.tgz",
5579 +
      "integrity": "sha512-LgOWAkrN0rFaQpfdWBQlv/VhkOxb5AsBjk6NQVx4yEzWS923T07X0M1Y0VNko2H52HeSpZrZNNMJ0aFqsdVzQg==",
5580 +
      "license": "ISC"
5581 +
    },
5256 5582
    "node_modules/html-comment-regex": {
5257 5583
      "version": "1.1.2",
5258 5584
      "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz",
5259 5585
      "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==",
5260 5586
      "license": "MIT"
▸ 22 unchanged lines
5283 5609
      "version": "2.3.0",
5284 5610
      "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz",
5285 5611
      "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==",
5286 5612
      "devOptional": true,
5287 5613
      "license": "MIT"
5614 +
    },
5615 +
    "node_modules/hyphen": {
5616 +
      "version": "1.14.1",
5617 +
      "resolved": "https://registry.npmjs.org/hyphen/-/hyphen-1.14.1.tgz",
5618 +
      "integrity": "sha512-kvL8xYl5QMTh+LwohVN72ciOxC0OEV79IPdJSTwEXok9y9QHebXGdFgrED4sWfiax/ODx++CAMk3hMy4XPJPOw==",
5619 +
      "license": "ISC"
5288 5620
    },
5289 5621
    "node_modules/iconv-lite": {
5290 5622
      "version": "0.7.0",
5291 5623
      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz",
5292 5624
      "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==",
▸ 154 unchanged lines
5447 5779
      "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
5448 5780
      "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==",
5449 5781
      "devOptional": true,
5450 5782
      "license": "MIT"
5451 5783
    },
5784 +
    "node_modules/is-url": {
5785 +
      "version": "1.2.4",
5786 +
      "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz",
5787 +
      "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==",
5788 +
      "license": "MIT"
5789 +
    },
5452 5790
    "node_modules/isexe": {
5453 5791
      "version": "2.0.0",
5454 5792
      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
5455 5793
      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
5456 5794
      "devOptional": true,
5457 5795
      "license": "ISC"
5458 5796
    },
5797 +
    "node_modules/jay-peg": {
5798 +
      "version": "1.1.1",
5799 +
      "resolved": "https://registry.npmjs.org/jay-peg/-/jay-peg-1.1.1.tgz",
5800 +
      "integrity": "sha512-D62KEuBxz/ip2gQKOEhk/mx14o7eiFRaU+VNNSP4MOiIkwb/D6B3G1Mfas7C/Fit8EsSV2/IWjZElx/Gs6A4ww==",
5801 +
      "license": "MIT",
5802 +
      "dependencies": {
5803 +
        "restructure": "^3.0.0"
5804 +
      }
5805 +
    },
5459 5806
    "node_modules/jiti": {
5460 5807
      "version": "2.6.1",
5461 5808
      "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
5462 5809
      "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
5463 5810
      "devOptional": true,
▸ 10 unchanged lines
5474 5821
      "license": "MIT",
5475 5822
      "engines": {
5476 5823
        "node": ">=10"
5477 5824
      }
5478 5825
    },
5826 +
    "node_modules/js-md5": {
5827 +
      "version": "0.8.3",
5828 +
      "resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.8.3.tgz",
5829 +
      "integrity": "sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ==",
5830 +
      "license": "MIT"
5831 +
    },
5479 5832
    "node_modules/js-tokens": {
5480 5833
      "version": "3.0.2",
5481 5834
      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
5482 5835
      "integrity": "sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==",
5483 -
      "dev": true,
5484 5836
      "license": "MIT"
5485 5837
    },
5486 5838
    "node_modules/jsesc": {
5487 5839
      "version": "1.3.0",
5488 5840
      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
▸ 55 unchanged lines
5544 5896
      "license": "MIT",
5545 5897
      "engines": {
5546 5898
        "node": ">=10"
5547 5899
      }
5548 5900
    },
5901 +
    "node_modules/linebreak": {
5902 +
      "version": "1.1.0",
5903 +
      "resolved": "https://registry.npmjs.org/linebreak/-/linebreak-1.1.0.tgz",
5904 +
      "integrity": "sha512-MHp03UImeVhB7XZtjd0E4n6+3xr5Dq/9xI/5FptGk5FrbDR3zagPa2DS6U8ks/3HjbKWG9Q1M2ufOzxV2qLYSQ==",
5905 +
      "license": "MIT",
5906 +
      "dependencies": {
5907 +
        "base64-js": "0.0.8",
5908 +
        "unicode-trie": "^2.0.0"
5909 +
      }
5910 +
    },
5911 +
    "node_modules/linebreak/node_modules/base64-js": {
5912 +
      "version": "0.0.8",
5913 +
      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-0.0.8.tgz",
5914 +
      "integrity": "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw==",
5915 +
      "license": "MIT",
5916 +
      "engines": {
5917 +
        "node": ">= 0.4"
5918 +
      }
5919 +
    },
5549 5920
    "node_modules/lodash": {
5550 5921
      "version": "4.17.21",
5551 5922
      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
5552 5923
      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
5553 5924
      "devOptional": true,
▸ 62 unchanged lines
5616 5987
    },
5617 5988
    "node_modules/loose-envify": {
5618 5989
      "version": "1.4.0",
5619 5990
      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
5620 5991
      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
5621 -
      "dev": true,
5622 5992
      "license": "MIT",
5623 5993
      "dependencies": {
5624 5994
        "js-tokens": "^3.0.0 || ^4.0.0"
5625 5995
      },
5626 5996
      "bin": {
▸ 48 unchanged lines
5675 6045
      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
5676 6046
      "license": "MIT",
5677 6047
      "engines": {
5678 6048
        "node": ">= 0.4"
5679 6049
      }
6050 +
    },
6051 +
    "node_modules/media-engine": {
6052 +
      "version": "1.0.3",
6053 +
      "resolved": "https://registry.npmjs.org/media-engine/-/media-engine-1.0.3.tgz",
6054 +
      "integrity": "sha512-aa5tG6sDoK+k70B9iEX1NeyfT8ObCKhNDs6lJVpwF6r8vhUfuKMslIcirq6HIUYuuUYLefcEQOn9bSBOvawtwg==",
6055 +
      "license": "MIT"
5680 6056
    },
5681 6057
    "node_modules/media-typer": {
5682 6058
      "version": "1.1.0",
5683 6059
      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
5684 6060
      "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
▸ 568 unchanged lines
6253 6629
      "license": "MIT",
6254 6630
      "engines": {
6255 6631
        "node": ">=0.10.0"
6256 6632
      }
6257 6633
    },
6634 +
    "node_modules/normalize-svg-path": {
6635 +
      "version": "1.1.0",
6636 +
      "resolved": "https://registry.npmjs.org/normalize-svg-path/-/normalize-svg-path-1.1.0.tgz",
6637 +
      "integrity": "sha512-r9KHKG2UUeB5LoTouwDzBy2VxXlHsiM6fyLQvnJa0S5hrhzqElH/CH7TUGhT1fVvIYBIKf3OpY4YJ4CK+iaqHg==",
6638 +
      "license": "MIT",
6639 +
      "dependencies": {
6640 +
        "svg-arc-to-cubic-bezier": "^3.0.0"
6641 +
      }
6642 +
    },
6258 6643
    "node_modules/nypm": {
6259 6644
      "version": "0.6.5",
6260 6645
      "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.5.tgz",
6261 6646
      "integrity": "sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==",
6262 6647
      "devOptional": true,
▸ 101 unchanged lines
6364 6749
      "dev": true,
6365 6750
      "license": "MIT",
6366 6751
      "engines": {
6367 6752
        "node": ">=0.10.0"
6368 6753
      }
6754 +
    },
6755 +
    "node_modules/pako": {
6756 +
      "version": "1.0.11",
6757 +
      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
6758 +
      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
6759 +
      "license": "(MIT AND Zlib)"
6760 +
    },
6761 +
    "node_modules/parse-svg-path": {
6762 +
      "version": "0.1.2",
6763 +
      "resolved": "https://registry.npmjs.org/parse-svg-path/-/parse-svg-path-0.1.2.tgz",
6764 +
      "integrity": "sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ==",
6765 +
      "license": "MIT"
6369 6766
    },
6370 6767
    "node_modules/parseurl": {
6371 6768
      "version": "1.3.3",
6372 6769
      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
6373 6770
      "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
▸ 299 unchanged lines
6673 7070
      },
6674 7071
      "engines": {
6675 7072
        "node": ">=12"
6676 7073
      }
6677 7074
    },
7075 +
    "node_modules/png-js": {
7076 +
      "version": "2.0.0",
7077 +
      "resolved": "https://registry.npmjs.org/png-js/-/png-js-2.0.0.tgz",
7078 +
      "integrity": "sha512-GdzJuUMc6ZSpxFJWVxtOH1bzYHym+TOnveqUjb+VJIbZWbZzyiRGFiKhbiielfpYbgMlhHVhsJ0FTazfuRFkMA==",
7079 +
      "dependencies": {
7080 +
        "fflate": "^0.8.2"
7081 +
      }
7082 +
    },
6678 7083
    "node_modules/postal-mime": {
6679 7084
      "version": "2.7.4",
6680 7085
      "resolved": "https://registry.npmjs.org/postal-mime/-/postal-mime-2.7.4.tgz",
6681 7086
      "integrity": "sha512-0WdnFQYUrPGGTFu1uOqD2s7omwua8xaeYGdO6rb88oD5yJ/4pPHDA4sdWqfD8wQVfCny563n/HQS7zTFft+f/g==",
6682 7087
      "license": "MIT-0"
▸ 24 unchanged lines
6707 7112
        "source-map-js": "^1.2.1"
6708 7113
      },
6709 7114
      "engines": {
6710 7115
        "node": "^10 || ^12 || >=14"
6711 7116
      }
7117 +
    },
7118 +
    "node_modules/postcss-value-parser": {
7119 +
      "version": "4.2.0",
7120 +
      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
7121 +
      "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
7122 +
      "license": "MIT"
6712 7123
    },
6713 7124
    "node_modules/postgres": {
6714 7125
      "version": "3.4.7",
6715 7126
      "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.7.tgz",
6716 7127
      "integrity": "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==",
▸ 130 unchanged lines
6847 7258
          "url": "https://opencollective.com/fastify"
6848 7259
        }
6849 7260
      ],
6850 7261
      "license": "MIT"
6851 7262
    },
7263 +
    "node_modules/prop-types": {
7264 +
      "version": "15.8.1",
7265 +
      "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
7266 +
      "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
7267 +
      "license": "MIT",
7268 +
      "dependencies": {
7269 +
        "loose-envify": "^1.4.0",
7270 +
        "object-assign": "^4.1.1",
7271 +
        "react-is": "^16.13.1"
7272 +
      }
7273 +
    },
6852 7274
    "node_modules/proper-lockfile": {
6853 7275
      "version": "4.1.2",
6854 7276
      "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz",
6855 7277
      "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==",
6856 7278
      "devOptional": true,
▸ 78 unchanged lines
6935 7357
      },
6936 7358
      "funding": {
6937 7359
        "url": "https://github.com/sponsors/ljharb"
6938 7360
      }
6939 7361
    },
7362 +
    "node_modules/queue": {
7363 +
      "version": "6.0.2",
7364 +
      "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz",
7365 +
      "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==",
7366 +
      "license": "MIT",
7367 +
      "dependencies": {
7368 +
        "inherits": "~2.0.3"
7369 +
      }
7370 +
    },
6940 7371
    "node_modules/queue-lit": {
6941 7372
      "version": "1.5.2",
6942 7373
      "resolved": "https://registry.npmjs.org/queue-lit/-/queue-lit-1.5.2.tgz",
6943 7374
      "integrity": "sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw==",
6944 7375
      "dev": true,
▸ 77 unchanged lines
7022 7453
        "defu": "^6.1.4",
7023 7454
        "destr": "^2.0.3"
7024 7455
      }
7025 7456
    },
7026 7457
    "node_modules/react": {
7027 -
      "version": "19.2.4",
7028 -
      "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
7029 -
      "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
7030 -
      "devOptional": true,
7458 +
      "version": "19.2.7",
7459 +
      "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz",
7460 +
      "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==",
7031 7461
      "license": "MIT",
7032 -
      "peer": true,
7033 7462
      "engines": {
7034 7463
        "node": ">=0.10.0"
7035 7464
      }
7036 7465
    },
7037 7466
    "node_modules/react-dom": {
▸ 7 unchanged lines
7045 7474
        "scheduler": "^0.27.0"
7046 7475
      },
7047 7476
      "peerDependencies": {
7048 7477
        "react": "^19.2.4"
7049 7478
      }
7479 +
    },
7480 +
    "node_modules/react-is": {
7481 +
      "version": "16.13.1",
7482 +
      "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
7483 +
      "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
7484 +
      "license": "MIT"
7050 7485
    },
7051 7486
    "node_modules/readable-stream": {
7052 7487
      "version": "3.6.2",
7053 7488
      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
7054 7489
      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
▸ 146 unchanged lines
7201 7636
      },
7202 7637
      "engines": {
7203 7638
        "node": ">=0.10.0"
7204 7639
      }
7205 7640
    },
7641 +
    "node_modules/require-from-string": {
7642 +
      "version": "2.0.2",
7643 +
      "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
7644 +
      "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
7645 +
      "license": "MIT",
7646 +
      "engines": {
7647 +
        "node": ">=0.10.0"
7648 +
      }
7649 +
    },
7206 7650
    "node_modules/resend": {
7207 7651
      "version": "6.12.3",
7208 7652
      "resolved": "https://registry.npmjs.org/resend/-/resend-6.12.3.tgz",
7209 7653
      "integrity": "sha512-FkEi6YPnVL96/LvH8+QP7NaeaBy5brYXwlRqUCqZZeNL0/iyKij18IPmyPXYauT/2ODn1JG04qKz+qlJfzqzTw==",
7210 7654
      "license": "MIT",
▸ 20 unchanged lines
7231 7675
      "dev": true,
7232 7676
      "license": "MIT",
7233 7677
      "funding": {
7234 7678
        "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
7235 7679
      }
7680 +
    },
7681 +
    "node_modules/restructure": {
7682 +
      "version": "3.0.2",
7683 +
      "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz",
7684 +
      "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==",
7685 +
      "license": "MIT"
7236 7686
    },
7237 7687
    "node_modules/retry": {
7238 7688
      "version": "0.12.0",
7239 7689
      "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
7240 7690
      "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
▸ 600 unchanged lines
7841 8291
      "license": "MIT",
7842 8292
      "engines": {
7843 8293
        "node": ">=0.8.0"
7844 8294
      }
7845 8295
    },
8296 +
    "node_modules/svg-arc-to-cubic-bezier": {
8297 +
      "version": "3.2.0",
8298 +
      "resolved": "https://registry.npmjs.org/svg-arc-to-cubic-bezier/-/svg-arc-to-cubic-bezier-3.2.0.tgz",
8299 +
      "integrity": "sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g==",
8300 +
      "license": "ISC"
8301 +
    },
7846 8302
    "node_modules/svix": {
7847 8303
      "version": "1.92.2",
7848 8304
      "resolved": "https://registry.npmjs.org/svix/-/svix-1.92.2.tgz",
7849 8305
      "integrity": "sha512-ZmuA3UVvlnF9EgxlzmPtF7CKjQb64Z6OFlyfdDfU0sdcC7dJa+3aOYX5B9mA+RS6ch1AxBa4UP/l6KmqfGtWBQ==",
7850 8306
      "license": "MIT",
▸ 43 unchanged lines
7894 8350
    },
7895 8351
    "node_modules/thread-stream/node_modules/real-require": {
7896 8352
      "version": "1.0.0",
7897 8353
      "resolved": "https://registry.npmjs.org/real-require/-/real-require-1.0.0.tgz",
7898 8354
      "integrity": "sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==",
8355 +
      "license": "MIT"
8356 +
    },
8357 +
    "node_modules/tiny-inflate": {
8358 +
      "version": "1.0.3",
8359 +
      "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz",
8360 +
      "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==",
7899 8361
      "license": "MIT"
7900 8362
    },
7901 8363
    "node_modules/tinybench": {
7902 8364
      "version": "2.9.0",
7903 8365
      "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
▸ 307 unchanged lines
8211 8673
      "version": "7.18.2",
8212 8674
      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
8213 8675
      "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
8214 8676
      "license": "MIT"
8215 8677
    },
8678 +
    "node_modules/unicode-properties": {
8679 +
      "version": "1.4.1",
8680 +
      "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz",
8681 +
      "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==",
8682 +
      "license": "MIT",
8683 +
      "dependencies": {
8684 +
        "base64-js": "^1.3.0",
8685 +
        "unicode-trie": "^2.0.0"
8686 +
      }
8687 +
    },
8688 +
    "node_modules/unicode-trie": {
8689 +
      "version": "2.0.0",
8690 +
      "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz",
8691 +
      "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==",
8692 +
      "license": "MIT",
8693 +
      "dependencies": {
8694 +
        "pako": "^0.2.5",
8695 +
        "tiny-inflate": "^1.0.0"
8696 +
      }
8697 +
    },
8698 +
    "node_modules/unicode-trie/node_modules/pako": {
8699 +
      "version": "0.2.9",
8700 +
      "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
8701 +
      "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==",
8702 +
      "license": "MIT"
8703 +
    },
8216 8704
    "node_modules/unpipe": {
8217 8705
      "version": "1.0.0",
8218 8706
      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
8219 8707
      "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
8220 8708
      "license": "MIT",
▸ 102 unchanged lines
8323 8811
          "optional": true
8324 8812
        },
8325 8813
        "yaml": {
8326 8814
          "optional": true
8327 8815
        }
8816 +
      }
8817 +
    },
8818 +
    "node_modules/vite-compatible-readable-stream": {
8819 +
      "version": "3.6.1",
8820 +
      "resolved": "https://registry.npmjs.org/vite-compatible-readable-stream/-/vite-compatible-readable-stream-3.6.1.tgz",
8821 +
      "integrity": "sha512-t20zYkrSf868+j/p31cRIGN28Phrjm3nRSLR2fyc2tiWi4cZGVdv68yNlwnIINTkMTmPoMiSlc0OadaO7DXZaQ==",
8822 +
      "license": "MIT",
8823 +
      "dependencies": {
8824 +
        "inherits": "^2.0.3",
8825 +
        "string_decoder": "^1.1.1",
8826 +
        "util-deprecate": "^1.0.1"
8827 +
      },
8828 +
      "engines": {
8829 +
        "node": ">= 6"
8328 8830
      }
8329 8831
    },
8330 8832
    "node_modules/vite-node": {
8331 8833
      "version": "3.2.4",
8332 8834
      "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz",
▸ 199 unchanged lines
8532 9034
      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
8533 9035
      "license": "MIT",
8534 9036
      "engines": {
8535 9037
        "node": ">=0.4"
8536 9038
      }
9039 +
    },
9040 +
    "node_modules/yoga-layout": {
9041 +
      "version": "3.2.1",
9042 +
      "resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz",
9043 +
      "integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==",
9044 +
      "license": "MIT"
8537 9045
    },
8538 9046
    "node_modules/zeptomatch": {
8539 9047
      "version": "2.1.0",
8540 9048
      "resolved": "https://registry.npmjs.org/zeptomatch/-/zeptomatch-2.1.0.tgz",
8541 9049
      "integrity": "sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA==",
▸ 25 unchanged lines
8567 9075
        "zod": "^3.25.0 || ^4.0.0"
8568 9076
      }
8569 9077
    }
8570 9078
  }
8571 9079
}
api/package.json
1 1
{
2 2
  "name": "orbit-api",
3 3
  "version": "0.0.0",
4 4
  "description": "",
5 5
  "license": "ISC",
▸ 17 unchanged lines
23 23
  "dependencies": {
24 24
    "@aws-sdk/client-s3": "^3.1048.0",
25 25
    "@prisma/adapter-better-sqlite3": "^7.5.0",
26 26
    "@prisma/adapter-pg": "^7.7.0",
27 27
    "@prisma/client": "^7.5.0",
28 +
    "@react-pdf/renderer": "^4.5.1",
28 29
    "@types/cors": "^2.8.19",
29 30
    "@types/jsonwebtoken": "^9.0.10",
30 31
    "bcryptjs": "^3.0.3",
31 32
    "better-sqlite3": "^12.5.0",
32 33
    "body-parser": "^2.2.2",
▸ 6 unchanged lines
39 40
    "multer": "^2.1.1",
40 41
    "multer-s3": "^3.0.1",
41 42
    "pg": "^8.20.0",
42 43
    "pino": "^10.3.1",
43 44
    "pino-http": "^11.0.0",
45 +
    "react": "^19.2.7",
44 46
    "resend": "^6.12.3",
45 47
    "zod": "^4.4.3",
46 48
    "zod-validation-error": "^5.0.0"
47 49
  },
48 50
  "devDependencies": {
▸ 1 unchanged lines
50 52
    "@types/express": "^5.0.6",
51 53
    "@types/morgan": "^1.9.10",
52 54
    "@types/multer": "^2.1.0",
53 55
    "@types/multer-s3": "^3.0.3",
54 56
    "@types/node": "^25.5.2",
57 +
    "@types/react": "^19.2.17",
55 58
    "@types/supertest": "^6.0.3",
56 59
    "cross-var": "^1.1.0",
57 60
    "dotenv-cli": "^11.0.0",
58 61
    "nodemon": "^3.1.14",
59 62
    "pino-pretty": "^13.1.3",
▸ 4 unchanged lines
64 67
    "typescript": "^6.0.2",
65 68
    "vite-tsconfig-paths": "^5.1.4",
66 69
    "vitest": "^3.0.0"
67 70
  }
68 71
}
api/src/createApp.ts
1 1
import express from "express";
2 2
import cors from "cors";
3 3
import bodyParser from "body-parser";
4 4
import morgan from "morgan";
5 5
import mainRouter from "@app/routes/main";
▸ 3 unchanged lines
9 9
import triggerRouter from "@app/routes/triggers";
10 10
import sleepRecordRouter from "@app/routes/sleepRecords";
11 11
import insightRouter from "@app/routes/insights";
12 12
import careActionsRouter from "@app/routes/careActions";
13 13
import medicineRegimensRouter from "@app/routes/medicineRegimens";
14 +
import reportRouter from "@app/reports/report.router";
14 15
import { errorHandler } from "@app/middleware/errorHandler";
15 16
import { PrismaClient } from "@prisma/client";
16 17
import {
17 18
  bootWorkers,
18 19
  closeAllWorkers,
▸ 45 unchanged lines
64 65
  app.use("/sleep_records", sleepRecordRouter);
65 66
  app.use("/triggers", triggerRouter);
66 67
  app.use("/insights", insightRouter);
67 68
  app.use("/care-actions", careActionsRouter);
68 69
  app.use("/medicine-regimens", medicineRegimensRouter);
70 +
  app.use("/reports", reportRouter);
69 71

70 72
  app.use(errorHandler); // always last
71 73

72 74
  // TODO: flesh the actual page here
73 75
  app.use(express.static(path.join(__dirname, "./static")));
▸ 9 unchanged lines
83 85

84 86
const shutdown = async () => {
85 87
  await Promise.all([closeAllWorkers(), closeAllQueues()]);
86 88
  process.exit(0);
87 89
};
api/src/reports/ReportDocument.tsx
@@ -0,0 +1,519 @@
1 + import React from 'react'
2 + import {
3 + Document,
4 + Page,
5 + View,
6 + Text,
7 + Svg,
8 + Rect,
9 + Image,
10 + StyleSheet,
11 + } from '@react-pdf/renderer'
12 + import { format } from 'date-fns'
13 + import { ptBR } from 'date-fns/locale'
14 + import { BaseMoodOption, IntensityLevel, MoodComponentOption } from '@prisma/client'
15 + import { ReportData } from './report.data'
16 + import { LOGO_DATA_URI } from './logo'
17 +
18 + const T = {
19 + title: 'Relatório de Humor e Bem-Estar',
20 + appName: 'Nexo',
21 + tagline: 'Nexo · Rastreador de humor',
22 + confidential: 'Confidencial — uso clínico',
23 + sections: {
24 + glance: 'Visão Geral do Período',
25 + distribution: 'Distribuição de Humor',
26 + heatmap: 'Mapa de Calor Diário',
27 + components: 'Sentimentos & Emoções',
28 + log: 'Registros e níveis (ansiedade, energia e estresse)',
29 + },
30 + stats: {
31 + checkins: 'Check-ins',
32 + topMood: 'Humor predominante',
33 + daysTracked: 'Dias rastreados',
34 + },
35 + moods: {
36 + GREAT: 'Ótimo', GOOD: 'Bom', NEUTRAL: 'Neutro',
37 + SAD: 'Triste', ANGRY: 'Irritado',
38 + } as Record<BaseMoodOption, string>,
39 + intensity: {
40 + LIGHT: 'Leve', MODERATE: 'Moderado', HIGH: 'Alto',
41 + } as Record<IntensityLevel, string>,
42 + components: {
43 + JOY: 'Alegria', ANGER: 'Raiva', SADNESS: 'Tristeza',
44 + FEAR: 'Medo', GUILT: 'Culpa', FRUSTRATION: 'Frustração',
45 + CALM: 'Calma', MOTIVATED: 'Motivação', TIREDNESS: 'Cansaço',
46 + GRATITUDE: 'Gratidão', FOCUS: 'Foco', RESTLESS: 'Agitação',
47 + RELAXED: 'Relaxado', OVERWHELMED: 'Sobrecarga',
48 + } as Record<MoodComponentOption, string>,
49 + tableHeaders: {
50 + component: 'Componente', count: 'Qtd', avgIntensity: 'Int. Média',
51 + date: 'Data', mood: 'Humor', anxiety: 'Ans', stress: 'Est',
52 + energy: 'Ene', note: 'Nota',
53 + },
54 + }
55 +
56 + const MOOD_COLORS: Record<BaseMoodOption, string> = {
57 + GREAT: '#378ADD',
58 + GOOD: '#639922',
59 + NEUTRAL: '#888780',
60 + SAD: '#1D9E75',
61 + ANGRY: '#D85A30',
62 + }
63 +
64 + const INTENSITY_COLORS: Record<IntensityLevel, string> = {
65 + LIGHT: '#639922',
66 + MODERATE: '#BA7517',
67 + HIGH: '#D85A30',
68 + }
69 +
70 + const EMPTY_CELL = '#E8E7E2'
71 +
72 + const s = StyleSheet.create({
73 + page: {
74 + fontFamily: 'Helvetica',
75 + fontSize: 9,
76 + paddingTop: 40,
77 + paddingBottom: 50,
78 + paddingHorizontal: 40,
79 + color: '#1a1a1a',
80 + },
81 + header: {
82 + flexDirection: 'row',
83 + justifyContent: 'space-between',
84 + alignItems: 'flex-start',
85 + marginBottom: 4,
86 + paddingBottom: 8,
87 + borderBottomWidth: 1,
88 + borderBottomColor: '#e0e0e0',
89 + },
90 + headerTitle: {
91 + fontSize: 13,
92 + fontFamily: 'Helvetica-Bold',
93 + },
94 + headerLogo: {
95 + width: 48,
96 + height: 48,
97 + objectFit: 'contain',
98 + },
99 + headerSub: {
100 + fontSize: 8,
101 + color: '#666666',
102 + marginTop: 2,
103 + },
104 + footer: {
105 + position: 'absolute',
106 + bottom: 20,
107 + left: 40,
108 + right: 40,
109 + flexDirection: 'column',
110 + borderTopWidth: 1,
111 + borderTopColor: '#e0e0e0',
112 + paddingTop: 5,
113 + },
114 + footerRow: {
115 + flexDirection: 'row',
116 + justifyContent: 'space-between',
117 + },
118 + footerText: {
119 + fontSize: 7,
120 + color: '#888888',
121 + },
122 + section: {
123 + marginTop: 16,
124 + marginBottom: 4,
125 + },
126 + sectionTitle: {
127 + fontFamily: 'Helvetica-Bold',
128 + fontSize: 10,
129 + marginBottom: 6,
130 + paddingBottom: 3,
131 + borderBottomWidth: 1,
132 + borderBottomColor: '#e0e0e0',
133 + },
134 + statRow: {
135 + flexDirection: 'row',
136 + gap: 8,
137 + marginBottom: 4,
138 + },
139 + statBox: {
140 + flex: 1,
141 + borderWidth: 1,
142 + borderColor: '#e0e0e0',
143 + borderRadius: 4,
144 + padding: 8,
145 + },
146 + statLabel: {
147 + fontSize: 7,
148 + color: '#888888',
149 + marginBottom: 3,
150 + },
151 + statValue: {
152 + fontSize: 12,
153 + fontFamily: 'Helvetica-Bold',
154 + },
155 + barRow: {
156 + flexDirection: 'row',
157 + alignItems: 'center',
158 + marginBottom: 4,
159 + },
160 + barLabel: {
161 + width: 60,
162 + fontSize: 8,
163 + },
164 + barPct: {
165 + width: 32,
166 + fontSize: 8,
167 + textAlign: 'right',
168 + color: '#666666',
169 + },
170 + heatmapRow: {
171 + flexDirection: 'row',
172 + alignItems: 'center',
173 + marginBottom: 2,
174 + },
175 + heatmapWeekLabel: {
176 + width: 24,
177 + fontSize: 7,
178 + color: '#888888',
179 + },
180 + tableHeaderRow: {
181 + flexDirection: 'row',
182 + backgroundColor: '#f0f0f0',
183 + paddingVertical: 4,
184 + paddingHorizontal: 4,
185 + },
186 + tableRow: {
187 + flexDirection: 'row',
188 + paddingVertical: 3,
189 + paddingHorizontal: 4,
190 + },
191 + tableCell: {
192 + fontSize: 7,
193 + },
194 + })
195 +
196 + function formatPeriod(start: Date, end: Date): string {
197 + const s = format(start, 'd MMM', { locale: ptBR })
198 + const e = format(end, 'd MMM yyyy', { locale: ptBR })
199 + return `${s} – ${e}`
200 + }
201 +
202 + function truncate(str: string | null, max: number): string {
203 + if (!str) return '—'
204 + return str.length > max ? str.slice(0, max) + '…' : str
205 + }
206 +
207 + function topMoodLabel(dist: ReportData['moodDistribution']): string {
208 + const top = [...dist].sort((a, b) => b.count - a.count)[0]
209 + return top && top.count > 0 ? T.moods[top.mood] : '—'
210 + }
211 +
212 + type SectionHeaderProps = { label: string }
213 + function SectionHeader({ label }: SectionHeaderProps) {
214 + return (
215 + <View style={s.section}>
216 + <Text style={s.sectionTitle}>{label}</Text>
217 + </View>
218 + )
219 + }
220 +
221 + type StatBoxProps = { label: string; value: string }
222 + function StatBox({ label, value }: StatBoxProps) {
223 + return (
224 + <View style={s.statBox}>
225 + <Text style={s.statLabel}>{label}</Text>
226 + <Text style={s.statValue}>{value}</Text>
227 + </View>
228 + )
229 + }
230 +
231 + const BAR_MAX_WIDTH = 350
232 +
233 + type MoodBarProps = {
234 + mood: BaseMoodOption
235 + count: number
236 + pct: number
237 + maxPct: number
238 + }
239 + function MoodBar({ mood, count, pct, maxPct }: MoodBarProps) {
240 + const barWidth = maxPct > 0 ? (pct / maxPct) * BAR_MAX_WIDTH : 0
241 + const color = MOOD_COLORS[mood]
242 + return (
243 + <View style={s.barRow}>
244 + <Text style={s.barLabel}>{T.moods[mood]}</Text>
245 + <Svg width={BAR_MAX_WIDTH} height={12}>
246 + <Rect x={0} y={2} width={BAR_MAX_WIDTH} height={8}
247 + rx={2} fill={EMPTY_CELL} />
248 + {barWidth > 0 && (
249 + <Rect x={0} y={2} width={barWidth} height={8}
250 + rx={2} fill={color} />
251 + )}
252 + </Svg>
253 + <Text style={s.barPct}>{pct}%</Text>
254 + <Text style={[s.barPct, { width: 24 }]}>{count}</Text>
255 + </View>
256 + )
257 + }
258 +
259 + const CELL = 52
260 + const GAP = 4
261 +
262 + type HeatmapRowProps = {
263 + week: number
264 + cells: Array<{ date: string; mood: BaseMoodOption | null }>
265 + startDow: number
266 + }
267 + function HeatmapRow({ week, cells, startDow }: HeatmapRowProps) {
268 + const totalCols = 7
269 + const svgWidth = totalCols * (CELL + GAP)
270 +
271 + return (
272 + <View style={s.heatmapRow}>
273 + <Text style={s.heatmapWeekLabel}>S{week}</Text>
274 + <Svg width={svgWidth} height={CELL}>
275 + {Array.from({ length: totalCols }).map((_, col) => {
276 + const dayIndex = col - startDow
277 + const cell = dayIndex >= 0 ? cells[dayIndex] : undefined
278 + const fill = cell
279 + ? cell.mood ? MOOD_COLORS[cell.mood] : EMPTY_CELL
280 + : '#f8f8f8'
281 + return (
282 + <Rect
283 + key={col}
284 + x={col * (CELL + GAP)}
285 + y={0}
286 + width={CELL}
287 + height={CELL}
288 + rx={3}
289 + fill={fill}
290 + />
291 + )
292 + })}
293 + </Svg>
294 + </View>
295 + )
296 + }
297 +
298 + type TableHeaderProps = { cols: string[]; widths: number[] }
299 + function TableHeader({ cols, widths }: TableHeaderProps) {
300 + return (
301 + <View style={s.tableHeaderRow}>
302 + {cols.map((col, i) => (
303 + <Text key={i}
304 + style={[s.tableCell, { width: widths[i], fontFamily: 'Helvetica-Bold' }]}>
305 + {col}
306 + </Text>
307 + ))}
308 + </View>
309 + )
310 + }
311 +
312 + type TableRowProps = {
313 + cols: string[]
314 + widths: number[]
315 + even: boolean
316 + colors?: (string | undefined)[]
317 + }
318 + function TableRow({ cols, widths, even, colors }: TableRowProps) {
319 + return (
320 + <View style={[s.tableRow, { backgroundColor: even ? '#F8F7F4' : '#ffffff' }]}>
321 + {cols.map((col, i) => (
322 + <Text key={i}
323 + style={[s.tableCell, { width: widths[i], color: colors?.[i] ?? '#1a1a1a' }]}>
324 + {col}
325 + </Text>
326 + ))}
327 + </View>
328 + )
329 + }
330 +
331 + type PageHeaderProps = { data: ReportData }
332 + function PageHeader({ data }: PageHeaderProps) {
333 + const name = [data.user.firstName, data.user.lastName].filter(Boolean).join(' ')
334 + const period = formatPeriod(data.periodStart, data.periodEnd)
335 + return (
336 + <View style={s.header}>
337 + <View>
338 + <Text style={s.headerTitle}>{T.title}</Text>
339 + <Text style={s.headerSub}>{name} · {period}</Text>
340 + </View>
341 +
342 + <Image src={LOGO_DATA_URI} style={s.headerLogo} />
343 + </View>
344 + )
345 + }
346 +
347 + function PageFooter() {
348 + return (
349 + <View style={s.footer} fixed>
350 + <View style={s.footerRow}>
351 + <Text style={s.footerText}>{T.tagline}</Text>
352 + <Text
353 + style={s.footerText}
354 + render={({ pageNumber, totalPages }: { pageNumber: number; totalPages: number }) =>
355 + `${pageNumber} / ${totalPages}`
356 + }
357 + />
358 + </View>
359 + <Text style={[s.footerText, { marginTop: 2 }]}>{T.confidential}</Text>
360 + </View>
361 + )
362 + }
363 +
364 + function buildHeatmapWeeks(
365 + dailyMoods: ReportData['dailyMoods'],
366 + ): Array<{
367 + week: number
368 + cells: Array<{ date: string; mood: BaseMoodOption | null }>
369 + startDow: number
370 + }> {
371 + const weeks: ReturnType<typeof buildHeatmapWeeks> = []
372 + let weekNum = 1
373 + let i = 0
374 +
375 + if (dailyMoods.length === 0) return weeks
376 +
377 + const firstDow = new Date(dailyMoods[0].date + 'T12:00:00').getDay()
378 +
379 + while (i < dailyMoods.length) {
380 + const slots = 7 - (weekNum === 1 ? firstDow : 0)
381 + const cells = dailyMoods.slice(i, i + slots)
382 +
383 + weeks.push({ week: weekNum, cells, startDow: weekNum === 1 ? firstDow : 0 })
384 + i += slots
385 + weekNum++
386 + }
387 +
388 + return weeks
389 + }
390 +
391 + type Props = { data: ReportData }
392 +
393 + export default function ReportDocument({ data }: Props) {
394 + const maxPct = Math.max(...data.moodDistribution.map(d => d.pct), 0)
395 + const daysTracked = data.dailyMoods.filter(d => d.mood !== null).length
396 + const heatmapWeeks = buildHeatmapWeeks(data.dailyMoods)
397 +
398 + return (
399 + <Document>
400 + {/* Page 1 */}
401 + <Page size="A4" style={s.page}>
402 + <PageHeader data={data} />
403 + <PageFooter />
404 +
405 + <SectionHeader label={T.sections.glance} />
406 + <View style={s.statRow}>
407 + <StatBox label={T.stats.checkins} value={String(data.totalCheckins)} />
408 + <StatBox label={T.stats.topMood} value={topMoodLabel(data.moodDistribution)} />
409 + <StatBox label={T.stats.daysTracked} value={String(daysTracked)} />
410 + <StatBox
411 + label="Período"
412 + value={`${data.dailyMoods.length}d`}
413 + />
414 + </View>
415 +
416 + <SectionHeader label={T.sections.distribution} />
417 + {data.moodDistribution.map(d => (
418 + <MoodBar
419 + key={d.mood}
420 + mood={d.mood}
421 + count={d.count}
422 + pct={d.pct}
423 + maxPct={maxPct}
424 + />
425 + ))}
426 +
427 + <SectionHeader label={T.sections.heatmap} />
428 + {heatmapWeeks.map(({ week, cells, startDow }) => (
429 + <HeatmapRow key={week} week={week} cells={cells} startDow={startDow} />
430 + ))}
431 + </Page>
432 +
433 + {/* Page 2 */}
434 + <Page size="A4" style={s.page}>
435 + <PageHeader data={data} />
436 + <PageFooter />
437 +
438 + <SectionHeader label={T.sections.components} />
439 + {data.topComponents.length > 0 ? (
440 + <View>
441 + <TableHeader
442 + cols={[
443 + T.tableHeaders.component,
444 + T.tableHeaders.count,
445 + T.tableHeaders.avgIntensity,
446 + ]}
447 + widths={[200, 60, 100]}
448 + />
449 + {data.topComponents.map((c, i) => (
450 + <TableRow
451 + key={c.component}
452 + even={i % 2 === 1}
453 + cols={[
454 + T.components[c.component] ?? c.component,
455 + String(c.count),
456 + T.intensity[c.avgIntensity],
457 + ]}
458 + widths={[200, 60, 100]}
459 + colors={[
460 + undefined,
461 + undefined,
462 + INTENSITY_COLORS[c.avgIntensity],
463 + ]}
464 + />
465 + ))}
466 + </View>
467 + ) : (
468 + <Text style={[s.tableCell, { color: '#888888', marginTop: 4 }]}>
469 + Sem componentes registrados no período.
470 + </Text>
471 + )}
472 +
473 + <SectionHeader label={T.sections.log} />
474 + {data.checkinLog.length > 0 ? (
475 + <View>
476 + <TableHeader
477 + cols={[
478 + T.tableHeaders.date,
479 + T.tableHeaders.mood,
480 + T.tableHeaders.anxiety,
481 + T.tableHeaders.stress,
482 + T.tableHeaders.energy,
483 + T.tableHeaders.note,
484 + ]}
485 + widths={[48, 62, 28, 28, 28, 150]}
486 + />
487 + {data.checkinLog.map((entry, i) => (
488 + <TableRow
489 + key={i}
490 + even={i % 2 === 1}
491 + cols={[
492 + format(entry.moment, 'd MMM', { locale: ptBR }),
493 + T.moods[entry.mood] ?? entry.mood,
494 + String(entry.anxietyLevel),
495 + String(entry.stressLevel),
496 + String(entry.energyLevel),
497 + truncate(entry.annotation, 28),
498 + ]}
499 + widths={[48, 62, 28, 28, 28, 150]}
500 + colors={[
501 + undefined,
502 + MOOD_COLORS[entry.mood],
503 + undefined,
504 + undefined,
505 + undefined,
506 + undefined,
507 + ]}
508 + />
509 + ))}
510 + </View>
511 + ) : (
512 + <Text style={[s.tableCell, { color: '#888888', marginTop: 4 }]}>
513 + Sem check-ins no período.
514 + </Text>
515 + )}
516 + </Page>
517 + </Document>
518 + )
519 + }
api/src/reports/logo.ts
@@ -0,0 +1,8 @@
1 + // XXX: This file embeds logo.png as a base64 data URI so the PDF renderer has
2 + // no filesystem dependency at runtime. To regenerate after the logo changes,
3 + // run from the repo root, which I most certainly will forget in the future:
4 + //
5 + // base64 -w 0 frontend/assets/images/logo.png \
6 + // | xargs -I{} echo "export const LOGO_DATA_URI = 'data:image/png;base64,{}'" \
7 + // > api/src/reports/logo.ts
8 + export const LOGO_DATA_URI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAMgCAYAAADbcAZoAAAAtGVYSWZJSSoACAAAAAYAEgEDAAEAAAABAAAAGgEFAAEAAABWAAAAGwEFAAEAAABeAAAAKAEDAAEAAAACAAAAEwIDAAEAAAABAAAAaYcEAAEAAABmAAAAAAAAAGAAAAABAAAAYAAAAAEAAAAGAACQBwAEAAAAMDIxMAGRBwAEAAAAAQIDAACgBwAEAAAAMDEwMAGgAwABAAAA//8AAAKgBAABAAAAIAMAAAOgBAABAAAAIAMAAAAAAACRvww8AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAF22lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSfvu78nIGlkPSdXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQnPz4KPHg6eG1wbWV0YSB4bWxuczp4PSdhZG9iZTpuczptZXRhLyc+CjxyZGY6UkRGIHhtbG5zOnJkZj0naHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyc+CgogPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9JycKICB4bWxuczpBdHRyaWI9J2h0dHA6Ly9ucy5hdHRyaWJ1dGlvbi5jb20vYWRzLzEuMC8nPgogIDxBdHRyaWI6QWRzPgogICA8cmRmOlNlcT4KICAgIDxyZGY6bGkgcmRmOnBhcnNlVHlwZT0nUmVzb3VyY2UnPgogICAgIDxBdHRyaWI6Q3JlYXRlZD4yMDI2LTA2LTE1PC9BdHRyaWI6Q3JlYXRlZD4KICAgICA8QXR0cmliOkRhdGE+eyZxdW90O2RvYyZxdW90OzomcXVvdDtEQUhMY19ubmwzQSZxdW90OywmcXVvdDt1c2VyJnF1b3Q7OiZxdW90O1VBRHMzc1B4U3JFJnF1b3Q7LCZxdW90O2JyYW5kJnF1b3Q7OiZxdW90O0JBRHMzb3ktQWNjJnF1b3Q7LCZxdW90O3RlbXBsYXRlJnF1b3Q7OiZxdW90O05hdnkgQmx1ZSBhbmQgV2hpdGUgU2ltcGxlIEd5bSBZb3VUdWJlIENoYW5uZWwgTG9nbyZxdW90O308L0F0dHJpYjpEYXRhPgogICAgIDxBdHRyaWI6RXh0SWQ+YTQ4Yzg0ZjktNmRjNi00MGQ1LTkwNGItNmIyMTM4MDU2YWRhPC9BdHRyaWI6RXh0SWQ+CiAgICAgPEF0dHJpYjpGYklkPjUyNTI2NTkxNDE3OTU4MDwvQXR0cmliOkZiSWQ+CiAgICAgPEF0dHJpYjpUb3VjaFR5cGU+MjwvQXR0cmliOlRvdWNoVHlwZT4KICAgIDwvcmRmOmxpPgogICA8L3JkZjpTZXE+CiAgPC9BdHRyaWI6QWRzPgogPC9yZGY6RGVzY3JpcHRpb24+CgogPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9JycKICB4bWxuczpkYz0naHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8nPgogIDxkYzp0aXRsZT4KICAgPHJkZjpBbHQ+CiAgICA8cmRmOmxpIHhtbDpsYW5nPSd4LWRlZmF1bHQnPk1DVlFDIC0gNDwvcmRmOmxpPgogICA8L3JkZjpBbHQ+CiAgPC9kYzp0aXRsZT4KIDwvcmRmOkRlc2NyaXB0aW9uPgoKIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PScnCiAgeG1sbnM6cGRmPSdodHRwOi8vbnMuYWRvYmUuY29tL3BkZi8xLjMvJz4KICA8cGRmOkF1dGhvcj5QZWRybyBMdWNhcyBQb3JjZWxsaXMgQnJhbmNvPC9wZGY6QXV0aG9yPgogPC9yZGY6RGVzY3JpcHRpb24+CgogPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9JycKICB4bWxuczp4bXA9J2h0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8nPgogIDx4bXA6Q3JlYXRvclRvb2w+Q2FudmEgZG9jPURBSExjX25ubDNBIHVzZXI9VUFEczNzUHhTckUgYnJhbmQ9QkFEczNveS1BY2MgdGVtcGxhdGU9TmF2eSBCbHVlIGFuZCBXaGl0ZSBTaW1wbGUgR3ltIFlvdVR1YmUgQ2hhbm5lbCBMb2dvPC94bXA6Q3JlYXRvclRvb2w+CiA8L3JkZjpEZXNjcmlwdGlvbj4KPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KPD94cGFja2V0IGVuZD0ncic/PhcyU0gAACAASURBVHic7N13nCRF3cfx+xPubnemaw6zoJgIj+kRTCgimOFA0XF3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'
api/src/reports/report.data.ts
@@ -0,0 +1,136 @@
1 + import { eachDayOfInterval, format } from 'date-fns'
2 + import { prisma } from '@app/lib/prisma'
3 + import {
4 + BaseMoodOption,
5 + MoodComponentOption,
6 + IntensityLevel,
7 + } from '@prisma/client'
8 +
9 + export type ReportData = {
10 + user: { firstName: string; lastName: string | null }
11 + periodStart: Date
12 + periodEnd: Date
13 + totalCheckins: number
14 + moodDistribution: Array<{
15 + mood: BaseMoodOption
16 + count: number
17 + pct: number
18 + }>
19 + dailyMoods: Array<{ date: string; mood: BaseMoodOption | null }>
20 + topComponents: Array<{
21 + component: MoodComponentOption
22 + count: number
23 + avgIntensity: IntensityLevel
24 + }>
25 + checkinLog: Array<{
26 + moment: Date
27 + mood: BaseMoodOption
28 + anxietyLevel: number
29 + stressLevel: number
30 + energyLevel: number
31 + annotation: string | null
32 + }>
33 + }
34 +
35 + const MOOD_ORDER: BaseMoodOption[] = [
36 + 'GREAT', 'GOOD', 'NEUTRAL', 'SAD', 'ANGRY',
37 + ]
38 +
39 +
40 + export async function buildReportData(
41 + userId: number,
42 + start: Date,
43 + end: Date,
44 + ): Promise<ReportData> {
45 + const [user, moods] = await Promise.all([
46 + prisma.user.findUniqueOrThrow({
47 + where: { id: userId },
48 + select: { firstName: true, lastName: true },
49 + }),
50 + prisma.mood.findMany({
51 + where: {
52 + userId,
53 + moment: { gte: start, lte: end },
54 + },
55 + include: { moodComponents: true },
56 + orderBy: { moment: 'desc' },
57 + }),
58 + ])
59 +
60 + const total = moods.length
61 +
62 + const moodCounts = Object.fromEntries(
63 + MOOD_ORDER.map(m => [m, 0]),
64 + ) as Record<BaseMoodOption, number>
65 +
66 + for (const m of moods) moodCounts[m.selectedMood]++
67 +
68 + const moodDistribution = MOOD_ORDER.map(mood => ({
69 + mood,
70 + count: moodCounts[mood],
71 + pct: total === 0 ? 0 : Math.round((moodCounts[mood] / total) * 1000) / 10,
72 + }))
73 +
74 + const byDate = new Map<string, typeof moods[number][]>()
75 + for (const m of moods) {
76 + const key = format(m.moment, 'yyyy-MM-dd')
77 + const group = byDate.get(key) ?? []
78 + group.push(m)
79 + byDate.set(key, group)
80 + }
81 +
82 + const days = eachDayOfInterval({ start, end })
83 + const dailyMoods = days.map(day => {
84 + const key = format(day, 'yyyy-MM-dd')
85 + const group = byDate.get(key)
86 + if (!group || group.length === 0) return { date: key, mood: null }
87 + const last = group.reduce((a, b) =>
88 + b.moment > a.moment ? b : a,
89 + )
90 + return { date: key, mood: last.selectedMood }
91 + })
92 +
93 + const componentGroups = new Map<
94 + MoodComponentOption,
95 + IntensityLevel[]
96 + >()
97 + for (const m of moods) {
98 + for (const c of m.moodComponents) {
99 + const arr = componentGroups.get(c.component) ?? []
100 + arr.push(c.intensity)
101 + componentGroups.set(c.component, arr)
102 + }
103 + }
104 +
105 + const topComponents = Array.from(componentGroups.entries())
106 + .map(([component, intensities]) => {
107 + const freq = new Map<IntensityLevel, number>()
108 + for (const i of intensities) freq.set(i, (freq.get(i) ?? 0) + 1)
109 + const avgIntensity = Array.from(freq.entries()).reduce((a, b) =>
110 + b[1] > a[1] ? b : a,
111 + )[0]
112 + return { component, count: intensities.length, avgIntensity }
113 + })
114 + .sort((a, b) => b.count - a.count)
115 + .slice(0, 10)
116 +
117 + const checkinLog = moods.slice(0, 50).map(m => ({
118 + moment: m.moment,
119 + mood: m.selectedMood,
120 + anxietyLevel: m.anxietyLevel,
121 + stressLevel: m.stressLevel,
122 + energyLevel: m.energyLevel,
123 + annotation: m.annotation,
124 + }))
125 +
126 + return {
127 + user,
128 + periodStart: start,
129 + periodEnd: end,
130 + totalCheckins: total,
131 + moodDistribution,
132 + dailyMoods,
133 + topComponents,
134 + checkinLog,
135 + }
136 + }
api/src/reports/report.router.ts
@@ -0,0 +1,60 @@
1 + import { Router, Response, NextFunction } from 'express'
2 + import React from 'react'
3 + import { renderToBuffer } from '@react-pdf/renderer'
4 + import { z } from 'zod'
5 + import { requireAuth, AuthenticatedRequest } from '@app/middleware/auth'
6 + import { buildReportData } from './report.data'
7 + import ReportDocument from './ReportDocument'
8 +
9 + const router = Router()
10 +
11 + const ReportSchema = z
12 + .object({
13 + periodStart: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
14 + periodEnd: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
15 + })
16 + .refine(
17 + (d) => {
18 + const start = new Date(d.periodStart)
19 + const end = new Date(d.periodEnd)
20 + const diff = (end.getTime() - start.getTime()) / 86_400_000
21 + return diff > 0 && diff <= 90
22 + },
23 + { message: 'periodEnd must be after periodStart and range ≤ 90 days' },
24 + )
25 +
26 + router.post(
27 + '/',
28 + requireAuth,
29 + async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
30 + try {
31 + const parsed = ReportSchema.safeParse(req.body)
32 + if (!parsed.success) {
33 + return res.status(400).json({ error: parsed.error.message })
34 + }
35 +
36 + const { periodStart, periodEnd } = parsed.data
37 + const data = await buildReportData(
38 + req.userId!,
39 + new Date(periodStart),
40 + new Date(periodEnd),
41 + )
42 +
43 + const element = React.createElement(ReportDocument, { data })
44 + const buffer = await renderToBuffer(
45 + element as React.ReactElement<any>,
46 + )
47 +
48 + res.setHeader('Content-Type', 'application/pdf')
49 + res.setHeader(
50 + 'Content-Disposition',
51 + `attachment; filename="Nexo-report-${periodStart.slice(0, 7)}.pdf"`,
52 + )
53 + return res.send(buffer)
54 + } catch (err) {
55 + return next(err)
56 + }
57 + },
58 + )
59 +
60 + export default router
api/tsconfig.json
1 1
{
2 2
  "compilerOptions": {
3 3
    "target": "ES2020",
4 4
    "module": "CommonJS",
5 5
    "moduleResolution": "node",
▸ 1 unchanged lines
7 7
    "strict": true,
8 8
    "esModuleInterop": true,
9 9
    "baseUrl": ".",
10 10
    "paths": {
11 11
      "@app/*": ["src/*"]
12 -
    }
12 +
    },
13 +
    "jsx": "react"
13 14
  }
14 15
}
15 16