mirror of
https://github.com/Lifeforge-app/lifeforge.git
synced 2026-06-28 14:55:45 +00:00
24w15
Former-commit-id: 9df4dbc3772273d29024452577f63b8cfa111347 [formerly 29dc4d2e9a85edffbf207e8ee3af21306722c634] [formerly ff49ec6aaef41900de3ac62d321c6af55e17acb5 [formerly 865d23809a689fb95a94210df62ee972fdbf821f]] Former-commit-id: d45f51f4f143edbddbe17c3d8cf1cb03ffc9d220 [formerly 0db73af8405bc86e7bfc8b42ba046b56145a606b] Former-commit-id: 4d40862c144b50a0c513d536d9c4814aceb30613
This commit is contained in:
@@ -44,8 +44,18 @@ module.exports = {
|
||||
".ts",
|
||||
".tsx",
|
||||
".d.ts"
|
||||
]
|
||||
],
|
||||
"moduleDirectory": ["node_modules", "src/"]
|
||||
},
|
||||
"alias": {
|
||||
"extensions": [".js", ".jsx", ".ts", ".tsx", ".d.ts"],
|
||||
"map": [
|
||||
['@components', './src/components/general/'],
|
||||
['@providers', './src/providers/'],
|
||||
['@hooks', './src/hooks/'],
|
||||
['@sidebar', './src/components/Sidebar/'],
|
||||
],
|
||||
}
|
||||
},
|
||||
"react": {
|
||||
"version": "detect"
|
||||
|
||||
24
keys/cert.pem
Normal file
24
keys/cert.pem
Normal file
@@ -0,0 +1,24 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID7zCCAtegAwIBAgIUNXnBYQb9NkWTygrQz59+iVwTP4gwDQYJKoZIhvcNAQEL
|
||||
BQAwgYYxCzAJBgNVBAYTAk1ZMQ4wDAYDVQQIDAVKb2hvcjEUMBIGA1UEBwwLSm9o
|
||||
b3IgQmFocnUxETAPBgNVBAoMCENvZGVibG9nMRMwEQYDVQQDDApNRUxWSU5DSElB
|
||||
MSkwJwYJKoZIhvcNAQkBFhptZWx2aW5jaGlhNjIzNjAwQGdtYWlsLmNvbTAeFw0y
|
||||
NDA0MTMwODMzNTlaFw0yNTA0MTMwODMzNTlaMIGGMQswCQYDVQQGEwJNWTEOMAwG
|
||||
A1UECAwFSm9ob3IxFDASBgNVBAcMC0pvaG9yIEJhaHJ1MREwDwYDVQQKDAhDb2Rl
|
||||
YmxvZzETMBEGA1UEAwwKTUVMVklOQ0hJQTEpMCcGCSqGSIb3DQEJARYabWVsdmlu
|
||||
Y2hpYTYyMzYwMEBnbWFpbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
|
||||
AoIBAQCsrWnnNFEeMYoxdsWDdDyMv9WRLczSrKVdW4qdl6ooJSQQ5sRNnm/HbvTv
|
||||
NnfZzHSYaLIGGZRv81XnQB1WQVkA6izxx9Jc23ZxqyRMSt3NyWFHP+n4SIOQNVBL
|
||||
OD/0Ite+4qfiHBAjy2E8xWe6UL+sQ1fvZTHXdsGLZimBnkT4nNp68ygsRiRMAeIl
|
||||
e1SbRxUoJ5/4N2Iicxyq5xUqfbXKI3F0HsYAxpWd1UX89Eh845jG87ImxoXrXBLR
|
||||
DtskHGxFHPylIl3CoEcIxek8Uw7HfEPE4otGLNpOaM9For3bpOogkMoLChjiCWcY
|
||||
MOcX5DDQnY0380pntniM2IaNZNKtAgMBAAGjUzBRMB0GA1UdDgQWBBREnrYHXvTb
|
||||
+IkUaAjjaDIHb0aUGDAfBgNVHSMEGDAWgBREnrYHXvTb+IkUaAjjaDIHb0aUGDAP
|
||||
BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAOY//lNrQjFH/dj2ta
|
||||
Wvj2NqVKWWGX7NZy8kNpzwisfOhs0hkf1WGzUj4uTEthk+SZCgmgi5x9G/QJ7E9Y
|
||||
gnuVvsa10CJ2rAnlCC83IEx6TO9vkoOWKYwltNTaAXit5o7aajU1eiIBJ1Z6+sUq
|
||||
+Z1bBGLIHb0csGa5wVYkRhSBaA6wiRGo8KekITG0VXP/f2rQWmTjkMZpBQVNB487
|
||||
xNs8O4DnRE2mjiWM2EaNeD6zWG18xckUK7Wace9hGDBYCfRR5ANt67I6P/Tj7axp
|
||||
pbMF5SAUZ9W4RJ0asr1LxGcsAip4vRTD1NgSayCDmU8wQ/tQsOpxHxJIZmFkayhi
|
||||
SVPx
|
||||
-----END CERTIFICATE-----
|
||||
28
keys/key.pem
Normal file
28
keys/key.pem
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCsrWnnNFEeMYox
|
||||
dsWDdDyMv9WRLczSrKVdW4qdl6ooJSQQ5sRNnm/HbvTvNnfZzHSYaLIGGZRv81Xn
|
||||
QB1WQVkA6izxx9Jc23ZxqyRMSt3NyWFHP+n4SIOQNVBLOD/0Ite+4qfiHBAjy2E8
|
||||
xWe6UL+sQ1fvZTHXdsGLZimBnkT4nNp68ygsRiRMAeIle1SbRxUoJ5/4N2Iicxyq
|
||||
5xUqfbXKI3F0HsYAxpWd1UX89Eh845jG87ImxoXrXBLRDtskHGxFHPylIl3CoEcI
|
||||
xek8Uw7HfEPE4otGLNpOaM9For3bpOogkMoLChjiCWcYMOcX5DDQnY0380pntniM
|
||||
2IaNZNKtAgMBAAECggEAC3XlqH21azlDKxiZ7/6VTODSjjR++qCQx8sdPmu5fw6a
|
||||
qoXWaNxnc3zEGY2rk14ELbVifhB75JjpjlPtXoiEcrttc41H7ffMOwEZLfhuE4wW
|
||||
aRcvMoEFklr80vE5yryhRb8KAMd3XsN51wG3FQuU8nGfHXlsPxkCVtEsrLinao9K
|
||||
ZJfxWM+B6FkPlUr4VXrQNnuYE28de+UKwsaMkQHT2Q6AgntM5lKqKM9m0i9xNS37
|
||||
rH4GYHUG6/Rw5uChv22Qi2V7OlJf9VecRL2UpRtnCwEZ3W5Q1fbNV6fluMYcHwNj
|
||||
AR3fjbZw2mx6twXWpCke2rx9zm3TgPwIlRCYQyOllQKBgQDuKo7CPbOuSp9Yp7jM
|
||||
OuAcjo9yhVs0WNPcCbwS7kwABFzTdlsODJeDlCyPr0C+MyqGMrbg50gAF8tBGegd
|
||||
NeWc56TJFSnZGpxpOZkUJoKS+8mp2IPKx0ezoVnf1+VaoQ7C1txHjOQ6F0egborO
|
||||
G6CJHhPD+OdXwye7r+shku9hbwKBgQC5m326nomrPRDZq5hWAmbpJJUQh5DZY4lR
|
||||
3X7vgY+mu2qX5BNdxVJV46oba9bZh3hmQztBb8pdWtUjhl5IsAt0fsKn1RcMKEkk
|
||||
ZSWSDEvz5R1VNUfriqDrhPG9lAFpIHHRuOO7HblY8aP8uJDAwv4gb+7jB4xfokoZ
|
||||
VpIDMkxHowKBgQDdZR5UHnt7V88M4Mz+vPQ8V6GteZ4w6Sn4b+wvROnNUkDgKjO2
|
||||
uanXHNTXtLhglru/CEHMi/L4cyX0nJqTHNkXpJYgftXqBbzEbFCK1MmozPrIModA
|
||||
50MWDrW3ScDhILrSEF2w/Z3jCmUBFAFgW3o+PIFvOWAEy1uSM5RGH4r7bQKBgGWp
|
||||
PRxSaaVZNTmEO1QgzK02VE9RcY0gb75fS39zv3LbhG7dhZ3+zjItGKccUfmjsdaR
|
||||
TIty56xqbAOhPVMi2aNdKtEybgVCq407uXBdO9zCyWY6YMIqlClsD9x+jKdAFheA
|
||||
5Xs/tJ16/Q8XKHHsW7T4mifPoptfEKcAO8l0a0NxAoGAAW5PkDeLAZIvh0kp+FB4
|
||||
+I2/ZF9MXBzsmHvHjL183Dp1L8uPKvLJfsiqM1TuT+8CjQU0o9LeaWP2Gl9KBW4y
|
||||
RONsiJ8RmF8StjYaqiMuQwkeS9duTWC6hdcjc4WHoT+9HJ/fvy9sd21HtFYRplOj
|
||||
PPn4imkWznFC4DzeUHRJQfg=
|
||||
-----END PRIVATE KEY-----
|
||||
30
keys/keytmp.pem
Normal file
30
keys/keytmp.pem
Normal file
@@ -0,0 +1,30 @@
|
||||
-----BEGIN ENCRYPTED PRIVATE KEY-----
|
||||
MIIFJDBWBgkqhkiG9w0BBQ0wSTAxBgkqhkiG9w0BBQwwJAQQ4x3R3RfiB8csIoHc
|
||||
ggVb6wICCAAwDAYIKoZIhvcNAgkFADAUBggqhkiG9w0DBwQIaf/S8DLOO7sEggTI
|
||||
3CcHFPYtq4VixpEMMS3td2qMYc/9JpINOlIOEH3b+qVGdGQVzUY26EpTyVi3nd19
|
||||
m/RlSwxLdRxOgiFx95FHP7KmarE63AY694SAHItzX6noH5ieqtswDsKKsuaY6m+8
|
||||
EBhAbMys4f1Rx/qZ5cOKH6YpbMhvKktg4VPLXmY/TwlABIMEquj+U93/UDM5tKXE
|
||||
6oUux7eBjheQnpmUSkFhwMKaV7m26D/9GA1+ybS2hgomY2uFUcPGChQBJKsaXIjM
|
||||
j7Ngo2FXVzklqbmmgiIWGnkGTdq/zlKsWPHsr+Nu5cx6pPSKN7eDdvYACpJAo7Hw
|
||||
rkk9sNsVVRSx8ghOh9wsPZEI+zCl7+YFM9MsTUmV/TSl6M8KgG9l7nj53X59eVl4
|
||||
zJx5YseuMi47epmb6BQYn9U2p6famJlcdIdry2EMfCp1myemE5JYbLw9zbhDyAy5
|
||||
VGHZjAskGKkZTdgwP2EDSL12yVMgvcCZOf1BzJLekzJV5BnprHY5vqu/9tn4cI/1
|
||||
YEukO7ayEObwS+0QD82R91ZHwNLC52/HpkIqfpaTMJWTnLccg5IvtHPMDzv3r2Pa
|
||||
HjCOWeWhNoaBWwOGK6ldBe2PM+20TS9ExpNnoSQNR62kO/wEOcu6EaBLGdI56kmD
|
||||
8c2xJEKTza+bbADTBS5PLd6SDYJRPae8ExVIOTrgUomE8LVoL/Ux5D32057GFE/z
|
||||
TPtcx1A/5XAZbIpW598AZr763hElqi/paEDRsxFnlpY3nF7pJ6aU/gWW4xgYhuug
|
||||
2o10nSNMm6Fg34vYXsouK5vMEZZlcb4Jst0k8vZMNCSO48nKNuxUaeHmsYL5JVFC
|
||||
mEfdWY6quwQ1OU78kceGmasz0T3U0OLqY+ytjwAkcWhF4v3STDRVuZMLJ+7dByPC
|
||||
sYU2i8JOksG6yeuQasv8toQ34QEhixzYJp64j06KgmrgVUyUMrzQV7Sh2gkZPd4Z
|
||||
BQ+82dDK0p99vBEpCp5oDzHDIxNG/p83Y0cgY6D3Dl4hwrpxcuHmJFF4Wk9bdY/y
|
||||
s7wgmQrqALI9FaEwkPctpAkXoobw5JRiJHw65a+wCGH05H9vhWiKh6qiPez0sSqa
|
||||
WzkmDtGvkxcsT8Cs3RZG21wij3M3uQqEkqlEXzZv9YDSffwuuXEwJEfSiEjT2bW4
|
||||
OprTdTnfeM4M+VtEqTqtK+JTHLoO3X8VvGkciE6qUG4V9/bvVcytEmwfYWoP5JSq
|
||||
v+JruxLE9HPNKFWuVUv5UZYVckN4DYV0EawHVoOuubOmw1K0vRIvks3vPl146VrO
|
||||
MoFdy09ZG8RSOS+hdTE39aHE/0H3yWzlv66ZcvQBsNO+L0wPqeZf7uPW9cHMvCR3
|
||||
TyDVa+kGUE/0Lyq+sm0EjjEVt/aZW7ZPLXBEuhvCDiZMgH4BbyMfiPWIxjmc/AkB
|
||||
URbR2GNAq3q1+z1VXYj8oTVLyXsQ12AG4gpHDwIAt/KQq3jBsCmMHGCf5iYHnolw
|
||||
t/vq3xwnxdS7W6qeC2uQpP2ESVc73NXtKYKOcq3C/cVqA6QoPVQmOnywUKkQWejq
|
||||
ltG3MGHg18VlvpY3yodo3emSGGN7qeSX05lfQm3axcrQbHd5fUII9ZkTDrB4ezTC
|
||||
5qvopDwCAyJIBKhVFoJluyXpxt/O4Lmu
|
||||
-----END ENCRYPTED PRIVATE KEY-----
|
||||
@@ -14,6 +14,7 @@
|
||||
"@headlessui/react": "^1.7.17",
|
||||
"@iconify/collections": "^1.0.375",
|
||||
"@iconify/react": "^4.1.1",
|
||||
"@passwordless-id/webauthn": "^1.5.0",
|
||||
"@react-pdf-viewer/core": "3.12.0",
|
||||
"@types/react-custom-scrollbars": "^4.0.12",
|
||||
"@types/react-helmet": "^6.1.11",
|
||||
@@ -31,6 +32,7 @@
|
||||
"cors": "^2.8.5",
|
||||
"daisyui": "^4.4.14",
|
||||
"moment": "^2.30.1",
|
||||
"path": "^0.12.7",
|
||||
"pdfjs-dist": "3.4.120",
|
||||
"pocketbase": "^0.19.0",
|
||||
"postcss": "^8.4.31",
|
||||
@@ -64,6 +66,7 @@
|
||||
"@vitejs/plugin-react": "^4.2.0",
|
||||
"eslint": "^8.0.1",
|
||||
"eslint-config-standard-with-typescript": "^40.0.0",
|
||||
"eslint-import-resolver-alias": "^1.1.2",
|
||||
"eslint-plugin-import": "^2.29.0",
|
||||
"eslint-plugin-n": "^15.0.0 || ^16.0.0 ",
|
||||
"eslint-plugin-promise": "^6.0.0",
|
||||
@@ -72,6 +75,7 @@
|
||||
"eslint-plugin-react-refresh": "^0.4.4",
|
||||
"eslint-plugin-tailwindcss": "^3.14.3",
|
||||
"typescript": "*",
|
||||
"vite": "^5.0.0"
|
||||
"vite": "^5.0.0",
|
||||
"vite-plugin-mkcert": "^1.17.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@ import React from 'react'
|
||||
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import AppRouter from './Router'
|
||||
import GlobalStateProvider from './providers/GlobalStateProvider'
|
||||
import GlobalStateProvider from '@providers/GlobalStateProvider'
|
||||
import { ToastContainer } from 'react-toastify'
|
||||
import AuthProvider from './providers/AuthProvider'
|
||||
import PersonalizationProvider from './providers/PersonalizationProvider'
|
||||
import SpotifyProvider from './providers/SpotifyProvider'
|
||||
import AuthProvider from '@providers/AuthProvider'
|
||||
import PersonalizationProvider from '@providers/PersonalizationProvider'
|
||||
import SpotifyProvider from '@providers/SpotifyProvider'
|
||||
|
||||
function App(): React.ReactElement {
|
||||
return (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react'
|
||||
import Sidebar from './components/Sidebar'
|
||||
import Sidebar from '@sidebar'
|
||||
import Header from './components/Header'
|
||||
import { Outlet } from 'react-router'
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||
import React, { Suspense, lazy, useContext, useMemo } from 'react'
|
||||
import { Route, Routes, useLocation, useNavigate } from 'react-router-dom'
|
||||
import { AuthContext } from './providers/AuthProvider'
|
||||
import { titleToPath } from './components/Sidebar/components/SidebarItem'
|
||||
import Loading from './components/general/Loading'
|
||||
import { AuthContext } from '@providers/AuthProvider'
|
||||
import { titleToPath } from '@sidebar/components/SidebarItem'
|
||||
import Loading from '@components/Loading'
|
||||
import PhotosFavouritesGallery from './modules/Photos/pages/FavouritesGallery'
|
||||
|
||||
const PhotosProvider = lazy(
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React, { useContext, useState } from 'react'
|
||||
import Input from '../../components/general/Input'
|
||||
import Input from '@components/Input'
|
||||
import AuthSignInButton from './AuthSignInButtons'
|
||||
import { AuthContext } from '../../providers/AuthProvider'
|
||||
import { AuthContext } from '@providers/AuthProvider'
|
||||
import { toast } from 'react-toastify'
|
||||
import { AUTH_ERROR_MESSAGES } from '../../constants/auth'
|
||||
import * as webauthn from '@passwordless-id/webauthn'
|
||||
|
||||
function AuthForm(): React.ReactElement {
|
||||
const [emailOrUsername, setEmail] = useState('')
|
||||
@@ -11,11 +12,37 @@ function AuthForm(): React.ReactElement {
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const {
|
||||
setAuth,
|
||||
setUserData,
|
||||
authenticate,
|
||||
authWithOauth,
|
||||
loginQuota: { quota, dismissQuota }
|
||||
loginQuota: { quota, dismissQuota },
|
||||
verifyToken
|
||||
} = useContext(AuthContext)
|
||||
|
||||
async function fetchPassKeyChallenge(): Promise<string> {
|
||||
return await fetch(
|
||||
`${import.meta.env.VITE_API_HOST}/user/passkey/challenge`,
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}
|
||||
)
|
||||
.then(async res => {
|
||||
const data = await res.json()
|
||||
if (res.ok && data.state === 'success') {
|
||||
return data.data
|
||||
} else {
|
||||
throw new Error(data.message)
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
toast.error('Couldn’t fetch the challenge. Please try again.')
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
function signIn(): void {
|
||||
if (emailOrUsername.length === 0 || password.length === 0) {
|
||||
toast.error(AUTH_ERROR_MESSAGES.EMPTY_FIELDS)
|
||||
@@ -43,19 +70,91 @@ function AuthForm(): React.ReactElement {
|
||||
})
|
||||
}
|
||||
|
||||
function signInWithGithub(): void {
|
||||
setLoading(true)
|
||||
authWithOauth('github')
|
||||
.then(res => {
|
||||
if (!res.startsWith('success')) {
|
||||
toast.error(res)
|
||||
async function registerWithPasskey(): Promise<void> {
|
||||
const res = await webauthn.client.register(
|
||||
'melvinchia623600@gmail.com',
|
||||
'20e47b44-293a-417a-8559-d7f32affd8b4',
|
||||
{
|
||||
authenticatorType: 'both',
|
||||
userVerification: 'required',
|
||||
discoverable: 'preferred',
|
||||
timeout: 60000,
|
||||
attestation: true
|
||||
}
|
||||
)
|
||||
|
||||
await fetch(`${import.meta.env.VITE_API_HOST}/user/passkey/register`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(res)
|
||||
})
|
||||
.then(async res => {
|
||||
const data = await res.json()
|
||||
if (res.ok && data.state === 'success') {
|
||||
toast.success('Passkey created successfully')
|
||||
} else {
|
||||
toast.success('Welcome back, ' + res.split(' ').slice(1).join(' '))
|
||||
throw new Error(data.message)
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
toast.error("Couldn't create the passkey. Please try again.")
|
||||
console.error(err)
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false)
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error(AUTH_ERROR_MESSAGES.UNKNOWN_ERROR)
|
||||
}
|
||||
|
||||
async function signInWithPasskey(): Promise<void> {
|
||||
setLoading(true)
|
||||
const challenge = await fetchPassKeyChallenge()
|
||||
|
||||
const res = await webauthn.client.authenticate([], challenge)
|
||||
|
||||
await fetch(`${import.meta.env.VITE_API_HOST}/user/passkey/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(res)
|
||||
})
|
||||
.then(async res => {
|
||||
const data = await res.json()
|
||||
|
||||
if (res.ok && data.state === 'success') {
|
||||
document.cookie = `token=${data.token}; path=/; expires=${new Date(
|
||||
Date.now() + 7 * 24 * 60 * 60 * 1000
|
||||
).toUTCString()}`
|
||||
|
||||
verifyToken(data.token)
|
||||
.then(async ({ success, userData }) => {
|
||||
if (success) {
|
||||
setUserData(userData)
|
||||
setAuth(true)
|
||||
|
||||
toast.success(`Welcome back, ${userData.name}!`)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
setAuth(false)
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false)
|
||||
})
|
||||
} else {
|
||||
throw new Error(data.message)
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
toast.error(
|
||||
'Failed to authenticate with the passkey. Please try again.'
|
||||
)
|
||||
console.error(err)
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -82,6 +181,7 @@ function AuthForm(): React.ReactElement {
|
||||
signIn()
|
||||
}
|
||||
}}
|
||||
darker
|
||||
/>
|
||||
<Input
|
||||
name="Password"
|
||||
@@ -95,13 +195,14 @@ function AuthForm(): React.ReactElement {
|
||||
signIn()
|
||||
}
|
||||
}}
|
||||
darker
|
||||
/>
|
||||
<AuthSignInButton
|
||||
emailOrUsername={emailOrUsername}
|
||||
password={password}
|
||||
loading={loading}
|
||||
signIn={signIn}
|
||||
signInWithGithub={signInWithGithub}
|
||||
signInWithPasskey={signInWithPasskey}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useContext } from 'react'
|
||||
import { AuthContext } from '../../providers/AuthProvider'
|
||||
import { AuthContext } from '@providers/AuthProvider'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
|
||||
function AuthSignInButton({
|
||||
@@ -7,13 +7,13 @@ function AuthSignInButton({
|
||||
password,
|
||||
loading,
|
||||
signIn,
|
||||
signInWithGithub
|
||||
signInWithPasskey
|
||||
}: {
|
||||
emailOrUsername: string
|
||||
password: string
|
||||
loading: boolean
|
||||
signIn: () => void
|
||||
signInWithGithub: () => void
|
||||
signInWithPasskey: () => void
|
||||
}): React.ReactElement {
|
||||
const { auth } = useContext(AuthContext)
|
||||
|
||||
@@ -31,14 +31,21 @@ function AuthSignInButton({
|
||||
>
|
||||
{loading ? <Icon icon="svg-spinners:180-ring" /> : 'Sign In'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={signInWithGithub}
|
||||
className="flex items-center justify-center gap-3 rounded-lg bg-bg-400 p-6 font-semibold uppercase tracking-widest text-bg-100 transition-all hover:bg-bg-500 dark:bg-bg-800 dark:hover:bg-bg-700"
|
||||
>
|
||||
<Icon icon="tabler:brand-github" className="text-2xl" />
|
||||
Sign In with Github
|
||||
</button>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="h-[2px] w-full bg-bg-600"></div>
|
||||
<div className="shrink-0 font-medium text-bg-600">Or Sign In With</div>
|
||||
<div className="h-[2px] w-full bg-bg-600"></div>
|
||||
</div>
|
||||
<div className="flex w-full gap-4">
|
||||
<button
|
||||
type="button"
|
||||
onClick={signInWithPasskey}
|
||||
className="flex w-full items-center justify-center gap-3 rounded-lg bg-bg-400 p-6 font-semibold uppercase tracking-widest text-bg-100 transition-all hover:bg-bg-500 dark:bg-bg-800 dark:hover:bg-bg-700"
|
||||
>
|
||||
<Icon icon="tabler:key" className="text-2xl" />
|
||||
Passkey
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { Fragment, useContext } from 'react'
|
||||
import { Icon } from '@iconify/react'
|
||||
import { GlobalStateContext } from '../providers/GlobalStateProvider'
|
||||
import { AuthContext } from '../providers/AuthProvider'
|
||||
import { GlobalStateContext } from '@providers/GlobalStateProvider'
|
||||
import { AuthContext } from '@providers/AuthProvider'
|
||||
import { Menu, Transition } from '@headlessui/react'
|
||||
import { toast } from 'react-toastify'
|
||||
import MenuItem from './general/HamburgerMenu/MenuItem'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useContext } from 'react'
|
||||
import { GlobalStateContext } from '../../../providers/GlobalStateProvider'
|
||||
import { GlobalStateContext } from '@providers/GlobalStateProvider'
|
||||
import { Icon } from '@iconify/react'
|
||||
|
||||
function SidebarHeader(): React.ReactElement {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/* eslint-disable multiline-ternary */
|
||||
import { Icon } from '@iconify/react'
|
||||
import React, { useContext, useEffect, useState } from 'react'
|
||||
import { GlobalStateContext } from '../../../providers/GlobalStateProvider'
|
||||
import { GlobalStateContext } from '@providers/GlobalStateProvider'
|
||||
import { useLocation } from 'react-router'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
/* eslint-disable multiline-ternary */
|
||||
import React, { useContext, useEffect, useState } from 'react'
|
||||
import { GlobalStateContext } from '../../../providers/GlobalStateProvider'
|
||||
import { GlobalStateContext } from '@providers/GlobalStateProvider'
|
||||
import SidebarItem, { titleToPath } from './SidebarItem'
|
||||
import SidebarTitle from './SidebarTitle'
|
||||
import SidebarDivider from './SidebarDivider'
|
||||
import { type INotesWorkspace } from '../../../modules/Notes'
|
||||
import useFetch from '../../../hooks/useFetch'
|
||||
import { AuthContext } from '../../../providers/AuthProvider'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import { AuthContext } from '@providers/AuthProvider'
|
||||
import { ROUTES } from '../../../Router'
|
||||
|
||||
function SidebarItems(): React.ReactElement {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable multiline-ternary */
|
||||
import React, { useContext } from 'react'
|
||||
import { GlobalStateContext } from '../../providers/GlobalStateProvider'
|
||||
import { GlobalStateContext } from '@providers/GlobalStateProvider'
|
||||
import SidebarItems from './components/SidebarItems'
|
||||
import SidebarHeader from './components/SidebarHeader'
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/* eslint-disable multiline-ternary */
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import React, { useContext } from 'react'
|
||||
import { PersonalizationContext } from '../../providers/PersonalizationProvider'
|
||||
import { PersonalizationContext } from '@providers/PersonalizationProvider'
|
||||
|
||||
function CreateOrModifyButton({
|
||||
type,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/* eslint-disable multiline-ternary */
|
||||
/* eslint-disable @typescript-eslint/indent */
|
||||
import React from 'react'
|
||||
import ModuleHeader from '../../components/general/ModuleHeader'
|
||||
import ModuleHeader from '@components/ModuleHeader'
|
||||
import { Icon } from '@iconify/react'
|
||||
import ModuleWrapper from '../../components/general/ModuleWrapper'
|
||||
import ModuleWrapper from '@components/ModuleWrapper'
|
||||
|
||||
function Calendar(): React.ReactElement {
|
||||
return (
|
||||
|
||||
@@ -7,15 +7,14 @@ function LogItemContent({
|
||||
entry: IChangeLogVersion
|
||||
}): React.ReactElement {
|
||||
return (
|
||||
<ul className="flex list-inside list-disc flex-col gap-2 text-bg-500 dark:text-bg-500">
|
||||
<ul className="list-inside list-disc space-y-2">
|
||||
{entry.entries
|
||||
.sort((a, b) => a.feature.localeCompare(b.feature))
|
||||
.map(subEntry => (
|
||||
<li key={subEntry.id}>
|
||||
<span className="font-semibold text-bg-800 dark:text-bg-100">
|
||||
{subEntry.feature}:
|
||||
</span>{' '}
|
||||
<span className="font-semibold">{subEntry.feature}:</span>{' '}
|
||||
<span
|
||||
className="text-bg-500 dark:text-bg-500"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: subEntry.description.replace(
|
||||
/<code>(.*?)<\/code>/g,
|
||||
|
||||
@@ -9,19 +9,17 @@ function LogItemHeader({
|
||||
return (
|
||||
<h3 className="mb-2 flex flex-col gap-2 text-2xl font-semibold sm:flex-row sm:items-end">
|
||||
Ver. {entry.version}{' '}
|
||||
<span className="mb-0.5 block text-sm">
|
||||
<span className="mb-0.5 text-sm">
|
||||
(
|
||||
{new Date(entry.date_range[0]).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
})}{' '}
|
||||
-{' '}
|
||||
{new Date(entry.date_range[1]).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
})}
|
||||
{entry.date_range
|
||||
.map(date =>
|
||||
new Date(date).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
})
|
||||
)
|
||||
.join(' - ')}
|
||||
)
|
||||
</span>
|
||||
</h3>
|
||||
|
||||
@@ -7,7 +7,7 @@ function LogItem({ entry }: { entry: IChangeLogVersion }): React.ReactElement {
|
||||
return (
|
||||
<li
|
||||
key={entry.version}
|
||||
className="flex flex-col gap-2 rounded-lg bg-bg-50 p-6 shadow-[4px_4px_10px_0px_rgba(0,0,0,0.05)] dark:bg-bg-900"
|
||||
className="space-y-2 rounded-lg bg-bg-50 p-6 shadow-[4px_4px_10px_0px_rgba(0,0,0,0.05)] dark:bg-bg-900"
|
||||
>
|
||||
<LogItemHeader entry={entry} />
|
||||
<LogItemContent entry={entry} />
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
/* eslint-disable @typescript-eslint/no-throw-literal */
|
||||
/* eslint-disable @typescript-eslint/indent */
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import ModuleHeader from '../../components/general/ModuleHeader'
|
||||
import useFetch from '../../hooks/useFetch'
|
||||
import ModuleHeader from '@components/ModuleHeader'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import LogItem from './components/LogItem'
|
||||
import APIComponentWithFallback from '../../components/general/APIComponentWithFallback'
|
||||
import ModuleWrapper from '../../components/general/ModuleWrapper'
|
||||
import EmptyStateScreen from '../../components/general/EmptyStateScreen'
|
||||
import SearchInput from '../../components/general/SearchInput'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
import ModuleWrapper from '@components/ModuleWrapper'
|
||||
import EmptyStateScreen from '@components/EmptyStateScreen'
|
||||
import SearchInput from '@components/SearchInput'
|
||||
|
||||
export interface IChangeLogVersion {
|
||||
version: string
|
||||
@@ -66,7 +66,7 @@ function Changelog(): React.ReactElement {
|
||||
stuffToSearch="features"
|
||||
/>
|
||||
<APIComponentWithFallback data={data}>
|
||||
<ul className="my-8 flex flex-1 flex-col gap-4">
|
||||
<ul className="my-8 space-y-4">
|
||||
{typeof data !== 'string' &&
|
||||
(filteredData.length > 0 ? (
|
||||
filteredData.map(entry => (
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import React, { useContext, useEffect, useState } from 'react'
|
||||
import ActivityCalendar from 'react-activity-calendar'
|
||||
import THEME_COLOR_HEX from '../../../constants/theme_color_hex'
|
||||
import { PersonalizationContext } from '../../../providers/PersonalizationProvider'
|
||||
import { PersonalizationContext } from '@providers/PersonalizationProvider'
|
||||
import { Tooltip } from 'react-tooltip'
|
||||
|
||||
function CodeTimeActivityCalendar(): React.ReactElement {
|
||||
@@ -43,8 +43,8 @@ function CodeTimeActivityCalendar(): React.ReactElement {
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className="mt-16 flex w-full flex-col gap-6">
|
||||
<h1 className="mb-2 flex items-center gap-2 text-2xl font-semibold">
|
||||
<div className="space-y-2">
|
||||
<h1 className="flex items-center gap-2 text-2xl font-semibold">
|
||||
<Icon icon="tabler:activity" className="text-3xl" />
|
||||
<span className="ml-2">Activities Calendar</span>
|
||||
</h1>
|
||||
@@ -120,7 +120,7 @@ function CodeTimeActivityCalendar(): React.ReactElement {
|
||||
</div>
|
||||
<Tooltip id="react-tooltip" className="z-[9999]" />
|
||||
{firstYear && (
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="space-y-2">
|
||||
{Array(new Date().getFullYear() - firstYear + 1)
|
||||
.fill(0)
|
||||
.map((_, index) => (
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/restrict-plus-operands */
|
||||
/* eslint-disable @typescript-eslint/indent */
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import React, { useState } from 'react'
|
||||
import HoursAndMinutesFromSeconds from './HoursAndMinutesFromSeconds'
|
||||
import useFetch from '../../../hooks/useFetch'
|
||||
import APIComponentWithFallback from '../../../components/general/APIComponentWithFallback'
|
||||
|
||||
function CodeTimeMostProjects(): React.ReactElement {
|
||||
const [lastForProjects, setLastForProjects] = useState<
|
||||
'24 hours' | '7 days' | '30 days'
|
||||
>('24 hours')
|
||||
const [topProjects] = useFetch<Record<string, number>>(
|
||||
`code-time/projects?last=${lastForProjects}`
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="mt-16 flex w-full flex-col gap-6">
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<h1 className="mb-2 flex items-center gap-2 text-2xl font-semibold">
|
||||
<Icon icon="tabler:clipboard" className="text-3xl" />
|
||||
<span className="ml-2">
|
||||
Projects You've Spent Most Time Doing
|
||||
</span>
|
||||
</h1>
|
||||
<div className="flex items-center gap-2">
|
||||
<p className="font-medium tracking-wider">in the last</p>
|
||||
<div className="flex gap-2 rounded-lg p-2">
|
||||
{['24 hours', '7 days', '30 days'].map((last, index) => (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => {
|
||||
setLastForProjects(last as '24 hours' | '7 days' | '30 days')
|
||||
}}
|
||||
className={`rounded-md p-4 px-6 tracking-wide ${
|
||||
lastForProjects === last
|
||||
? 'bg-bg-200 font-semibold text-bg-800 dark:bg-bg-700/50 dark:text-bg-100'
|
||||
: 'text-bg-500 hover:bg-bg-200/50 dark:hover:bg-bg-700/50'
|
||||
}`}
|
||||
>
|
||||
{last}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<APIComponentWithFallback data={topProjects}>
|
||||
<div className="flex w-full">
|
||||
{typeof topProjects !== 'string' &&
|
||||
Object.keys(topProjects).length > 0 &&
|
||||
Object.entries(topProjects)
|
||||
.slice(0, 5)
|
||||
.map(([key, value], index) => (
|
||||
<div
|
||||
className={`h-6 border ${index === 0 && 'rounded-l-lg'} ${
|
||||
index === 4 && 'rounded-r-lg'
|
||||
} ${
|
||||
[
|
||||
'bg-red-500/20 border-red-500',
|
||||
'bg-orange-500/20 border-orange-500',
|
||||
'bg-yellow-500/20 border-yellow-500',
|
||||
'bg-blue-500/20 border-blue-500',
|
||||
'bg-emerald-500/20 border-emerald-500'
|
||||
][index]
|
||||
}`}
|
||||
key={key}
|
||||
style={{
|
||||
width: `${Math.round(
|
||||
(value /
|
||||
Object.entries(topProjects)
|
||||
.slice(0, 5)
|
||||
.reduce((a, b) => a + b[1], 0)) *
|
||||
100
|
||||
)}%`
|
||||
}}
|
||||
></div>
|
||||
))}
|
||||
</div>
|
||||
<ul className="flex flex-col gap-4">
|
||||
{topProjects !== null &&
|
||||
Object.keys(topProjects).length > 0 &&
|
||||
Object.entries(topProjects)
|
||||
.slice(0, 5)
|
||||
.map(([key, value], index) => (
|
||||
<li
|
||||
key={key}
|
||||
className="relative flex items-center justify-between gap-4 rounded-lg bg-bg-50 p-6 shadow-[4px_4px_10px_0px_rgba(0,0,0,0.05)] dark:bg-bg-900"
|
||||
>
|
||||
<div className="flex items-center gap-4 text-lg font-medium">
|
||||
<div
|
||||
className={`h-4 w-4 rounded-md border ${
|
||||
[
|
||||
'bg-red-500/20 border-red-500',
|
||||
'bg-orange-500/20 border-orange-500',
|
||||
'bg-yellow-500/20 border-yellow-500',
|
||||
'bg-blue-500/20 border-blue-500',
|
||||
'bg-emerald-500/20 border-emerald-500'
|
||||
][index]
|
||||
} rounded-full`}
|
||||
></div>
|
||||
{key}
|
||||
</div>
|
||||
<div className="text-3xl font-semibold">
|
||||
<HoursAndMinutesFromSeconds seconds={value} />
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</APIComponentWithFallback>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CodeTimeMostProjects
|
||||
@@ -5,8 +5,8 @@
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import React from 'react'
|
||||
import HoursAndMinutesFromSeconds from './HoursAndMinutesFromSeconds'
|
||||
import useFetch from '../../../hooks/useFetch'
|
||||
import APIComponentWithFallback from '../../../components/general/APIComponentWithFallback'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
|
||||
function CodeTimeStatistics(): React.ReactElement {
|
||||
const [stats] = useFetch<Record<string, number>>('code-time/statistics')
|
||||
|
||||
@@ -2,25 +2,30 @@
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import React, { useState } from 'react'
|
||||
import HoursAndMinutesFromSeconds from './HoursAndMinutesFromSeconds'
|
||||
import useFetch from '../../../hooks/useFetch'
|
||||
import APIComponentWithFallback from '../../../components/general/APIComponentWithFallback'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
|
||||
function CodeTimeMostLanguages(): React.ReactElement {
|
||||
const [lastForLanguages, setLastForLanguages] = useState<
|
||||
'24 hours' | '7 days' | '30 days'
|
||||
>('24 hours')
|
||||
function CodeTimeTopEntries({
|
||||
type
|
||||
}: {
|
||||
type: 'languages' | 'projects'
|
||||
}): React.ReactElement {
|
||||
const [lastFor, setLastFor] = useState<'24 hours' | '7 days' | '30 days'>(
|
||||
'24 hours'
|
||||
)
|
||||
|
||||
const [topLanguages] = useFetch<Record<string, number>>(
|
||||
`code-time/languages?last=${lastForLanguages}`
|
||||
const [topEntries] = useFetch<Record<string, number>>(
|
||||
`code-time/${type}?last=${lastFor}`
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="mb-6 mt-16 flex w-full flex-col gap-6 pb-6">
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="space-y-6 pb-8">
|
||||
<div className="flex items-center justify-between">
|
||||
<h1 className="mb-2 flex items-center gap-2 text-2xl font-semibold">
|
||||
<Icon icon="tabler:code" className="text-3xl" />
|
||||
<span className="ml-2">
|
||||
Languages You've Spent Most Time Using
|
||||
{type[0].toUpperCase()}
|
||||
{type.slice(1)} You've Spent Most Time Using
|
||||
</span>
|
||||
</h1>
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -30,10 +35,10 @@ function CodeTimeMostLanguages(): React.ReactElement {
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => {
|
||||
setLastForLanguages(last as '24 hours' | '7 days' | '30 days')
|
||||
setLastFor(last as '24 hours' | '7 days' | '30 days')
|
||||
}}
|
||||
className={`rounded-md p-4 px-6 tracking-wide hover:bg-bg-700/50 ${
|
||||
lastForLanguages === last
|
||||
lastFor === last
|
||||
? 'bg-bg-200 font-semibold text-bg-800 dark:bg-bg-700/50 dark:text-bg-100'
|
||||
: 'text-bg-500 hover:bg-bg-200/50 dark:hover:bg-bg-700/50'
|
||||
}`}
|
||||
@@ -44,16 +49,17 @@ function CodeTimeMostLanguages(): React.ReactElement {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<APIComponentWithFallback data={topLanguages}>
|
||||
<APIComponentWithFallback data={topEntries}>
|
||||
<div className="flex w-full">
|
||||
{typeof topLanguages !== 'string' &&
|
||||
Object.keys(topLanguages).length > 0 &&
|
||||
Object.entries(topLanguages)
|
||||
{typeof topEntries !== 'string' &&
|
||||
Object.keys(topEntries).length > 0 &&
|
||||
Object.entries(topEntries)
|
||||
.slice(0, 5)
|
||||
.map(([key, value], index) => (
|
||||
<div
|
||||
className={`h-6 border ${index === 0 && 'rounded-l-lg'} ${
|
||||
index === 4 && 'rounded-r-lg'
|
||||
index === Object.entries(topEntries).length - 1 &&
|
||||
'rounded-r-lg'
|
||||
} ${
|
||||
[
|
||||
'bg-red-500/20 border-red-500',
|
||||
@@ -67,7 +73,7 @@ function CodeTimeMostLanguages(): React.ReactElement {
|
||||
style={{
|
||||
width: `${Math.round(
|
||||
(value /
|
||||
Object.entries(topLanguages)
|
||||
Object.entries(topEntries)
|
||||
.slice(0, 5)
|
||||
.reduce((a, b) => a + b[1], 0)) *
|
||||
100
|
||||
@@ -77,9 +83,9 @@ function CodeTimeMostLanguages(): React.ReactElement {
|
||||
))}
|
||||
</div>
|
||||
<ul className="flex flex-col gap-4">
|
||||
{topLanguages !== null &&
|
||||
Object.keys(topLanguages).length > 0 &&
|
||||
Object.entries(topLanguages)
|
||||
{topEntries !== null &&
|
||||
Object.keys(topEntries).length > 0 &&
|
||||
Object.entries(topEntries)
|
||||
.slice(0, 5)
|
||||
.map(([key, value], index) => (
|
||||
<li
|
||||
@@ -111,4 +117,4 @@ function CodeTimeMostLanguages(): React.ReactElement {
|
||||
)
|
||||
}
|
||||
|
||||
export default CodeTimeMostLanguages
|
||||
export default CodeTimeTopEntries
|
||||
@@ -1,11 +1,10 @@
|
||||
import React from 'react'
|
||||
|
||||
import ModuleHeader from '../../components/general/ModuleHeader'
|
||||
import ModuleHeader from '@components/ModuleHeader'
|
||||
import CodeTimeStatistics from './components/CodeTimeStatistics'
|
||||
import CodeTimeActivityCalendar from './components/CodeTimeActivityCalendar'
|
||||
import CodeTimeMostProjects from './components/CodeTimeMostProjects'
|
||||
import CodeTimeMostLanguages from './components/CodeTimeMostLanguages'
|
||||
import ModuleWrapper from '../../components/general/ModuleWrapper'
|
||||
import CodeTimeTopEntries from './components/CodeTimeTopEntries'
|
||||
import ModuleWrapper from '@components/ModuleWrapper'
|
||||
|
||||
export default function CodeTime(): React.ReactElement {
|
||||
return (
|
||||
@@ -14,11 +13,15 @@ export default function CodeTime(): React.ReactElement {
|
||||
title="Code Time"
|
||||
desc="See how much time you spend grinding code."
|
||||
/>
|
||||
<div className="mt-6 flex min-h-0 w-full flex-1 flex-col items-center">
|
||||
<div className="mt-8 min-h-0 w-full space-y-12">
|
||||
<CodeTimeStatistics />
|
||||
<CodeTimeActivityCalendar />
|
||||
<CodeTimeMostProjects />
|
||||
<CodeTimeMostLanguages />
|
||||
{['projects', 'languages'].map(type => (
|
||||
<CodeTimeTopEntries
|
||||
key={type}
|
||||
type={type as 'projects' | 'languages'}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</ModuleWrapper>
|
||||
)
|
||||
|
||||
@@ -19,9 +19,9 @@ import {
|
||||
Title,
|
||||
Filler
|
||||
} from 'chart.js'
|
||||
import ModuleHeader from '../../components/general/ModuleHeader'
|
||||
import { AuthContext } from '../../providers/AuthProvider'
|
||||
import ModuleWrapper from '../../components/general/ModuleWrapper'
|
||||
import ModuleHeader from '@components/ModuleHeader'
|
||||
import { AuthContext } from '@providers/AuthProvider'
|
||||
import ModuleWrapper from '@components/ModuleWrapper'
|
||||
|
||||
ChartJS.register(
|
||||
ArcElement,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import GoBackButton from '../../../components/general/GoBackButton'
|
||||
import useFetch from '../../../hooks/useFetch'
|
||||
import GoBackButton from '@components/GoBackButton'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import { type IFlashcardDeck } from '..'
|
||||
import APIComponentWithFallback from '../../../components/general/APIComponentWithFallback'
|
||||
import HamburgerMenu from '../../../components/general/HamburgerMenu'
|
||||
import MenuItem from '../../../components/general/HamburgerMenu/MenuItem'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
import HamburgerMenu from '@components/HamburgerMenu'
|
||||
import MenuItem from '@components/HamburgerMenu/MenuItem'
|
||||
import EditCardModal from './EditCardModal'
|
||||
|
||||
// Generated by https://quicktype.io
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* eslint-disable @typescript-eslint/indent */
|
||||
/* eslint-disable multiline-ternary */
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import Modal from '../../../components/general/Modal'
|
||||
import Modal from '@components/Modal'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { type IFlashcardCard } from './CardSet'
|
||||
import CreateOrModifyButton from '../../../components/general/CreateOrModifyButton'
|
||||
import CreateOrModifyButton from '@components/CreateOrModifyButton'
|
||||
import { toast } from 'react-toastify'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||
/* eslint-disable @typescript-eslint/indent */
|
||||
import React from 'react'
|
||||
import SidebarDivider from '../../../components/Sidebar/components/SidebarDivider'
|
||||
import SidebarTitle from '../../../components/Sidebar/components/SidebarTitle'
|
||||
import SidebarDivider from '@sidebar/components/SidebarDivider'
|
||||
import SidebarTitle from '@sidebar/components/SidebarTitle'
|
||||
import { Icon } from '@iconify/react'
|
||||
import GoBackButton from '../../../components/general/GoBackButton'
|
||||
import SidebarItem from '../../../components/Sidebar/components/SidebarItem'
|
||||
import useFetch from '../../../hooks/useFetch'
|
||||
import APIComponentWithFallback from '../../../components/general/APIComponentWithFallback'
|
||||
import GoBackButton from '@components/GoBackButton'
|
||||
import SidebarItem from '@sidebar/components/SidebarItem'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
|
||||
export interface IFlashcardTag {
|
||||
amount: number
|
||||
|
||||
@@ -2,12 +2,12 @@ import { faker } from '@faker-js/faker'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import React, { useState } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import ModuleHeader from '../../components/general/ModuleHeader'
|
||||
import ModuleWrapper from '../../components/general/ModuleWrapper'
|
||||
import SearchInput from '../../components/general/SearchInput'
|
||||
import Sidebar, { type IFlashcardTag } from './components/Sidebar'
|
||||
import useFetch from '../../hooks/useFetch'
|
||||
import APIComponentWithFallback from '../../components/general/APIComponentWithFallback'
|
||||
import ModuleHeader from '@components/ModuleHeader'
|
||||
import ModuleWrapper from '@components/ModuleWrapper'
|
||||
import SearchInput from '@components/SearchInput'
|
||||
import Sidebar, { type IFlashcardTag } from '@sidebar'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
|
||||
export interface IFlashcardDeck {
|
||||
card_amount: number
|
||||
@@ -73,7 +73,7 @@ export default function Flashcards(): React.ReactElement {
|
||||
key={deck.id}
|
||||
className="group relative flex flex-col justify-start gap-6 rounded-lg bg-bg-50 p-8 shadow-[4px_4px_10px_0px_rgba(0,0,0,0.05)] hover:bg-bg-200/50 dark:bg-bg-900 dark:hover:bg-bg-800"
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="space-y-2">
|
||||
{deck.tag !== '' && (
|
||||
<span
|
||||
className="relative isolate mb-1 w-min whitespace-nowrap rounded-full px-3 py-0.5 text-sm"
|
||||
@@ -92,7 +92,7 @@ export default function Flashcards(): React.ReactElement {
|
||||
{deck.card_amount} cards
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-auto flex flex-col gap-2">
|
||||
<div className="mt-auto space-y-2">
|
||||
<progress
|
||||
className="progress h-2 w-full rounded-lg bg-bg-200 dark:bg-bg-700"
|
||||
value={faker.datatype.number(100)}
|
||||
@@ -124,7 +124,7 @@ export default function Flashcards(): React.ReactElement {
|
||||
key={index}
|
||||
className="group relative flex flex-col justify-start gap-6 rounded-lg bg-bg-50 p-8 shadow-[4px_4px_10px_0px_rgba(0,0,0,0.05)] transition-all hover:bg-bg-200/50 dark:bg-bg-900 dark:hover:bg-bg-800/50"
|
||||
>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm font-medium text-bg-500">
|
||||
{faker.datatype.number(100)} cards
|
||||
</p>
|
||||
|
||||
@@ -3,8 +3,8 @@ import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import React from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { type IIdeaBoxContainer } from '../../..'
|
||||
import HamburgerMenu from '../../../../../components/general/HamburgerMenu'
|
||||
import MenuItem from '../../../../../components/general/HamburgerMenu/MenuItem'
|
||||
import HamburgerMenu from '@components/HamburgerMenu'
|
||||
import MenuItem from '@components/HamburgerMenu/MenuItem'
|
||||
|
||||
function ContainerItem({
|
||||
container,
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
/* eslint-disable multiline-ternary */
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import Modal from '../../../../../components/general/Modal'
|
||||
import Modal from '@components/Modal'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import ColorPickerModal from '../../../../../components/general/ColorPicker/ColorPickerModal'
|
||||
import ColorPickerModal from '@components/ColorPicker/ColorPickerModal'
|
||||
import { toast } from 'react-toastify'
|
||||
import { type IIdeaBoxContainer } from '../../..'
|
||||
import { useDebounce } from '@uidotdev/usehooks'
|
||||
import IconInput from '../../../../../components/general/IconSelector/IconInput'
|
||||
import Input from '../../../../../components/general/Input'
|
||||
import IconSelector from '../../../../../components/general/IconSelector'
|
||||
import CreateOrModifyButton from '../../../../../components/general/CreateOrModifyButton'
|
||||
import ColorInput from '../../../../../components/general/ColorPicker/ColorInput'
|
||||
import IconInput from '@components/IconSelector/IconInput'
|
||||
import Input from '@components/Input'
|
||||
import IconSelector from '@components/IconSelector'
|
||||
import CreateOrModifyButton from '@components/CreateOrModifyButton'
|
||||
import ColorInput from '@components/ColorPicker/ColorInput'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
|
||||
function ModifyContainerModal({
|
||||
|
||||
@@ -3,10 +3,10 @@ import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import React from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { type IIdeaBoxContainer } from '../../..'
|
||||
import GoBackButton from '../../../../../components/general/GoBackButton'
|
||||
import useFetch from '../../../../../hooks/useFetch'
|
||||
import HamburgerMenu from '../../../../../components/general/HamburgerMenu'
|
||||
import MenuItem from '../../../../../components/general/HamburgerMenu/MenuItem'
|
||||
import GoBackButton from '@components/GoBackButton'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import HamburgerMenu from '@components/HamburgerMenu'
|
||||
import MenuItem from '@components/HamburgerMenu/MenuItem'
|
||||
|
||||
function ContainerHeader({
|
||||
id,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import React from 'react'
|
||||
import { type IIdeaBoxEntry } from '..'
|
||||
import { toast } from 'react-toastify'
|
||||
import MenuItem from '../../../../../components/general/HamburgerMenu/MenuItem'
|
||||
import MenuItem from '@components/HamburgerMenu/MenuItem'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
|
||||
function EntryContextMenu({
|
||||
|
||||
@@ -24,7 +24,7 @@ function EntryLink({
|
||||
updateIdeaList: () => void
|
||||
}): React.ReactElement {
|
||||
return (
|
||||
<div className="group relative my-4 flex flex-col gap-2 rounded-lg bg-bg-50 p-4 shadow-[4px_4px_10px_0px_rgba(0,0,0,0.05)] dark:bg-bg-900">
|
||||
<div className="group relative my-4 space-y-2 rounded-lg bg-bg-50 p-4 shadow-[4px_4px_10px_0px_rgba(0,0,0,0.05)] dark:bg-bg-900">
|
||||
{entry.pinned && (
|
||||
<Icon
|
||||
icon="tabler:pin"
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import React, { useEffect, useState, useCallback, useContext } from 'react'
|
||||
import Modal from '../../../../../../components/general/Modal'
|
||||
import Modal from '@components/Modal'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { useDebounce } from '@uidotdev/usehooks'
|
||||
import { useDropzone } from 'react-dropzone'
|
||||
import { toast } from 'react-toastify'
|
||||
import { type IIdeaBoxEntry } from '../..'
|
||||
import { PersonalizationContext } from '../../../../../../providers/PersonalizationProvider'
|
||||
import Input from '../../../../../../components/general/Input'
|
||||
import { PersonalizationContext } from '@providers/PersonalizationProvider'
|
||||
import Input from '@components/Input'
|
||||
import ModalHeader from './components/ModalHeader'
|
||||
import IdeaContentInput from './components/IdeaContentInput'
|
||||
import IdeaImagePreview from './components/IdeaImagePreview'
|
||||
|
||||
@@ -5,16 +5,16 @@ import React, { useEffect, useState } from 'react'
|
||||
import { useNavigate, useParams, useSearchParams } from 'react-router-dom'
|
||||
// @ts-expect-error - no types available
|
||||
import Column from 'react-columns'
|
||||
import EmptyStateScreen from '../../../../components/general/EmptyStateScreen'
|
||||
import EmptyStateScreen from '@components/EmptyStateScreen'
|
||||
import ModifyIdeaModal from './components/ModifyIdeaModal'
|
||||
import EntryImage from './components/IdeaEntry/EntryImage'
|
||||
import EntryText from './components/IdeaEntry/EntryText'
|
||||
import EntryLink from './components/IdeaEntry/EntryLink'
|
||||
import ContainerHeader from './components/ContainerHeader'
|
||||
import FAB from './components/FAB'
|
||||
import DeleteConfirmationModal from '../../../../components/general/DeleteConfirmationModal'
|
||||
import useFetch from '../../../../hooks/useFetch'
|
||||
import APIComponentWithFallback from '../../../../components/general/APIComponentWithFallback'
|
||||
import DeleteConfirmationModal from '@components/DeleteConfirmationModal'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
|
||||
export interface IIdeaBoxEntry {
|
||||
collectionId: string
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/* eslint-disable @typescript-eslint/indent */
|
||||
/* eslint-disable multiline-ternary */
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import ModuleHeader from '../../components/general/ModuleHeader'
|
||||
import ModuleHeader from '@components/ModuleHeader'
|
||||
import ModifyContainerModal from './components/Containers/components/ModifyContainerModal'
|
||||
import { useDebounce } from '@uidotdev/usehooks'
|
||||
import EmptyStateScreen from '../../components/general/EmptyStateScreen'
|
||||
import EmptyStateScreen from '@components/EmptyStateScreen'
|
||||
import Containers from './components/Containers'
|
||||
import DeleteConfirmationModal from '../../components/general/DeleteConfirmationModal'
|
||||
import useFetch from '../../hooks/useFetch'
|
||||
import APIComponentWithFallback from '../../components/general/APIComponentWithFallback'
|
||||
import ModuleWrapper from '../../components/general/ModuleWrapper'
|
||||
import SearchInput from '../../components/general/SearchInput'
|
||||
import DeleteConfirmationModal from '@components/DeleteConfirmationModal'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
import ModuleWrapper from '@components/ModuleWrapper'
|
||||
import SearchInput from '@components/SearchInput'
|
||||
|
||||
export interface IIdeaBoxContainer {
|
||||
collectionId: string
|
||||
|
||||
@@ -3,7 +3,7 @@ import React, { useState } from 'react'
|
||||
import { type Module } from '.'
|
||||
import { Switch } from '@headlessui/react'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import Input from '../../components/general/Input'
|
||||
import Input from '@components/Input'
|
||||
|
||||
function ModuleItem({
|
||||
module,
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||
/* eslint-disable multiline-ternary */
|
||||
import React, { useContext } from 'react'
|
||||
import ModuleHeader from '../../components/general/ModuleHeader'
|
||||
import ModuleWrapper from '../../components/general/ModuleWrapper'
|
||||
import { AuthContext } from '../../providers/AuthProvider'
|
||||
import Loading from '../../components/general/Loading'
|
||||
import { titleToPath } from '../../components/Sidebar/components/SidebarItem'
|
||||
import ModuleHeader from '@components/ModuleHeader'
|
||||
import ModuleWrapper from '@components/ModuleWrapper'
|
||||
import { AuthContext } from '@providers/AuthProvider'
|
||||
import Loading from '@components/Loading'
|
||||
import { titleToPath } from '@sidebar/components/SidebarItem'
|
||||
import { toast } from 'react-toastify'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
import ModuleItem from './ModuleItem'
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import React from 'react'
|
||||
import ModuleWrapper from '../../../components/general/ModuleWrapper'
|
||||
import ModuleWrapper from '@components/ModuleWrapper'
|
||||
import { Worker, Viewer, type RenderPageProps } from '@react-pdf-viewer/core'
|
||||
|
||||
// Import the styles
|
||||
import '@react-pdf-viewer/core/lib/styles/index.css'
|
||||
import { useNavigate, useParams } from 'react-router'
|
||||
import useFetch from '../../../hooks/useFetch'
|
||||
import APIComponentWithFallback from '../../../components/general/APIComponentWithFallback'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
import { type INotesEntry } from '../Subject'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import FILE_ICONS from '../../../constants/file_icons'
|
||||
import GoBackButton from '../../../components/general/GoBackButton'
|
||||
import GoBackButton from '@components/GoBackButton'
|
||||
|
||||
function NotesFile(): React.ReactElement {
|
||||
const { id } = useParams<{ id: string }>()
|
||||
@@ -53,10 +53,7 @@ function NotesFile(): React.ReactElement {
|
||||
<Icon icon="tabler:download" className="text-2xl" />
|
||||
</button>
|
||||
<button className="rounded-lg p-4 text-bg-500 transition-all hover:bg-bg-200/50 hover:text-bg-800 dark:hover:bg-bg-800 dark:hover:text-bg-100">
|
||||
<Icon
|
||||
icon="tabler:dots-vertical"
|
||||
u="text-xl sm:text-2xl"
|
||||
/>
|
||||
<Icon icon="tabler:dots-vertical" u="text-xl sm:text-2xl" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,11 +5,11 @@ import { Menu, Transition } from '@headlessui/react'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import React from 'react'
|
||||
import { Link, useNavigate, useParams } from 'react-router-dom'
|
||||
import GoBackButton from '../../../../../../components/general/GoBackButton'
|
||||
import MenuItem from '../../../../../../components/general/HamburgerMenu/MenuItem'
|
||||
import GoBackButton from '@components/GoBackButton'
|
||||
import MenuItem from '@components/HamburgerMenu/MenuItem'
|
||||
import { type INotesEntry, type INotesPath } from '../../..'
|
||||
import { toast } from 'react-toastify'
|
||||
import useFetch from '../../../../../../hooks/useFetch'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
|
||||
function DirectoryHeader({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/indent */
|
||||
import React from 'react'
|
||||
import HamburgerMenu from '../../../../../../../../components/general/HamburgerMenu'
|
||||
import MenuItem from '../../../../../../../../components/general/HamburgerMenu/MenuItem'
|
||||
import HamburgerMenu from '@components/HamburgerMenu'
|
||||
import MenuItem from '@components/HamburgerMenu/MenuItem'
|
||||
import { type INotesEntry } from '../../../../..'
|
||||
|
||||
function EntryMenu({
|
||||
|
||||
@@ -4,11 +4,11 @@ import React, { useEffect, useState } from 'react'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { toast } from 'react-toastify'
|
||||
import { useDebounce } from '@uidotdev/usehooks'
|
||||
import Modal from '../../../../components/general/Modal'
|
||||
import Modal from '@components/Modal'
|
||||
import { useParams } from 'react-router'
|
||||
import { type INotesEntry } from '..'
|
||||
import Input from '../../../../components/general/Input'
|
||||
import CreateOrModifyButton from '../../../../components/general/CreateOrModifyButton'
|
||||
import Input from '@components/Input'
|
||||
import CreateOrModifyButton from '@components/CreateOrModifyButton'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
|
||||
function ModifyFolderModal({
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
/* eslint-disable @typescript-eslint/indent */
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import EmptyStateScreen from '../../../components/general/EmptyStateScreen'
|
||||
import EmptyStateScreen from '@components/EmptyStateScreen'
|
||||
import ModifyFolderModal from './components/ModifyFolderModal'
|
||||
import DirectoryHeader from './components/Directory/components/DirectoryHeader'
|
||||
import Directory from './components/Directory'
|
||||
import DeleteConfirmationModal from '../../../components/general/DeleteConfirmationModal'
|
||||
import useFetch from '../../../hooks/useFetch'
|
||||
import APIComponentWithFallback from '../../../components/general/APIComponentWithFallback'
|
||||
import DeleteConfirmationModal from '@components/DeleteConfirmationModal'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
|
||||
// Generated by https://quicktype.io
|
||||
|
||||
|
||||
@@ -4,13 +4,13 @@ import React, { useEffect, useState } from 'react'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { toast } from 'react-toastify'
|
||||
import { useDebounce } from '@uidotdev/usehooks'
|
||||
import Modal from '../../../../components/general/Modal'
|
||||
import Modal from '@components/Modal'
|
||||
import { useParams } from 'react-router'
|
||||
import Input from '../../../../components/general/Input'
|
||||
import IconInput from '../../../../components/general/IconSelector/IconInput'
|
||||
import Input from '@components/Input'
|
||||
import IconInput from '@components/IconSelector/IconInput'
|
||||
import { type INotesSubject } from './SubjectItem'
|
||||
import IconSelector from '../../../../components/general/IconSelector'
|
||||
import CreateOrModifyButton from '../../../../components/general/CreateOrModifyButton'
|
||||
import IconSelector from '@components/IconSelector'
|
||||
import CreateOrModifyButton from '@components/CreateOrModifyButton'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
|
||||
function ModifySubjectModal({
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import React from 'react'
|
||||
import { Link, useParams } from 'react-router-dom'
|
||||
import HamburgerMenu from '../../../../components/general/HamburgerMenu'
|
||||
import MenuItem from '../../../../components/general/HamburgerMenu/MenuItem'
|
||||
import HamburgerMenu from '@components/HamburgerMenu'
|
||||
import MenuItem from '@components/HamburgerMenu/MenuItem'
|
||||
|
||||
export interface INotesSubject {
|
||||
workspace: string
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
/* eslint-disable @typescript-eslint/indent */
|
||||
/* eslint-disable multiline-ternary */
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import ModuleHeader from '../../../components/general/ModuleHeader'
|
||||
import ModuleHeader from '@components/ModuleHeader'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { useNavigate, useParams } from 'react-router'
|
||||
import { type INotesWorkspace } from '..'
|
||||
import EmptyStateScreen from '../../../components/general/EmptyStateScreen'
|
||||
import EmptyStateScreen from '@components/EmptyStateScreen'
|
||||
import ModifySubjectModal from './components/ModifySubjectModal'
|
||||
import GoBackButton from '../../../components/general/GoBackButton'
|
||||
import GoBackButton from '@components/GoBackButton'
|
||||
import SubjectItem, { type INotesSubject } from './components/SubjectItem'
|
||||
import CreateSubjectButton from './components/CreateSubjectButton'
|
||||
import DeleteConfirmationModal from '../../../components/general/DeleteConfirmationModal'
|
||||
import useFetch from '../../../hooks/useFetch'
|
||||
import APIComponentWithFallback from '../../../components/general/APIComponentWithFallback'
|
||||
import DeleteConfirmationModal from '@components/DeleteConfirmationModal'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
|
||||
function NotesCategory(): React.ReactElement {
|
||||
const { workspace } = useParams<{ workspace: string }>()
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import ModuleHeader from '../../components/general/ModuleHeader'
|
||||
import Loading from '../../components/general/Loading'
|
||||
import Error from '../../components/general/Error'
|
||||
import ModuleHeader from '@components/ModuleHeader'
|
||||
import Loading from '@components/Loading'
|
||||
import Error from '@components/Error'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { Link } from 'react-router-dom'
|
||||
import useFetch from '../../hooks/useFetch'
|
||||
import ModuleWrapper from '../../components/general/ModuleWrapper'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import ModuleWrapper from '@components/ModuleWrapper'
|
||||
|
||||
export interface INotesWorkspace {
|
||||
collectionId: string
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/* eslint-disable multiline-ternary */
|
||||
import React, { useContext, useRef, useState } from 'react'
|
||||
import Input from '../../components/general/Input'
|
||||
import Input from '@components/Input'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { toast } from 'react-toastify'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
import { AuthContext } from '../../providers/AuthProvider'
|
||||
import Modal from '../../components/general/Modal'
|
||||
import { AuthContext } from '@providers/AuthProvider'
|
||||
import Modal from '@components/Modal'
|
||||
|
||||
function CreatePassword(): React.ReactElement {
|
||||
const { setUserData, userData } = useContext(AuthContext)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/* eslint-disable multiline-ternary */
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import Modal from '../../components/general/Modal'
|
||||
import Modal from '@components/Modal'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import Input from '../../components/general/Input'
|
||||
import IconInput from '../../components/general/IconSelector/IconInput'
|
||||
import IconSelector from '../../components/general/IconSelector'
|
||||
import ColorInput from '../../components/general/ColorPicker/ColorInput'
|
||||
import ColorPickerModal from '../../components/general/ColorPicker/ColorPickerModal'
|
||||
import Input from '@components/Input'
|
||||
import IconInput from '@components/IconSelector/IconInput'
|
||||
import IconSelector from '@components/IconSelector'
|
||||
import ColorInput from '@components/ColorPicker/ColorInput'
|
||||
import ColorPickerModal from '@components/ColorPicker/ColorPickerModal'
|
||||
import { toast } from 'react-toastify'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ import { type IPasswordEntry } from '.'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
import { toast } from 'react-toastify'
|
||||
import HamburgerMenu from '../../components/general/HamburgerMenu'
|
||||
import MenuItem from '../../components/general/HamburgerMenu/MenuItem'
|
||||
import HamburgerMenu from '@components/HamburgerMenu'
|
||||
import MenuItem from '@components/HamburgerMenu/MenuItem'
|
||||
|
||||
function PasswordEntryITem({
|
||||
password,
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
/* eslint-disable multiline-ternary */
|
||||
import React, { useContext, useState } from 'react'
|
||||
import ModuleWrapper from '../../components/general/ModuleWrapper'
|
||||
import ModuleHeader from '../../components/general/ModuleHeader'
|
||||
import Input from '../../components/general/Input'
|
||||
import ModuleWrapper from '@components/ModuleWrapper'
|
||||
import ModuleHeader from '@components/ModuleHeader'
|
||||
import Input from '@components/Input'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { AuthContext } from '../../providers/AuthProvider'
|
||||
import { AuthContext } from '@providers/AuthProvider'
|
||||
import CreatePassword from './CreatePassword'
|
||||
import { toast } from 'react-toastify'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
import CreatePasswordModal from './CreatePasswordModal'
|
||||
import EmptyStateScreen from '../../components/general/EmptyStateScreen'
|
||||
import useFetch from '../../hooks/useFetch'
|
||||
import APIComponentWithFallback from '../../components/general/APIComponentWithFallback'
|
||||
import EmptyStateScreen from '@components/EmptyStateScreen'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
import PasswordEntryITem from './PasswordEntryITem'
|
||||
|
||||
export interface IPasswordEntry {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useContext } from 'react'
|
||||
import { PersonalizationContext } from '../../../providers/PersonalizationProvider'
|
||||
import { PersonalizationContext } from '@providers/PersonalizationProvider'
|
||||
|
||||
const COLORS = ['bg-slate', 'bg-gray', 'bg-zinc', 'bg-neutral', 'bg-stone']
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { Fragment, useContext } from 'react'
|
||||
import { PersonalizationContext } from '../../../providers/PersonalizationProvider'
|
||||
import { PersonalizationContext } from '@providers/PersonalizationProvider'
|
||||
import { Listbox, Transition } from '@headlessui/react'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useContext } from 'react'
|
||||
import { PersonalizationContext } from '../../../providers/PersonalizationProvider'
|
||||
import { PersonalizationContext } from '@providers/PersonalizationProvider'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
|
||||
function ThemeSelector(): React.ReactElement {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from 'react'
|
||||
import ModuleHeader from '../../components/general/ModuleHeader'
|
||||
import ModuleHeader from '@components/ModuleHeader'
|
||||
import ThemeSelector from './components/ThemeSelector'
|
||||
import ThemeColorSelector from './components/ThemeColorSelector'
|
||||
import BgTempSelector from './components/BgTempSelector'
|
||||
import ModuleWrapper from '../../components/general/ModuleWrapper'
|
||||
import ModuleWrapper from '@components/ModuleWrapper'
|
||||
|
||||
function Personalization(): React.ReactElement {
|
||||
return (
|
||||
|
||||
@@ -7,12 +7,12 @@ import {
|
||||
import { LazyLoadImage } from 'react-lazy-load-image-component'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import Zoom from 'react-medium-image-zoom'
|
||||
import HamburgerMenu from '../../../components/general/HamburgerMenu'
|
||||
import MenuItem from '../../../components/general/HamburgerMenu/MenuItem'
|
||||
import HamburgerMenu from '@components/HamburgerMenu'
|
||||
import MenuItem from '@components/HamburgerMenu/MenuItem'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
import { toast } from 'react-toastify'
|
||||
import { useParams } from 'react-router'
|
||||
import useFetch from '../../../hooks/useFetch'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
|
||||
function forceDown(url: string, filename: string): void {
|
||||
fetch(url)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/* eslint-disable multiline-ternary */
|
||||
/* eslint-disable @typescript-eslint/indent */
|
||||
import React, { useContext } from 'react'
|
||||
import SidebarItem from '../../../components/Sidebar/components/SidebarItem'
|
||||
import SidebarDivider from '../../../components/Sidebar/components/SidebarDivider'
|
||||
import SidebarTitle from '../../../components/Sidebar/components/SidebarTitle'
|
||||
import SidebarItem from '@sidebar/components/SidebarItem'
|
||||
import SidebarDivider from '@sidebar/components/SidebarDivider'
|
||||
import SidebarTitle from '@sidebar/components/SidebarTitle'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import APIComponentWithFallback from '../../../components/general/APIComponentWithFallback'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
import { Link, useNavigate } from 'react-router-dom'
|
||||
import { PhotosContext } from '../../../providers/PhotosProvider'
|
||||
import { PhotosContext } from '@providers/PhotosProvider'
|
||||
|
||||
function PhotosSidebar(): React.ReactElement {
|
||||
const {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/* eslint-disable multiline-ternary */
|
||||
import React, { useContext, useEffect, useState } from 'react'
|
||||
import Modal from '../../../../components/general/Modal'
|
||||
import Modal from '@components/Modal'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { PhotosContext } from '../../../../providers/PhotosProvider'
|
||||
import APIComponentWithFallback from '../../../../components/general/APIComponentWithFallback'
|
||||
import { PhotosContext } from '@providers/PhotosProvider'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
import { toast } from 'react-toastify'
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ import React, { useContext, useState } from 'react'
|
||||
import { toast } from 'react-toastify'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
import Modal from '../../../../components/general/Modal'
|
||||
import { PhotosContext } from '../../../../providers/PhotosProvider'
|
||||
import Modal from '@components/Modal'
|
||||
import { PhotosContext } from '@providers/PhotosProvider'
|
||||
|
||||
function DeletePhotosConfirmationModal({
|
||||
refreshPhotos,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { useContext, useEffect, useRef, useState } from 'react'
|
||||
import Modal from '../../../../components/general/Modal'
|
||||
import Modal from '@components/Modal'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import Input from '../../../../components/general/Input'
|
||||
import CreateOrModifyButton from '../../../../components/general/CreateOrModifyButton'
|
||||
import Input from '@components/Input'
|
||||
import CreateOrModifyButton from '@components/CreateOrModifyButton'
|
||||
import { toast } from 'react-toastify'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
import {
|
||||
|
||||
@@ -4,8 +4,8 @@ import React, { useContext, useState } from 'react'
|
||||
import { toast } from 'react-toastify'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
import Modal from '../../../../components/general/Modal'
|
||||
import { PhotosContext } from '../../../../providers/PhotosProvider'
|
||||
import Modal from '@components/Modal'
|
||||
import { PhotosContext } from '@providers/PhotosProvider'
|
||||
|
||||
function RemovePhotosFromAlbumConfirmationModal({
|
||||
refreshPhotos,
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
/* eslint-disable multiline-ternary */
|
||||
import React, { useContext, useEffect } from 'react'
|
||||
import ModuleWrapper from '../../../../components/general/ModuleWrapper'
|
||||
import ModuleWrapper from '@components/ModuleWrapper'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { useNavigate, useParams } from 'react-router'
|
||||
import GoBackButton from '../../../../components/general/GoBackButton'
|
||||
import useFetch from '../../../../hooks/useFetch'
|
||||
import APIComponentWithFallback from '../../../../components/general/APIComponentWithFallback'
|
||||
import GoBackButton from '@components/GoBackButton'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
import {
|
||||
type IPhotosEntryDimensionsItem,
|
||||
type IPhotosAlbum,
|
||||
@@ -19,8 +19,8 @@ import moment from 'moment'
|
||||
import BottomBar from '../../components/BottomBar'
|
||||
import DeletePhotosConfirmationModal from '../../components/modals/DeletePhotosConfirmationModal.tsx'
|
||||
import RemovePhotosFromAlbumConfirmationModal from '../../components/modals/RemovePhotosFromAlbumConfirmationModal.tsx.tsx'
|
||||
import HamburgerMenu from '../../../../components/general/HamburgerMenu/index.tsx'
|
||||
import MenuItem from '../../../../components/general/HamburgerMenu/MenuItem.tsx'
|
||||
import HamburgerMenu from '@components/HamburgerMenu/index.tsx'
|
||||
import MenuItem from '@components/HamburgerMenu/MenuItem.tsx'
|
||||
import ModifyAlbumModal from '../../components/modals/ModifyAlbumModal.tsx'
|
||||
|
||||
export interface IPhotoAlbumEntryItem extends IPhotosEntryDimensionsItem {
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import React, { useContext } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import HamburgerMenu from '../../../../../components/general/HamburgerMenu'
|
||||
import MenuItem from '../../../../../components/general/HamburgerMenu/MenuItem'
|
||||
import HamburgerMenu from '@components/HamburgerMenu'
|
||||
import MenuItem from '@components/HamburgerMenu/MenuItem'
|
||||
import {
|
||||
PhotosContext,
|
||||
type IPhotosAlbum
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useContext } from 'react'
|
||||
import { PhotosContext } from '../../../../../providers/PhotosProvider'
|
||||
import { PhotosContext } from '@providers/PhotosProvider'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
|
||||
function AlbumListHeader(): React.ReactElement {
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
/* eslint-disable multiline-ternary */
|
||||
/* eslint-disable @typescript-eslint/indent */
|
||||
import React, { useContext, useEffect, useState } from 'react'
|
||||
import ModuleWrapper from '../../../../components/general/ModuleWrapper'
|
||||
import ModuleHeader from '../../../../components/general/ModuleHeader'
|
||||
import ModuleWrapper from '@components/ModuleWrapper'
|
||||
import ModuleHeader from '@components/ModuleHeader'
|
||||
import PhotosSidebar from '../../components/PhotosSidebar'
|
||||
import {
|
||||
type IPhotosAlbum,
|
||||
PhotosContext
|
||||
} from '../../../../providers/PhotosProvider'
|
||||
import APIComponentWithFallback from '../../../../components/general/APIComponentWithFallback'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
import ModifyAlbumModal from '../../components/modals/ModifyAlbumModal'
|
||||
import DeleteConfirmationModal from '../../../../components/general/DeleteConfirmationModal'
|
||||
import DeleteConfirmationModal from '@components/DeleteConfirmationModal'
|
||||
import AlbumItem from './components/AlbumItem'
|
||||
import AlbumListHeader from './components/AlbumListHeader'
|
||||
import SearchInput from '../../../../components/general/SearchInput'
|
||||
import SearchInput from '@components/SearchInput'
|
||||
import { useDebounce } from '@uidotdev/usehooks'
|
||||
import EmptyStateScreen from '../../../../components/general/EmptyStateScreen'
|
||||
import EmptyStateScreen from '@components/EmptyStateScreen'
|
||||
|
||||
function PhotosAlbumList(): React.ReactElement {
|
||||
const { albumList, refreshAlbumList, refreshPhotos } =
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
/* eslint-disable multiline-ternary */
|
||||
import React, { useContext, useEffect } from 'react'
|
||||
import ModuleWrapper from '../../../../components/general/ModuleWrapper.tsx'
|
||||
import ModuleWrapper from '@components/ModuleWrapper.tsx'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { useNavigate, useParams } from 'react-router'
|
||||
import GoBackButton from '../../../../components/general/GoBackButton.tsx'
|
||||
import useFetch from '../../../../hooks/useFetch.ts'
|
||||
import APIComponentWithFallback from '../../../../components/general/APIComponentWithFallback.tsx'
|
||||
import GoBackButton from '@components/GoBackButton.tsx'
|
||||
import useFetch from '@hooks/useFetch.ts'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback.tsx'
|
||||
import {
|
||||
type IPhotosEntryDimensionsItem,
|
||||
PhotosContext
|
||||
@@ -15,8 +15,8 @@ import {
|
||||
import Gallery from 'react-photo-gallery'
|
||||
import ImageObject from '../../components/ImageObject.tsx'
|
||||
import BottomBar from '../../components/BottomBar.tsx'
|
||||
import HamburgerMenu from '../../../../components/general/HamburgerMenu/index.tsx'
|
||||
import MenuItem from '../../../../components/general/HamburgerMenu/MenuItem.tsx'
|
||||
import HamburgerMenu from '@components/HamburgerMenu/index.tsx'
|
||||
import MenuItem from '@components/HamburgerMenu/MenuItem.tsx'
|
||||
|
||||
export interface IPhotoAlbumEntryItem extends IPhotosEntryDimensionsItem {
|
||||
collectionId: string
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
PhotosContext,
|
||||
type IPhotosEntry
|
||||
} from '../../../../../providers/PhotosProvider'
|
||||
import useOnScreen from '../../../../../hooks/useOnScreen'
|
||||
import useOnScreen from '@hooks/useOnScreen'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
import { toast } from 'react-toastify'
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
/* eslint-disable @typescript-eslint/indent */
|
||||
import React, { useContext, useEffect } from 'react'
|
||||
import DateGroup from './DateGroup'
|
||||
import { PhotosContext } from '../../../../../providers/PhotosProvider'
|
||||
import { PhotosContext } from '@providers/PhotosProvider'
|
||||
import BottomBar from '../../../components/BottomBar'
|
||||
import EmptyStateScreen from '../../../../../components/general/EmptyStateScreen'
|
||||
import EmptyStateScreen from '@components/EmptyStateScreen'
|
||||
|
||||
function Gallery(): React.ReactElement {
|
||||
const { photos, selectedPhotos, setSelectedPhotos } =
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
/* eslint-disable @typescript-eslint/indent */
|
||||
import React, { useContext } from 'react'
|
||||
import moment from 'moment'
|
||||
import APIComponentWithFallback from '../../../../../components/general/APIComponentWithFallback'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
import MobileSlidingScrollbar from '../Scrollbars/MobileSlidingScrollbar'
|
||||
import TimelineScrollbar from '../Scrollbars/TimelineScrollbar'
|
||||
import Gallery from './Gallery'
|
||||
import { PhotosContext } from '../../../../../providers/PhotosProvider'
|
||||
import { PhotosContext } from '@providers/PhotosProvider'
|
||||
|
||||
function GalleryContainer(): React.ReactElement {
|
||||
const {
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
/* eslint-disable react/jsx-no-undef */
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import React, { useContext, useEffect, useState } from 'react'
|
||||
import { PhotosContext } from '../../../../../providers/PhotosProvider'
|
||||
import useFetch from '../../../../../hooks/useFetch'
|
||||
import { PhotosContext } from '@providers/PhotosProvider'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
import { toast } from 'react-toastify'
|
||||
import HamburgerMenu from '../../../../../components/general/HamburgerMenu'
|
||||
import MenuItem from '../../../../../components/general/HamburgerMenu/MenuItem'
|
||||
import HamburgerMenu from '@components/HamburgerMenu'
|
||||
import MenuItem from '@components/HamburgerMenu/MenuItem'
|
||||
|
||||
function GalleryHeader(): React.ReactElement {
|
||||
const { refreshPhotos, hidePhotosInAlbum, setHidePhotosInAlbum, setReady } =
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import React, { useContext } from 'react'
|
||||
import { PhotosContext } from '../../../../../providers/PhotosProvider'
|
||||
import { PhotosContext } from '@providers/PhotosProvider'
|
||||
|
||||
function MobileSlidingScrollbar(): React.ReactElement {
|
||||
const { galleryWrapperRef, sideSliderRef, mobileDateDisplayRef, photos } =
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import moment from 'moment'
|
||||
import React, { useContext, useRef } from 'react'
|
||||
import { PhotosContext } from '../../../../../providers/PhotosProvider'
|
||||
import { PhotosContext } from '@providers/PhotosProvider'
|
||||
|
||||
function TimelineScrollbar(): React.ReactElement {
|
||||
const {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/* eslint-disable multiline-ternary */
|
||||
import React, { useContext, useEffect, useState } from 'react'
|
||||
import ModuleHeader from '../../../../components/general/ModuleHeader'
|
||||
import ModuleHeader from '@components/ModuleHeader'
|
||||
import GalleryHeader from './Gallery/GalleryHeader'
|
||||
import PhotosSidebar from '../../components/PhotosSidebar'
|
||||
import GalleryContainer from './Gallery/GalleryContainer'
|
||||
import ModifyAlbumModal from '../../components/modals/ModifyAlbumModal'
|
||||
import AddPhotosToAlbumModal from '../../components/modals/AddPhotosToAlbumModal'
|
||||
import DeletePhotosConfirmationModal from '../../components/modals/DeletePhotosConfirmationModal'
|
||||
import { PhotosContext } from '../../../../providers/PhotosProvider'
|
||||
import { GlobalStateContext } from '../../../../providers/GlobalStateProvider'
|
||||
import { PhotosContext } from '@providers/PhotosProvider'
|
||||
import { GlobalStateContext } from '@providers/GlobalStateProvider'
|
||||
|
||||
function PhotosMainGallery(): React.ReactElement {
|
||||
const { sidebarExpanded } = useContext(GlobalStateContext)
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/* eslint-disable multiline-ternary */
|
||||
import React from 'react'
|
||||
import ModuleHeader from '../../components/general/ModuleHeader'
|
||||
import ModuleHeader from '@components/ModuleHeader'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import Timer from './components/Timer'
|
||||
import ModuleWrapper from '../../components/general/ModuleWrapper'
|
||||
import ModuleWrapper from '@components/ModuleWrapper'
|
||||
|
||||
export default function PomodoroTimer(): React.ReactElement {
|
||||
return (
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
import React, { Fragment, useEffect } from 'react'
|
||||
import { Icon } from '@iconify/react'
|
||||
import { useLocation, useNavigate, useParams } from 'react-router-dom'
|
||||
import GoBackButton from '../../../../components/general/GoBackButton'
|
||||
import GoBackButton from '@components/GoBackButton'
|
||||
import { PROJECT_STATUS, type IProjectsKEntry } from '../ProjectList'
|
||||
import useFetch from '../../../../hooks/useFetch'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import ProjectProgress from './sections/ProjectProgress'
|
||||
import ProjectFiles from './sections/ProjectFiles'
|
||||
import { Listbox, Transition } from '@headlessui/react'
|
||||
import { toast } from 'react-toastify'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
import APIComponentWithFallback from '../../../../components/general/APIComponentWithFallback'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
|
||||
export interface IProjectsKVersion {
|
||||
collectionId: string
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
/* eslint-disable multiline-ternary */
|
||||
import React, { useState } from 'react'
|
||||
import { type IProjectsKEntry } from '../../ProjectList'
|
||||
import APIComponentWithFallback from '../../../../../components/general/APIComponentWithFallback'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import EmptyStateScreen from '../../../../../components/general/EmptyStateScreen'
|
||||
import EmptyStateScreen from '@components/EmptyStateScreen'
|
||||
import { toast } from 'react-toastify'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
import FILE_ICONS from '../../../../../constants/file_icons'
|
||||
import useFetch from '../../../../../hooks/useFetch'
|
||||
import HamburgerMenu from '../../../../../components/general/HamburgerMenu'
|
||||
import MenuItem from '../../../../../components/general/HamburgerMenu/MenuItem'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import HamburgerMenu from '@components/HamburgerMenu'
|
||||
import MenuItem from '@components/HamburgerMenu/MenuItem'
|
||||
|
||||
export default function ProjectFiles({
|
||||
projectData,
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
import React from 'react'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { type IProjectsKEntry } from '../../ProjectList'
|
||||
import APIComponentWithFallback from '../../../../../components/general/APIComponentWithFallback'
|
||||
import useFetch from '../../../../../hooks/useFetch'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import { toast } from 'react-toastify'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
|
||||
@@ -96,7 +96,7 @@ export default function ProjectProgress({
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<ul className="mt-6 flex flex-col gap-2">
|
||||
<ul className="mt-6 space-y-2">
|
||||
{progress.steps.map((id: string, index: number) => (
|
||||
<li
|
||||
key={id}
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
import React, { Fragment, useState } from 'react'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { toast } from 'react-toastify'
|
||||
import Modal from '../../../../../components/general/Modal'
|
||||
import CreateOrModifyButton from '../../../../../components/general/CreateOrModifyButton'
|
||||
import Input from '../../../../../components/general/Input'
|
||||
import Modal from '@components/Modal'
|
||||
import CreateOrModifyButton from '@components/CreateOrModifyButton'
|
||||
import Input from '@components/Input'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
import useFetch from '../../../../../hooks/useFetch'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import { type Step } from '../../ProjectEntry/sections/ProjectProgress'
|
||||
import APIComponentWithFallback from '../../../../../components/general/APIComponentWithFallback'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
import { Listbox, Transition } from '@headlessui/react'
|
||||
import { PROJECT_STATUS } from '..'
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import React from 'react'
|
||||
import { type IProjectsKEntry, PROJECT_STATUS } from '..'
|
||||
import SidebarDivider from '../../../../../components/Sidebar/components/SidebarDivider'
|
||||
import SidebarItem from '../../../../../components/Sidebar/components/SidebarItem'
|
||||
import SidebarTitle from '../../../../../components/Sidebar/components/SidebarTitle'
|
||||
import APIComponentWithFallback from '../../../../../components/general/APIComponentWithFallback'
|
||||
import SidebarDivider from '@sidebar/components/SidebarDivider'
|
||||
import SidebarItem from '@sidebar/components/SidebarItem'
|
||||
import SidebarTitle from '@sidebar/components/SidebarTitle'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
import { useSearchParams } from 'react-router-dom'
|
||||
|
||||
function Sidebar({
|
||||
|
||||
@@ -2,18 +2,18 @@
|
||||
/* eslint-disable multiline-ternary */
|
||||
/* eslint-disable @typescript-eslint/indent */
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import ModuleHeader from '../../../../components/general/ModuleHeader'
|
||||
import ModuleWrapper from '../../../../components/general/ModuleWrapper'
|
||||
import useFetch from '../../../../hooks/useFetch'
|
||||
import APIComponentWithFallback from '../../../../components/general/APIComponentWithFallback'
|
||||
import ModuleHeader from '@components/ModuleHeader'
|
||||
import ModuleWrapper from '@components/ModuleWrapper'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
import { useSearchParams } from 'react-router-dom'
|
||||
import { type IProjectsKProgress } from '../ProjectEntry/sections/ProjectProgress'
|
||||
import CreateProjectModal from './components/CreateProjectModal'
|
||||
import EmptyStateScreen from '../../../../components/general/EmptyStateScreen'
|
||||
import Sidebar from './components/Sidebar'
|
||||
import EmptyStateScreen from '@components/EmptyStateScreen'
|
||||
import Sidebar from '@sidebar'
|
||||
import ProjectList from './components/ProjectList'
|
||||
import Header from './components/Header'
|
||||
import SearchInput from '../../../../components/general/SearchInput'
|
||||
import SearchInput from '@components/SearchInput'
|
||||
|
||||
export interface IProjectsKEntry {
|
||||
collectionId: string
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react'
|
||||
import { Icon } from '@iconify/react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import GoBackButton from '../../../components/general/GoBackButton'
|
||||
import GoBackButton from '@components/GoBackButton'
|
||||
|
||||
function Kanban(): React.ReactElement {
|
||||
const navigate = useNavigate()
|
||||
@@ -66,7 +66,7 @@ function Kanban(): React.ReactElement {
|
||||
<Icon icon="tabler:dots-vertical" className="text-xl" />
|
||||
</button>
|
||||
</div>
|
||||
<ul className="mt-6 flex flex-col gap-2 overflow-y-auto pr-2">
|
||||
<ul className="mt-6 space-y-2 overflow-y-auto pr-2">
|
||||
{Array(Math.floor(Math.random() * 10))
|
||||
.fill(0)
|
||||
.map((_, index) => (
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
/* eslint-disable multiline-ternary */
|
||||
/* eslint-disable @typescript-eslint/indent */
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import ModuleHeader from '../../components/general/ModuleHeader'
|
||||
import SidebarTitle from '../../components/Sidebar/components/SidebarTitle'
|
||||
import SidebarDivider from '../../components/Sidebar/components/SidebarDivider'
|
||||
import ModuleHeader from '@components/ModuleHeader'
|
||||
import SidebarTitle from '@sidebar/components/SidebarTitle'
|
||||
import SidebarDivider from '@sidebar/components/SidebarDivider'
|
||||
import { Icon } from '@iconify/react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import SidebarItem from '../../components/Sidebar/components/SidebarItem'
|
||||
import SidebarItem from '@sidebar/components/SidebarItem'
|
||||
import { faker } from '@faker-js/faker'
|
||||
import ModuleWrapper from '../../components/general/ModuleWrapper'
|
||||
import SearchInput from '../../components/general/SearchInput'
|
||||
import ModuleWrapper from '@components/ModuleWrapper'
|
||||
import SearchInput from '@components/SearchInput'
|
||||
|
||||
function shuffle(array: any[]): any[] {
|
||||
let currentIndex = array.length
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { faker } from '@faker-js/faker'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import React, { useState } from 'react'
|
||||
import ModuleHeader from '../../components/general/ModuleHeader'
|
||||
import SidebarDivider from '../../components/Sidebar/components/SidebarDivider'
|
||||
import SidebarItem from '../../components/Sidebar/components/SidebarItem'
|
||||
import SidebarTitle from '../../components/Sidebar/components/SidebarTitle'
|
||||
import ModuleWrapper from '../../components/general/ModuleWrapper'
|
||||
import SearchInput from '../../components/general/SearchInput'
|
||||
import ModuleHeader from '@components/ModuleHeader'
|
||||
import SidebarDivider from '@sidebar/components/SidebarDivider'
|
||||
import SidebarItem from '@sidebar/components/SidebarItem'
|
||||
import SidebarTitle from '@sidebar/components/SidebarTitle'
|
||||
import ModuleWrapper from '@components/ModuleWrapper'
|
||||
import SearchInput from '@components/SearchInput'
|
||||
|
||||
function ReferenceBooks(): React.ReactElement {
|
||||
const [searchQuery, setSearchQuery] = useState('')
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
/* eslint-disable multiline-ternary */
|
||||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||
import React, { useEffect } from 'react'
|
||||
import ModuleHeader from '../../components/general/ModuleHeader'
|
||||
import ModuleHeader from '@components/ModuleHeader'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import useFetch from '../../hooks/useFetch'
|
||||
import APIComponentWithFallback from '../../components/general/APIComponentWithFallback'
|
||||
import ModuleWrapper from '../../components/general/ModuleWrapper'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
import ModuleWrapper from '@components/ModuleWrapper'
|
||||
import GaugeComponent from 'react-gauge-component'
|
||||
import moment from 'moment'
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import React, { useContext, useEffect, useState } from 'react'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { SpotifyContext } from '../../providers/SpotifyProvider'
|
||||
import EmptyStateScreen from '../../components/general/EmptyStateScreen'
|
||||
import { SpotifyContext } from '@providers/SpotifyProvider'
|
||||
import EmptyStateScreen from '@components/EmptyStateScreen'
|
||||
|
||||
function WebPlayback(): React.ReactElement {
|
||||
const { player, isPaused, isActive, currentTrack } =
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||
/* eslint-disable multiline-ternary */
|
||||
import React, { useContext } from 'react'
|
||||
import ModuleHeader from '../../components/general/ModuleHeader'
|
||||
import { AuthContext } from '../../providers/AuthProvider'
|
||||
import ModuleHeader from '@components/ModuleHeader'
|
||||
import { AuthContext } from '@providers/AuthProvider'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import WebPlayback from './WebPlayback'
|
||||
import ModuleWrapper from '../../components/general/ModuleWrapper'
|
||||
import ModuleWrapper from '@components/ModuleWrapper'
|
||||
|
||||
function Spotify(): React.ReactElement {
|
||||
const { userData } = useContext(AuthContext)
|
||||
|
||||
@@ -4,14 +4,14 @@ import React, { useEffect, useState } from 'react'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { toast } from 'react-toastify'
|
||||
import { useDebounce } from '@uidotdev/usehooks'
|
||||
import Modal from '../../../components/general/Modal'
|
||||
import ColorPickerModal from '../../../components/general/ColorPicker/ColorPickerModal'
|
||||
import Modal from '@components/Modal'
|
||||
import ColorPickerModal from '@components/ColorPicker/ColorPickerModal'
|
||||
import { type ITodoListList } from './Sidebar'
|
||||
import CreateOrModifyButton from '../../../components/general/CreateOrModifyButton'
|
||||
import IconSelector from '../../../components/general/IconSelector'
|
||||
import Input from '../../../components/general/Input'
|
||||
import IconInput from '../../../components/general/IconSelector/IconInput'
|
||||
import ColorInput from '../../../components/general/ColorPicker/ColorInput'
|
||||
import CreateOrModifyButton from '@components/CreateOrModifyButton'
|
||||
import IconSelector from '@components/IconSelector'
|
||||
import Input from '@components/Input'
|
||||
import IconInput from '@components/IconSelector/IconInput'
|
||||
import ColorInput from '@components/ColorPicker/ColorInput'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
|
||||
function ModifyListModal({
|
||||
|
||||
@@ -4,9 +4,9 @@ import React, { useEffect, useState } from 'react'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { toast } from 'react-toastify'
|
||||
import { useDebounce } from '@uidotdev/usehooks'
|
||||
import Modal from '../../../components/general/Modal'
|
||||
import CreateOrModifyButton from '../../../components/general/CreateOrModifyButton'
|
||||
import Input from '../../../components/general/Input'
|
||||
import Modal from '@components/Modal'
|
||||
import CreateOrModifyButton from '@components/CreateOrModifyButton'
|
||||
import Input from '@components/Input'
|
||||
import { type ITodoListTag } from './Sidebar'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
/* eslint-disable multiline-ternary */
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import Input from '../../../../components/general/Input'
|
||||
import Input from '@components/Input'
|
||||
import { type ITodoListTag, type ITodoListList } from '../Sidebar'
|
||||
import { type ITodoListEntryItem } from '../..'
|
||||
import HamburgerMenu from '../../../../components/general/HamburgerMenu'
|
||||
import MenuItem from '../../../../components/general/HamburgerMenu/MenuItem'
|
||||
import HamburgerMenu from '@components/HamburgerMenu'
|
||||
import MenuItem from '@components/HamburgerMenu/MenuItem'
|
||||
import { toast } from 'react-toastify'
|
||||
import { cookieParse } from 'pocketbase'
|
||||
import CreateOrModifyButton from '../../../../components/general/CreateOrModifyButton'
|
||||
import CreateOrModifyButton from '@components/CreateOrModifyButton'
|
||||
import PrioritySelector from './components/PrioritySelector'
|
||||
import ListSelector from './components/ListSelector'
|
||||
import TagsSelector from './components/TagsSelector'
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||
/* eslint-disable @typescript-eslint/indent */
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import SidebarDivider from '../../../components/Sidebar/components/SidebarDivider'
|
||||
import SidebarTitle from '../../../components/Sidebar/components/SidebarTitle'
|
||||
import SidebarDivider from '@sidebar/components/SidebarDivider'
|
||||
import SidebarTitle from '@sidebar/components/SidebarTitle'
|
||||
import { Icon } from '@iconify/react'
|
||||
import APIComponentWithFallback from '../../../components/general/APIComponentWithFallback'
|
||||
import GoBackButton from '../../../components/general/GoBackButton'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
import GoBackButton from '@components/GoBackButton'
|
||||
import ModifyListModal from './ModifyListModal'
|
||||
import ModifyTagModal from './ModifyTagModal'
|
||||
|
||||
|
||||
@@ -6,14 +6,14 @@ import Sidebar, {
|
||||
type ITodoListTag,
|
||||
type ITodoListList
|
||||
} from './components/Sidebar'
|
||||
import ModuleHeader from '../../components/general/ModuleHeader'
|
||||
import useFetch from '../../hooks/useFetch'
|
||||
import APIComponentWithFallback from '../../components/general/APIComponentWithFallback'
|
||||
import ModuleHeader from '@components/ModuleHeader'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import APIComponentWithFallback from '@components/APIComponentWithFallback'
|
||||
import TaskItem from './components/TaskItem'
|
||||
import ModifyTaskWindow from './components/ModifyTaskWindow'
|
||||
import ModuleWrapper from '../../components/general/ModuleWrapper'
|
||||
import SearchInput from '../../components/general/SearchInput'
|
||||
import DeleteConfirmationModal from '../../components/general/DeleteConfirmationModal'
|
||||
import ModuleWrapper from '@components/ModuleWrapper'
|
||||
import SearchInput from '@components/SearchInput'
|
||||
import DeleteConfirmationModal from '@components/DeleteConfirmationModal'
|
||||
|
||||
export interface ITodoListEntryItem {
|
||||
collectionId: string
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||
/* eslint-disable @typescript-eslint/member-delimiter-style */
|
||||
import React, { createContext, useContext, useEffect, useState } from 'react'
|
||||
import { GlobalStateContext } from './GlobalStateProvider'
|
||||
import React, { createContext, useEffect, useState } from 'react'
|
||||
import { AUTH_ERROR_MESSAGES } from '../constants/auth'
|
||||
import { toast } from 'react-toastify'
|
||||
|
||||
const AUTH_DATA: {
|
||||
auth: boolean
|
||||
setAuth: React.Dispatch<React.SetStateAction<boolean>>
|
||||
authenticate: ({
|
||||
email,
|
||||
password
|
||||
@@ -14,7 +14,7 @@ const AUTH_DATA: {
|
||||
email: string
|
||||
password: string
|
||||
}) => Promise<string>
|
||||
authWithOauth: (provider: string) => Promise<string>
|
||||
verifyToken: (token: string) => Promise<{ success: boolean; userData: any }>
|
||||
logout: () => void
|
||||
loginQuota: {
|
||||
quota: number
|
||||
@@ -26,8 +26,9 @@ const AUTH_DATA: {
|
||||
getAvatarURL: () => string
|
||||
} = {
|
||||
auth: false,
|
||||
setAuth: () => {},
|
||||
authenticate: async () => '',
|
||||
authWithOauth: async () => '',
|
||||
verifyToken: async () => ({ success: false, userData: null }),
|
||||
logout: () => {},
|
||||
loginQuota: {
|
||||
quota: 5,
|
||||
@@ -50,9 +51,6 @@ export default function AuthProvider({
|
||||
const [userData, setUserData] = useState<any>(null)
|
||||
const [quota, setQuota] = useState(5)
|
||||
const [authLoading, setAuthLoading] = useState(true)
|
||||
const {
|
||||
pocketbase: { pocketbase, loading, error }
|
||||
} = useContext(GlobalStateContext)
|
||||
|
||||
function updateQuota(): number {
|
||||
const storedQuota = window.localStorage.getItem('quota')
|
||||
@@ -97,6 +95,29 @@ export default function AuthProvider({
|
||||
toast.error(AUTH_ERROR_MESSAGES.QUOTA_EXCEEDED)
|
||||
}
|
||||
|
||||
async function verifyToken(token: string): Promise<{
|
||||
success: boolean
|
||||
userData: any
|
||||
}> {
|
||||
return await fetch(`${import.meta.env.VITE_API_HOST}/user/auth/verify`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
})
|
||||
.then(async res => {
|
||||
const data = await res.json()
|
||||
if (res.ok && data.state === 'success') {
|
||||
return { success: true, userData: data.userData }
|
||||
} else {
|
||||
return { success: false, userData: null }
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
return { success: false, userData: null }
|
||||
})
|
||||
}
|
||||
|
||||
async function authenticate({
|
||||
email,
|
||||
password
|
||||
@@ -104,109 +125,65 @@ export default function AuthProvider({
|
||||
email: string
|
||||
password: string
|
||||
}): Promise<string> {
|
||||
if (!loading && pocketbase !== null && error === null) {
|
||||
try {
|
||||
await pocketbase
|
||||
.collection('users')
|
||||
.authWithPassword(email, password)
|
||||
.catch(e => {
|
||||
throw e.message
|
||||
})
|
||||
const res = fetch(`${import.meta.env.VITE_API_HOST}/user/auth/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ email, password })
|
||||
})
|
||||
.then(async res => {
|
||||
const data = await res.json()
|
||||
if (res.ok && data.state === 'success') {
|
||||
window.localStorage.setItem('quota', '5')
|
||||
window.localStorage.removeItem('lastQuotaExceeded')
|
||||
|
||||
window.localStorage.setItem('quota', '5')
|
||||
window.localStorage.removeItem('lastQuotaExceeded')
|
||||
document.cookie = `token=${data.token}; path=/; expires=${new Date(
|
||||
Date.now() + 7 * 24 * 60 * 60 * 1000
|
||||
).toUTCString()}`
|
||||
|
||||
document.cookie = `token=${
|
||||
pocketbase.authStore.token
|
||||
}; path=/; expires=${new Date(
|
||||
Date.now() + 7 * 24 * 60 * 60 * 1000
|
||||
).toUTCString()}`
|
||||
setUserData(data.userData)
|
||||
setAuth(true)
|
||||
|
||||
setUserData(pocketbase.authStore.model)
|
||||
setAuth(pocketbase.authStore.isValid)
|
||||
|
||||
return 'success: ' + pocketbase.authStore.model?.name
|
||||
} catch (error) {
|
||||
switch (error) {
|
||||
return 'success: ' + data.userData.name
|
||||
} else {
|
||||
throw new Error(data.message)
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
switch (err) {
|
||||
case 'Failed to authenticate.':
|
||||
return AUTH_ERROR_MESSAGES.INVALID_CREDENTIALS
|
||||
default:
|
||||
return AUTH_ERROR_MESSAGES.UNKNOWN_ERROR
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return AUTH_ERROR_MESSAGES.DATABASE_NOT_READY
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
async function authWithOauth(provider: string): Promise<string> {
|
||||
if (!loading && pocketbase !== null && error === null) {
|
||||
try {
|
||||
const w = window.open()
|
||||
|
||||
await pocketbase
|
||||
.collection('users')
|
||||
.authWithOAuth2({
|
||||
provider,
|
||||
urlCallback: url => {
|
||||
if (w !== null) {
|
||||
w.location.href = url
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
throw e.message
|
||||
})
|
||||
|
||||
window.localStorage.setItem('quota', '5')
|
||||
window.localStorage.removeItem('lastQuotaExceeded')
|
||||
|
||||
document.cookie = `token=${
|
||||
pocketbase.authStore.token
|
||||
}; path=/; expires=${new Date(
|
||||
Date.now() + 7 * 24 * 60 * 60 * 1000
|
||||
).toUTCString()}`
|
||||
|
||||
setUserData(pocketbase.authStore.model)
|
||||
|
||||
return 'success: ' + pocketbase.authStore.model?.name
|
||||
} catch (error) {
|
||||
switch (error) {
|
||||
case 'Failed to authenticate.':
|
||||
return AUTH_ERROR_MESSAGES.INVALID_CREDENTIALS
|
||||
default:
|
||||
return AUTH_ERROR_MESSAGES.UNKNOWN_ERROR
|
||||
}
|
||||
} finally {
|
||||
setAuth(pocketbase.authStore.isValid)
|
||||
}
|
||||
} else {
|
||||
return AUTH_ERROR_MESSAGES.DATABASE_NOT_READY
|
||||
}
|
||||
return await res
|
||||
}
|
||||
|
||||
function logout(): void {
|
||||
if (!loading && pocketbase !== null) {
|
||||
pocketbase.authStore.clear()
|
||||
setAuth(false)
|
||||
document.cookie = `token=; path=/; expires=${new Date(
|
||||
Date.now() + 7 * 24 * 60 * 60 * 1000
|
||||
).toUTCString()}`
|
||||
setUserData(null)
|
||||
setAuth(false)
|
||||
document.cookie = `token=; path=/; expires=${new Date(
|
||||
Date.now() + 7 * 24 * 60 * 60 * 1000
|
||||
).toUTCString()}`
|
||||
setUserData(null)
|
||||
|
||||
window.localStorage.setItem('quota', '5')
|
||||
window.localStorage.removeItem('lastQuotaExceeded')
|
||||
}
|
||||
window.localStorage.setItem('quota', '5')
|
||||
window.localStorage.removeItem('lastQuotaExceeded')
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setAuthLoading(true)
|
||||
if (!loading && pocketbase !== null && document.cookie.includes('token')) {
|
||||
;(async () => {
|
||||
await pocketbase.collection('users').authRefresh()
|
||||
setAuth(pocketbase.authStore.isValid)
|
||||
setUserData(pocketbase.authStore.model)
|
||||
})()
|
||||
updateQuota()
|
||||
if (document.cookie.includes('token')) {
|
||||
verifyToken(document.cookie.split('=')[1])
|
||||
.then(async ({ success, userData }) => {
|
||||
if (success) {
|
||||
setUserData(userData)
|
||||
setAuth(true)
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
setAuth(false)
|
||||
})
|
||||
@@ -216,18 +193,13 @@ export default function AuthProvider({
|
||||
} else {
|
||||
setAuthLoading(false)
|
||||
}
|
||||
}, [loading])
|
||||
|
||||
useEffect(() => {
|
||||
updateQuota()
|
||||
}, [])
|
||||
|
||||
function getAvatarURL(): string {
|
||||
const _userData = pocketbase?.authStore.model
|
||||
if (_userData) {
|
||||
if (userData) {
|
||||
return `${import.meta.env.VITE_POCKETBASE_ENDPOINT}/api/files/${
|
||||
_userData.collectionId
|
||||
}/${_userData.id}/${_userData.avatar}`
|
||||
userData.collectionId
|
||||
}/${userData.id}/${userData.avatar}`
|
||||
}
|
||||
return ''
|
||||
}
|
||||
@@ -236,8 +208,9 @@ export default function AuthProvider({
|
||||
<AuthContext.Provider
|
||||
value={{
|
||||
auth,
|
||||
setAuth,
|
||||
authenticate,
|
||||
authWithOauth,
|
||||
verifyToken,
|
||||
logout,
|
||||
loginQuota: {
|
||||
quota,
|
||||
|
||||
@@ -1,23 +1,11 @@
|
||||
import React, { createContext, useEffect, useState } from 'react'
|
||||
import usePocketbase from '../hooks/usePocketbase'
|
||||
import type Pocketbase from 'pocketbase'
|
||||
|
||||
const GLOBAL_STATE: {
|
||||
sidebarExpanded: boolean
|
||||
toggleSidebar: () => void
|
||||
pocketbase: {
|
||||
pocketbase: Pocketbase | null
|
||||
loading: boolean
|
||||
error: any
|
||||
}
|
||||
} = {
|
||||
sidebarExpanded: true,
|
||||
toggleSidebar: () => {},
|
||||
pocketbase: {
|
||||
pocketbase: null,
|
||||
loading: false,
|
||||
error: null
|
||||
}
|
||||
toggleSidebar: () => {}
|
||||
}
|
||||
|
||||
export const GlobalStateContext = createContext(GLOBAL_STATE)
|
||||
@@ -28,7 +16,6 @@ export default function GlobalStateProvider({
|
||||
children: React.ReactNode
|
||||
}): React.ReactElement {
|
||||
const [navbarExpanded, setNavbarExpanded] = useState(true)
|
||||
const { pocketbase, loading, error } = usePocketbase()
|
||||
|
||||
function toggleNavbarExpanded(): void {
|
||||
setNavbarExpanded(!navbarExpanded)
|
||||
@@ -44,12 +31,7 @@ export default function GlobalStateProvider({
|
||||
<GlobalStateContext.Provider
|
||||
value={{
|
||||
sidebarExpanded: navbarExpanded,
|
||||
toggleSidebar: toggleNavbarExpanded,
|
||||
pocketbase: {
|
||||
pocketbase,
|
||||
loading,
|
||||
error
|
||||
}
|
||||
toggleSidebar: toggleNavbarExpanded
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/* eslint-disable @typescript-eslint/indent */
|
||||
import moment from 'moment'
|
||||
import React, { createContext, useEffect, useRef, useState } from 'react'
|
||||
import useFetch from '../hooks/useFetch'
|
||||
import useFetch from '@hooks/useFetch'
|
||||
import { Outlet } from 'react-router'
|
||||
|
||||
export interface IPhotosEntryDimensionsItem {
|
||||
|
||||
@@ -19,7 +19,14 @@
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
|
||||
"paths": {
|
||||
"@components/*": ["./src/components/general/*"],
|
||||
"@providers/*": ["./src/providers/*"],
|
||||
"@hooks/*": ["./src/hooks/*"],
|
||||
"@sidebar/*": ["./src/components/Sidebar/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
|
||||
@@ -4,7 +4,12 @@
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true
|
||||
"allowSyntheticDefaultImports": true,
|
||||
|
||||
"paths": {
|
||||
"@components/*": ["./src/components/general/*"],
|
||||
"@providers/*": ["./src/providers/*"]
|
||||
}
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
|
||||
@@ -1,7 +1,20 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import mkcert from 'vite-plugin-mkcert'
|
||||
import path from 'path'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
plugins: [react(), mkcert()],
|
||||
server: {
|
||||
https: false
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@components': path.resolve(__dirname, './src/components/general'),
|
||||
'@providers': path.resolve(__dirname, './src/providers'),
|
||||
'@hooks': path.resolve(__dirname, './src/hooks'),
|
||||
'@sidebar': path.resolve(__dirname, './src/components/Sidebar')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user