react-doctor-cli-dev 1.0.7 → 1.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/backend/dist/db.js +33 -11
- package/backend/dist/index.js +29 -3
- package/backend/dist/routes/reports.js +106 -55
- package/backend/public/assets/index-BpODc0fS.css +1 -0
- package/backend/public/assets/index-zKyZPsv1.js +118 -0
- package/backend/public/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/backend/public/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/backend/public/assets/jetbrains-mono-cyrillic-500-normal-DJqRU3vO.woff +0 -0
- package/backend/public/assets/jetbrains-mono-cyrillic-500-normal-DmUKJPL_.woff2 +0 -0
- package/backend/public/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/backend/public/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/backend/public/assets/jetbrains-mono-greek-500-normal-D7SFKleX.woff +0 -0
- package/backend/public/assets/jetbrains-mono-greek-500-normal-JpySY46c.woff2 +0 -0
- package/backend/public/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/backend/public/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/backend/public/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
- package/backend/public/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
- package/backend/public/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/backend/public/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/backend/public/assets/jetbrains-mono-latin-ext-500-normal-Cut-4mMH.woff2 +0 -0
- package/backend/public/assets/jetbrains-mono-latin-ext-500-normal-ckzbgY84.woff +0 -0
- package/backend/public/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
- package/backend/public/assets/jetbrains-mono-vietnamese-500-normal-DNRqzVM1.woff +0 -0
- package/backend/public/assets/tajawal-arabic-300-normal-Bq0yWa0Z.woff +0 -0
- package/backend/public/assets/tajawal-arabic-300-normal-By07C9pa.woff2 +0 -0
- package/backend/public/assets/tajawal-arabic-400-normal-CyCXRvzh.woff2 +0 -0
- package/backend/public/assets/tajawal-arabic-400-normal-DCQxawbB.woff +0 -0
- package/backend/public/assets/tajawal-arabic-500-normal-BZ8ojJNu.woff2 +0 -0
- package/backend/public/assets/tajawal-arabic-500-normal-CbVEaYEW.woff +0 -0
- package/backend/public/assets/tajawal-arabic-700-normal-9L7Zusdl.woff +0 -0
- package/backend/public/assets/tajawal-arabic-700-normal-D2-eand5.woff2 +0 -0
- package/backend/public/assets/tajawal-latin-300-normal-C0-xR3ms.woff +0 -0
- package/backend/public/assets/tajawal-latin-300-normal-CeEKeOxZ.woff2 +0 -0
- package/backend/public/assets/tajawal-latin-400-normal-BVNSOH3d.woff2 +0 -0
- package/backend/public/assets/tajawal-latin-400-normal-BdYcZznU.woff +0 -0
- package/backend/public/assets/tajawal-latin-500-normal-CoYeBiSI.woff2 +0 -0
- package/backend/public/assets/tajawal-latin-500-normal-DU9v6xgj.woff +0 -0
- package/backend/public/assets/tajawal-latin-700-normal-BypgxfGb.woff2 +0 -0
- package/backend/public/assets/tajawal-latin-700-normal-CV3bxpHe.woff +0 -0
- package/backend/public/favicon.svg +1 -0
- package/backend/public/icons.svg +24 -0
- package/backend/public/index.html +254 -0
- package/backend/src/db.ts +42 -18
- package/backend/src/index.ts +31 -3
- package/backend/src/routes/reports.ts +141 -52
- package/cli/dist/commands/full.js +82 -48
- package/cli/src/commands/full.ts +161 -115
- package/dashboard/index.html +253 -0
- package/dashboard/package-lock.json +913 -0
- package/dashboard/package.json +19 -0
- package/dashboard/public/favicon.svg +1 -0
- package/dashboard/public/icons.svg +24 -0
- package/dashboard/src/api.js +107 -0
- package/dashboard/src/assets/hero.png +0 -0
- package/dashboard/src/assets/javascript.svg +1 -0
- package/dashboard/src/assets/vite.svg +1 -0
- package/dashboard/src/css/main.css +441 -0
- package/dashboard/src/data.js +202 -0
- package/dashboard/src/main.js +53 -0
- package/dashboard/src/pages.js +797 -0
- package/dashboard/src/router.js +77 -0
- package/dashboard/src/utils.js +163 -0
- package/dashboard/vite.config.js +19 -0
- package/data/screenshots/5--fcp.png +0 -0
- package/data/screenshots/5--fullLoad.png +0 -0
- package/data/screenshots/5-docs-fullLoad.png +0 -0
- package/data/screenshots/5-white-fullLoad.png +0 -0
- package/data/screenshots/6--fcp.png +0 -0
- package/data/screenshots/6--fullLoad.png +0 -0
- package/data/screenshots/6-docs-fullLoad.png +0 -0
- package/data/screenshots/6-white-fullLoad.png +0 -0
- package/package.json +4 -1
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="46" fill="none" viewBox="0 0 48 46"><path fill="#863bff" d="M25.946 44.938c-.664.845-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.287c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.497 0-3.578-1.842-3.578H1.237c-.92 0-1.456-1.04-.92-1.788L10.013.474c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.579 1.842 3.579h11.377c.943 0 1.473 1.088.89 1.83L25.947 44.94z" style="fill:#863bff;fill:color(display-p3 .5252 .23 1);fill-opacity:1"/><mask id="a" width="48" height="46" x="0" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#000" d="M25.842 44.938c-.664.844-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.183c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.498 0-3.579-1.842-3.579H1.133c-.92 0-1.456-1.04-.92-1.787L9.91.473c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.578 1.842 3.578h11.377c.943 0 1.473 1.088.89 1.832L25.843 44.94z" style="fill:#000;fill-opacity:1"/></mask><g mask="url(#a)"><g filter="url(#b)"><ellipse cx="5.508" cy="14.704" fill="#ede6ff" rx="5.508" ry="14.704" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -4.47 31.516)"/></g><g filter="url(#c)"><ellipse cx="10.399" cy="29.851" fill="#ede6ff" rx="10.399" ry="29.851" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -39.328 7.883)"/></g><g filter="url(#d)"><ellipse cx="5.508" cy="30.487" fill="#7e14ff" rx="5.508" ry="30.487" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -25.913 -14.639)scale(1 -1)"/></g><g filter="url(#e)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -32.644 -3.334)scale(1 -1)"/></g><g filter="url(#f)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -34.34 30.47)"/></g><g filter="url(#g)"><ellipse cx="14.072" cy="22.078" fill="#ede6ff" rx="14.072" ry="22.078" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="rotate(93.35 24.506 48.493)scale(-1 1)"/></g><g filter="url(#h)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#i)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#j)"><ellipse cx=".387" cy="8.972" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(39.51 .387 8.972)"/></g><g filter="url(#k)"><ellipse cx="47.523" cy="-6.092" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 47.523 -6.092)"/></g><g filter="url(#l)"><ellipse cx="41.412" cy="6.333" fill="#47bfff" rx="5.971" ry="9.665" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 41.412 6.333)"/></g><g filter="url(#m)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#n)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#o)"><ellipse cx="35.651" cy="29.907" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 35.651 29.907)"/></g><g filter="url(#p)"><ellipse cx="38.418" cy="32.4" fill="#47bfff" rx="5.971" ry="15.297" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 38.418 32.4)"/></g></g><defs><filter id="b" width="60.045" height="41.654" x="-19.77" y="16.149" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="c" width="90.34" height="51.437" x="-54.613" y="-7.533" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="d" width="79.355" height="29.4" x="-49.64" y="2.03" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="e" width="79.579" height="29.4" x="-45.045" y="20.029" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="f" width="79.579" height="29.4" x="-43.513" y="21.178" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="g" width="74.749" height="58.852" x="15.756" y="-17.901" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="h" width="61.377" height="25.362" x="23.548" y="2.284" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="i" width="61.377" height="25.362" x="23.548" y="2.284" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="j" width="56.045" height="63.649" x="-27.636" y="-22.853" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="k" width="54.814" height="64.646" x="20.116" y="-38.415" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="l" width="33.541" height="35.313" x="24.641" y="-11.323" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="m" width="54.814" height="64.646" x="-29.286" y="6.009" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="n" width="54.814" height="64.646" x="-29.286" y="6.009" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="o" width="54.814" height="64.646" x="8.244" y="-2.416" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="p" width="39.409" height="43.623" x="18.713" y="10.588" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter></defs></svg>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<symbol id="bluesky-icon" viewBox="0 0 16 17">
|
|
3
|
+
<g clip-path="url(#bluesky-clip)"><path fill="#08060d" d="M7.75 7.735c-.693-1.348-2.58-3.86-4.334-5.097-1.68-1.187-2.32-.981-2.74-.79C.188 2.065.1 2.812.1 3.251s.241 3.602.398 4.13c.52 1.744 2.367 2.333 4.07 2.145-2.495.37-4.71 1.278-1.805 4.512 3.196 3.309 4.38-.71 4.987-2.746.608 2.036 1.307 5.91 4.93 2.746 2.72-2.746.747-4.143-1.747-4.512 1.702.189 3.55-.4 4.07-2.145.156-.528.397-3.691.397-4.13s-.088-1.186-.575-1.406c-.42-.19-1.06-.395-2.741.79-1.755 1.24-3.64 3.752-4.334 5.099"/></g>
|
|
4
|
+
<defs><clipPath id="bluesky-clip"><path fill="#fff" d="M.1.85h15.3v15.3H.1z"/></clipPath></defs>
|
|
5
|
+
</symbol>
|
|
6
|
+
<symbol id="discord-icon" viewBox="0 0 20 19">
|
|
7
|
+
<path fill="#08060d" d="M16.224 3.768a14.5 14.5 0 0 0-3.67-1.153c-.158.286-.343.67-.47.976a13.5 13.5 0 0 0-4.067 0c-.128-.306-.317-.69-.476-.976A14.4 14.4 0 0 0 3.868 3.77C1.546 7.28.916 10.703 1.231 14.077a14.7 14.7 0 0 0 4.5 2.306q.545-.748.965-1.587a9.5 9.5 0 0 1-1.518-.74q.191-.14.372-.293c2.927 1.369 6.107 1.369 8.999 0q.183.152.372.294-.723.437-1.52.74.418.838.963 1.588a14.6 14.6 0 0 0 4.504-2.308c.37-3.911-.63-7.302-2.644-10.309m-9.13 8.234c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.894 0 1.614.82 1.599 1.82.001 1-.705 1.82-1.6 1.82m5.91 0c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.893 0 1.614.82 1.599 1.82 0 1-.706 1.82-1.6 1.82"/>
|
|
8
|
+
</symbol>
|
|
9
|
+
<symbol id="documentation-icon" viewBox="0 0 21 20">
|
|
10
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="m15.5 13.333 1.533 1.322c.645.555.967.833.967 1.178s-.322.623-.967 1.179L15.5 18.333m-3.333-5-1.534 1.322c-.644.555-.966.833-.966 1.178s.322.623.966 1.179l1.534 1.321"/>
|
|
11
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M17.167 10.836v-4.32c0-1.41 0-2.117-.224-2.68-.359-.906-1.118-1.621-2.08-1.96-.599-.21-1.349-.21-2.848-.21-2.623 0-3.935 0-4.983.369-1.684.591-3.013 1.842-3.641 3.428C3 6.449 3 7.684 3 10.154v2.122c0 2.558 0 3.838.706 4.726q.306.383.713.671c.76.536 1.79.64 3.581.66"/>
|
|
12
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M3 10a2.78 2.78 0 0 1 2.778-2.778c.555 0 1.209.097 1.748-.047.48-.129.854-.503.982-.982.145-.54.048-1.194.048-1.749a2.78 2.78 0 0 1 2.777-2.777"/>
|
|
13
|
+
</symbol>
|
|
14
|
+
<symbol id="github-icon" viewBox="0 0 19 19">
|
|
15
|
+
<path fill="#08060d" fill-rule="evenodd" d="M9.356 1.85C5.05 1.85 1.57 5.356 1.57 9.694a7.84 7.84 0 0 0 5.324 7.44c.387.079.528-.168.528-.376 0-.182-.013-.805-.013-1.454-2.165.467-2.616-.935-2.616-.935-.349-.91-.864-1.143-.864-1.143-.71-.48.051-.48.051-.48.787.051 1.2.805 1.2.805.695 1.194 1.817.857 2.268.649.064-.507.27-.857.49-1.052-1.728-.182-3.545-.857-3.545-3.87 0-.857.31-1.558.8-2.104-.078-.195-.349-1 .077-2.078 0 0 .657-.208 2.14.805a7.5 7.5 0 0 1 1.946-.26c.657 0 1.328.092 1.946.26 1.483-1.013 2.14-.805 2.14-.805.426 1.078.155 1.883.078 2.078.502.546.799 1.247.799 2.104 0 3.013-1.818 3.675-3.558 3.87.284.247.528.714.528 1.454 0 1.052-.012 1.896-.012 2.156 0 .208.142.455.528.377a7.84 7.84 0 0 0 5.324-7.441c.013-4.338-3.48-7.844-7.773-7.844" clip-rule="evenodd"/>
|
|
16
|
+
</symbol>
|
|
17
|
+
<symbol id="social-icon" viewBox="0 0 20 20">
|
|
18
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M12.5 6.667a4.167 4.167 0 1 0-8.334 0 4.167 4.167 0 0 0 8.334 0"/>
|
|
19
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M2.5 16.667a5.833 5.833 0 0 1 8.75-5.053m3.837.474.513 1.035c.07.144.257.282.414.309l.93.155c.596.1.736.536.307.965l-.723.73a.64.64 0 0 0-.152.531l.207.903c.164.715-.213.991-.84.618l-.872-.52a.63.63 0 0 0-.577 0l-.872.52c-.624.373-1.003.094-.84-.618l.207-.903a.64.64 0 0 0-.152-.532l-.723-.729c-.426-.43-.289-.864.306-.964l.93-.156a.64.64 0 0 0 .412-.31l.513-1.034c.28-.562.735-.562 1.012 0"/>
|
|
20
|
+
</symbol>
|
|
21
|
+
<symbol id="x-icon" viewBox="0 0 19 19">
|
|
22
|
+
<path fill="#08060d" fill-rule="evenodd" d="M1.893 1.98c.052.072 1.245 1.769 2.653 3.77l2.892 4.114c.183.261.333.48.333.486s-.068.089-.152.183l-.522.593-.765.867-3.597 4.087c-.375.426-.734.834-.798.905a1 1 0 0 0-.118.148c0 .01.236.017.664.017h.663l.729-.83c.4-.457.796-.906.879-.999a692 692 0 0 0 1.794-2.038c.034-.037.301-.34.594-.675l.551-.624.345-.392a7 7 0 0 1 .34-.374c.006 0 .93 1.306 2.052 2.903l2.084 2.965.045.063h2.275c1.87 0 2.273-.003 2.266-.021-.008-.02-1.098-1.572-3.894-5.547-2.013-2.862-2.28-3.246-2.273-3.266.008-.019.282-.332 2.085-2.38l2-2.274 1.567-1.782c.022-.028-.016-.03-.65-.03h-.674l-.3.342a871 871 0 0 1-1.782 2.025c-.067.075-.405.458-.75.852a100 100 0 0 1-.803.91c-.148.172-.299.344-.99 1.127-.304.343-.32.358-.345.327-.015-.019-.904-1.282-1.976-2.808L6.365 1.85H1.8zm1.782.91 8.078 11.294c.772 1.08 1.413 1.973 1.425 1.984.016.017.241.02 1.05.017l1.03-.004-2.694-3.766L7.796 5.75 5.722 2.852l-1.039-.004-1.039-.004z" clip-rule="evenodd"/>
|
|
23
|
+
</symbol>
|
|
24
|
+
</svg>
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>React Doctor — Dashboard</title>
|
|
7
|
+
<meta name="theme-color" content="#0B0C10" />
|
|
8
|
+
<!-- main.css is imported by src/main.js via Vite — no link tag needed here -->
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-zKyZPsv1.js"></script>
|
|
10
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BpODc0fS.css">
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
|
|
14
|
+
<!-- ── Sidebar ─────────────────────────────────────────────── -->
|
|
15
|
+
<aside id="sidebar">
|
|
16
|
+
<div class="sidebar-logo">
|
|
17
|
+
<div>
|
|
18
|
+
<div class="logo-text">React Doctor</div>
|
|
19
|
+
<div class="logo-sub">Performance Dashboard</div>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<div class="sidebar-section">Analysis</div>
|
|
24
|
+
|
|
25
|
+
<button class="nav-item" data-route="overview">
|
|
26
|
+
Overview
|
|
27
|
+
<span class="nav-badge" id="nav-score"></span>
|
|
28
|
+
</button>
|
|
29
|
+
<button class="nav-item" data-route="vitals">
|
|
30
|
+
Web Vitals
|
|
31
|
+
</button>
|
|
32
|
+
<button class="nav-item" data-route="issues">
|
|
33
|
+
Code Issues
|
|
34
|
+
<span class="nav-badge" id="nav-issues-count"></span>
|
|
35
|
+
</button>
|
|
36
|
+
<button class="nav-item" data-route="suggestions">
|
|
37
|
+
Suggestions
|
|
38
|
+
<span class="nav-badge" id="nav-sug-count"></span>
|
|
39
|
+
</button>
|
|
40
|
+
|
|
41
|
+
<div class="sidebar-section">Reports</div>
|
|
42
|
+
|
|
43
|
+
<button class="nav-item" data-route="history">
|
|
44
|
+
History
|
|
45
|
+
<span class="nav-badge" id="nav-hist-count"></span>
|
|
46
|
+
</button>
|
|
47
|
+
|
|
48
|
+
<div class="sidebar-footer">
|
|
49
|
+
<div class="project-pill">
|
|
50
|
+
<div class="status-dot"></div>
|
|
51
|
+
<div>
|
|
52
|
+
<div class="proj-name" id="sidebar-project">—</div>
|
|
53
|
+
<div class="proj-sub" id="sidebar-date">—</div>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
</aside>
|
|
58
|
+
|
|
59
|
+
<!-- ── Main ───────────────────────────────────────────────── -->
|
|
60
|
+
<div id="main">
|
|
61
|
+
|
|
62
|
+
<!-- Topbar -->
|
|
63
|
+
<div class="topbar">
|
|
64
|
+
<div>
|
|
65
|
+
<div class="topbar-title" id="topbar-title">Overview</div>
|
|
66
|
+
<div class="topbar-sub" id="topbar-sub">Performance summary</div>
|
|
67
|
+
</div>
|
|
68
|
+
<div class="topbar-right">
|
|
69
|
+
<div class="topbar-time" id="topbar-time"></div>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<!-- ══ PAGE: OVERVIEW ═══════════════════════════════════════ -->
|
|
74
|
+
<div class="page-content" id="page-overview">
|
|
75
|
+
|
|
76
|
+
<!-- Score + meta -->
|
|
77
|
+
<div class="card section-gap">
|
|
78
|
+
<div class="score-ring-wrap">
|
|
79
|
+
<div id="ov-score-ring"></div>
|
|
80
|
+
<div class="score-details">
|
|
81
|
+
<h2>Performance Score</h2>
|
|
82
|
+
<p id="ov-summary" style="margin-bottom:12px">Loading…</p>
|
|
83
|
+
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap">
|
|
84
|
+
<div id="ov-grade"></div>
|
|
85
|
+
<span style="font-size:.78rem;color:var(--text3)">Project: <strong id="ov-project" style="color:var(--text)">—</strong></span>
|
|
86
|
+
<span style="font-size:.78rem;color:var(--text3)">Analyzed: <span id="ov-analyzed" style="color:var(--text2)">—</span></span>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<!-- Vital stat tiles -->
|
|
93
|
+
<div class="grid-4 section-gap">
|
|
94
|
+
<div class="stat-tile" id="tile-lcp">
|
|
95
|
+
<div class="stat-label">Avg LCP</div>
|
|
96
|
+
<div class="stat-value">—</div>
|
|
97
|
+
<div class="stat-meta">Good < 2.5s</div>
|
|
98
|
+
</div>
|
|
99
|
+
<div class="stat-tile" id="tile-fcp">
|
|
100
|
+
<div class="stat-label">Avg FCP</div>
|
|
101
|
+
<div class="stat-value">—</div>
|
|
102
|
+
<div class="stat-meta">Good < 1.8s</div>
|
|
103
|
+
</div>
|
|
104
|
+
<div class="stat-tile" id="tile-cls">
|
|
105
|
+
<div class="stat-label">Avg CLS</div>
|
|
106
|
+
<div class="stat-value">—</div>
|
|
107
|
+
<div class="stat-meta">Good < 0.1</div>
|
|
108
|
+
</div>
|
|
109
|
+
<div class="stat-tile" id="tile-issues">
|
|
110
|
+
<div class="stat-label">Total Issues</div>
|
|
111
|
+
<div class="stat-value">—</div>
|
|
112
|
+
<div class="stat-meta">Static analysis</div>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
<!-- Routes table -->
|
|
117
|
+
<div class="card section-gap">
|
|
118
|
+
<div class="card-title"> Routes Profiled</div>
|
|
119
|
+
<div id="ov-routes"></div>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
<!-- Top suggestions -->
|
|
123
|
+
<div class="card">
|
|
124
|
+
<div class="card-title" style="margin-bottom:14px">
|
|
125
|
+
Top Suggestions
|
|
126
|
+
<button onclick="window.location.hash='suggestions'" style="margin-left:auto;background:none;border:1px solid var(--border);color:var(--text2);border-radius:var(--radius-sm);padding:3px 12px;cursor:pointer;font-size:.72rem">View all →</button>
|
|
127
|
+
</div>
|
|
128
|
+
<div id="ov-suggestions"></div>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
</div><!-- /page-overview -->
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
<!-- ══ PAGE: VITALS ══════════════════════════════════════════ -->
|
|
135
|
+
<div class="page-content" id="page-vitals">
|
|
136
|
+
|
|
137
|
+
<!-- Route selector -->
|
|
138
|
+
<div class="route-tabs" id="vitals-tabs"></div>
|
|
139
|
+
|
|
140
|
+
<div class="grid-2 section-gap" style="grid-template-columns:1fr 320px">
|
|
141
|
+
|
|
142
|
+
<!-- Bars -->
|
|
143
|
+
<div class="card">
|
|
144
|
+
<div class="card-title">
|
|
145
|
+
Core Web Vitals
|
|
146
|
+
<div id="vitals-meta" style="display:flex;gap:8px;flex-wrap:wrap;margin-left:8px"></div>
|
|
147
|
+
</div>
|
|
148
|
+
<div id="vitals-bars"></div>
|
|
149
|
+
<div style="margin-top:16px;display:flex;align-items:center;gap:10px">
|
|
150
|
+
<span style="font-size:.75rem;color:var(--text3)">Performance score:</span>
|
|
151
|
+
<div id="vitals-score"></div>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
<!-- Re-renders -->
|
|
156
|
+
<div class="card">
|
|
157
|
+
<div class="card-title">Component Re-renders</div>
|
|
158
|
+
<div id="vitals-rerenders"></div>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
<!-- Chart: all routes comparison -->
|
|
163
|
+
<div class="card section-gap">
|
|
164
|
+
<div class="card-title">All Routes — LCP / FCP / TTFB Comparison</div>
|
|
165
|
+
<div style="height:240px;position:relative">
|
|
166
|
+
<canvas id="vitals-chart"></canvas>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
<!-- Errors -->
|
|
171
|
+
<div class="card section-gap">
|
|
172
|
+
<div class="card-title">JS Errors & Console Warnings</div>
|
|
173
|
+
<div id="vitals-errors"></div>
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<!-- Screenshots -->
|
|
177
|
+
<div class="card">
|
|
178
|
+
<div class="card-title">Screenshots</div>
|
|
179
|
+
<div class="filmstrip" id="vitals-filmstrip"></div>
|
|
180
|
+
</div>
|
|
181
|
+
|
|
182
|
+
</div><!-- /page-vitals -->
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
<!-- ══ PAGE: ISSUES ══════════════════════════════════════════ -->
|
|
186
|
+
<div class="page-content" id="page-issues">
|
|
187
|
+
|
|
188
|
+
<div class="filter-bar" id="issues-filters">
|
|
189
|
+
<button class="filter-btn active" data-filter="all">All</button>
|
|
190
|
+
<button class="filter-btn fc" data-filter="critical">🔴 Critical</button>
|
|
191
|
+
<button class="filter-btn fw" data-filter="warning">🟠 Warning</button>
|
|
192
|
+
<button class="filter-btn fi" data-filter="info">🔵 Info</button>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
<div class="card">
|
|
196
|
+
<div id="issues-table"></div>
|
|
197
|
+
<div id="issues-pagination"></div>
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
</div><!-- /page-issues -->
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
<!-- ══ PAGE: SUGGESTIONS ═════════════════════════════════════ -->
|
|
204
|
+
<div class="page-content" id="page-suggestions">
|
|
205
|
+
|
|
206
|
+
<div style="display:flex;align-items:center;gap:12px;margin-bottom:16px;flex-wrap:wrap">
|
|
207
|
+
<div class="filter-bar" id="sug-filters" style="margin-bottom:0">
|
|
208
|
+
<button class="filter-btn active" data-filter="all">All</button>
|
|
209
|
+
<button class="filter-btn fc" data-filter="critical">🔴 Critical</button>
|
|
210
|
+
<button class="filter-btn fw" data-filter="warning">🟠 Warning</button>
|
|
211
|
+
<button class="filter-btn fi" data-filter="info">🔵 Info</button>
|
|
212
|
+
</div>
|
|
213
|
+
<div id="sug-counts" style="display:flex;gap:8px;margin-left:auto;flex-wrap:wrap"></div>
|
|
214
|
+
</div>
|
|
215
|
+
|
|
216
|
+
<div id="sug-list"></div>
|
|
217
|
+
<div id="sug-pagination"></div>
|
|
218
|
+
|
|
219
|
+
</div><!-- /page-suggestions -->
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
<!-- ══ PAGE: HISTORY ══════════════════════════════════════════ -->
|
|
223
|
+
<div class="page-content" id="page-history">
|
|
224
|
+
|
|
225
|
+
<!-- Trend chart -->
|
|
226
|
+
<div class="card section-gap">
|
|
227
|
+
<div class="card-title">Score Trend — my-react-app</div>
|
|
228
|
+
<div style="height:200px;position:relative">
|
|
229
|
+
<canvas id="hist-chart"></canvas>
|
|
230
|
+
</div>
|
|
231
|
+
</div>
|
|
232
|
+
|
|
233
|
+
<!-- Table -->
|
|
234
|
+
<div class="card">
|
|
235
|
+
<div class="card-title"> All Runs</div>
|
|
236
|
+
<div id="hist-table"></div>
|
|
237
|
+
<div id="hist-pagination"></div>
|
|
238
|
+
</div>
|
|
239
|
+
|
|
240
|
+
</div><!-- /page-history -->
|
|
241
|
+
|
|
242
|
+
</div><!-- /main -->
|
|
243
|
+
|
|
244
|
+
<!-- Lightbox -->
|
|
245
|
+
<div id="lightbox">
|
|
246
|
+
<button id="lb-close">✕</button>
|
|
247
|
+
<img src="" alt="screenshot" />
|
|
248
|
+
<div id="lb-caption"></div>
|
|
249
|
+
</div>
|
|
250
|
+
|
|
251
|
+
<!-- Scripts -->
|
|
252
|
+
|
|
253
|
+
</body>
|
|
254
|
+
</html>
|
package/backend/src/db.ts
CHANGED
|
@@ -1,20 +1,15 @@
|
|
|
1
|
-
import Database from
|
|
2
|
-
import path from
|
|
3
|
-
import fs from
|
|
4
|
-
import dotenv from "dotenv";
|
|
5
|
-
|
|
6
|
-
dotenv.config({ path: path.join(__dirname, "..", ".env") });
|
|
7
|
-
|
|
8
|
-
const PACKAGE_ROOT = path.resolve(__dirname, "..", "..");
|
|
9
|
-
const DEFAULT_DB = path.join(PACKAGE_ROOT, "backend", "data", "reports.db");
|
|
10
|
-
const dbPath = process.env.DB_PATH || DEFAULT_DB;
|
|
11
|
-
|
|
12
|
-
fs.mkdirSync(path.dirname(dbPath), { recursive: true });
|
|
13
|
-
fs.mkdirSync(path.join(path.dirname(dbPath), "screenshots"), { recursive: true });
|
|
1
|
+
import Database from 'better-sqlite3';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs';
|
|
14
4
|
|
|
5
|
+
// ── Initialize database ──────────────────────────────────────
|
|
6
|
+
const dbPath = process.env.DB_PATH || path.join(__dirname, '../../reports.db');
|
|
15
7
|
const db = new Database(dbPath);
|
|
16
8
|
|
|
17
|
-
//
|
|
9
|
+
// ── Enable foreign keys ──────────────────────────────────────
|
|
10
|
+
db.pragma('foreign_keys = ON');
|
|
11
|
+
|
|
12
|
+
// ── Create reports table if it doesn't exist ──────────────
|
|
18
13
|
db.exec(`
|
|
19
14
|
CREATE TABLE IF NOT EXISTS reports (
|
|
20
15
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
@@ -22,12 +17,41 @@ db.exec(`
|
|
|
22
17
|
score INTEGER NOT NULL,
|
|
23
18
|
grade TEXT NOT NULL,
|
|
24
19
|
analyzed_at TEXT NOT NULL,
|
|
25
|
-
created_at TEXT
|
|
20
|
+
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
26
21
|
static_json TEXT NOT NULL,
|
|
27
22
|
runtime_json TEXT NOT NULL,
|
|
28
23
|
suggestions TEXT NOT NULL
|
|
29
|
-
)
|
|
24
|
+
)
|
|
30
25
|
`);
|
|
31
26
|
|
|
32
|
-
|
|
33
|
-
|
|
27
|
+
// ── Create screenshots table if it doesn't exist ───────────
|
|
28
|
+
db.exec(`
|
|
29
|
+
CREATE TABLE IF NOT EXISTS screenshots (
|
|
30
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
31
|
+
report_id INTEGER NOT NULL,
|
|
32
|
+
route TEXT NOT NULL,
|
|
33
|
+
label TEXT NOT NULL,
|
|
34
|
+
taken_at INTEGER NOT NULL,
|
|
35
|
+
data_url TEXT NOT NULL,
|
|
36
|
+
FOREIGN KEY (report_id) REFERENCES reports(id) ON DELETE CASCADE
|
|
37
|
+
)
|
|
38
|
+
`);
|
|
39
|
+
|
|
40
|
+
// ── Create indexes for performance ──────────────────────────
|
|
41
|
+
db.exec(`
|
|
42
|
+
CREATE INDEX IF NOT EXISTS idx_reports_project ON reports(project);
|
|
43
|
+
CREATE INDEX IF NOT EXISTS idx_reports_created_at ON reports(created_at DESC);
|
|
44
|
+
CREATE INDEX IF NOT EXISTS idx_screenshots_report_id ON screenshots(report_id);
|
|
45
|
+
`);
|
|
46
|
+
|
|
47
|
+
// ── Define screenshots directory ────────────────────────────
|
|
48
|
+
const screenshotsDir = path.join(__dirname, '../../data/screenshots');
|
|
49
|
+
|
|
50
|
+
// ── Ensure screenshots directory exists ──────────────────────
|
|
51
|
+
if (!fs.existsSync(screenshotsDir)) {
|
|
52
|
+
fs.mkdirSync(screenshotsDir, { recursive: true });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ── Export both the database and the screenshots dir ──────
|
|
56
|
+
export default db;
|
|
57
|
+
export { screenshotsDir };
|
package/backend/src/index.ts
CHANGED
|
@@ -16,24 +16,52 @@ const API_KEY = process.env.API_KEY || "react-doctor-secret-key-change-this";
|
|
|
16
16
|
// Make API_KEY available globally so auth middleware can use it
|
|
17
17
|
process.env.API_KEY = API_KEY;
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
// ── Security headers ────────────────────────────────────────────
|
|
20
|
+
// CSP disabled so the bundled dashboard JS/CSS can load without
|
|
21
|
+
// Express blocking inline scripts or the Vite-built bundle.
|
|
22
|
+
app.use(helmet({ contentSecurityPolicy: false }));
|
|
20
23
|
app.use(cors());
|
|
21
24
|
app.use(express.json({ limit: '50mb' }));
|
|
25
|
+
|
|
26
|
+
// ── API routes ───────────────────────────────────────────────────
|
|
22
27
|
app.use('/api/reports', reportRoutes);
|
|
23
28
|
|
|
24
29
|
app.get('/health', (_req: Request, res: Response) => {
|
|
25
30
|
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
|
26
31
|
});
|
|
27
32
|
|
|
28
|
-
|
|
29
|
-
|
|
33
|
+
// ── Serve the built dashboard (frontend/ built via `npm run build`
|
|
34
|
+
// into backend/public/) ───────────────────────────────────────
|
|
35
|
+
const publicDir = path.join(__dirname, '..', 'public');
|
|
36
|
+
app.use(express.static(publicDir));
|
|
37
|
+
|
|
38
|
+
// ── SPA fallback ─────────────────────────────────────────────────
|
|
39
|
+
// Any route not matched above (e.g. /report/7, /history, /overview)
|
|
40
|
+
// gets index.html so the dashboard's client-side router can take
|
|
41
|
+
// over and render the right page based on the URL.
|
|
42
|
+
//
|
|
43
|
+
// IMPORTANT: this must come AFTER /api/reports and /health so API
|
|
44
|
+
// calls are never accidentally swallowed by this fallback — but
|
|
45
|
+
// BEFORE the 404 handler below, since it's the last real route.
|
|
46
|
+
app.get('/{*path}', (req: Request, res: Response) => {
|
|
47
|
+
const indexPath = path.join(publicDir, 'index.html');
|
|
48
|
+
res.sendFile(indexPath, (err) => {
|
|
49
|
+
if (err) {
|
|
50
|
+
res.status(404).json({
|
|
51
|
+
message: 'Dashboard not built yet.',
|
|
52
|
+
hint: 'Run "npm run build" inside the frontend/ folder, then restart the backend.',
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
});
|
|
30
56
|
});
|
|
31
57
|
|
|
58
|
+
// ── Error handler ────────────────────────────────────────────────
|
|
32
59
|
app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {
|
|
33
60
|
console.error(err.stack);
|
|
34
61
|
res.status(500).json({ message: 'Internal Server Error' });
|
|
35
62
|
});
|
|
36
63
|
|
|
64
|
+
// ── Start ─────────────────────────────────────────────────────────
|
|
37
65
|
app.listen(PORT, () => {
|
|
38
66
|
console.log(`🩺 React Doctor backend running on http://localhost:${PORT}`);
|
|
39
67
|
});
|