Changeset a762898 for lan-frontend


Ignore:
Timestamp:
01/26/26 16:19:33 (5 months ago)
Author:
istevanoska <ilinastevanoska@…>
Branches:
master
Children:
ba17441
Parents:
e4c61dd
Message:

Added visualizations

Location:
lan-frontend
Files:
4 added
4 edited

Legend:

Unmodified
Added
Removed
  • lan-frontend/package-lock.json

    re4c61dd ra762898  
    2020        "@types/react-dom": "^19.2.3",
    2121        "@vitejs/plugin-react": "^5.1.1",
     22        "autoprefixer": "^10.4.23",
    2223        "eslint": "^9.39.1",
    2324        "eslint-plugin-react-hooks": "^7.0.1",
    2425        "eslint-plugin-react-refresh": "^0.4.24",
    2526        "globals": "^16.5.0",
     27        "postcss": "^8.5.6",
     28        "tailwindcss": "^3.4.17",
    2629        "vite": "^7.2.4"
     30      }
     31    },
     32    "node_modules/@alloc/quick-lru": {
     33      "version": "5.2.0",
     34      "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
     35      "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
     36      "dev": true,
     37      "license": "MIT",
     38      "engines": {
     39        "node": ">=10"
     40      },
     41      "funding": {
     42        "url": "https://github.com/sponsors/sindresorhus"
    2743      }
    2844    },
     
    10161032      "license": "MIT"
    10171033    },
     1034    "node_modules/@nodelib/fs.scandir": {
     1035      "version": "2.1.5",
     1036      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
     1037      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
     1038      "dev": true,
     1039      "license": "MIT",
     1040      "dependencies": {
     1041        "@nodelib/fs.stat": "2.0.5",
     1042        "run-parallel": "^1.1.9"
     1043      },
     1044      "engines": {
     1045        "node": ">= 8"
     1046      }
     1047    },
     1048    "node_modules/@nodelib/fs.stat": {
     1049      "version": "2.0.5",
     1050      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
     1051      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
     1052      "dev": true,
     1053      "license": "MIT",
     1054      "engines": {
     1055        "node": ">= 8"
     1056      }
     1057    },
     1058    "node_modules/@nodelib/fs.walk": {
     1059      "version": "1.2.8",
     1060      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
     1061      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
     1062      "dev": true,
     1063      "license": "MIT",
     1064      "dependencies": {
     1065        "@nodelib/fs.scandir": "2.1.5",
     1066        "fastq": "^1.6.0"
     1067      },
     1068      "engines": {
     1069        "node": ">= 8"
     1070      }
     1071    },
    10181072    "node_modules/@react-oauth/google": {
    10191073      "version": "0.13.4",
     
    16141668      }
    16151669    },
     1670    "node_modules/any-promise": {
     1671      "version": "1.3.0",
     1672      "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
     1673      "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
     1674      "dev": true,
     1675      "license": "MIT"
     1676    },
     1677    "node_modules/anymatch": {
     1678      "version": "3.1.3",
     1679      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
     1680      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
     1681      "dev": true,
     1682      "license": "ISC",
     1683      "dependencies": {
     1684        "normalize-path": "^3.0.0",
     1685        "picomatch": "^2.0.4"
     1686      },
     1687      "engines": {
     1688        "node": ">= 8"
     1689      }
     1690    },
     1691    "node_modules/anymatch/node_modules/picomatch": {
     1692      "version": "2.3.1",
     1693      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
     1694      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
     1695      "dev": true,
     1696      "license": "MIT",
     1697      "engines": {
     1698        "node": ">=8.6"
     1699      },
     1700      "funding": {
     1701        "url": "https://github.com/sponsors/jonschlinkert"
     1702      }
     1703    },
     1704    "node_modules/arg": {
     1705      "version": "5.0.2",
     1706      "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
     1707      "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
     1708      "dev": true,
     1709      "license": "MIT"
     1710    },
    16161711    "node_modules/argparse": {
    16171712      "version": "2.0.1",
     
    16211716      "license": "Python-2.0"
    16221717    },
     1718    "node_modules/autoprefixer": {
     1719      "version": "10.4.23",
     1720      "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz",
     1721      "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==",
     1722      "dev": true,
     1723      "funding": [
     1724        {
     1725          "type": "opencollective",
     1726          "url": "https://opencollective.com/postcss/"
     1727        },
     1728        {
     1729          "type": "tidelift",
     1730          "url": "https://tidelift.com/funding/github/npm/autoprefixer"
     1731        },
     1732        {
     1733          "type": "github",
     1734          "url": "https://github.com/sponsors/ai"
     1735        }
     1736      ],
     1737      "license": "MIT",
     1738      "dependencies": {
     1739        "browserslist": "^4.28.1",
     1740        "caniuse-lite": "^1.0.30001760",
     1741        "fraction.js": "^5.3.4",
     1742        "picocolors": "^1.1.1",
     1743        "postcss-value-parser": "^4.2.0"
     1744      },
     1745      "bin": {
     1746        "autoprefixer": "bin/autoprefixer"
     1747      },
     1748      "engines": {
     1749        "node": "^10 || ^12 || >=14"
     1750      },
     1751      "peerDependencies": {
     1752        "postcss": "^8.1.0"
     1753      }
     1754    },
    16231755    "node_modules/balanced-match": {
    16241756      "version": "1.0.2",
     
    16381770      }
    16391771    },
     1772    "node_modules/binary-extensions": {
     1773      "version": "2.3.0",
     1774      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
     1775      "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
     1776      "dev": true,
     1777      "license": "MIT",
     1778      "engines": {
     1779        "node": ">=8"
     1780      },
     1781      "funding": {
     1782        "url": "https://github.com/sponsors/sindresorhus"
     1783      }
     1784    },
    16401785    "node_modules/brace-expansion": {
    16411786      "version": "1.1.12",
     
    16471792        "balanced-match": "^1.0.0",
    16481793        "concat-map": "0.0.1"
     1794      }
     1795    },
     1796    "node_modules/braces": {
     1797      "version": "3.0.3",
     1798      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
     1799      "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
     1800      "dev": true,
     1801      "license": "MIT",
     1802      "dependencies": {
     1803        "fill-range": "^7.1.1"
     1804      },
     1805      "engines": {
     1806        "node": ">=8"
    16491807      }
    16501808    },
     
    16931851      }
    16941852    },
     1853    "node_modules/camelcase-css": {
     1854      "version": "2.0.1",
     1855      "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
     1856      "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
     1857      "dev": true,
     1858      "license": "MIT",
     1859      "engines": {
     1860        "node": ">= 6"
     1861      }
     1862    },
    16951863    "node_modules/caniuse-lite": {
    16961864      "version": "1.0.30001760",
     
    17431911      }
    17441912    },
     1913    "node_modules/chokidar": {
     1914      "version": "3.6.0",
     1915      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
     1916      "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
     1917      "dev": true,
     1918      "license": "MIT",
     1919      "dependencies": {
     1920        "anymatch": "~3.1.2",
     1921        "braces": "~3.0.2",
     1922        "glob-parent": "~5.1.2",
     1923        "is-binary-path": "~2.1.0",
     1924        "is-glob": "~4.0.1",
     1925        "normalize-path": "~3.0.0",
     1926        "readdirp": "~3.6.0"
     1927      },
     1928      "engines": {
     1929        "node": ">= 8.10.0"
     1930      },
     1931      "funding": {
     1932        "url": "https://paulmillr.com/funding/"
     1933      },
     1934      "optionalDependencies": {
     1935        "fsevents": "~2.3.2"
     1936      }
     1937    },
     1938    "node_modules/chokidar/node_modules/glob-parent": {
     1939      "version": "5.1.2",
     1940      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
     1941      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
     1942      "dev": true,
     1943      "license": "ISC",
     1944      "dependencies": {
     1945        "is-glob": "^4.0.1"
     1946      },
     1947      "engines": {
     1948        "node": ">= 6"
     1949      }
     1950    },
    17451951    "node_modules/clsx": {
    17461952      "version": "2.1.1",
     
    17721978      "license": "MIT"
    17731979    },
     1980    "node_modules/commander": {
     1981      "version": "4.1.1",
     1982      "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
     1983      "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
     1984      "dev": true,
     1985      "license": "MIT",
     1986      "engines": {
     1987        "node": ">= 6"
     1988      }
     1989    },
    17741990    "node_modules/concat-map": {
    17751991      "version": "0.0.1",
     
    17992015      "engines": {
    18002016        "node": ">= 8"
     2017      }
     2018    },
     2019    "node_modules/cssesc": {
     2020      "version": "3.0.0",
     2021      "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
     2022      "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
     2023      "dev": true,
     2024      "license": "MIT",
     2025      "bin": {
     2026        "cssesc": "bin/cssesc"
     2027      },
     2028      "engines": {
     2029        "node": ">=4"
    18012030      }
    18022031    },
     
    19572186      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
    19582187      "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
     2188      "dev": true,
     2189      "license": "MIT"
     2190    },
     2191    "node_modules/didyoumean": {
     2192      "version": "1.2.2",
     2193      "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
     2194      "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
     2195      "dev": true,
     2196      "license": "Apache-2.0"
     2197    },
     2198    "node_modules/dlv": {
     2199      "version": "1.1.3",
     2200      "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
     2201      "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
    19592202      "dev": true,
    19602203      "license": "MIT"
     
    22392482      "license": "MIT"
    22402483    },
     2484    "node_modules/fast-glob": {
     2485      "version": "3.3.3",
     2486      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
     2487      "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
     2488      "dev": true,
     2489      "license": "MIT",
     2490      "dependencies": {
     2491        "@nodelib/fs.stat": "^2.0.2",
     2492        "@nodelib/fs.walk": "^1.2.3",
     2493        "glob-parent": "^5.1.2",
     2494        "merge2": "^1.3.0",
     2495        "micromatch": "^4.0.8"
     2496      },
     2497      "engines": {
     2498        "node": ">=8.6.0"
     2499      }
     2500    },
     2501    "node_modules/fast-glob/node_modules/glob-parent": {
     2502      "version": "5.1.2",
     2503      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
     2504      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
     2505      "dev": true,
     2506      "license": "ISC",
     2507      "dependencies": {
     2508        "is-glob": "^4.0.1"
     2509      },
     2510      "engines": {
     2511        "node": ">= 6"
     2512      }
     2513    },
    22412514    "node_modules/fast-json-stable-stringify": {
    22422515      "version": "2.1.0",
     
    22532526      "license": "MIT"
    22542527    },
     2528    "node_modules/fastq": {
     2529      "version": "1.20.1",
     2530      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
     2531      "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
     2532      "dev": true,
     2533      "license": "ISC",
     2534      "dependencies": {
     2535        "reusify": "^1.0.4"
     2536      }
     2537    },
    22552538    "node_modules/fdir": {
    22562539      "version": "6.5.0",
     
    22842567      }
    22852568    },
     2569    "node_modules/fill-range": {
     2570      "version": "7.1.1",
     2571      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
     2572      "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
     2573      "dev": true,
     2574      "license": "MIT",
     2575      "dependencies": {
     2576        "to-regex-range": "^5.0.1"
     2577      },
     2578      "engines": {
     2579        "node": ">=8"
     2580      }
     2581    },
    22862582    "node_modules/find-up": {
    22872583      "version": "5.0.0",
     
    23222618      "license": "ISC"
    23232619    },
     2620    "node_modules/fraction.js": {
     2621      "version": "5.3.4",
     2622      "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
     2623      "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
     2624      "dev": true,
     2625      "license": "MIT",
     2626      "engines": {
     2627        "node": "*"
     2628      },
     2629      "funding": {
     2630        "type": "github",
     2631        "url": "https://github.com/sponsors/rawify"
     2632      }
     2633    },
    23242634    "node_modules/fsevents": {
    23252635      "version": "2.3.3",
     
    23372647      }
    23382648    },
     2649    "node_modules/function-bind": {
     2650      "version": "1.1.2",
     2651      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
     2652      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
     2653      "dev": true,
     2654      "license": "MIT",
     2655      "funding": {
     2656        "url": "https://github.com/sponsors/ljharb"
     2657      }
     2658    },
    23392659    "node_modules/gensync": {
    23402660      "version": "1.0.0-beta.2",
     
    23812701      "engines": {
    23822702        "node": ">=8"
     2703      }
     2704    },
     2705    "node_modules/hasown": {
     2706      "version": "2.0.2",
     2707      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
     2708      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
     2709      "dev": true,
     2710      "license": "MIT",
     2711      "dependencies": {
     2712        "function-bind": "^1.1.2"
     2713      },
     2714      "engines": {
     2715        "node": ">= 0.4"
    23832716      }
    23842717    },
     
    24562789      }
    24572790    },
     2791    "node_modules/is-binary-path": {
     2792      "version": "2.1.0",
     2793      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
     2794      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
     2795      "dev": true,
     2796      "license": "MIT",
     2797      "dependencies": {
     2798        "binary-extensions": "^2.0.0"
     2799      },
     2800      "engines": {
     2801        "node": ">=8"
     2802      }
     2803    },
     2804    "node_modules/is-core-module": {
     2805      "version": "2.16.1",
     2806      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
     2807      "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
     2808      "dev": true,
     2809      "license": "MIT",
     2810      "dependencies": {
     2811        "hasown": "^2.0.2"
     2812      },
     2813      "engines": {
     2814        "node": ">= 0.4"
     2815      },
     2816      "funding": {
     2817        "url": "https://github.com/sponsors/ljharb"
     2818      }
     2819    },
    24582820    "node_modules/is-extglob": {
    24592821      "version": "2.1.1",
     
    24792841      }
    24802842    },
     2843    "node_modules/is-number": {
     2844      "version": "7.0.0",
     2845      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
     2846      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
     2847      "dev": true,
     2848      "license": "MIT",
     2849      "engines": {
     2850        "node": ">=0.12.0"
     2851      }
     2852    },
    24812853    "node_modules/isexe": {
    24822854      "version": "2.0.0",
     
    24862858      "license": "ISC"
    24872859    },
     2860    "node_modules/jiti": {
     2861      "version": "1.21.7",
     2862      "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
     2863      "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
     2864      "dev": true,
     2865      "license": "MIT",
     2866      "bin": {
     2867        "jiti": "bin/jiti.js"
     2868      }
     2869    },
    24882870    "node_modules/js-tokens": {
    24892871      "version": "4.0.0",
     
    25772959      }
    25782960    },
     2961    "node_modules/lilconfig": {
     2962      "version": "3.1.3",
     2963      "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
     2964      "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
     2965      "dev": true,
     2966      "license": "MIT",
     2967      "engines": {
     2968        "node": ">=14"
     2969      },
     2970      "funding": {
     2971        "url": "https://github.com/sponsors/antonk52"
     2972      }
     2973    },
     2974    "node_modules/lines-and-columns": {
     2975      "version": "1.2.4",
     2976      "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
     2977      "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
     2978      "dev": true,
     2979      "license": "MIT"
     2980    },
    25792981    "node_modules/locate-path": {
    25802982      "version": "6.0.0",
     
    26103012      }
    26113013    },
     3014    "node_modules/merge2": {
     3015      "version": "1.4.1",
     3016      "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
     3017      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
     3018      "dev": true,
     3019      "license": "MIT",
     3020      "engines": {
     3021        "node": ">= 8"
     3022      }
     3023    },
     3024    "node_modules/micromatch": {
     3025      "version": "4.0.8",
     3026      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
     3027      "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
     3028      "dev": true,
     3029      "license": "MIT",
     3030      "dependencies": {
     3031        "braces": "^3.0.3",
     3032        "picomatch": "^2.3.1"
     3033      },
     3034      "engines": {
     3035        "node": ">=8.6"
     3036      }
     3037    },
     3038    "node_modules/micromatch/node_modules/picomatch": {
     3039      "version": "2.3.1",
     3040      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
     3041      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
     3042      "dev": true,
     3043      "license": "MIT",
     3044      "engines": {
     3045        "node": ">=8.6"
     3046      },
     3047      "funding": {
     3048        "url": "https://github.com/sponsors/jonschlinkert"
     3049      }
     3050    },
    26123051    "node_modules/minimatch": {
    26133052      "version": "3.1.2",
     
    26293068      "dev": true,
    26303069      "license": "MIT"
     3070    },
     3071    "node_modules/mz": {
     3072      "version": "2.7.0",
     3073      "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
     3074      "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
     3075      "dev": true,
     3076      "license": "MIT",
     3077      "dependencies": {
     3078        "any-promise": "^1.0.0",
     3079        "object-assign": "^4.0.1",
     3080        "thenify-all": "^1.0.0"
     3081      }
    26313082    },
    26323083    "node_modules/nanoid": {
     
    26623113      "dev": true,
    26633114      "license": "MIT"
     3115    },
     3116    "node_modules/normalize-path": {
     3117      "version": "3.0.0",
     3118      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
     3119      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
     3120      "dev": true,
     3121      "license": "MIT",
     3122      "engines": {
     3123        "node": ">=0.10.0"
     3124      }
     3125    },
     3126    "node_modules/object-assign": {
     3127      "version": "4.1.1",
     3128      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
     3129      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
     3130      "dev": true,
     3131      "license": "MIT",
     3132      "engines": {
     3133        "node": ">=0.10.0"
     3134      }
     3135    },
     3136    "node_modules/object-hash": {
     3137      "version": "3.0.0",
     3138      "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
     3139      "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
     3140      "dev": true,
     3141      "license": "MIT",
     3142      "engines": {
     3143        "node": ">= 6"
     3144      }
    26643145    },
    26653146    "node_modules/optionator": {
     
    27463227      }
    27473228    },
     3229    "node_modules/path-parse": {
     3230      "version": "1.0.7",
     3231      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
     3232      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
     3233      "dev": true,
     3234      "license": "MIT"
     3235    },
    27483236    "node_modules/picocolors": {
    27493237      "version": "1.1.1",
     
    27643252      "funding": {
    27653253        "url": "https://github.com/sponsors/jonschlinkert"
     3254      }
     3255    },
     3256    "node_modules/pify": {
     3257      "version": "2.3.0",
     3258      "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
     3259      "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
     3260      "dev": true,
     3261      "license": "MIT",
     3262      "engines": {
     3263        "node": ">=0.10.0"
     3264      }
     3265    },
     3266    "node_modules/pirates": {
     3267      "version": "4.0.7",
     3268      "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
     3269      "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==",
     3270      "dev": true,
     3271      "license": "MIT",
     3272      "engines": {
     3273        "node": ">= 6"
    27663274      }
    27673275    },
     
    27953303      }
    27963304    },
     3305    "node_modules/postcss-import": {
     3306      "version": "15.1.0",
     3307      "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
     3308      "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
     3309      "dev": true,
     3310      "license": "MIT",
     3311      "dependencies": {
     3312        "postcss-value-parser": "^4.0.0",
     3313        "read-cache": "^1.0.0",
     3314        "resolve": "^1.1.7"
     3315      },
     3316      "engines": {
     3317        "node": ">=14.0.0"
     3318      },
     3319      "peerDependencies": {
     3320        "postcss": "^8.0.0"
     3321      }
     3322    },
     3323    "node_modules/postcss-js": {
     3324      "version": "4.1.0",
     3325      "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz",
     3326      "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==",
     3327      "dev": true,
     3328      "funding": [
     3329        {
     3330          "type": "opencollective",
     3331          "url": "https://opencollective.com/postcss/"
     3332        },
     3333        {
     3334          "type": "github",
     3335          "url": "https://github.com/sponsors/ai"
     3336        }
     3337      ],
     3338      "license": "MIT",
     3339      "dependencies": {
     3340        "camelcase-css": "^2.0.1"
     3341      },
     3342      "engines": {
     3343        "node": "^12 || ^14 || >= 16"
     3344      },
     3345      "peerDependencies": {
     3346        "postcss": "^8.4.21"
     3347      }
     3348    },
     3349    "node_modules/postcss-load-config": {
     3350      "version": "4.0.2",
     3351      "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
     3352      "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
     3353      "dev": true,
     3354      "funding": [
     3355        {
     3356          "type": "opencollective",
     3357          "url": "https://opencollective.com/postcss/"
     3358        },
     3359        {
     3360          "type": "github",
     3361          "url": "https://github.com/sponsors/ai"
     3362        }
     3363      ],
     3364      "license": "MIT",
     3365      "dependencies": {
     3366        "lilconfig": "^3.0.0",
     3367        "yaml": "^2.3.4"
     3368      },
     3369      "engines": {
     3370        "node": ">= 14"
     3371      },
     3372      "peerDependencies": {
     3373        "postcss": ">=8.0.9",
     3374        "ts-node": ">=9.0.0"
     3375      },
     3376      "peerDependenciesMeta": {
     3377        "postcss": {
     3378          "optional": true
     3379        },
     3380        "ts-node": {
     3381          "optional": true
     3382        }
     3383      }
     3384    },
     3385    "node_modules/postcss-nested": {
     3386      "version": "6.2.0",
     3387      "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
     3388      "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
     3389      "dev": true,
     3390      "funding": [
     3391        {
     3392          "type": "opencollective",
     3393          "url": "https://opencollective.com/postcss/"
     3394        },
     3395        {
     3396          "type": "github",
     3397          "url": "https://github.com/sponsors/ai"
     3398        }
     3399      ],
     3400      "license": "MIT",
     3401      "dependencies": {
     3402        "postcss-selector-parser": "^6.1.1"
     3403      },
     3404      "engines": {
     3405        "node": ">=12.0"
     3406      },
     3407      "peerDependencies": {
     3408        "postcss": "^8.2.14"
     3409      }
     3410    },
     3411    "node_modules/postcss-selector-parser": {
     3412      "version": "6.1.2",
     3413      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
     3414      "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
     3415      "dev": true,
     3416      "license": "MIT",
     3417      "dependencies": {
     3418        "cssesc": "^3.0.0",
     3419        "util-deprecate": "^1.0.2"
     3420      },
     3421      "engines": {
     3422        "node": ">=4"
     3423      }
     3424    },
     3425    "node_modules/postcss-value-parser": {
     3426      "version": "4.2.0",
     3427      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
     3428      "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
     3429      "dev": true,
     3430      "license": "MIT"
     3431    },
    27973432    "node_modules/prelude-ls": {
    27983433      "version": "1.2.1",
     
    28143449        "node": ">=6"
    28153450      }
     3451    },
     3452    "node_modules/queue-microtask": {
     3453      "version": "1.2.3",
     3454      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
     3455      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
     3456      "dev": true,
     3457      "funding": [
     3458        {
     3459          "type": "github",
     3460          "url": "https://github.com/sponsors/feross"
     3461        },
     3462        {
     3463          "type": "patreon",
     3464          "url": "https://www.patreon.com/feross"
     3465        },
     3466        {
     3467          "type": "consulting",
     3468          "url": "https://feross.org/support"
     3469        }
     3470      ],
     3471      "license": "MIT"
    28163472    },
    28173473    "node_modules/react": {
     
    28743530      "engines": {
    28753531        "node": ">=0.10.0"
     3532      }
     3533    },
     3534    "node_modules/read-cache": {
     3535      "version": "1.0.0",
     3536      "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
     3537      "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
     3538      "dev": true,
     3539      "license": "MIT",
     3540      "dependencies": {
     3541        "pify": "^2.3.0"
     3542      }
     3543    },
     3544    "node_modules/readdirp": {
     3545      "version": "3.6.0",
     3546      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
     3547      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
     3548      "dev": true,
     3549      "license": "MIT",
     3550      "dependencies": {
     3551        "picomatch": "^2.2.1"
     3552      },
     3553      "engines": {
     3554        "node": ">=8.10.0"
     3555      }
     3556    },
     3557    "node_modules/readdirp/node_modules/picomatch": {
     3558      "version": "2.3.1",
     3559      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
     3560      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
     3561      "dev": true,
     3562      "license": "MIT",
     3563      "engines": {
     3564        "node": ">=8.6"
     3565      },
     3566      "funding": {
     3567        "url": "https://github.com/sponsors/jonschlinkert"
    28763568      }
    28773569    },
     
    29273619      "license": "MIT"
    29283620    },
     3621    "node_modules/resolve": {
     3622      "version": "1.22.11",
     3623      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
     3624      "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
     3625      "dev": true,
     3626      "license": "MIT",
     3627      "dependencies": {
     3628        "is-core-module": "^2.16.1",
     3629        "path-parse": "^1.0.7",
     3630        "supports-preserve-symlinks-flag": "^1.0.0"
     3631      },
     3632      "bin": {
     3633        "resolve": "bin/resolve"
     3634      },
     3635      "engines": {
     3636        "node": ">= 0.4"
     3637      },
     3638      "funding": {
     3639        "url": "https://github.com/sponsors/ljharb"
     3640      }
     3641    },
    29293642    "node_modules/resolve-from": {
    29303643      "version": "4.0.0",
     
    29353648      "engines": {
    29363649        "node": ">=4"
     3650      }
     3651    },
     3652    "node_modules/reusify": {
     3653      "version": "1.1.0",
     3654      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
     3655      "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
     3656      "dev": true,
     3657      "license": "MIT",
     3658      "engines": {
     3659        "iojs": ">=1.0.0",
     3660        "node": ">=0.10.0"
    29373661      }
    29383662    },
     
    29793703      }
    29803704    },
     3705    "node_modules/run-parallel": {
     3706      "version": "1.2.0",
     3707      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
     3708      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
     3709      "dev": true,
     3710      "funding": [
     3711        {
     3712          "type": "github",
     3713          "url": "https://github.com/sponsors/feross"
     3714        },
     3715        {
     3716          "type": "patreon",
     3717          "url": "https://www.patreon.com/feross"
     3718        },
     3719        {
     3720          "type": "consulting",
     3721          "url": "https://feross.org/support"
     3722        }
     3723      ],
     3724      "license": "MIT",
     3725      "dependencies": {
     3726        "queue-microtask": "^1.2.2"
     3727      }
     3728    },
    29813729    "node_modules/scheduler": {
    29823730      "version": "0.27.0",
     
    30413789      }
    30423790    },
     3791    "node_modules/sucrase": {
     3792      "version": "3.35.1",
     3793      "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz",
     3794      "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==",
     3795      "dev": true,
     3796      "license": "MIT",
     3797      "dependencies": {
     3798        "@jridgewell/gen-mapping": "^0.3.2",
     3799        "commander": "^4.0.0",
     3800        "lines-and-columns": "^1.1.6",
     3801        "mz": "^2.7.0",
     3802        "pirates": "^4.0.1",
     3803        "tinyglobby": "^0.2.11",
     3804        "ts-interface-checker": "^0.1.9"
     3805      },
     3806      "bin": {
     3807        "sucrase": "bin/sucrase",
     3808        "sucrase-node": "bin/sucrase-node"
     3809      },
     3810      "engines": {
     3811        "node": ">=16 || 14 >=14.17"
     3812      }
     3813    },
    30433814    "node_modules/supports-color": {
    30443815      "version": "7.2.0",
     
    30543825      }
    30553826    },
     3827    "node_modules/supports-preserve-symlinks-flag": {
     3828      "version": "1.0.0",
     3829      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
     3830      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
     3831      "dev": true,
     3832      "license": "MIT",
     3833      "engines": {
     3834        "node": ">= 0.4"
     3835      },
     3836      "funding": {
     3837        "url": "https://github.com/sponsors/ljharb"
     3838      }
     3839    },
     3840    "node_modules/tailwindcss": {
     3841      "version": "3.4.17",
     3842      "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
     3843      "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
     3844      "dev": true,
     3845      "license": "MIT",
     3846      "dependencies": {
     3847        "@alloc/quick-lru": "^5.2.0",
     3848        "arg": "^5.0.2",
     3849        "chokidar": "^3.6.0",
     3850        "didyoumean": "^1.2.2",
     3851        "dlv": "^1.1.3",
     3852        "fast-glob": "^3.3.2",
     3853        "glob-parent": "^6.0.2",
     3854        "is-glob": "^4.0.3",
     3855        "jiti": "^1.21.6",
     3856        "lilconfig": "^3.1.3",
     3857        "micromatch": "^4.0.8",
     3858        "normalize-path": "^3.0.0",
     3859        "object-hash": "^3.0.0",
     3860        "picocolors": "^1.1.1",
     3861        "postcss": "^8.4.47",
     3862        "postcss-import": "^15.1.0",
     3863        "postcss-js": "^4.0.1",
     3864        "postcss-load-config": "^4.0.2",
     3865        "postcss-nested": "^6.2.0",
     3866        "postcss-selector-parser": "^6.1.2",
     3867        "resolve": "^1.22.8",
     3868        "sucrase": "^3.35.0"
     3869      },
     3870      "bin": {
     3871        "tailwind": "lib/cli.js",
     3872        "tailwindcss": "lib/cli.js"
     3873      },
     3874      "engines": {
     3875        "node": ">=14.0.0"
     3876      }
     3877    },
     3878    "node_modules/thenify": {
     3879      "version": "3.3.1",
     3880      "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
     3881      "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
     3882      "dev": true,
     3883      "license": "MIT",
     3884      "dependencies": {
     3885        "any-promise": "^1.0.0"
     3886      }
     3887    },
     3888    "node_modules/thenify-all": {
     3889      "version": "1.6.0",
     3890      "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
     3891      "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
     3892      "dev": true,
     3893      "license": "MIT",
     3894      "dependencies": {
     3895        "thenify": ">= 3.1.0 < 4"
     3896      },
     3897      "engines": {
     3898        "node": ">=0.8"
     3899      }
     3900    },
    30563901    "node_modules/tiny-invariant": {
    30573902      "version": "1.3.3",
     
    30763921        "url": "https://github.com/sponsors/SuperchupuDev"
    30773922      }
     3923    },
     3924    "node_modules/to-regex-range": {
     3925      "version": "5.0.1",
     3926      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
     3927      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
     3928      "dev": true,
     3929      "license": "MIT",
     3930      "dependencies": {
     3931        "is-number": "^7.0.0"
     3932      },
     3933      "engines": {
     3934        "node": ">=8.0"
     3935      }
     3936    },
     3937    "node_modules/ts-interface-checker": {
     3938      "version": "0.1.13",
     3939      "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
     3940      "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
     3941      "dev": true,
     3942      "license": "Apache-2.0"
    30783943    },
    30793944    "node_modules/type-check": {
     
    31394004        "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
    31404005      }
     4006    },
     4007    "node_modules/util-deprecate": {
     4008      "version": "1.0.2",
     4009      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
     4010      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
     4011      "dev": true,
     4012      "license": "MIT"
    31414013    },
    31424014    "node_modules/victory-vendor": {
     
    32704142      "license": "ISC"
    32714143    },
     4144    "node_modules/yaml": {
     4145      "version": "2.8.2",
     4146      "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz",
     4147      "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
     4148      "dev": true,
     4149      "license": "ISC",
     4150      "bin": {
     4151        "yaml": "bin.mjs"
     4152      },
     4153      "engines": {
     4154        "node": ">= 14.6"
     4155      },
     4156      "funding": {
     4157        "url": "https://github.com/sponsors/eemeli"
     4158      }
     4159    },
    32724160    "node_modules/yocto-queue": {
    32734161      "version": "0.1.0",
  • lan-frontend/package.json

    re4c61dd ra762898  
    2222    "@types/react-dom": "^19.2.3",
    2323    "@vitejs/plugin-react": "^5.1.1",
     24    "autoprefixer": "^10.4.23",
    2425    "eslint": "^9.39.1",
    2526    "eslint-plugin-react-hooks": "^7.0.1",
    2627    "eslint-plugin-react-refresh": "^0.4.24",
    2728    "globals": "^16.5.0",
     29    "postcss": "^8.5.6",
     30    "tailwindcss": "^3.4.17",
    2831    "vite": "^7.2.4"
    2932  }
  • lan-frontend/src/App.jsx

    re4c61dd ra762898  
    1 import React, { useEffect, useMemo, useState } from "react";
     1import React, {useEffect, useMemo, useState} from "react";
    22import "./styles/theme.css";
     3import "./styles/netinel.css";
    34
    45import Login from "./components/Login";
     
    78import ComputerDetailsModal from "./components/ComputerDetailsModal";
    89import AIAssistantDrawer from "./components/AIAssistantDrawer";
     10import NetIntelViz from "./components/NetIntelViz.jsx";
     11import SideMenu from "./components/SideMenu";
     12
    913
    1014export default function App() {
    11   const [stats, setStats] = useState(null);
    12   const [computers, setComputers] = useState([]);
    13   const [loading, setLoading] = useState(true);
    14   const [lastUpdated, setLastUpdated] = useState(null);
    15 
    16   const [query, setQuery] = useState("");
    17   const [status, setStatus] = useState("");
    18 
    19   const [selectedComputer, setSelectedComputer] = useState(null);
    20   const [aiOpen, setAiOpen] = useState(false);
    21   const [aiScope, setAiScope] = useState(null);
    22   const [selectedEnv, setSelectedEnv] = useState("default");
    23 
    24   const [me, setMe] = useState(null);
    25   const [meLoading, setMeLoading] = useState(true);
    26 
    27   // NEW: environment setting (switch)
    28   const [saveProcHistory, setSaveProcHistory] = useState(false);
    29 
    30   async function loadMe() {
    31     setMeLoading(true);
    32     try {
    33       const r = await fetch("/api/me", { credentials: "include" });
    34       if (r.ok) {
    35         const data = await r.json();
    36         setMe(data.user);
    37       } else {
    38         setMe(null);
    39       }
    40     } catch (e) {
    41       console.error("loadMe error", e);
    42       setMe(null);
    43     } finally {
    44       setMeLoading(false);
     15    const [stats, setStats] = useState(null);
     16    const [computers, setComputers] = useState([]);
     17    const [loading, setLoading] = useState(true);
     18    const [lastUpdated, setLastUpdated] = useState(null);
     19
     20    const [query, setQuery] = useState("");
     21    const [status, setStatus] = useState("");
     22
     23    const [selectedComputer, setSelectedComputer] = useState(null);
     24    const [aiOpen, setAiOpen] = useState(false);
     25    const [aiScope, setAiScope] = useState(null);
     26    const [selectedEnv, setSelectedEnv] = useState("default");
     27
     28    const [me, setMe] = useState(null);
     29    const [meLoading, setMeLoading] = useState(true);
     30
     31    const [activeView, setActiveView] = useState("dashboard");
     32// "dashboard" | "visualizations" | "account"
     33
     34
     35    // NEW: environment setting (switch)
     36    const [saveProcHistory, setSaveProcHistory] = useState(false);
     37
     38    async function loadMe() {
     39        setMeLoading(true);
     40        try {
     41            const r = await fetch("/api/me", {credentials: "include"});
     42            if (r.ok) {
     43                const data = await r.json();
     44                setMe(data.user);
     45            } else {
     46                setMe(null);
     47            }
     48        } catch (e) {
     49            console.error("loadMe error", e);
     50            setMe(null);
     51        } finally {
     52            setMeLoading(false);
     53        }
    4554    }
    46   }
    47 
    48   async function refresh() {
    49     try {
    50       setLoading(true);
    51       const [s, c] = await Promise.all([
    52         fetch("/api/stats", { credentials: "include" }).then((r) => r.json()),
    53         fetch("/api/computers", {
    54           credentials: "include",
    55           headers: { "X-Env": selectedEnv },
    56         }).then((r) => r.json()),
    57       ]);
    58 
    59       setStats(s);
    60       setComputers(Array.isArray(c) ? c : []);
    61       setLastUpdated(new Date().toISOString());
    62     } catch (e) {
    63       console.error("refresh error", e);
    64     } finally {
    65       setLoading(false);
     55
     56    async function refresh() {
     57        try {
     58            setLoading(true);
     59            const [s, c] = await Promise.all([
     60                fetch("/api/stats", {credentials: "include"}).then((r) => r.json()),
     61                fetch("/api/computers", {
     62                    credentials: "include",
     63                    headers: {"X-Env": selectedEnv},
     64                }).then((r) => r.json()),
     65            ]);
     66
     67            setStats(s);
     68            setComputers(Array.isArray(c) ? c : []);
     69            setLastUpdated(new Date().toISOString());
     70        } catch (e) {
     71            console.error("refresh error", e);
     72        } finally {
     73            setLoading(false);
     74        }
    6675    }
    67   }
    68 
    69   // NEW: load env settings (switch state) for selected env
    70   async function loadEnvSettings(env) {
    71     try {
    72       // само admin ќе има пристап (ако не си admin, ќе падне во else и ќе стане false)
    73       const r = await fetch(`/api/admin/env-settings/${encodeURIComponent(env)}`, {
    74         credentials: "include",
    75       });
    76 
    77       if (r.ok) {
    78         const data = await r.json();
    79         setSaveProcHistory(!!data.save_process_history);
    80       } else {
    81         setSaveProcHistory(false);
    82       }
    83     } catch (e) {
    84       console.error("loadEnvSettings error", e);
    85       setSaveProcHistory(false);
     76
     77    // NEW: load env settings (switch state) for selected env
     78    async function loadEnvSettings(env) {
     79        try {
     80            // само admin ќе има пристап (ако не си admin, ќе падне во else и ќе стане false)
     81            const r = await fetch(`/api/admin/env-settings/${encodeURIComponent(env)}`, {
     82                credentials: "include",
     83            });
     84
     85            if (r.ok) {
     86                const data = await r.json();
     87                setSaveProcHistory(!!data.save_process_history);
     88            } else {
     89                setSaveProcHistory(false);
     90            }
     91        } catch (e) {
     92            console.error("loadEnvSettings error", e);
     93            setSaveProcHistory(false);
     94        }
    8695    }
    87   }
    88 
    89   // NEW: set env setting (toggle)
    90   async function setEnvProcessHistory(env, enabled) {
    91     // optimistic UI
    92     setSaveProcHistory(enabled);
    93 
    94     try {
    95       const r = await fetch(`/api/admin/env-settings/${encodeURIComponent(env)}`, {
    96         method: "POST",
    97         credentials: "include",
    98         headers: { "Content-Type": "application/json" },
    99         body: JSON.stringify({ save_process_history: enabled }),
    100       });
    101 
    102       if (r.ok) {
    103         const data = await r.json();
    104         setSaveProcHistory(!!data.save_process_history);
    105       } else {
    106         // rollback
    107         setSaveProcHistory(!enabled);
    108         console.error("setEnvProcessHistory failed", await r.text());
    109       }
    110     } catch (e) {
    111       setSaveProcHistory(!enabled);
    112       console.error("setEnvProcessHistory error", e);
     96
     97    // NEW: set env setting (toggle)
     98    async function setEnvProcessHistory(env, enabled) {
     99        // optimistic UI
     100        setSaveProcHistory(enabled);
     101
     102        try {
     103            const r = await fetch(`/api/admin/env-settings/${encodeURIComponent(env)}`, {
     104                method: "POST",
     105                credentials: "include",
     106                headers: {"Content-Type": "application/json"},
     107                body: JSON.stringify({save_process_history: enabled}),
     108            });
     109
     110            if (r.ok) {
     111                const data = await r.json();
     112                setSaveProcHistory(!!data.save_process_history);
     113            } else {
     114                // rollback
     115                setSaveProcHistory(!enabled);
     116                console.error("setEnvProcessHistory failed", await r.text());
     117            }
     118        } catch (e) {
     119            setSaveProcHistory(!enabled);
     120            console.error("setEnvProcessHistory error", e);
     121        }
    113122    }
    114   }
    115 
    116   // on mount: check session
    117   useEffect(() => {
    118     loadMe();
    119   }, []);
    120 
    121   // when logged in OR env changed, refresh data + load env switch state
    122   useEffect(() => {
    123     if (!me) return;
    124     loadEnvSettings(selectedEnv);
    125     refresh();
    126     // eslint-disable-next-line react-hooks/exhaustive-deps
    127   }, [me, selectedEnv]);
    128 
    129   const filtered = useMemo(() => {
    130     const q = query.trim().toLowerCase();
    131     return (computers || [])
    132       .filter((c) => (status ? c.status === status : true))
    133       .filter((c) => {
    134         if (!q) return true;
    135         const hay = [c.name, c.user, c.ip, c.os, c.status]
    136           .filter(Boolean)
    137           .join(" ")
    138           .toLowerCase();
    139         return hay.includes(q);
    140       });
    141   }, [computers, query, status]);
    142 
    143   // Gate UI
    144   if (meLoading) return <div style={{ padding: 20 }}>Loading…</div>;
    145   if (!me) return <Login onDone={loadMe} />;
    146 
    147   return (
    148     <div className="app-wrap">
    149       <TopBar
    150         onOpenAI={() => setAiOpen(true)}
    151         onRefresh={refresh}
    152         lastUpdated={lastUpdated}
    153         onEnvChange={(env) => setSelectedEnv(env)}
    154         selectedEnv={selectedEnv}
    155         me={me}
    156         onLoggedOut={loadMe}
    157         // NEW props for switch:
    158         saveProcHistory={saveProcHistory}
    159         onToggleProcHistory={(enabled) => setEnvProcessHistory(selectedEnv, enabled)}
    160       />
    161 
    162       {/* STATS */}
    163       <div className="stats-grid">
    164         <div className="stat-card">
    165           <div className="label">Total Computers</div>
    166           <div className="value">{computers.length}</div>
    167           <div className="sub">Monitored Systems</div>
     123
     124    // on mount: check session
     125    useEffect(() => {
     126        loadMe();
     127    }, []);
     128
     129    // when logged in OR env changed, refresh data + load env switch state
     130    useEffect(() => {
     131        if (!me) return;
     132        loadEnvSettings(selectedEnv);
     133        refresh();
     134        // eslint-disable-next-line react-hooks/exhaustive-deps
     135    }, [me, selectedEnv]);
     136
     137    const filtered = useMemo(() => {
     138        const q = query.trim().toLowerCase();
     139        return (computers || [])
     140            .filter((c) => (status ? c.status === status : true))
     141            .filter((c) => {
     142                if (!q) return true;
     143                const hay = [c.name, c.user, c.ip, c.os, c.status]
     144                    .filter(Boolean)
     145                    .join(" ")
     146                    .toLowerCase();
     147                return hay.includes(q);
     148            });
     149    }, [computers, query, status]);
     150
     151    // Gate UI
     152    if (meLoading) return <div style={{padding: 20}}>Loading…</div>;
     153    if (!me) return <Login onDone={loadMe}/>;
     154
     155    return (
     156        <div className="app-shell">
     157            <div className="layout">
     158                <SideMenu activeView={activeView} setActiveView={setActiveView}/>
     159                <main className="content">
     160                    {/* TopBar + pages */}
     161                </main>
     162            </div>
     163
     164            <div className="cont">
     165                <TopBar
     166                    onOpenAI={() => setAiOpen(true)}
     167                    onRefresh={refresh}
     168                    lastUpdated={lastUpdated}
     169                    onEnvChange={(env) => setSelectedEnv(env)}
     170                    selectedEnv={selectedEnv}
     171                    me={me}
     172                    onLoggedOut={loadMe}
     173                    saveProcHistory={saveProcHistory}
     174                    onToggleProcHistory={(enabled) => setEnvProcessHistory(selectedEnv, enabled)}
     175                />
     176
     177                <div className="app-main">
     178                    {/* ====== VIEW SWITCH ====== */}
     179                    {activeView === "dashboard" && (
     180                        <>
     181                            {/* STATS */}
     182                            <div className="stats-grid">
     183                                <div className="stat-card">
     184                                    <div className="label">Total Computers</div>
     185                                    <div className="value">{computers.length}</div>
     186                                    <div className="sub">Monitored Systems</div>
     187                                </div>
     188
     189                                <div className="stat-card">
     190                                    <div className="label">Sysmon Events (24h)</div>
     191                                    <div className="value">{stats?.total_sysmon_events ?? "—"}</div>
     192                                    <div className="sub">Security Events</div>
     193                                </div>
     194
     195                                <div className="stat-card">
     196                                    <div className="label">Network Connections (24h)</div>
     197                                    <div className="value">{stats?.total_network_connections ?? "—"}</div>
     198                                    <div className="sub">Captured Connections</div>
     199                                </div>
     200                            </div>
     201
     202                            {/* FILTERS */}
     203                            <div className="panel" style={{marginBottom: 14}}>
     204                                <div className="panel-head">
     205                                    <div className="panel-title">Filters</div>
     206                                    <div style={{color: "var(--dim)", fontSize: 12}}>
     207                                        {loading ? "Refreshing…" : `${filtered.length} shown`}
     208                                    </div>
     209                                </div>
     210                                <div className="panel-body">
     211                                    <div className="filters">
     212                                        <input
     213                                            className="input"
     214                                            value={query}
     215                                            onChange={(e) => setQuery(e.target.value)}
     216                                            placeholder="Search by name / user / ip / os…"
     217                                        />
     218
     219                                        <select
     220                                            className="select"
     221                                            value={status}
     222                                            onChange={(e) => setStatus(e.target.value)}
     223                                        >
     224                                            <option value="">All status</option>
     225                                            <option value="online">Online</option>
     226                                            <option value="idle">Idle</option>
     227                                            <option value="offline">Offline</option>
     228                                        </select>
     229
     230                                        <button className="btn primary" onClick={refresh}>
     231                                            Refresh
     232                                        </button>
     233                                    </div>
     234                                </div>
     235                            </div>
     236
     237                            {/* COMPUTERS */}
     238                            <div className="panel">
     239                                <div className="panel-head">
     240                                    <div className="panel-title">Connected Computers</div>
     241                                    <div style={{display: "flex", gap: 10, alignItems: "center"}}>
     242                                        <span className="pill">{filtered.length} computers</span>
     243                                        <button
     244                                            className="btn"
     245                                            onClick={() => {
     246                                                setQuery("");
     247                                                setStatus("");
     248                                            }}
     249                                        >
     250                                            Reset
     251                                        </button>
     252                                    </div>
     253                                </div>
     254                                <div className="panel-body">
     255                                    <div className="computers-grid">
     256                                        {filtered.map((c) => (
     257                                            <ComputerCard
     258                                                key={c.name}
     259                                                c={c}
     260                                                onOpen={(pc) => {
     261                                                    setSelectedComputer(pc);
     262                                                    setAiScope(pc.name);
     263                                                }}
     264                                            />
     265                                        ))}
     266                                        {filtered.length === 0 && (
     267                                            <div style={{color: "var(--dim)"}}>
     268                                                No computers match filter.
     269                                            </div>
     270                                        )}
     271                                    </div>
     272                                </div>
     273                            </div>
     274                        </>
     275                    )}
     276
     277                    {activeView === "visualizations" && (
     278                        <>
     279                            <div className="panel" style={{marginBottom: 14}}>
     280                                <div className="panel-head">
     281                                    <div className="panel-title">Visualizations</div>
     282                                    <div style={{color: "var(--dim)", fontSize: 12}}>
     283                                        Network graphs / charts / tables
     284                                    </div>
     285                                </div>
     286                                <div className="panel-body">
     287                                    <NetIntelViz baseUrl="http://localhost:5555"/>
     288                                </div>
     289                            </div>
     290                        </>
     291                    )}
     292
     293                    {activeView === "account" && (
     294                        <div className="panel">
     295                            <div className="panel-head">
     296                                <div className="panel-title">Account</div>
     297                                <div style={{color: "var(--dim)", fontSize: 12}}>
     298                                    Profile info
     299                                </div>
     300                            </div>
     301                            <div className="panel-body">
     302                                <div style={{display: "grid", gap: 10}}>
     303                                    <div className="pill">Email: {me.email}</div>
     304                                    <div className="pill">Role: {me.role}</div>
     305                                    <div className="pill">Tenant ID: {me.tenant_id}</div>
     306                                    <div className="pill">Name: {me.name || "—"}</div>
     307                                </div>
     308                            </div>
     309                        </div>
     310                    )}
     311                </div>
     312
     313                {/* DETAILS MODAL */}
     314                <ComputerDetailsModal
     315                    open={!!selectedComputer}
     316                    computer={selectedComputer}
     317                    onClose={() => setSelectedComputer(null)}
     318                    onAskAI={(name) => {
     319                        setAiScope(name);
     320                        setAiOpen(true);
     321                    }}
     322                />
     323
     324                {/* AI DRAWER */}
     325                <AIAssistantDrawer
     326                    open={aiOpen}
     327                    onClose={() => setAiOpen(false)}
     328                    computers={computers}
     329                    scope={aiScope}
     330                    setScope={setAiScope}
     331                    apiBase={import.meta.env.VITE_API_BASE}
     332                />
     333            </div>
    168334        </div>
    169 
    170         <div className="stat-card">
    171           <div className="label">Sysmon Events (24h)</div>
    172           <div className="value">{stats?.total_sysmon_events ?? "—"}</div>
    173           <div className="sub">Security Events</div>
    174         </div>
    175 
    176         <div className="stat-card">
    177           <div className="label">Network Connections (24h)</div>
    178           <div className="value">{stats?.total_network_connections ?? "—"}</div>
    179           <div className="sub">Captured Connections</div>
    180         </div>
    181       </div>
    182 
    183       {/* FILTERS */}
    184       <div className="panel" style={{ marginBottom: 14 }}>
    185         <div className="panel-head">
    186           <div className="panel-title">Filters</div>
    187           <div style={{ color: "var(--dim)", fontSize: 12 }}>
    188             {loading ? "Refreshing…" : `${filtered.length} shown`}
    189           </div>
    190         </div>
    191         <div className="panel-body">
    192           <div className="filters">
    193             <input
    194               className="input"
    195               value={query}
    196               onChange={(e) => setQuery(e.target.value)}
    197               placeholder="Search by name / user / ip / os…"
    198             />
    199 
    200             <select
    201               className="select"
    202               value={status}
    203               onChange={(e) => setStatus(e.target.value)}
    204             >
    205               <option value="">All status</option>
    206               <option value="online">Online</option>
    207               <option value="idle">Idle</option>
    208               <option value="offline">Offline</option>
    209             </select>
    210 
    211             <button className="btn primary" onClick={refresh}>
    212               Refresh
    213             </button>
    214           </div>
    215         </div>
    216       </div>
    217 
    218       {/* COMPUTERS */}
    219       <div className="panel">
    220         <div className="panel-head">
    221           <div className="panel-title">Connected Computers</div>
    222           <div style={{ display: "flex", gap: 10, alignItems: "center" }}>
    223             <span className="pill">{filtered.length} computers</span>
    224             <button
    225               className="btn"
    226               onClick={() => {
    227                 setQuery("");
    228                 setStatus("");
    229               }}
    230             >
    231               Reset
    232             </button>
    233           </div>
    234         </div>
    235         <div className="panel-body">
    236           <div className="computers-grid">
    237             {filtered.map((c) => (
    238               <ComputerCard
    239                 key={c.name}
    240                 c={c}
    241                 onOpen={(pc) => {
    242                   setSelectedComputer(pc);
    243                   setAiScope(pc.name);
    244                 }}
    245               />
    246             ))}
    247             {filtered.length === 0 && (
    248               <div style={{ color: "var(--dim)" }}>No computers match filter.</div>
    249             )}
    250           </div>
    251         </div>
    252       </div>
    253 
    254       {/* DETAILS MODAL */}
    255       <ComputerDetailsModal
    256         open={!!selectedComputer}
    257         computer={selectedComputer}
    258         onClose={() => setSelectedComputer(null)}
    259         onAskAI={(name) => {
    260           setAiScope(name);
    261           setAiOpen(true);
    262         }}
    263       />
    264 
    265       {/* AI DRAWER */}
    266       <AIAssistantDrawer
    267         open={aiOpen}
    268         onClose={() => setAiOpen(false)}
    269         computers={computers}
    270         scope={aiScope}
    271         setScope={setAiScope}
    272         apiBase={import.meta.env.VITE_API_BASE}
    273       />
    274     </div>
    275   );
     335    );
    276336}
  • lan-frontend/src/styles/theme.css

    re4c61dd ra762898  
    33   Dark Navy + Light Blue
    44   ========================= */
     5@tailwind base;
     6@tailwind components;
     7@tailwind utilities;
    58
    69:root{
     
    548551  border-color: rgba(88, 167, 255, .35);
    549552}
     553
     554/* ====== LAYOUT with LEFT SIDEBAR ====== */
     555.app-shell {
     556  display: grid;
     557  grid-template-columns: 240px 1fr;
     558  min-height: 100vh;
     559}
     560
     561.app-main {
     562  padding: 14px;
     563}
     564
     565/* Sidebar */
     566.side {
     567  background: rgba(255,255,255,0.04);
     568  border-right: 1px solid rgba(255,255,255,0.10);
     569  padding: 14px;
     570  position: sticky;
     571  top: 0;
     572  height: 100vh;
     573}
     574
     575.side-brand {
     576  display: flex;
     577  gap: 10px;
     578  align-items: center;
     579  padding: 10px;
     580  border: 1px solid rgba(255,255,255,0.10);
     581  border-radius: 14px;
     582  background: rgba(0,0,0,0.12);
     583  margin-bottom: 14px;
     584}
     585
     586.side-brand-dot {
     587  width: 10px;
     588  height: 10px;
     589  border-radius: 999px;
     590  background: #7dd3fc;
     591  box-shadow: 0 0 0 4px rgba(125,211,252,0.15);
     592}
     593
     594.side-brand-title {
     595  font-weight: 700;
     596  letter-spacing: 0.2px;
     597}
     598
     599.side-brand-sub {
     600  font-size: 12px;
     601  color: var(--dim);
     602}
     603
     604.side-section-title {
     605  font-size: 11px;
     606  color: var(--dim);
     607  margin: 12px 8px 8px;
     608  letter-spacing: 0.12em;
     609}
     610
     611.side-item {
     612  width: 100%;
     613  display: flex;
     614  align-items: center;
     615  gap: 10px;
     616  padding: 10px 12px;
     617  border-radius: 12px;
     618  border: 1px solid transparent;
     619  background: transparent;
     620  color: inherit;
     621  cursor: pointer;
     622  text-align: left;
     623}
     624
     625.side-item:hover {
     626  background: rgba(255,255,255,0.06);
     627  border-color: rgba(255,255,255,0.08);
     628}
     629
     630.side-item.active {
     631  background: rgba(125,211,252,0.12);
     632  border-color: rgba(125,211,252,0.25);
     633}
     634
     635.side-icon {
     636  width: 26px;
     637  display: grid;
     638  place-items: center;
     639  font-size: 18px;
     640}
     641
     642.side-label {
     643  font-size: 14px;
     644  font-weight: 600;
     645}
     646
     647.side-footer {
     648  position: absolute;
     649  bottom: 12px;
     650  left: 14px;
     651  right: 14px;
     652  padding: 10px;
     653  border-radius: 12px;
     654  border: 1px solid rgba(255,255,255,0.10);
     655  background: rgba(0,0,0,0.12);
     656}
     657
     658.side-footer-hint {
     659  font-size: 12px;
     660  color: var(--dim);
     661}
     662/* ==== Collapsible Side Menu ==== */
     663.side {
     664  position: sticky;
     665  top: 0;
     666  height: 100vh;
     667  z-index: 20;
     668
     669  width: 260px;
     670  transition: width 180ms ease;
     671  border-right: 1px solid rgba(255,255,255,0.08);
     672  background: rgba(6, 12, 24, 0.72);
     673  backdrop-filter: blur(10px);
     674}
     675
     676.side.collapsed {
     677  width: 54px; /* само тенка лента */
     678}
     679
     680.side-inner {
     681  height: 100%;
     682  padding: 14px 10px;
     683  display: flex;
     684  flex-direction: column;
     685}
     686
     687.side-handle {
     688  position: absolute;
     689  left: 6px;
     690  top: 10px;
     691  width: 42px;
     692  height: 34px;
     693  border-radius: 12px;
     694  border: 1px solid rgba(255,255,255,0.12);
     695  background: rgba(255,255,255,0.06);
     696  color: rgba(255,255,255,0.9);
     697  cursor: pointer;
     698}
     699
     700.side-brand {
     701  display: flex;
     702  align-items: center;
     703  gap: 10px;
     704  padding: 10px;
     705  margin-top: 38px; /* за да не се судри со handle */
     706  border-radius: 16px;
     707  background: rgba(255,255,255,0.03);
     708  border: 1px solid rgba(255,255,255,0.06);
     709}
     710
     711.side-brand-dot {
     712  width: 12px;
     713  height: 12px;
     714  border-radius: 999px;
     715  background: rgba(80, 160, 255, 0.95);
     716  box-shadow: 0 0 18px rgba(80,160,255,0.45);
     717}
     718
     719.side-brand-text { min-width: 0; }
     720.side-brand-title { font-weight: 800; color: rgba(255,255,255,0.92); }
     721.side-brand-sub { font-size: 12px; color: rgba(255,255,255,0.55); }
     722
     723.side-section { margin-top: 14px; }
     724.side-section-title {
     725  font-size: 11px;
     726  letter-spacing: 0.12em;
     727  color: rgba(255,255,255,0.45);
     728  padding: 10px;
     729}
     730
     731.side-item {
     732  width: 100%;
     733  display: flex;
     734  align-items: center;
     735  gap: 10px;
     736  padding: 10px 12px;
     737  margin: 6px 0;
     738  border-radius: 14px;
     739  border: 1px solid transparent;
     740  background: transparent;
     741  color: rgba(255,255,255,0.82);
     742  cursor: pointer;
     743  text-align: left;
     744}
     745
     746.side-item:hover {
     747  background: rgba(255,255,255,0.06);
     748  border-color: rgba(255,255,255,0.08);
     749}
     750
     751.side-item.active {
     752  background: rgba(80,160,255,0.16);
     753  border-color: rgba(80,160,255,0.25);
     754}
     755
     756.side-icon {
     757  width: 28px;
     758  display: grid;
     759  place-items: center;
     760  font-size: 18px;
     761}
     762
     763.side-label {
     764  white-space: nowrap;
     765  overflow: hidden;
     766  text-overflow: ellipsis;
     767}
     768
     769.side-footer {
     770  margin-top: auto;
     771  padding: 10px;
     772  color: rgba(255,255,255,0.55);
     773  font-size: 12px;
     774}
     775.layout { display: flex; }
     776.content { flex: 1; min-width: 0; }
     777/* collapsed = центрирање иконите + тргање празни простори */
     778.side.collapsed .side-inner {
     779  padding: 10px 6px;
     780  align-items: center;
     781}
     782
     783.side.collapsed .side-brand {
     784  width: 42px;
     785  padding: 10px 0;
     786  justify-content: center;
     787  margin-top: 48px;
     788}
     789
     790.side.collapsed .side-item {
     791  justify-content: center;
     792  padding: 10px 0;
     793}
     794
     795.side.collapsed .side-icon {
     796  width: 42px;
     797}
     798
     799/* handle во средина (поубаво) */
     800.side-handle {
     801  left: 6px;
     802  top: 10px;
     803  width: 42px;
     804  height: 34px;
     805}
     806
     807/* кога е open да си е нормално */
     808.side.open .side-inner {
     809  align-items: stretch;
     810}
     811
     812.cont{
     813  margin-right: 50px;
     814}
Note: See TracChangeset for help on using the changeset viewer.