Made a lot of the front end
This commit is contained in:
parent
04a6322b55
commit
6ff62ca0a6
17
.env.example
17
.env.example
@ -1,17 +0,0 @@
|
|||||||
# When adding additional environment variables, the schema in "/src/env.js"
|
|
||||||
# should be updated accordingly.
|
|
||||||
|
|
||||||
# Examples:
|
|
||||||
# SERVERVAR="foo"
|
|
||||||
# NEXT_PUBLIC_CLIENTVAR="bar"
|
|
||||||
|
|
||||||
# Drizzle
|
|
||||||
## WAN
|
|
||||||
DATABASE_URL="postgresql://postgres:password@localhost:5432/example_db"
|
|
||||||
## LAN
|
|
||||||
DATABASE_URL="postgresql://postgres:password@localhost:5432/example_db"
|
|
||||||
|
|
||||||
# Auth.js
|
|
||||||
# openssl rand -base64 33
|
|
||||||
AUTH_SECRET=""
|
|
||||||
# Auth.js Providers:
|
|
@ -27,7 +27,11 @@
|
|||||||
"@radix-ui/react-label": "^2.1.0",
|
"@radix-ui/react-label": "^2.1.0",
|
||||||
"@radix-ui/react-navigation-menu": "^1.2.0",
|
"@radix-ui/react-navigation-menu": "^1.2.0",
|
||||||
"@radix-ui/react-popover": "^1.1.1",
|
"@radix-ui/react-popover": "^1.1.1",
|
||||||
|
"@radix-ui/react-progress": "^1.1.0",
|
||||||
|
"@radix-ui/react-select": "^2.1.2",
|
||||||
|
"@radix-ui/react-slider": "^1.2.1",
|
||||||
"@radix-ui/react-slot": "^1.1.0",
|
"@radix-ui/react-slot": "^1.1.0",
|
||||||
|
"@radix-ui/react-tabs": "^1.1.1",
|
||||||
"@radix-ui/react-toast": "^1.2.1",
|
"@radix-ui/react-toast": "^1.2.1",
|
||||||
"@t3-oss/env-nextjs": "^0.10.1",
|
"@t3-oss/env-nextjs": "^0.10.1",
|
||||||
"@types/jsonwebtoken": "^9.0.6",
|
"@types/jsonwebtoken": "^9.0.6",
|
||||||
|
270
pnpm-lock.yaml
generated
270
pnpm-lock.yaml
generated
@ -38,9 +38,21 @@ importers:
|
|||||||
'@radix-ui/react-popover':
|
'@radix-ui/react-popover':
|
||||||
specifier: ^1.1.1
|
specifier: ^1.1.1
|
||||||
version: 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
version: 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-progress':
|
||||||
|
specifier: ^1.1.0
|
||||||
|
version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-select':
|
||||||
|
specifier: ^2.1.2
|
||||||
|
version: 2.1.2(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-slider':
|
||||||
|
specifier: ^1.2.1
|
||||||
|
version: 1.2.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
'@radix-ui/react-slot':
|
'@radix-ui/react-slot':
|
||||||
specifier: ^1.1.0
|
specifier: ^1.1.0
|
||||||
version: 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
version: 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-tabs':
|
||||||
|
specifier: ^1.1.1
|
||||||
|
version: 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
'@radix-ui/react-toast':
|
'@radix-ui/react-toast':
|
||||||
specifier: ^1.2.1
|
specifier: ^1.2.1
|
||||||
version: 1.2.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
version: 1.2.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
@ -778,6 +790,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
|
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
|
'@radix-ui/number@1.1.0':
|
||||||
|
resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==}
|
||||||
|
|
||||||
'@radix-ui/primitive@1.0.1':
|
'@radix-ui/primitive@1.0.1':
|
||||||
resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==}
|
resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==}
|
||||||
|
|
||||||
@ -885,6 +900,15 @@ packages:
|
|||||||
'@types/react':
|
'@types/react':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-context@1.1.1':
|
||||||
|
resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/react-dialog@1.0.5':
|
'@radix-ui/react-dialog@1.0.5':
|
||||||
resolution: {integrity: sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==}
|
resolution: {integrity: sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -946,6 +970,19 @@ packages:
|
|||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-dismissable-layer@1.1.1':
|
||||||
|
resolution: {integrity: sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/react-dropdown-menu@2.1.1':
|
'@radix-ui/react-dropdown-menu@2.1.1':
|
||||||
resolution: {integrity: sha512-y8E+x9fBq9qvteD2Zwa4397pUVhYsh9iq44b5RD5qu1GMJWBCBuVg1hMyItbc6+zH00TxGRqd9Iot4wzf3OoBQ==}
|
resolution: {integrity: sha512-y8E+x9fBq9qvteD2Zwa4397pUVhYsh9iq44b5RD5qu1GMJWBCBuVg1hMyItbc6+zH00TxGRqd9Iot4wzf3OoBQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -977,6 +1014,15 @@ packages:
|
|||||||
'@types/react':
|
'@types/react':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-focus-guards@1.1.1':
|
||||||
|
resolution: {integrity: sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/react-focus-scope@1.0.4':
|
'@radix-ui/react-focus-scope@1.0.4':
|
||||||
resolution: {integrity: sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==}
|
resolution: {integrity: sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -1112,6 +1158,19 @@ packages:
|
|||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-portal@1.1.2':
|
||||||
|
resolution: {integrity: sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/react-presence@1.0.1':
|
'@radix-ui/react-presence@1.0.1':
|
||||||
resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==}
|
resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -1138,6 +1197,19 @@ packages:
|
|||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-presence@1.1.1':
|
||||||
|
resolution: {integrity: sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/react-primitive@1.0.3':
|
'@radix-ui/react-primitive@1.0.3':
|
||||||
resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==}
|
resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -1164,6 +1236,19 @@ packages:
|
|||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-progress@1.1.0':
|
||||||
|
resolution: {integrity: sha512-aSzvnYpP725CROcxAOEBVZZSIQVQdHgBr2QQFKySsaD14u8dNT0batuXI+AAGDdAHfXH8rbnHmjYFqVJ21KkRg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/react-roving-focus@1.1.0':
|
'@radix-ui/react-roving-focus@1.1.0':
|
||||||
resolution: {integrity: sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==}
|
resolution: {integrity: sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -1177,6 +1262,32 @@ packages:
|
|||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-select@2.1.2':
|
||||||
|
resolution: {integrity: sha512-rZJtWmorC7dFRi0owDmoijm6nSJH1tVw64QGiNIZ9PNLyBDtG+iAq+XGsya052At4BfarzY/Dhv9wrrUr6IMZA==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-slider@1.2.1':
|
||||||
|
resolution: {integrity: sha512-bEzQoDW0XP+h/oGbutF5VMWJPAl/UU8IJjr7h02SOHDIIIxq+cep8nItVNoBV+OMmahCdqdF38FTpmXoqQUGvw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/react-slot@1.0.2':
|
'@radix-ui/react-slot@1.0.2':
|
||||||
resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==}
|
resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -1195,6 +1306,19 @@ packages:
|
|||||||
'@types/react':
|
'@types/react':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/react-tabs@1.1.1':
|
||||||
|
resolution: {integrity: sha512-3GBUDmP2DvzmtYLMsHmpA1GtR46ZDZ+OreXM/N+kkQJOPIgytFWWTfDQmBQKBvaFS0Vno0FktdbVzN28KGrMdw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': '*'
|
||||||
|
'@types/react-dom': '*'
|
||||||
|
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
'@types/react-dom':
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/react-toast@1.2.1':
|
'@radix-ui/react-toast@1.2.1':
|
||||||
resolution: {integrity: sha512-5trl7piMXcZiCq7MW6r8YYmu0bK5qDpTWz+FdEPdKyft2UixkspheYbjbrLXVN5NGKHFbOP7lm8eD0biiSqZqg==}
|
resolution: {integrity: sha512-5trl7piMXcZiCq7MW6r8YYmu0bK5qDpTWz+FdEPdKyft2UixkspheYbjbrLXVN5NGKHFbOP7lm8eD0biiSqZqg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -2959,6 +3083,16 @@ packages:
|
|||||||
'@types/react':
|
'@types/react':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
react-remove-scroll@2.6.0:
|
||||||
|
resolution: {integrity: sha512-I2U4JVEsQenxDAKaVa3VZ/JeJZe0/2DxPWL8Tj8yLKctQJQiZM52pn/GWFpSp8dftjM3pSAHVJZscAnC/y+ySQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
peerDependencies:
|
||||||
|
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@types/react':
|
||||||
|
optional: true
|
||||||
|
|
||||||
react-style-singleton@2.2.1:
|
react-style-singleton@2.2.1:
|
||||||
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
|
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@ -3759,6 +3893,8 @@ snapshots:
|
|||||||
'@pkgjs/parseargs@0.11.0':
|
'@pkgjs/parseargs@0.11.0':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@radix-ui/number@1.1.0': {}
|
||||||
|
|
||||||
'@radix-ui/primitive@1.0.1':
|
'@radix-ui/primitive@1.0.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.25.6
|
'@babel/runtime': 7.25.6
|
||||||
@ -3854,6 +3990,12 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 18.3.3
|
'@types/react': 18.3.3
|
||||||
|
|
||||||
|
'@radix-ui/react-context@1.1.1(@types/react@18.3.3)(react@18.3.1)':
|
||||||
|
dependencies:
|
||||||
|
react: 18.3.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 18.3.3
|
||||||
|
|
||||||
'@radix-ui/react-dialog@1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
'@radix-ui/react-dialog@1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.25.6
|
'@babel/runtime': 7.25.6
|
||||||
@ -3932,6 +4074,19 @@ snapshots:
|
|||||||
'@types/react': 18.3.3
|
'@types/react': 18.3.3
|
||||||
'@types/react-dom': 18.3.0
|
'@types/react-dom': 18.3.0
|
||||||
|
|
||||||
|
'@radix-ui/react-dismissable-layer@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/primitive': 1.1.0
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
react: 18.3.1
|
||||||
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 18.3.3
|
||||||
|
'@types/react-dom': 18.3.0
|
||||||
|
|
||||||
'@radix-ui/react-dropdown-menu@2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
'@radix-ui/react-dropdown-menu@2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@radix-ui/primitive': 1.1.0
|
'@radix-ui/primitive': 1.1.0
|
||||||
@ -3960,6 +4115,12 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 18.3.3
|
'@types/react': 18.3.3
|
||||||
|
|
||||||
|
'@radix-ui/react-focus-guards@1.1.1(@types/react@18.3.3)(react@18.3.1)':
|
||||||
|
dependencies:
|
||||||
|
react: 18.3.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 18.3.3
|
||||||
|
|
||||||
'@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
'@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.25.6
|
'@babel/runtime': 7.25.6
|
||||||
@ -4116,6 +4277,16 @@ snapshots:
|
|||||||
'@types/react': 18.3.3
|
'@types/react': 18.3.3
|
||||||
'@types/react-dom': 18.3.0
|
'@types/react-dom': 18.3.0
|
||||||
|
|
||||||
|
'@radix-ui/react-portal@1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
react: 18.3.1
|
||||||
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 18.3.3
|
||||||
|
'@types/react-dom': 18.3.0
|
||||||
|
|
||||||
'@radix-ui/react-presence@1.0.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
'@radix-ui/react-presence@1.0.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.25.6
|
'@babel/runtime': 7.25.6
|
||||||
@ -4137,6 +4308,16 @@ snapshots:
|
|||||||
'@types/react': 18.3.3
|
'@types/react': 18.3.3
|
||||||
'@types/react-dom': 18.3.0
|
'@types/react-dom': 18.3.0
|
||||||
|
|
||||||
|
'@radix-ui/react-presence@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
react: 18.3.1
|
||||||
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 18.3.3
|
||||||
|
'@types/react-dom': 18.3.0
|
||||||
|
|
||||||
'@radix-ui/react-primitive@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
'@radix-ui/react-primitive@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.25.6
|
'@babel/runtime': 7.25.6
|
||||||
@ -4156,6 +4337,16 @@ snapshots:
|
|||||||
'@types/react': 18.3.3
|
'@types/react': 18.3.3
|
||||||
'@types/react-dom': 18.3.0
|
'@types/react-dom': 18.3.0
|
||||||
|
|
||||||
|
'@radix-ui/react-progress@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/react-context': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
react: 18.3.1
|
||||||
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 18.3.3
|
||||||
|
'@types/react-dom': 18.3.0
|
||||||
|
|
||||||
'@radix-ui/react-roving-focus@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
'@radix-ui/react-roving-focus@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@radix-ui/primitive': 1.1.0
|
'@radix-ui/primitive': 1.1.0
|
||||||
@ -4173,6 +4364,54 @@ snapshots:
|
|||||||
'@types/react': 18.3.3
|
'@types/react': 18.3.3
|
||||||
'@types/react-dom': 18.3.0
|
'@types/react-dom': 18.3.0
|
||||||
|
|
||||||
|
'@radix-ui/react-select@2.1.2(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/number': 1.1.0
|
||||||
|
'@radix-ui/primitive': 1.1.0
|
||||||
|
'@radix-ui/react-collection': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-context': 1.1.1(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-direction': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-dismissable-layer': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-focus-guards': 1.1.1(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-focus-scope': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-id': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-popper': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-portal': 1.1.2(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-slot': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-visually-hidden': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
aria-hidden: 1.2.4
|
||||||
|
react: 18.3.1
|
||||||
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
|
react-remove-scroll: 2.6.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 18.3.3
|
||||||
|
'@types/react-dom': 18.3.0
|
||||||
|
|
||||||
|
'@radix-ui/react-slider@1.2.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/number': 1.1.0
|
||||||
|
'@radix-ui/primitive': 1.1.0
|
||||||
|
'@radix-ui/react-collection': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-context': 1.1.1(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-direction': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-use-size': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
react: 18.3.1
|
||||||
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 18.3.3
|
||||||
|
'@types/react-dom': 18.3.0
|
||||||
|
|
||||||
'@radix-ui/react-slot@1.0.2(@types/react@18.3.3)(react@18.3.1)':
|
'@radix-ui/react-slot@1.0.2(@types/react@18.3.3)(react@18.3.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.25.6
|
'@babel/runtime': 7.25.6
|
||||||
@ -4188,6 +4427,22 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 18.3.3
|
'@types/react': 18.3.3
|
||||||
|
|
||||||
|
'@radix-ui/react-tabs@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
|
dependencies:
|
||||||
|
'@radix-ui/primitive': 1.1.0
|
||||||
|
'@radix-ui/react-context': 1.1.1(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-direction': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-id': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
'@radix-ui/react-presence': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-roving-focus': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
react: 18.3.1
|
||||||
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 18.3.3
|
||||||
|
'@types/react-dom': 18.3.0
|
||||||
|
|
||||||
'@radix-ui/react-toast@1.2.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
'@radix-ui/react-toast@1.2.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@radix-ui/primitive': 1.1.0
|
'@radix-ui/primitive': 1.1.0
|
||||||
@ -5105,7 +5360,7 @@ snapshots:
|
|||||||
debug: 4.3.6
|
debug: 4.3.6
|
||||||
enhanced-resolve: 5.17.1
|
enhanced-resolve: 5.17.1
|
||||||
eslint: 8.57.0
|
eslint: 8.57.0
|
||||||
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)
|
eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
|
||||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)
|
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)
|
||||||
fast-glob: 3.3.2
|
fast-glob: 3.3.2
|
||||||
get-tsconfig: 4.7.6
|
get-tsconfig: 4.7.6
|
||||||
@ -5127,7 +5382,7 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0):
|
eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
@ -6120,6 +6375,17 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 18.3.3
|
'@types/react': 18.3.3
|
||||||
|
|
||||||
|
react-remove-scroll@2.6.0(@types/react@18.3.3)(react@18.3.1):
|
||||||
|
dependencies:
|
||||||
|
react: 18.3.1
|
||||||
|
react-remove-scroll-bar: 2.3.6(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
react-style-singleton: 2.2.1(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
tslib: 2.6.3
|
||||||
|
use-callback-ref: 1.3.2(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
use-sidecar: 1.1.2(@types/react@18.3.3)(react@18.3.1)
|
||||||
|
optionalDependencies:
|
||||||
|
'@types/react': 18.3.3
|
||||||
|
|
||||||
react-style-singleton@2.2.1(@types/react@18.3.3)(react@18.3.1):
|
react-style-singleton@2.2.1(@types/react@18.3.3)(react@18.3.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
get-nonce: 1.0.1
|
get-nonce: 1.0.1
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use server"
|
"use server"
|
||||||
import { auth } from "~/auth"
|
import { auth } from "~/auth"
|
||||||
import BreadCrumbBillTracker from "~/components/home/breadcrumb/BreadCrumbBillTracker"
|
import BreadCrumbBillTracker from "~/components/portal/home/breadcrumb/BreadCrumbBillTracker"
|
||||||
import BillTrackerCalendar from "~/components/billtracker/BillTrackerCalendar"
|
import BillTrackerCalendar from "~/components/portal/billtracker/BillTrackerCalendar"
|
||||||
|
|
||||||
export default async function HomePage() {
|
export default async function HomePage() {
|
||||||
const session = await auth()
|
const session = await auth()
|
0
src/app/account/documents/page.tsx
Normal file
0
src/app/account/documents/page.tsx
Normal file
59
src/app/account/layout.tsx
Normal file
59
src/app/account/layout.tsx
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import Link from 'next/link'
|
||||||
|
import { Button } from "~/components/ui/button"
|
||||||
|
import { Card, CardContent } from "~/components/ui/card"
|
||||||
|
import { CreditCard, FileText, MessageSquare, PenToolIcon as Tool, BarChart } from 'lucide-react'
|
||||||
|
|
||||||
|
export default function AccountLayout({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto px-4 py-8">
|
||||||
|
<h1 className="text-3xl font-bold mb-8 text-primary">My Account</h1>
|
||||||
|
<div className="flex flex-col md:flex-row gap-8">
|
||||||
|
<Card className="w-full md:w-64 h-fit">
|
||||||
|
<CardContent className="p-4">
|
||||||
|
<nav className="space-y-2">
|
||||||
|
<Link href="/account" passHref>
|
||||||
|
<Button variant="ghost" className="w-full justify-start">
|
||||||
|
<BarChart className="mr-2 h-4 w-4" /> Dashboard
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
<Link href="/account/payments" passHref>
|
||||||
|
<Button variant="ghost" className="w-full justify-start">
|
||||||
|
<CreditCard className="mr-2 h-4 w-4" /> Payments
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
<Link href="/account/workorders" passHref>
|
||||||
|
<Button variant="ghost" className="w-full justify-start">
|
||||||
|
<Tool className="mr-2 h-4 w-4" /> Work Orders
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
<Link href="/account/messages" passHref>
|
||||||
|
<Button variant="ghost" className="w-full justify-start">
|
||||||
|
<MessageSquare className="mr-2 h-4 w-4" /> Messages
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
<Link href="/account/documents" passHref>
|
||||||
|
<Button variant="ghost" className="w-full justify-start">
|
||||||
|
<FileText className="mr-2 h-4 w-4" /> Documents
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
<Link href="/account/billtracker" passHref>
|
||||||
|
<Button variant="ghost" className="w-full justify-start">
|
||||||
|
<BarChart className="mr-2 h-4 w-4" /> Bill Tracker
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</nav>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
<main className="flex-1">
|
||||||
|
{children}
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
0
src/app/account/messages/page.tsx
Normal file
0
src/app/account/messages/page.tsx
Normal file
112
src/app/account/page.tsx
Normal file
112
src/app/account/page.tsx
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"
|
||||||
|
import { Button } from "~/components/ui/button"
|
||||||
|
import { Progress } from "~/components/ui/progress"
|
||||||
|
import { CreditCard, AlertTriangle, CheckCircle } from 'lucide-react'
|
||||||
|
|
||||||
|
export default function AccountHomePage() {
|
||||||
|
// This data would typically come from your backend
|
||||||
|
const accountData = {
|
||||||
|
balance: 1200,
|
||||||
|
nextPaymentDue: "2023-07-01",
|
||||||
|
recentPayment: {
|
||||||
|
amount: 1200,
|
||||||
|
date: "2023-06-01"
|
||||||
|
},
|
||||||
|
workOrders: [
|
||||||
|
{ id: 1, title: "Leaky faucet", status: "In Progress" },
|
||||||
|
{ id: 2, title: "Broken AC", status: "Scheduled" }
|
||||||
|
],
|
||||||
|
documents: [
|
||||||
|
{ id: 1, title: "Lease Agreement", date: "2023-01-15" },
|
||||||
|
{ id: 2, title: "Move-in Checklist", date: "2023-01-15" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="space-y-8">
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Account Overview</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||||
|
<Card>
|
||||||
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||||
|
<CardTitle className="text-sm font-medium">Current Balance</CardTitle>
|
||||||
|
<CreditCard className="h-4 w-4 text-muted-foreground" />
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="text-2xl font-bold">${accountData.balance}</div>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
Next payment due: {accountData.nextPaymentDue}
|
||||||
|
</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
<Card>
|
||||||
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||||
|
<CardTitle className="text-sm font-medium">Recent Payment</CardTitle>
|
||||||
|
<CheckCircle className="h-4 w-4 text-muted-foreground" />
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="text-2xl font-bold">${accountData.recentPayment.amount}</div>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
Paid on: {accountData.recentPayment.date}
|
||||||
|
</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
<Card>
|
||||||
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
||||||
|
<CardTitle className="text-sm font-medium">Lease Progress</CardTitle>
|
||||||
|
<AlertTriangle className="h-4 w-4 text-muted-foreground" />
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<Progress value={33} className="w-full" />
|
||||||
|
<p className="text-xs text-muted-foreground mt-2">
|
||||||
|
4 months remaining
|
||||||
|
</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<div className="grid gap-4 md:grid-cols-2">
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Recent Work Orders</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<ul className="space-y-4">
|
||||||
|
{accountData.workOrders.map(order => (
|
||||||
|
<li key={order.id} className="flex justify-between items-center">
|
||||||
|
<span>{order.title}</span>
|
||||||
|
<span className="text-sm text-muted-foreground">{order.status}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
<Button className="w-full mt-4">View All Work Orders</Button>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Recent Documents</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<ul className="space-y-4">
|
||||||
|
{accountData.documents.map(doc => (
|
||||||
|
<li key={doc.id} className="flex justify-between items-center">
|
||||||
|
<span>{doc.title}</span>
|
||||||
|
<span className="text-sm text-muted-foreground">{doc.date}</span>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
<Button className="w-full mt-4">View All Documents</Button>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
0
src/app/account/payments/page.tsx
Normal file
0
src/app/account/payments/page.tsx
Normal file
0
src/app/account/workorders/page.tsx
Normal file
0
src/app/account/workorders/page.tsx
Normal file
83
src/app/contact/page.tsx
Normal file
83
src/app/contact/page.tsx
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import type { Metadata } from 'next'
|
||||||
|
import ContactForm from '~/components/contact/contact-form'
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"
|
||||||
|
import { MapPin, Phone, Mail, Clock } from 'lucide-react'
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: 'Contact Us | PropertyPro',
|
||||||
|
description: 'Get in touch with PropertyPro for any inquiries about our properties or services.',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ContactPage() {
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto px-4 py-8">
|
||||||
|
<h1 className="text-3xl font-bold mb-8 text-primary">Contact Us</h1>
|
||||||
|
|
||||||
|
<div className="grid md:grid-cols-2 gap-8">
|
||||||
|
<div>
|
||||||
|
<ContactForm />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-6">
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Our Office</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-2">
|
||||||
|
<p className="flex items-center">
|
||||||
|
<MapPin className="mr-2 h-4 w-4 text-primary" />
|
||||||
|
123 Property Street, Cityville, State 12345
|
||||||
|
</p>
|
||||||
|
<p className="flex items-center">
|
||||||
|
<Phone className="mr-2 h-4 w-4 text-primary" />
|
||||||
|
(123) 456-7890
|
||||||
|
</p>
|
||||||
|
<p className="flex items-center">
|
||||||
|
<Mail className="mr-2 h-4 w-4 text-primary" />
|
||||||
|
info@propertypro.com
|
||||||
|
</p>
|
||||||
|
<p className="flex items-center">
|
||||||
|
<Clock className="mr-2 h-4 w-4 text-primary" />
|
||||||
|
Mon-Fri: 9am-5pm, Sat: 10am-3pm, Sun: Closed
|
||||||
|
</p>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>FAQ</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<h3 className="font-semibold mb-1">How do I schedule a property viewing?</h3>
|
||||||
|
<p className="text-sm text-muted-foreground">You can schedule a viewing by contacting our office or using the form on this page.</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="font-semibold mb-1">What documents do I need to apply for a rental?</h3>
|
||||||
|
<p className="text-sm text-muted-foreground">Typically, you'll need proof of income, identification, and references. Specific requirements may vary.</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 className="font-semibold mb-1">How do I report maintenance issues?</h3>
|
||||||
|
<p className="text-sm text-muted-foreground">Tenants can report maintenance issues through their online account or by contacting our office directly.</p>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<div className="aspect-w-16 aspect-h-9">
|
||||||
|
<iframe
|
||||||
|
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3022.1422937950147!2d-73.98731968459391!3d40.74844797932681!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x89c259a9b3117469%3A0xd134e199a405a163!2sEmpire%20State%20Building!5e0!3m2!1sen!2sus!4v1616562308246!5m2!1sen!2sus"
|
||||||
|
width="600"
|
||||||
|
height="450"
|
||||||
|
style={{border:0}}
|
||||||
|
allowFullScreen={true}
|
||||||
|
loading="lazy"
|
||||||
|
className="w-full h-full rounded-lg"
|
||||||
|
title="PropertyPro Office Location"
|
||||||
|
></iframe>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ import { type Metadata } from "next";
|
|||||||
import Theme_Toggle from '~/components/theme/theme_toggle'
|
import Theme_Toggle from '~/components/theme/theme_toggle'
|
||||||
import { Button } from "~/components/ui/button"
|
import { Button } from "~/components/ui/button"
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
import Avatar_Popover from "~/components/auth/AvatarPopover";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Tenant Portal",
|
title: "Tenant Portal",
|
||||||
@ -25,8 +26,76 @@ export default async function RootLayout({
|
|||||||
}: Readonly<{ children: React.ReactNode }>) {
|
}: Readonly<{ children: React.ReactNode }>) {
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
if (!session?.user) {
|
if (!session?.user) {
|
||||||
}
|
return (
|
||||||
return (
|
<html lang="en">
|
||||||
|
<body
|
||||||
|
className={cn(
|
||||||
|
"min-h-screen bg-background font-sans antialiased",
|
||||||
|
fontSans.variable)}
|
||||||
|
>
|
||||||
|
<Theme_Provider
|
||||||
|
attribute="class"
|
||||||
|
defaultTheme="system"
|
||||||
|
enableSystem={true}
|
||||||
|
disableTransitionOnChange={true}
|
||||||
|
>
|
||||||
|
<SessionProvider>
|
||||||
|
<div className="flex flex-col min-h-screen">
|
||||||
|
<header className="bg-background shadow-sm">
|
||||||
|
<div className="container mx-auto px-4 py-4 flex justify-between items-center">
|
||||||
|
<Link href="/" className="md:text-2xl font-bold text-primary">Magnolia Coast Properties</Link>
|
||||||
|
<nav className="hidden md:flex space-x-2 lg:space-x-4 text-sm lg:text-lg">
|
||||||
|
<Link href="/properties" className="text-gray-600 hover:text-primary">Properties</Link>
|
||||||
|
<Link href="/services" className="text-gray-600 hover:text-primary">Services</Link>
|
||||||
|
<Link href="/about" className="text-gray-600 hover:text-primary">About</Link>
|
||||||
|
<Link href="/contact" className="text-gray-600 hover:text-primary">Contact</Link>
|
||||||
|
</nav>
|
||||||
|
<div className="flex space-x-2 md:space-x-4">
|
||||||
|
<Theme_Toggle/>
|
||||||
|
<Link href="/login">
|
||||||
|
<Button variant="outline">Login</Button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
{children}
|
||||||
|
<footer className="bg-background text-primary py-8">
|
||||||
|
<div className="container mx-auto px-4">
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-xl font-bold mb-4">Magnolia Coast Property Management</h3>
|
||||||
|
<p>Your trusted partner in property management</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="text-lg font-semibold mb-4">Quick Links</h4>
|
||||||
|
<ul className="space-y-2">
|
||||||
|
<li><Link href="/properties" className="hover:text-primary-foreground">Properties</Link></li>
|
||||||
|
<li><Link href="/services" className="hover:text-primary-foreground">Services</Link></li>
|
||||||
|
<li><Link href="/about" className="hover:text-primary-foreground">About Us</Link></li>
|
||||||
|
<li><Link href="/contact" className="hover:text-primary-foreground">Contact</Link></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="text-lg font-semibold mb-4">Contact Us</h4>
|
||||||
|
<p>123 Property Street</p>
|
||||||
|
<p>City, State 12345</p>
|
||||||
|
<p>Phone: (123) 456-7890</p>
|
||||||
|
<p>Email: info@propertypro.com</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-8 text-center">
|
||||||
|
<p>© 2024 Magnolia Coast Property Management LLC. All rights reserved.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</SessionProvider>
|
||||||
|
</Theme_Provider>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<body
|
<body
|
||||||
className={cn(
|
className={cn(
|
||||||
@ -44,18 +113,17 @@ export default async function RootLayout({
|
|||||||
<div className="flex flex-col min-h-screen">
|
<div className="flex flex-col min-h-screen">
|
||||||
<header className="bg-background shadow-sm">
|
<header className="bg-background shadow-sm">
|
||||||
<div className="container mx-auto px-4 py-4 flex justify-between items-center">
|
<div className="container mx-auto px-4 py-4 flex justify-between items-center">
|
||||||
<Link href="/" className="text-2xl:md font-bold text-primary">Magnolia Coast Properties</Link>
|
<Link href="/" className="md:text-2xl font-bold text-primary">Magnolia Coast Properties</Link>
|
||||||
<nav className="hidden md:flex space-x-2 lg:space-x-4 text-sm lg:text-lg">
|
<nav className="hidden md:flex space-x-2 lg:space-x-4 text-sm lg:text-lg">
|
||||||
<Link href="#" className="text-gray-600 hover:text-primary">Home</Link>
|
<Link href="/account" className="text-gray-600 hover:text-primary">My Account</Link>
|
||||||
<Link href="#" className="text-gray-600 hover:text-primary">Properties</Link>
|
<Link href="/properties" className="text-gray-600 hover:text-primary">Properties</Link>
|
||||||
<Link href="#" className="text-gray-600 hover:text-primary">Services</Link>
|
<Link href="/services" className="text-gray-600 hover:text-primary">Services</Link>
|
||||||
<Link href="#" className="text-gray-600 hover:text-primary">About</Link>
|
<Link href="/about" className="text-gray-600 hover:text-primary">About</Link>
|
||||||
<Link href="#" className="text-gray-600 hover:text-primary">Contact</Link>
|
<Link href="/contact" className="text-gray-600 hover:text-primary">Contact</Link>
|
||||||
</nav>
|
</nav>
|
||||||
<div className="flex space-x-2">
|
<div className="flex space-x-2 md:space-x-4">
|
||||||
<Theme_Toggle/>
|
<Theme_Toggle/>
|
||||||
<Button variant="outline">Login</Button>
|
<Avatar_Popover/>
|
||||||
<Button>Register</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
@ -70,11 +138,11 @@ export default async function RootLayout({
|
|||||||
<div>
|
<div>
|
||||||
<h4 className="text-lg font-semibold mb-4">Quick Links</h4>
|
<h4 className="text-lg font-semibold mb-4">Quick Links</h4>
|
||||||
<ul className="space-y-2">
|
<ul className="space-y-2">
|
||||||
<li><Link href="#" className="hover:text-primary-foreground">Home</Link></li>
|
<li><Link href="/account" className="hover:text-primary-foreground">My Account</Link></li>
|
||||||
<li><Link href="#" className="hover:text-primary-foreground">Properties</Link></li>
|
<li><Link href="/properties" className="hover:text-primary-foreground">Properties</Link></li>
|
||||||
<li><Link href="#" className="hover:text-primary-foreground">Services</Link></li>
|
<li><Link href="/services" className="hover:text-primary-foreground">Services</Link></li>
|
||||||
<li><Link href="#" className="hover:text-primary-foreground">About Us</Link></li>
|
<li><Link href="/about" className="hover:text-primary-foreground">About Us</Link></li>
|
||||||
<li><Link href="#" className="hover:text-primary-foreground">Contact</Link></li>
|
<li><Link href="/contact" className="hover:text-primary-foreground">Contact</Link></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@ -95,5 +163,6 @@ export default async function RootLayout({
|
|||||||
</Theme_Provider>
|
</Theme_Provider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
127
src/app/login/page.tsx
Normal file
127
src/app/login/page.tsx
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { Button } from "~/components/ui/button"
|
||||||
|
import { Input } from "~/components/ui/input"
|
||||||
|
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "~/components/ui/card"
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs"
|
||||||
|
import { Label } from "~/components/ui/label"
|
||||||
|
import { Eye, EyeOff } from 'lucide-react'
|
||||||
|
import Sign_In from "~/components/auth/client/SignInAppleButton"
|
||||||
|
|
||||||
|
export default function AuthPage() {
|
||||||
|
const [showPassword, setShowPassword] = useState(false)
|
||||||
|
|
||||||
|
const togglePasswordVisibility = () => setShowPassword(!showPassword)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-center min-h-screen bg-background">
|
||||||
|
<Card className="w-full max-w-md">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-2xl font-bold text-center text-primary">
|
||||||
|
Welcome to Magnolia Coast Property Management
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription className="text-center text-muted-foreground">
|
||||||
|
Login or create an account to get started
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<Tabs defaultValue="login" className="w-full">
|
||||||
|
<TabsList className="grid w-full grid-cols-2">
|
||||||
|
<TabsTrigger value="login">Login</TabsTrigger>
|
||||||
|
<TabsTrigger value="register">Register</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
<TabsContent value="login">
|
||||||
|
<form>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="email">Email</Label>
|
||||||
|
<Input id="email" placeholder="Enter your email" type="email" required />
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="password">Password</Label>
|
||||||
|
<div className="relative">
|
||||||
|
<Input
|
||||||
|
id="password"
|
||||||
|
placeholder="Enter your password"
|
||||||
|
type={showPassword ? "text" : "password"}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent"
|
||||||
|
onClick={togglePasswordVisibility}
|
||||||
|
>
|
||||||
|
{showPassword ? (
|
||||||
|
<EyeOff className="h-4 w-4 text-muted-foreground" />
|
||||||
|
) : (
|
||||||
|
<Eye className="h-4 w-4 text-muted-foreground" />
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col justify-center">
|
||||||
|
<Button className="w-full mt-6 mb-4 text-md" type="submit">Login</Button>
|
||||||
|
<Sign_In/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</TabsContent>
|
||||||
|
<TabsContent value="register">
|
||||||
|
<form>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="register-name">Full Name</Label>
|
||||||
|
<Input id="register-name" placeholder="Enter your full name" required />
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="register-email">Email</Label>
|
||||||
|
<Input id="register-email" placeholder="Enter your email" type="email" required />
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="register-password">Password</Label>
|
||||||
|
<div className="relative">
|
||||||
|
<Input
|
||||||
|
id="register-password"
|
||||||
|
placeholder="Create a password"
|
||||||
|
type={showPassword ? "text" : "password"}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="absolute right-0 top-0 h-full px-3 py-2 hover:bg-transparent"
|
||||||
|
onClick={togglePasswordVisibility}
|
||||||
|
>
|
||||||
|
{showPassword ? (
|
||||||
|
<EyeOff className="h-4 w-4 text-muted-foreground" />
|
||||||
|
) : (
|
||||||
|
<Eye className="h-4 w-4 text-muted-foreground" />
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col justify-center">
|
||||||
|
<Button className="w-full mt-6 mb-4 text-md" type="submit">Register</Button>
|
||||||
|
<Sign_In/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter className="flex justify-center">
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
By continuing, you agree to our{' '}
|
||||||
|
<a href="#" className="underline text-primary">Terms of Service</a> and{' '}
|
||||||
|
<a href="#" className="underline text-primary">Privacy Policy</a>
|
||||||
|
</p>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -34,7 +34,9 @@ export default async function HomePage() {
|
|||||||
<p className="text-gray-600">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
|
<p className="text-gray-600">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter>
|
<CardFooter>
|
||||||
<Button className="w-full">View Details</Button>
|
<Link href={`/properties/${i}`} passHref>
|
||||||
|
<Button className="w-full">View Details</Button>
|
||||||
|
</Link>
|
||||||
</CardFooter>
|
</CardFooter>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
|
86
src/app/properties/[id]/page.tsx
Normal file
86
src/app/properties/[id]/page.tsx
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import Link from 'next/link'
|
||||||
|
import { Button } from "~/components/ui/button"
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"
|
||||||
|
import { Home, Bed, Bath, Square, MapPin, Calendar, DollarSign } from 'lucide-react'
|
||||||
|
|
||||||
|
// This would typically come from your database
|
||||||
|
const property = {
|
||||||
|
id: 1,
|
||||||
|
title: "Luxurious Family Home",
|
||||||
|
type: "House",
|
||||||
|
bedrooms: 4,
|
||||||
|
bathrooms: 3,
|
||||||
|
area: 2500,
|
||||||
|
price: 750000,
|
||||||
|
address: "123 Main St, Anytown, USA",
|
||||||
|
description: "This beautiful family home features spacious living areas, a modern kitchen, and a large backyard perfect for entertaining. Located in a quiet neighborhood with easy access to schools and shopping.",
|
||||||
|
amenities: ["Central Air", "Garage", "Fireplace", "Hardwood Floors", "Swimming Pool"],
|
||||||
|
yearBuilt: 2015,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PropertyPage({ params }: { params: { id: string } }) {
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto px-4 py-8">
|
||||||
|
<Link href="/properties" className="text-primary hover:underline mb-4 inline-block">
|
||||||
|
← Back to Properties
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<div className="grid md:grid-cols-2 gap-8">
|
||||||
|
<div>
|
||||||
|
<img src="/placeholder.svg?height=400&width=600" alt={property.title} className="w-full h-[400px] object-cover rounded-lg" />
|
||||||
|
<div className="mt-4 grid grid-cols-2 gap-4">
|
||||||
|
<img src="/placeholder.svg?height=150&width=250" alt="Additional view 1" className="w-full h-[150px] object-cover rounded" />
|
||||||
|
<img src="/placeholder.svg?height=150&width=250" alt="Additional view 2" className="w-full h-[150px] object-cover rounded" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h1 className="text-3xl font-bold mb-4 text-primary">{property.title}</h1>
|
||||||
|
<p className="text-xl font-semibold mb-4 text-primary">${property.price.toLocaleString()}</p>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-4 mb-6">
|
||||||
|
<div className="flex items-center text-muted-foreground">
|
||||||
|
<Home className="mr-2" /> {property.type}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center text-muted-foreground">
|
||||||
|
<Bed className="mr-2" /> {property.bedrooms} Bedrooms
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center text-muted-foreground">
|
||||||
|
<Bath className="mr-2" /> {property.bathrooms} Bathrooms
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center text-muted-foreground">
|
||||||
|
<Square className="mr-2" /> {property.area} sqft
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center text-muted-foreground">
|
||||||
|
<MapPin className="mr-2" /> {property.address}
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center text-muted-foreground">
|
||||||
|
<Calendar className="mr-2" /> Built in {property.yearBuilt}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="mb-6 text-muted-foreground">{property.description}</p>
|
||||||
|
|
||||||
|
<Card className="mb-6">
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Amenities</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<ul className="grid grid-cols-2 gap-2">
|
||||||
|
{property.amenities.map((amenity, index) => (
|
||||||
|
<li key={index} className="flex items-center text-muted-foreground">
|
||||||
|
<DollarSign className="mr-2 h-4 w-4" /> {amenity}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Button className="w-full">Schedule a Viewing</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
116
src/app/properties/page.tsx
Normal file
116
src/app/properties/page.tsx
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useState } from 'react'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import { Button } from "~/components/ui/button"
|
||||||
|
import { Input } from "~/components/ui/input"
|
||||||
|
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "~/components/ui/card"
|
||||||
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/components/ui/select"
|
||||||
|
import { Slider } from "~/components/ui/slider"
|
||||||
|
import { Search, Home, Bed, Bath, Square } from 'lucide-react'
|
||||||
|
|
||||||
|
// Mock data for properties
|
||||||
|
const properties = Array.from({ length: 12 }, (_, i) => ({
|
||||||
|
id: i + 1,
|
||||||
|
title: `Property ${i + 1}`,
|
||||||
|
type: i % 3 === 0 ? 'House' : i % 3 === 1 ? 'Apartment' : 'Condo',
|
||||||
|
bedrooms: Math.floor(Math.random() * 5) + 1,
|
||||||
|
bathrooms: Math.floor(Math.random() * 3) + 1,
|
||||||
|
area: Math.floor(Math.random() * 1000) + 500,
|
||||||
|
price: Math.floor(Math.random() * 500000) + 100000,
|
||||||
|
}))
|
||||||
|
|
||||||
|
export default function PropertiesPage() {
|
||||||
|
const [sortBy, setSortBy] = useState('price')
|
||||||
|
const [filterType, setFilterType] = useState('All')
|
||||||
|
const [priceRange, setPriceRange] = useState([0, 1000000])
|
||||||
|
const [searchTerm, setSearchTerm] = useState('')
|
||||||
|
|
||||||
|
const filteredAndSortedProperties = properties
|
||||||
|
.filter(property =>
|
||||||
|
(filterType === 'All' || property.type === filterType) &&
|
||||||
|
property.price >= priceRange[0] && property.price <= priceRange[1] &&
|
||||||
|
property.title.toLowerCase().includes(searchTerm.toLowerCase())
|
||||||
|
)
|
||||||
|
.sort((a, b) => {
|
||||||
|
if (sortBy === 'price') return a.price - b.price
|
||||||
|
if (sortBy === 'bedrooms') return b.bedrooms - a.bedrooms
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="container mx-auto px-4 py-8">
|
||||||
|
<h1 className="text-3xl font-bold mb-8 text-primary">Available Properties</h1>
|
||||||
|
|
||||||
|
<div className="mb-8 flex flex-wrap gap-4">
|
||||||
|
<Input
|
||||||
|
placeholder="Search properties..."
|
||||||
|
value={searchTerm}
|
||||||
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
|
className="max-w-xs"
|
||||||
|
/>
|
||||||
|
<Select onValueChange={setSortBy} defaultValue={sortBy}>
|
||||||
|
<SelectTrigger className="w-[180px]">
|
||||||
|
<SelectValue placeholder="Sort by" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="price">Price</SelectItem>
|
||||||
|
<SelectItem value="bedrooms">Bedrooms</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<Select onValueChange={setFilterType} defaultValue={filterType}>
|
||||||
|
<SelectTrigger className="w-[180px]">
|
||||||
|
<SelectValue placeholder="Property type" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="All">All</SelectItem>
|
||||||
|
<SelectItem value="House">House</SelectItem>
|
||||||
|
<SelectItem value="Apartment">Apartment</SelectItem>
|
||||||
|
<SelectItem value="Condo">Condo</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-8">
|
||||||
|
<h2 className="text-xl font-semibold mb-2 text-primary">Price Range</h2>
|
||||||
|
<Slider
|
||||||
|
min={0}
|
||||||
|
max={1000000}
|
||||||
|
step={10000}
|
||||||
|
value={priceRange}
|
||||||
|
onValueChange={setPriceRange}
|
||||||
|
className="max-w-md"
|
||||||
|
/>
|
||||||
|
<div className="mt-2 text-sm text-muted-foreground">
|
||||||
|
${priceRange[0].toLocaleString()} - ${priceRange[1].toLocaleString()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
||||||
|
{filteredAndSortedProperties.map((property) => (
|
||||||
|
<Card key={property.id}>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>{property.title}</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<img src={`/placeholder.svg?height=200&width=300`} alt={property.title} className="w-full h-48 object-cover mb-4 rounded" />
|
||||||
|
<div className="flex justify-between text-muted-foreground">
|
||||||
|
<span><Home className="inline mr-1" /> {property.type}</span>
|
||||||
|
<span><Bed className="inline mr-1" /> {property.bedrooms}</span>
|
||||||
|
<span><Bath className="inline mr-1" /> {property.bathrooms}</span>
|
||||||
|
<span><Square className="inline mr-1" /> {property.area} sqft</span>
|
||||||
|
</div>
|
||||||
|
<p className="mt-4 text-xl font-bold text-primary">${property.price.toLocaleString()}</p>
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter>
|
||||||
|
<Link href={`/properties/${property.id}`} passHref>
|
||||||
|
<Button className="w-full">View Details</Button>
|
||||||
|
</Link>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
9
src/components/auth/No_Session.tsx
Normal file
9
src/components/auth/No_Session.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
const No_Session = () => {
|
||||||
|
return (
|
||||||
|
<div className="w-2/3 mx-auto text-center pt-10">
|
||||||
|
<h1 className="text-3xl font-semibold pb-4">Unauthorized Access</h1>
|
||||||
|
<p className="text-xl">Please sign in to view this page.</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default No_Session;
|
@ -4,12 +4,12 @@ import Image from "next/image"
|
|||||||
|
|
||||||
export default function Sign_In() {
|
export default function Sign_In() {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-row bg-primary py-3 px-10 rounded-xl text-lg font-semibold
|
<Button onClick={() => signIn("apple")} className="flex flex-row bg-primary py-3
|
||||||
mt-10 text-background my-auto">
|
px-10 rounded-md text-md font-semibold text-background">
|
||||||
<Image src="/logos/Apple_logo_black.svg" alt="Apple logo" width={20} height={20}
|
<Image src="/logos/Apple_logo_black.svg" alt="Apple logo" width={16} height={16}
|
||||||
className="mr-4 my-auto"
|
className="mr-4"
|
||||||
/>
|
/>
|
||||||
<Button onClick={() => signIn("apple")}>Sign in with Apple</Button>
|
Sign in with Apple
|
||||||
</div>
|
</Button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,5 +2,13 @@ import { signOut } from "next-auth/react"
|
|||||||
import { Button } from "~/components/ui/button"
|
import { Button } from "~/components/ui/button"
|
||||||
|
|
||||||
export default function Sign_Out() {
|
export default function Sign_Out() {
|
||||||
return <Button onClick={() => signOut()}>Sign Out</Button>
|
return (
|
||||||
|
<Button
|
||||||
|
onClick={() => signOut()}
|
||||||
|
className="flex flex-row bg-yellow py-3
|
||||||
|
px-10 rounded-md text-md font-semibold text-background"
|
||||||
|
>
|
||||||
|
Sign out
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { Button } from "~/components/ui/button"
|
||||||
import { signOut } from "~/auth"
|
import { signOut } from "~/auth"
|
||||||
|
|
||||||
export default function Sign_Out() {
|
export default function Sign_Out() {
|
||||||
@ -9,7 +10,10 @@ export default function Sign_Out() {
|
|||||||
await signOut()
|
await signOut()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<button type="submit" className="w-full">Sign Out</button>
|
<Button type="submit" className="w-full"
|
||||||
|
>
|
||||||
|
Sign Out
|
||||||
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
82
src/components/contact/contact-form.tsx
Normal file
82
src/components/contact/contact-form.tsx
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { Button } from "~/components/ui/button"
|
||||||
|
import { Input } from "~/components/ui/input"
|
||||||
|
import { Textarea } from "~/components/ui/textarea"
|
||||||
|
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "~/components/ui/card"
|
||||||
|
import { Label } from "~/components/ui/label"
|
||||||
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/components/ui/select"
|
||||||
|
|
||||||
|
export default function ContactForm() {
|
||||||
|
const [formStatus, setFormStatus] = useState<'idle' | 'submitting' | 'success' | 'error'>('idle')
|
||||||
|
|
||||||
|
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
event.preventDefault()
|
||||||
|
setFormStatus('submitting')
|
||||||
|
|
||||||
|
// Here you would typically send the form data to your server
|
||||||
|
// For this example, we'll just simulate a submission
|
||||||
|
setTimeout(() => {
|
||||||
|
setFormStatus('success')
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Send us a message</CardTitle>
|
||||||
|
<CardDescription>We'll get back to you as soon as possible.</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="grid w-full items-center gap-4">
|
||||||
|
<div className="flex flex-col space-y-1.5">
|
||||||
|
<Label htmlFor="name">Name</Label>
|
||||||
|
<Input id="name" name="name" placeholder="Your name" required />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col space-y-1.5">
|
||||||
|
<Label htmlFor="email">Email</Label>
|
||||||
|
<Input id="email" name="email" placeholder="Your email" type="email" required />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col space-y-1.5">
|
||||||
|
<Label htmlFor="phone">Phone</Label>
|
||||||
|
<Input id="phone" name="phone" placeholder="Your phone number" type="tel" />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col space-y-1.5">
|
||||||
|
<Label htmlFor="subject">Subject</Label>
|
||||||
|
<Select name="subject" required>
|
||||||
|
<SelectTrigger>
|
||||||
|
<SelectValue placeholder="Select a subject" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="general">General Inquiry</SelectItem>
|
||||||
|
<SelectItem value="viewing">Property Viewing</SelectItem>
|
||||||
|
<SelectItem value="application">Rental Application</SelectItem>
|
||||||
|
<SelectItem value="maintenance">Maintenance Request</SelectItem>
|
||||||
|
<SelectItem value="other">Other</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col space-y-1.5">
|
||||||
|
<Label htmlFor="message">Message</Label>
|
||||||
|
<Textarea id="message" name="message" placeholder="Your message" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<CardFooter className="flex justify-between mt-4 px-0">
|
||||||
|
<Button type="submit" disabled={formStatus === 'submitting'}>
|
||||||
|
{formStatus === 'submitting' ? 'Sending...' : 'Send Message'}
|
||||||
|
</Button>
|
||||||
|
{formStatus === 'success' && (
|
||||||
|
<p className="text-sm text-green-600">Message sent successfully!</p>
|
||||||
|
)}
|
||||||
|
{formStatus === 'error' && (
|
||||||
|
<p className="text-sm text-red-600">There was an error sending your message. Please try again.</p>
|
||||||
|
)}
|
||||||
|
</CardFooter>
|
||||||
|
</form>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import { Calendar } from "~/components/ui/BillTrackerCalendar"
|
import { Calendar } from "~/components/ui/BillTrackerCalendar"
|
||||||
import CreateBillForm from "~/components/billtracker/CreateBillForm"
|
import CreateBillForm from "~/components/portal/billtracker/CreateBillForm"
|
||||||
|
|
||||||
export default function BillTrackerCalendar() {
|
export default function BillTrackerCalendar() {
|
||||||
const [selectedDate, setSelectedDate] = React.useState<Date | undefined>(undefined)
|
const [selectedDate, setSelectedDate] = React.useState<Date | undefined>(undefined)
|
@ -3,7 +3,7 @@ import {
|
|||||||
Drawer,
|
Drawer,
|
||||||
DrawerTrigger,
|
DrawerTrigger,
|
||||||
} from "~/components/ui/drawer"
|
} from "~/components/ui/drawer"
|
||||||
import CreateBillDrawer from "~/components/billtracker/CreateBillDrawer"
|
import CreateBillDrawer from "~/components/portal/billtracker/CreateBillDrawer"
|
||||||
import { Button } from "~/components/ui/button"
|
import { Button } from "~/components/ui/button"
|
||||||
|
|
||||||
type CreateBillFormProps = {
|
type CreateBillFormProps = {
|
@ -7,7 +7,7 @@ import {
|
|||||||
DrawerHeader,
|
DrawerHeader,
|
||||||
DrawerTitle,
|
DrawerTitle,
|
||||||
} from "~/components/ui/drawer"
|
} from "~/components/ui/drawer"
|
||||||
import BillForm from "~/components/billtracker/BillForm"
|
import BillForm from "~/components/portal/billtracker/BillForm"
|
||||||
|
|
||||||
type CreateBillDrawerProps = {
|
type CreateBillDrawerProps = {
|
||||||
date: Date;
|
date: Date;
|
@ -13,28 +13,13 @@ const fontSans = FontSans({
|
|||||||
|
|
||||||
export default function Nav_Bar() {
|
export default function Nav_Bar() {
|
||||||
return (
|
return (
|
||||||
<div className={cn("flex flex-col justify-start items-start " +
|
<div className={cn("flex flex-col justify-start items-start h-5/6 my-auto" +
|
||||||
"py-6 text-lg md:text-xl lg:text-2xl font-semibold font-sans antialiased", fontSans.variable)}
|
"py-6 text-lg md:text-xl lg:text-2xl font-semibold font-sans antialiased", fontSans.variable)}
|
||||||
>
|
>
|
||||||
<Card className="md:p-4">
|
<Card className="md:p-4">
|
||||||
<CardContent className="py-4">
|
<CardContent className="py-4">
|
||||||
<Link href="/">
|
<Link href="/">
|
||||||
Make Payment
|
Payments
|
||||||
</Link>
|
|
||||||
</CardContent>
|
|
||||||
<CardContent className="py-4">
|
|
||||||
<Link href="/">
|
|
||||||
Auto-Payment
|
|
||||||
</Link>
|
|
||||||
</CardContent>
|
|
||||||
<CardContent className="py-4">
|
|
||||||
<Link href="/">
|
|
||||||
Payment Methods
|
|
||||||
</Link>
|
|
||||||
</CardContent>
|
|
||||||
<CardContent className="py-4">
|
|
||||||
<Link href="/">
|
|
||||||
Payment History
|
|
||||||
</Link>
|
</Link>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardContent className="py-4">
|
<CardContent className="py-4">
|
||||||
@ -53,7 +38,7 @@ export default function Nav_Bar() {
|
|||||||
</Link>
|
</Link>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardContent className="pt-4">
|
<CardContent className="pt-4">
|
||||||
<Link href="/billtracker">
|
<Link href="/account/billtracker">
|
||||||
Bill Tracker
|
Bill Tracker
|
||||||
</Link>
|
</Link>
|
||||||
</CardContent>
|
</CardContent>
|
@ -10,7 +10,7 @@ export default function Breadcrumb_Home() {
|
|||||||
<Breadcrumb className="w-full m-auto flex flex-row justify-center items-center">
|
<Breadcrumb className="w-full m-auto flex flex-row justify-center items-center">
|
||||||
<BreadcrumbList>
|
<BreadcrumbList>
|
||||||
<BreadcrumbItem>
|
<BreadcrumbItem>
|
||||||
<Link href="/">
|
<Link href="/account">
|
||||||
<h1 className="text-3xl font-bold text-center font-sans antialiased">
|
<h1 className="text-3xl font-bold text-center font-sans antialiased">
|
||||||
Dashboard
|
Dashboard
|
||||||
</h1>
|
</h1>
|
@ -13,6 +13,7 @@ import {
|
|||||||
export default function Theme_Toggle() {
|
export default function Theme_Toggle() {
|
||||||
const { setTheme } = useTheme()
|
const { setTheme } = useTheme()
|
||||||
return (
|
return (
|
||||||
|
<div className="mx-2">
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button variant="outline" size="icon" className="border-none">
|
<Button variant="outline" size="icon" className="border-none">
|
||||||
@ -33,5 +34,6 @@ export default function Theme_Toggle() {
|
|||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
28
src/components/ui/progress.tsx
Normal file
28
src/components/ui/progress.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as ProgressPrimitive from "@radix-ui/react-progress"
|
||||||
|
|
||||||
|
import { cn } from "~/lib/utils"
|
||||||
|
|
||||||
|
const Progress = React.forwardRef<
|
||||||
|
React.ElementRef<typeof ProgressPrimitive.Root>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
|
||||||
|
>(({ className, value, ...props }, ref) => (
|
||||||
|
<ProgressPrimitive.Root
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"relative h-4 w-full overflow-hidden rounded-full bg-secondary",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ProgressPrimitive.Indicator
|
||||||
|
className="h-full w-full flex-1 bg-primary transition-all"
|
||||||
|
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
||||||
|
/>
|
||||||
|
</ProgressPrimitive.Root>
|
||||||
|
))
|
||||||
|
Progress.displayName = ProgressPrimitive.Root.displayName
|
||||||
|
|
||||||
|
export { Progress }
|
160
src/components/ui/select.tsx
Normal file
160
src/components/ui/select.tsx
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as SelectPrimitive from "@radix-ui/react-select"
|
||||||
|
import { Check, ChevronDown, ChevronUp } from "lucide-react"
|
||||||
|
|
||||||
|
import { cn } from "~/lib/utils"
|
||||||
|
|
||||||
|
const Select = SelectPrimitive.Root
|
||||||
|
|
||||||
|
const SelectGroup = SelectPrimitive.Group
|
||||||
|
|
||||||
|
const SelectValue = SelectPrimitive.Value
|
||||||
|
|
||||||
|
const SelectTrigger = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
||||||
|
>(({ className, children, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Trigger
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<SelectPrimitive.Icon asChild>
|
||||||
|
<ChevronDown className="h-4 w-4 opacity-50" />
|
||||||
|
</SelectPrimitive.Icon>
|
||||||
|
</SelectPrimitive.Trigger>
|
||||||
|
))
|
||||||
|
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
|
||||||
|
|
||||||
|
const SelectScrollUpButton = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.ScrollUpButton
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"flex cursor-default items-center justify-center py-1",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ChevronUp className="h-4 w-4" />
|
||||||
|
</SelectPrimitive.ScrollUpButton>
|
||||||
|
))
|
||||||
|
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
|
||||||
|
|
||||||
|
const SelectScrollDownButton = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.ScrollDownButton
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"flex cursor-default items-center justify-center py-1",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ChevronDown className="h-4 w-4" />
|
||||||
|
</SelectPrimitive.ScrollDownButton>
|
||||||
|
))
|
||||||
|
SelectScrollDownButton.displayName =
|
||||||
|
SelectPrimitive.ScrollDownButton.displayName
|
||||||
|
|
||||||
|
const SelectContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
||||||
|
>(({ className, children, position = "popper", ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Portal>
|
||||||
|
<SelectPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||||
|
position === "popper" &&
|
||||||
|
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
position={position}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<SelectScrollUpButton />
|
||||||
|
<SelectPrimitive.Viewport
|
||||||
|
className={cn(
|
||||||
|
"p-1",
|
||||||
|
position === "popper" &&
|
||||||
|
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</SelectPrimitive.Viewport>
|
||||||
|
<SelectScrollDownButton />
|
||||||
|
</SelectPrimitive.Content>
|
||||||
|
</SelectPrimitive.Portal>
|
||||||
|
))
|
||||||
|
SelectContent.displayName = SelectPrimitive.Content.displayName
|
||||||
|
|
||||||
|
const SelectLabel = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Label>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Label
|
||||||
|
ref={ref}
|
||||||
|
className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
SelectLabel.displayName = SelectPrimitive.Label.displayName
|
||||||
|
|
||||||
|
const SelectItem = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Item>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
||||||
|
>(({ className, children, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Item
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||||
|
<SelectPrimitive.ItemIndicator>
|
||||||
|
<Check className="h-4 w-4" />
|
||||||
|
</SelectPrimitive.ItemIndicator>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||||
|
</SelectPrimitive.Item>
|
||||||
|
))
|
||||||
|
SelectItem.displayName = SelectPrimitive.Item.displayName
|
||||||
|
|
||||||
|
const SelectSeparator = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Separator>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Separator
|
||||||
|
ref={ref}
|
||||||
|
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
|
||||||
|
|
||||||
|
export {
|
||||||
|
Select,
|
||||||
|
SelectGroup,
|
||||||
|
SelectValue,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectContent,
|
||||||
|
SelectLabel,
|
||||||
|
SelectItem,
|
||||||
|
SelectSeparator,
|
||||||
|
SelectScrollUpButton,
|
||||||
|
SelectScrollDownButton,
|
||||||
|
}
|
28
src/components/ui/slider.tsx
Normal file
28
src/components/ui/slider.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as SliderPrimitive from "@radix-ui/react-slider"
|
||||||
|
|
||||||
|
import { cn } from "~/lib/utils"
|
||||||
|
|
||||||
|
const Slider = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SliderPrimitive.Root>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SliderPrimitive.Root
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"relative flex w-full touch-none select-none items-center",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<SliderPrimitive.Track className="relative h-2 w-full grow overflow-hidden rounded-full bg-secondary">
|
||||||
|
<SliderPrimitive.Range className="absolute h-full bg-primary" />
|
||||||
|
</SliderPrimitive.Track>
|
||||||
|
<SliderPrimitive.Thumb className="block h-5 w-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50" />
|
||||||
|
</SliderPrimitive.Root>
|
||||||
|
))
|
||||||
|
Slider.displayName = SliderPrimitive.Root.displayName
|
||||||
|
|
||||||
|
export { Slider }
|
55
src/components/ui/tabs.tsx
Normal file
55
src/components/ui/tabs.tsx
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as TabsPrimitive from "@radix-ui/react-tabs"
|
||||||
|
|
||||||
|
import { cn } from "~/lib/utils"
|
||||||
|
|
||||||
|
const Tabs = TabsPrimitive.Root
|
||||||
|
|
||||||
|
const TabsList = React.forwardRef<
|
||||||
|
React.ElementRef<typeof TabsPrimitive.List>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<TabsPrimitive.List
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
TabsList.displayName = TabsPrimitive.List.displayName
|
||||||
|
|
||||||
|
const TabsTrigger = React.forwardRef<
|
||||||
|
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<TabsPrimitive.Trigger
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
|
||||||
|
|
||||||
|
const TabsContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof TabsPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<TabsPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
TabsContent.displayName = TabsPrimitive.Content.displayName
|
||||||
|
|
||||||
|
export { Tabs, TabsList, TabsTrigger, TabsContent }
|
22
src/components/ui/textarea.tsx
Normal file
22
src/components/ui/textarea.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { cn } from "~/lib/utils"
|
||||||
|
|
||||||
|
const Textarea = React.forwardRef<
|
||||||
|
HTMLTextAreaElement,
|
||||||
|
React.ComponentProps<"textarea">
|
||||||
|
>(({ className, ...props }, ref) => {
|
||||||
|
return (
|
||||||
|
<textarea
|
||||||
|
className={cn(
|
||||||
|
"flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
Textarea.displayName = "Textarea"
|
||||||
|
|
||||||
|
export { Textarea }
|
Loading…
Reference in New Issue
Block a user