From 722d8e65ec2e8b4a0674b20f86d444910e173827 Mon Sep 17 00:00:00 2001 From: Sean Edmunds Date: Tue, 14 May 2024 16:34:32 -0500 Subject: [PATCH] Added Homepage components --- package-lock.json | 543 +++++++++++++++++- package.json | 9 +- src/App.tsx | 42 +- src/contexts/HomepageFilters.ts | 3 + src/hooks/index.ts | 1 + src/hooks/useAPI.ts | 37 ++ src/pages/homepage/Homepage.tsx | 65 +++ src/pages/homepage/components/AppItem.tsx | 52 ++ .../homepage/components/Applications.tsx | 51 ++ .../homepage/components/BookmarkGroups.tsx | 15 + src/pages/homepage/components/Bookmarks.tsx | 62 ++ src/pages/homepage/components/Header.tsx | 74 +++ src/pages/homepage/components/LinkGroups.tsx | 22 + src/pages/homepage/components/LinkItem.tsx | 12 + src/pages/homepage/components/MessageInfo.jsx | 34 ++ src/pages/homepage/components/Provider.jsx | 12 + src/pages/homepage/components/Search.tsx | 75 +++ .../homepage/components/SettingsButton.tsx | 97 ++++ src/styles/ActionIcon.module.css | 6 + src/styles/Button.module.css | 26 + src/styles/Menu.module.css | 5 + src/styles/NavLink.module.css | 22 + src/styles/Popover.module.css | 5 + src/styles/Select.module.css | 5 + src/styles/Stack.module.css | 66 +++ src/styles/Text.module.css | 98 ++++ src/styles/TextInput.module.css | 71 +++ src/styles/index.ts | 11 + vite.config.ts | 30 +- 29 files changed, 1499 insertions(+), 52 deletions(-) create mode 100644 src/contexts/HomepageFilters.ts create mode 100644 src/hooks/index.ts create mode 100644 src/hooks/useAPI.ts create mode 100644 src/pages/homepage/Homepage.tsx create mode 100644 src/pages/homepage/components/AppItem.tsx create mode 100644 src/pages/homepage/components/Applications.tsx create mode 100644 src/pages/homepage/components/BookmarkGroups.tsx create mode 100644 src/pages/homepage/components/Bookmarks.tsx create mode 100644 src/pages/homepage/components/Header.tsx create mode 100644 src/pages/homepage/components/LinkGroups.tsx create mode 100644 src/pages/homepage/components/LinkItem.tsx create mode 100644 src/pages/homepage/components/MessageInfo.jsx create mode 100644 src/pages/homepage/components/Provider.jsx create mode 100644 src/pages/homepage/components/Search.tsx create mode 100644 src/pages/homepage/components/SettingsButton.tsx create mode 100644 src/styles/ActionIcon.module.css create mode 100644 src/styles/Button.module.css create mode 100644 src/styles/Menu.module.css create mode 100644 src/styles/NavLink.module.css create mode 100644 src/styles/Popover.module.css create mode 100644 src/styles/Select.module.css create mode 100644 src/styles/Stack.module.css create mode 100644 src/styles/Text.module.css create mode 100644 src/styles/TextInput.module.css create mode 100644 src/styles/index.ts diff --git a/package-lock.json b/package-lock.json index d44186c..f1c0d55 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8 +8,15 @@ "name": "triggerfrontend", "version": "0.0.0", "dependencies": { + "@iconify/react": "^4.1.1", + "@mantine/core": "^7.9.1", + "@mantine/hooks": "^7.9.1", + "@tanstack/react-query": "^5.36.0", + "jotai": "^2.8.0", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-icons": "^5.2.1", + "react-query": "^3.39.3" }, "devDependencies": { "@types/react": "^18.2.66", @@ -334,6 +341,17 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", + "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.24.0", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", @@ -844,6 +862,54 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.1.tgz", + "integrity": "sha512-42UH54oPZHPdRHdw6BgoBD6cg/eVTmVrFcgeRDM3jbO7uxSoipVcmcIGFcA5jmOHO5apcyvBhkSKES3fQJnu7A==", + "dependencies": { + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.5.tgz", + "integrity": "sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==", + "dependencies": { + "@floating-ui/core": "^1.0.0", + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.26.14", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.14.tgz", + "integrity": "sha512-I2EhfezC+H0WfkMEkCcF9+++PU1Wq08bDKhHHGIoBZVCciiftEQHgrSI4dTUTsa7446SiIVW0gWATliIlVNgfg==", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@floating-ui/utils": "^0.2.0", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.9.tgz", + "integrity": "sha512-q0umO0+LQK4+p6aGyvzASqKbKOJcAHJ7ycE9CuUvfx3s9zTHWmGJTPOIlM/hmSBfUfg/XfY5YhLBLR/LHwShQQ==", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.2.tgz", + "integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==" + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -899,6 +965,25 @@ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, + "node_modules/@iconify/react": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@iconify/react/-/react-4.1.1.tgz", + "integrity": "sha512-jed14EjvKjee8mc0eoscGxlg7mSQRkwQG3iX3cPBCO7UlOjz0DtlvTqxqEcHUJGh+z1VJ31Yhu5B9PxfO0zbdg==", + "dependencies": { + "@iconify/types": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/cyberalien" + }, + "peerDependencies": { + "react": ">=16" + } + }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==" + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", @@ -947,6 +1032,43 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mantine/core": { + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/@mantine/core/-/core-7.9.1.tgz", + "integrity": "sha512-UXK11t1fAQ2ROQ9HTIZfyU52XdwS8sFJ2SRUh5uHMWSahJo3wTdKzaCvYN7wg+ONM9p6ENT0hC0dfv5qXoKU4w==", + "dependencies": { + "@floating-ui/react": "^0.26.9", + "clsx": "2.1.0", + "react-number-format": "^5.3.1", + "react-remove-scroll": "^2.5.7", + "react-textarea-autosize": "8.5.3", + "type-fest": "^4.12.0" + }, + "peerDependencies": { + "@mantine/hooks": "7.9.1", + "react": "^18.2.0", + "react-dom": "^18.2.0" + } + }, + "node_modules/@mantine/core/node_modules/type-fest": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.18.2.tgz", + "integrity": "sha512-+suCYpfJLAe4OXS6+PPXjW3urOS4IoP9waSiLuXfLgqZODKw/aWwASvzqE886wA0kQgGy0mIWyhd87VpqIy6Xg==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mantine/hooks": { + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-7.9.1.tgz", + "integrity": "sha512-nb2Ar+2HfGxLjGZgObapeUeqcQsUWJ+rvj4X2KNTUy3+YpvLIMD3CqREowWHF7/5OTGh4BCyBqQNZ9JFpnMeHQ==", + "peerDependencies": { + "react": "^18.2.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1190,6 +1312,30 @@ "win32" ] }, + "node_modules/@tanstack/query-core": { + "version": "5.36.0", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.36.0.tgz", + "integrity": "sha512-B5BD3pg/mztDR36i77hGcyySKKeYrbM5mnogOROTBi1SUml5ByRK7PGUUl16vvubvQC+mSnqziFG/VIy/DE3FQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.36.0", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.36.0.tgz", + "integrity": "sha512-BATvtM0rohwg7pRHUnxgeDiwLWRGZ8OM/4y8LImHVpecQWoH6Uhytu3Z8YV6V7hQ1sMQBFcUrGE1/e4MxR6YiA==", + "dependencies": { + "@tanstack/query-core": "5.36.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1241,13 +1387,13 @@ "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", - "dev": true + "devOptional": true }, "node_modules/@types/react": { "version": "18.3.2", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.2.tgz", "integrity": "sha512-Btgg89dAnqD4vV7R3hlwOxgqobUQKgx3MmrQRi0yYbs/P0ym8XozIAlkqVilPqHQwXs4e9Tf63rrCgl58BcO4w==", - "dev": true, + "devOptional": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -1548,8 +1694,15 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "engines": { + "node": ">=0.6" + } }, "node_modules/brace-expansion": { "version": "2.0.1", @@ -1572,6 +1725,21 @@ "node": ">=8" } }, + "node_modules/broadcast-channel": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/broadcast-channel/-/broadcast-channel-3.7.0.tgz", + "integrity": "sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==", + "dependencies": { + "@babel/runtime": "^7.7.2", + "detect-node": "^2.1.0", + "js-sha3": "0.8.0", + "microseconds": "0.2.0", + "nano-time": "1.0.0", + "oblivious-set": "1.0.0", + "rimraf": "3.0.2", + "unload": "2.2.0" + } + }, "node_modules/browserslist": { "version": "4.23.0", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", @@ -1647,6 +1815,14 @@ "node": ">=4" } }, + "node_modules/clsx": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", + "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -1665,8 +1841,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/convert-source-map": { "version": "2.0.0", @@ -1692,7 +1867,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true + "devOptional": true }, "node_modules/debug": { "version": "4.3.4", @@ -1717,6 +1892,16 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==" + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -2203,8 +2388,7 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.3", @@ -2229,11 +2413,18 @@ "node": ">=6.9.0" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -2265,7 +2456,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2275,7 +2465,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2365,7 +2554,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -2374,8 +2562,15 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } }, "node_modules/is-extglob": { "version": "2.1.1", @@ -2422,6 +2617,31 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/jotai": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.8.0.tgz", + "integrity": "sha512-yZNMC36FdLOksOr8qga0yLf14miCJlEThlp5DeFJNnqzm2+ZG7wLcJzoOyij5K6U6Xlc5ljQqPDlJRgqW0Y18g==", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=17.0.0", + "react": ">=17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -2544,6 +2764,15 @@ "yallist": "^3.0.2" } }, + "node_modules/match-sorter": { + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.4.tgz", + "integrity": "sha512-jfZW7cWS5y/1xswZo8VBOdudUiSd9nifYRWphc9M5D/ee4w4AoXLgBEdRbgVaxbMuagBPeUC5y2Hi8DO6o9aDg==", + "dependencies": { + "@babel/runtime": "^7.23.8", + "remove-accents": "0.5.0" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -2566,6 +2795,11 @@ "node": ">=8.6" } }, + "node_modules/microseconds": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz", + "integrity": "sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==" + }, "node_modules/minimatch": { "version": "9.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", @@ -2587,6 +2821,14 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "node_modules/nano-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz", + "integrity": "sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==", + "dependencies": { + "big-integer": "^1.6.16" + } + }, "node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", @@ -2617,11 +2859,23 @@ "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/oblivious-set": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz", + "integrity": "sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==" + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -2698,7 +2952,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -2776,6 +3029,16 @@ "node": ">= 0.8.0" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -2828,6 +3091,56 @@ "react": "^18.3.1" } }, + "node_modules/react-icons": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.2.1.tgz", + "integrity": "sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==", + "peerDependencies": { + "react": "*" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-number-format": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-5.3.4.tgz", + "integrity": "sha512-2hHN5mbLuCDUx19bv0Q8wet67QqYK6xmtLQeY5xx+h7UXiMmRtaCwqko4mMPoKXLc6xAzwRrutg8XbTRlsfjRg==", + "dependencies": { + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-query": { + "version": "3.39.3", + "resolved": "https://registry.npmjs.org/react-query/-/react-query-3.39.3.tgz", + "integrity": "sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "broadcast-channel": "^3.4.1", + "match-sorter": "^6.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", @@ -2837,6 +3150,99 @@ "node": ">=0.10.0" } }, + "node_modules/react-remove-scroll": { + "version": "2.5.10", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.10.tgz", + "integrity": "sha512-m3zvBRANPBw3qxVVjEIPEQinkcwlFZ4qyomuWVpNJdv4c6MvHfXV0C3L9Jx5rr3HeBHKNRX+1jreB5QloDIJjA==", + "dependencies": { + "react-remove-scroll-bar": "^2.3.6", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz", + "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==", + "dependencies": { + "react-style-singleton": "^2.2.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", + "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", + "dependencies": { + "get-nonce": "^1.0.0", + "invariant": "^2.2.4", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-textarea-autosize": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.3.tgz", + "integrity": "sha512-XT1024o2pqCuZSuBt9FwHlaDeNtVrtCXu0Rnz88t1jUGheCLa3PhjE1GH8Ctm2axEtvdCl5SUHYschyQ0L5QHQ==", + "dependencies": { + "@babel/runtime": "^7.20.13", + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, + "node_modules/remove-accents": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", + "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==" + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -2860,7 +3266,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, "dependencies": { "glob": "^7.1.3" }, @@ -3024,6 +3429,11 @@ "node": ">=4" } }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -3063,6 +3473,11 @@ "typescript": ">=4.2.0" } }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -3100,6 +3515,15 @@ "node": ">=14.17" } }, + "node_modules/unload": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz", + "integrity": "sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==", + "dependencies": { + "@babel/runtime": "^7.6.2", + "detect-node": "^2.0.4" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", @@ -3139,6 +3563,84 @@ "punycode": "^2.1.0" } }, + "node_modules/use-callback-ref": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", + "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-composed-ref": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.3.0.tgz", + "integrity": "sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-latest": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.2.1.tgz", + "integrity": "sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==", + "dependencies": { + "use-isomorphic-layout-effect": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", + "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/vite": { "version": "5.2.11", "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.11.tgz", @@ -3221,8 +3723,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/yallist": { "version": "3.1.1", diff --git a/package.json b/package.json index 5d2110a..8f095f3 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,15 @@ "preview": "vite preview" }, "dependencies": { + "@iconify/react": "^4.1.1", + "@mantine/core": "^7.9.1", + "@mantine/hooks": "^7.9.1", + "@tanstack/react-query": "^5.36.0", + "jotai": "^2.8.0", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "react-icons": "^5.2.1", + "react-query": "^3.39.3" }, "devDependencies": { "@types/react": "^18.2.66", diff --git a/src/App.tsx b/src/App.tsx index afe48ac..9ed6af2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,34 +1,24 @@ -import { useState } from 'react' -import reactLogo from './assets/react.svg' -import viteLogo from '/vite.svg' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import './App.css' +import Homepage from './pages/homepage/Homepage' +import { MantineProvider } from '@mantine/core' function App() { - const [count, setCount] = useState(0) + const queryClient = new QueryClient({ + defaultOptions: { + queries: { + gcTime: 1000 * 60 * 60 * 24, // 24 hours + staleTime: 1000 * 60 * 1, // 1 min + }, + }, + }); return ( - <> -
- - Vite logo - - - React logo - -
-

Vite + React

-
- -

- Edit src/App.tsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

- + + + + + ) } diff --git a/src/contexts/HomepageFilters.ts b/src/contexts/HomepageFilters.ts new file mode 100644 index 0000000..a96fcf1 --- /dev/null +++ b/src/contexts/HomepageFilters.ts @@ -0,0 +1,3 @@ +import { atom } from 'jotai'; + +export const HomepageFilters = atom([{value: 'All', label: 'All'}]); diff --git a/src/hooks/index.ts b/src/hooks/index.ts new file mode 100644 index 0000000..b580ef6 --- /dev/null +++ b/src/hooks/index.ts @@ -0,0 +1 @@ +export { useQueryAPI } from './useAPI'; \ No newline at end of file diff --git a/src/hooks/useAPI.ts b/src/hooks/useAPI.ts new file mode 100644 index 0000000..c3b85b1 --- /dev/null +++ b/src/hooks/useAPI.ts @@ -0,0 +1,37 @@ +import { useQuery, useQueryClient } from '@tanstack/react-query'; + +export function useQueryAPI({ endpoint }: { endpoint: string }) { + const queryClient = useQueryClient(); + const callEndpoint = async (endpoint: string) => { + const url = `http://localhost:5000/trigger/${endpoint}`; + const options: object = { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Headers': '*', + 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS', + 'Access-Control-Allow-Credentials': true, + 'Sec-Fetch-Mode': 'no-cors', + 'Sec-Fetch-Site': 'same-site', + }, + }; + const response = await fetch(url, options); + if (!response.ok) { + throw new Error(`${response.status} ${response.statusText}`); + } + const responseData = await response.json(); + return responseData; + }; + + return useQuery({ + queryKey: ['GET', endpoint], + queryFn: () => callEndpoint(endpoint), + retry: false, + refetchOnWindowFocus: false, + refetchOnMount: false, + refetchOnReconnect: false, + staleTime: 30 * (60 * 1000), // 30 minutes + // cacheTime: 60 * (60 * 1000), // 60 minutes + }); +} diff --git a/src/pages/homepage/Homepage.tsx b/src/pages/homepage/Homepage.tsx new file mode 100644 index 0000000..77b39e7 --- /dev/null +++ b/src/pages/homepage/Homepage.tsx @@ -0,0 +1,65 @@ +import { Stack } from '@mantine/core'; +import { useEffect, useState } from 'react'; +import stackClasses from '../../styles/Stack.module.css'; +import {useQueryAPI} from '../../hooks/useAPI'; +import Search from './components/Search'; +import Header from './components/Header'; +import Applications from './components/Applications'; +import Bookmarks from './components/Bookmarks'; +import SettingsButton from './components/SettingsButton'; +import { useAtom } from 'jotai'; +import { HomepageFilters } from '../../contexts/HomepageFilters'; + + +export default function Homepage() { + const { data, isFetching } = useQueryAPI({ endpoint: 'api/messages' }); + const [displayedGreeting, setDisplayedGreeting] = useState(''); + const [homepageFilter, setHomepageFilter] = useAtom(HomepageFilters); + + function date() { + const currentDate = new Date(); + const dateOptions: Intl.DateTimeFormatOptions = { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric', + }; + const date = currentDate.toLocaleDateString('en-GB', dateOptions); + return date; + } + + function greet(list: any) { + const currentTime = new Date(); + const greet = Math.floor(currentTime.getHours() / 6); + const easterEggChance = Math.floor(Math.random() * 100); + let timeBased: any = []; + let easterEggs: any = []; + for (const l of list) { + if (l.easteregg === 'N') { + timeBased = timeBased.concat(l); + } else { + easterEggs = easterEggs.concat(l); + } + } + if (easterEggChance > 10 || easterEggs.length === 0) { + return timeBased[greet].message; + } + return easterEggs[Math.floor(Math.random() * easterEggs.length)].message; + } + + useEffect(() => { + if (data) { + setDisplayedGreeting(greet(data)); + } + }, [isFetching]); + + return ( + + +
+ + + + + ); +} diff --git a/src/pages/homepage/components/AppItem.tsx b/src/pages/homepage/components/AppItem.tsx new file mode 100644 index 0000000..07ee35b --- /dev/null +++ b/src/pages/homepage/components/AppItem.tsx @@ -0,0 +1,52 @@ +import { Icon } from '@iconify/react'; +import { Box, Stack, Text } from '@mantine/core'; +import { useState } from 'react'; +import stacks from '../../../styles/Stack.module.css'; +import texts from '../../../styles/Text.module.css'; + +function AppItem({ url, icon, name }: any) { + const [textDecoration, setTextDecoration] = useState('none'); + + return ( + setTextDecoration('underline')} + onMouseOut={() => setTextDecoration('none')} + className={stacks.appItemBox} + > + + + + + {name} + + {url} + + + + ); +} + +export default AppItem; diff --git a/src/pages/homepage/components/Applications.tsx b/src/pages/homepage/components/Applications.tsx new file mode 100644 index 0000000..7e2bbe2 --- /dev/null +++ b/src/pages/homepage/components/Applications.tsx @@ -0,0 +1,51 @@ +import { Stack, Text } from '@mantine/core'; +import { useEffect, useState } from 'react'; +import {useQueryAPI} from '../../../hooks/useAPI'; +import texts from '../../../styles/Text.module.css'; +import AppItem from './AppItem'; + +function Applications() { + const { data, isFetching } = useQueryAPI({ endpoint: 'api/apps' }); + const [applications, setApplications] = useState([]); + const contentType = localStorage.getItem('contenttype'); + useEffect(() => { + if (!isFetching) { + setApplications(data); + } + }, [isFetching]); + + return ( + + Applications + + {applications?.map((item: any) => { + if (item.category === contentType || contentType === 'All') + return ( + + ); + })} + + + ); +} + +export default Applications; diff --git a/src/pages/homepage/components/BookmarkGroups.tsx b/src/pages/homepage/components/BookmarkGroups.tsx new file mode 100644 index 0000000..fa778e9 --- /dev/null +++ b/src/pages/homepage/components/BookmarkGroups.tsx @@ -0,0 +1,15 @@ +import BookmarkDetails from './BookmarkDetails'; + +function BookmarkGroups(props) { + const links = props.links; + return ( +
+

{links?.category}

+ {links?.map((links) => ( + + ))} +
+ ); +} + +export default BookmarkGroups; diff --git a/src/pages/homepage/components/Bookmarks.tsx b/src/pages/homepage/components/Bookmarks.tsx new file mode 100644 index 0000000..8629a8b --- /dev/null +++ b/src/pages/homepage/components/Bookmarks.tsx @@ -0,0 +1,62 @@ +import { Stack, Text } from '@mantine/core'; +import { useEffect, useState } from 'react'; +import {useQueryAPI} from '../../../hooks/useAPI'; +import texts from '../../../styles/Text.module.css'; +import LinkGroups from './LinkGroups'; + +function Links() { + const { data: linkData, isFetched: linksIsFetched } = useQueryAPI({ + endpoint: 'api/links', + }); + const { data: groupData, isFetched: groupsIsFetched } = useQueryAPI({ + endpoint: 'api/categories', + }); + const [bookmarks, setBookmarks] = useState([]); + const [categories, setCategories] = useState([]); + + useEffect(() => { + if (linksIsFetched && groupsIsFetched) { + setBookmarks(linkData); + setCategories(groupData); + } + }, [linksIsFetched, groupsIsFetched]); + + return ( + <> + {groupsIsFetched && linksIsFetched && categories?.length > 0 && ( + + Bookmarks + + {categories.map((category: any) => { + if ( + category.column_filter === localStorage.getItem('contenttype') || + localStorage.getItem('contenttype') === 'All' + ) { + return ( + bookmark.category === category.category_index, + )} + /> + ); + } + })} + + + )} + + ); +} + +export default Links; diff --git a/src/pages/homepage/components/Header.tsx b/src/pages/homepage/components/Header.tsx new file mode 100644 index 0000000..7fdc0dd --- /dev/null +++ b/src/pages/homepage/components/Header.tsx @@ -0,0 +1,74 @@ +import { Stack, Text } from '@mantine/core'; +import { useEffect, useState } from 'react'; +import {textClasses} from '../../../styles'; + +function Header({ greet, date }: any) { + const [time, setTime] = useState(new Date()); + + function fixDigits(k: any) { + if (k < 10) { + return `0${k}`; + } + return k; + } + + useEffect(() => { + setInterval(() => { + const nwDate = new Date(); + setTime(nwDate); + }, 1000); + }, []); + + return ( + + + {greet} + + + + {date} + + + {`${ + time.getHours() === 0 + ? 12 + : time.getHours() > 12 + ? time.getHours() - 12 + : time.getHours() + }:${fixDigits(time.getMinutes())} ${time.getHours() >= 12 ? 'PM' : 'AM'}`} + + + + ); +} + +export default Header; diff --git a/src/pages/homepage/components/LinkGroups.tsx b/src/pages/homepage/components/LinkGroups.tsx new file mode 100644 index 0000000..265caad --- /dev/null +++ b/src/pages/homepage/components/LinkGroups.tsx @@ -0,0 +1,22 @@ +import { Stack, Text } from '@mantine/core'; +import texts from '../../../styles/Text.module.css'; +import LinkItem from './LinkItem'; + +function LinkGroups({ links, category }: any) { + return ( + + {category} + {links?.map((link: any) => ( + + ))} + + ); +} + +export default LinkGroups; diff --git a/src/pages/homepage/components/LinkItem.tsx b/src/pages/homepage/components/LinkItem.tsx new file mode 100644 index 0000000..d46f9b2 --- /dev/null +++ b/src/pages/homepage/components/LinkItem.tsx @@ -0,0 +1,12 @@ +import { Text } from '@mantine/core'; +import texts from '../../../styles/Text.module.css'; + +function LinkItem({ link }: any) { + return ( + + {link.name} + + ); +} + +export default LinkItem; diff --git a/src/pages/homepage/components/MessageInfo.jsx b/src/pages/homepage/components/MessageInfo.jsx new file mode 100644 index 0000000..9c824a7 --- /dev/null +++ b/src/pages/homepage/components/MessageInfo.jsx @@ -0,0 +1,34 @@ +import { useEffect, useState } from 'react'; +import Messages from '../../../components/Messages'; +import useQueryAPI from '../../../hooks/useAPI'; + +function MessageInfo() { + const messageData = useQueryAPI({ endpoint: 'api/messages' }); + const [greetings, setGreetings] = useState([]); + + useEffect(() => { + if (!messageData.isFetching) { + setGreetings(messageData.data); + } + }, [messageData.isFetching]); + + return ( + <> + {greetings && ( +
+ {greetings.map((greeting, index) => ( + + ))} +
+ )} + + ); +} + +export default MessageInfo; diff --git a/src/pages/homepage/components/Provider.jsx b/src/pages/homepage/components/Provider.jsx new file mode 100644 index 0000000..04510c9 --- /dev/null +++ b/src/pages/homepage/components/Provider.jsx @@ -0,0 +1,12 @@ +function Provider(props) { + return ( + + + {props.provider.name} + + {props.provider.prefix} + + ); +} + +export default Provider; diff --git a/src/pages/homepage/components/Search.tsx b/src/pages/homepage/components/Search.tsx new file mode 100644 index 0000000..1365f5a --- /dev/null +++ b/src/pages/homepage/components/Search.tsx @@ -0,0 +1,75 @@ +import { Stack, TextInput } from "@mantine/core"; +import { textInputClasses } from "../../../styles"; +import { useQueryAPI } from "../../../hooks"; +import { useState } from "react"; + +export default function Search() { + const { data: providers } = useQueryAPI({ endpoint: 'api/providers' }); + + const [providerLabel, setProviderLabel] = useState('Google Search'); + const [providerURL, setProviderURL] = useState('https://www.google.com/search?q='); + + const handleKeyPress = (item: any) => { + const option = item.target.value.substr(1, item.target.value.indexOf(' ') - 1) || item.target.value.substr(1); + const subtext = item.target.value.substr(2 + option.length); + if (item.key === 'Enter') { + if (item.target.value.startsWith('/')) { + open(`${providerURL}${subtext}`, '_self'); + } else if (validURL(item.target.value)) { + if (containsProtocol(item.target.value)) window.location = item.target.value; + else open(`https://${item.target.value}`, '_self'); + } else { + open(`${providerURL}${subtext}`, '_self'); + } + } + } + + function validURL(str: any) { + const pattern = new RegExp( + '^(https?:\\/\\/)?' + // protocol + '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name + '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address + '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path + '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string + '(\\#[-a-z\\d_]*)?$', + 'i', + ); // fragment locator + return !!pattern.test(str); + } + function containsProtocol(str: any) { + const pattern = /^(https?:\/\/){1}.*/i; + return !!pattern.test(str); + } + + return ( + + { + if(item.target.value.startsWith('/')) { + setProviderLabel(providers?.find((p: any) => p.prefix === item.target.value.split(' ')[0]).name); + setProviderURL(providers?.find((p: any) => p.prefix === item.target.value.split(' ')[0]).url); + } else { + setProviderLabel('Google Search'); + setProviderURL('https://www.google.com/search?q='); + } + }} + onKeyUp={(item: any) => { + handleKeyPress(item); + }} + /> + + ); +} \ No newline at end of file diff --git a/src/pages/homepage/components/SettingsButton.tsx b/src/pages/homepage/components/SettingsButton.tsx new file mode 100644 index 0000000..7e625cc --- /dev/null +++ b/src/pages/homepage/components/SettingsButton.tsx @@ -0,0 +1,97 @@ +import { Button, Menu, NavLink, Select, Stack } from '@mantine/core'; +import { useEffect, useState } from 'react'; +import { FiMenu, FiMonitor } from 'react-icons/fi'; +import {useQueryAPI} from '../../../hooks/useAPI'; +import button from '../../../styles/Button.module.css'; +import menu from '../../../styles/Menu.module.css'; +import navLink from '../../../styles/NavLink.module.css'; +import selectClasses from '../../../styles/Select.module.css'; +import { useAtom } from 'jotai'; +import { HomepageFilters } from '../../../contexts/HomepageFilters'; + +function SettingsButton() { + const { data, isFetched } = useQueryAPI({ endpoint: 'api/appCategories' }); + const [categories, setCategories] = useState([{value: 'All', label: 'All'}]); + const [homepageFilter, setHomepageFilter] = useAtom(HomepageFilters); + const classNames = { + root: navLink.root, + body: navLink.body, + section: navLink.section, + label: navLink.label, + description: navLink.description, + chevron: navLink.chevron, + }; + + useEffect(() => { + if(data) { + setCategories(data.map((category: any) => {return {value: category.category, label: category.category}})); + } + }, [data, isFetched]); + + function runWakeCommand() { + const requestOptions = { + method: 'GET', + headers: { 'Content-Type': 'application/json' }, + }; + fetch('/trigger/api/wakeDakota', requestOptions); + } + return ( + + + + + + + + } + onClick={() => runWakeCommand()} + classNames={classNames} + /> + +