Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
7a27b3a5ae | |||
ccbb1d749a | |||
b6f50dc944 | |||
e3aa219389 | |||
ea7ab60e5c |
8
.idea/.gitignore
generated
vendored
8
.idea/.gitignore
generated
vendored
@ -1,8 +0,0 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
2
.idea/discord.xml
generated
2
.idea/discord.xml
generated
@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DiscordProjectSettings">
|
||||
<option name="show" value="PROJECT_FILES" />
|
||||
<option name="show" value="PROJECT" />
|
||||
<option name="description" value="" />
|
||||
</component>
|
||||
</project>
|
6
.idea/git_toolbox_blame.xml
generated
Normal file
6
.idea/git_toolbox_blame.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GitToolBoxBlameSettings">
|
||||
<option name="version" value="2" />
|
||||
</component>
|
||||
</project>
|
15
.idea/git_toolbox_prj.xml
generated
Normal file
15
.idea/git_toolbox_prj.xml
generated
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GitToolBoxProjectSettings">
|
||||
<option name="commitMessageIssueKeyValidationOverride">
|
||||
<BoolValueOverride>
|
||||
<option name="enabled" value="true" />
|
||||
</BoolValueOverride>
|
||||
</option>
|
||||
<option name="commitMessageValidationEnabledOverride">
|
||||
<BoolValueOverride>
|
||||
<option name="enabled" value="true" />
|
||||
</BoolValueOverride>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
10
.idea/inspectionProfiles/Project_Default.xml
generated
10
.idea/inspectionProfiles/Project_Default.xml
generated
@ -1,10 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<Languages>
|
||||
<language minSize="86" name="TypeScript" />
|
||||
</Languages>
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
3
.idea/modules.xml
generated
3
.idea/modules.xml
generated
@ -2,7 +2,8 @@
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/brief-07-front.iml" filepath="$PROJECT_DIR$/.idea/brief-07-front.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/../neptune-back/.idea/neptune-backend.iml" filepath="$PROJECT_DIR$/../neptune-back/.idea/neptune-backend.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/neptune-front.iml" filepath="$PROJECT_DIR$/.idea/neptune-front.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
@ -8,5 +8,6 @@
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="neptune-backend" />
|
||||
</component>
|
||||
</module>
|
6
.idea/nx-console.xml
generated
Normal file
6
.idea/nx-console.xml
generated
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="NxConsoleProjectSettingsProvider">
|
||||
<option name="workspacePath" value="" />
|
||||
</component>
|
||||
</project>
|
1
.idea/vcs.xml
generated
1
.idea/vcs.xml
generated
@ -2,5 +2,6 @@
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../neptune-back" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
21
.idea/webResources.xml
generated
Normal file
21
.idea/webResources.xml
generated
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="WebResourcesPaths">
|
||||
<contentEntries>
|
||||
<entry url="file://$PROJECT_DIR$/../neptune-back">
|
||||
<entryData>
|
||||
<resourceRoots>
|
||||
<path value="file://$PROJECT_DIR$/../neptune-back" />
|
||||
</resourceRoots>
|
||||
</entryData>
|
||||
</entry>
|
||||
<entry url="file://$PROJECT_DIR$">
|
||||
<entryData>
|
||||
<resourceRoots>
|
||||
<path value="file://$PROJECT_DIR$" />
|
||||
</resourceRoots>
|
||||
</entryData>
|
||||
</entry>
|
||||
</contentEntries>
|
||||
</component>
|
||||
</project>
|
155
.idea/workspace.xml
generated
Normal file
155
.idea/workspace.xml
generated
Normal file
@ -0,0 +1,155 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AutoImportSettings">
|
||||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="fe5a67da-16c2-44c1-97cf-5ff76c380f48" name="Changes" comment="">
|
||||
<change beforePath="$PROJECT_DIR$/../neptune-back/prisma/seed.ts" beforeDir="false" afterPath="$PROJECT_DIR$/../neptune-back/prisma/seed.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/../neptune-back" />
|
||||
</component>
|
||||
<component name="ProjectColorInfo">{
|
||||
"associatedIndex": 0
|
||||
}</component>
|
||||
<component name="ProjectId" id="2oC9EyMrMXnN5JRY3d8EUK3Fg2I" />
|
||||
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.git.unshallow": "true",
|
||||
"last_opened_file_path": "/home/avnyr/monoliths/neptune-back",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "pnpm",
|
||||
"npm.dev.executor": "Run",
|
||||
"settings.editor.selected.configurable": "preferences.lookFeel",
|
||||
"ts.external.directory.path": "/home/avnyr/monoliths/neptune-front/node_modules/typescript/lib",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
}
|
||||
}</component>
|
||||
<component name="RecentsManager">
|
||||
<key name="MoveFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$/public" />
|
||||
</key>
|
||||
</component>
|
||||
<component name="RunManager">
|
||||
<configuration name="dev" type="js.build_tools.npm" nameIsGenerated="true">
|
||||
<package-json value="$PROJECT_DIR$/package.json" />
|
||||
<command value="run" />
|
||||
<scripts>
|
||||
<script value="dev" />
|
||||
</scripts>
|
||||
<node-interpreter value="project" />
|
||||
<package-manager value="$USER_HOME$/.local/share/pnpm/pnpm" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
<component name="SharedIndexes">
|
||||
<attachedChunks>
|
||||
<set>
|
||||
<option value="bundled-js-predefined-d6986cc7102b-e768b9ed790e-JavaScript-WS-243.21565.180" />
|
||||
</set>
|
||||
</attachedChunks>
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="fe5a67da-16c2-44c1-97cf-5ff76c380f48" name="Changes" comment="" />
|
||||
<created>1730362584431</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1730362584431</updated>
|
||||
<workItem from="1730362585545" duration="2622000" />
|
||||
<workItem from="1730365668445" duration="599000" />
|
||||
<workItem from="1730370820744" duration="1688000" />
|
||||
<workItem from="1730715257858" duration="38000" />
|
||||
<workItem from="1730971311881" duration="4339000" />
|
||||
<workItem from="1732021386744" duration="27000" />
|
||||
<workItem from="1732021424352" duration="4000" />
|
||||
<workItem from="1732028162494" duration="139000" />
|
||||
<workItem from="1732110655402" duration="2406000" />
|
||||
<workItem from="1732115490707" duration="1304000" />
|
||||
<workItem from="1732177377647" duration="212000" />
|
||||
<workItem from="1732177642993" duration="25000" />
|
||||
<workItem from="1732177670017" duration="26000" />
|
||||
<workItem from="1732177712103" duration="20000" />
|
||||
<workItem from="1732177736286" duration="34000" />
|
||||
<workItem from="1732202348717" duration="1808000" />
|
||||
<workItem from="1732268531361" duration="8833000" />
|
||||
<workItem from="1732453974909" duration="11758000" />
|
||||
<workItem from="1732526630314" duration="3449000" />
|
||||
<workItem from="1732646974158" duration="1446000" />
|
||||
<workItem from="1732696522710" duration="884000" />
|
||||
</task>
|
||||
<task id="LOCAL-00001" summary="Remove IDE configuration files and add new selling feature Removed unnecessary IntelliJ IDEA (`.idea`) configuration files to clean up the repository. Added new sell-related functionality including the SellCryptoPage, SellModal, SellForm, and updated relevant types and UI components.">
|
||||
<option name="closed" value="true" />
|
||||
<created>1732269965774</created>
|
||||
<option name="number" value="00001" />
|
||||
<option name="presentableId" value="LOCAL-00001" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1732269965774</updated>
|
||||
</task>
|
||||
<task id="LOCAL-00002" summary="Enable user crypto amount selection and update deployment docs Added the ability to select user crypto amounts in the user service. Removed commented-out code in the offer controller for clarity. Updated the README with deployment and production instructions.">
|
||||
<option name="closed" value="true" />
|
||||
<created>1732270010458</created>
|
||||
<option name="number" value="00002" />
|
||||
<option name="presentableId" value="LOCAL-00002" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1732270010458</updated>
|
||||
</task>
|
||||
<task id="LOCAL-00003" summary="Fix offer retrieval method and add GDPR acknowledgment Return the result from `getOffersById` method to fix route handling. Add `id` field in offers selection query and introduce `gdpr_acknowledgement` to the user model and mock data for GDPR compliance.">
|
||||
<option name="closed" value="true" />
|
||||
<created>1732488599143</created>
|
||||
<option name="number" value="00003" />
|
||||
<option name="presentableId" value="LOCAL-00003" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1732488599143</updated>
|
||||
</task>
|
||||
<task id="LOCAL-00004" summary="Add defaultCryptoId prop to SellForm and enhance usability Introduced a new defaultCryptoId prop for the SellForm component to pre-select a cryptocurrency on mount. Adjusted responsive layout and usability for various components including headers, modals, and navigation links. Ensured better user feedback and message clarity for login and registration processes.">
|
||||
<option name="closed" value="true" />
|
||||
<created>1732488612776</created>
|
||||
<option name="number" value="00004" />
|
||||
<option name="presentableId" value="LOCAL-00004" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1732488612776</updated>
|
||||
</task>
|
||||
<task id="LOCAL-00005" summary="Some ide config files and libs changes...">
|
||||
<option name="closed" value="true" />
|
||||
<created>1732647552711</created>
|
||||
<option name="number" value="00005" />
|
||||
<option name="presentableId" value="LOCAL-00005" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1732647552711</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="6" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
<component name="VcsManagerConfiguration">
|
||||
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" />
|
||||
<option name="CHECK_NEW_TODO" value="false" />
|
||||
<MESSAGE value="Remove IDE configuration files and add new selling feature Removed unnecessary IntelliJ IDEA (`.idea`) configuration files to clean up the repository. Added new sell-related functionality including the SellCryptoPage, SellModal, SellForm, and updated relevant types and UI components." />
|
||||
<MESSAGE value="Enable user crypto amount selection and update deployment docs Added the ability to select user crypto amounts in the user service. Removed commented-out code in the offer controller for clarity. Updated the README with deployment and production instructions." />
|
||||
<MESSAGE value="Fix offer retrieval method and add GDPR acknowledgment Return the result from `getOffersById` method to fix route handling. Add `id` field in offers selection query and introduce `gdpr_acknowledgement` to the user model and mock data for GDPR compliance." />
|
||||
<MESSAGE value="Add defaultCryptoId prop to SellForm and enhance usability Introduced a new defaultCryptoId prop for the SellForm component to pre-select a cryptocurrency on mount. Adjusted responsive layout and usability for various components including headers, modals, and navigation links. Ensured better user feedback and message clarity for login and registration processes." />
|
||||
<MESSAGE value="Some ide config files and libs changes..." />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="Some ide config files and libs changes..." />
|
||||
</component>
|
||||
</project>
|
15
README.md
15
README.md
@ -1,3 +1,16 @@
|
||||
# neptune-front
|
||||
|
||||
The project to validate my DWWM diploma on the front end.
|
||||
The project to validate my DWWM diploma on the front end.
|
||||
|
||||
## Déploiement
|
||||
- Configurer les variables d'environnements
|
||||
### En développement
|
||||
```shell
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
### En production
|
||||
```shell
|
||||
pnpm build
|
||||
pnpm start
|
||||
```
|
@ -5,7 +5,7 @@
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"dev": "next dev --turbo",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
@ -41,6 +41,7 @@
|
||||
"@radix-ui/react-toggle": "^1.1.0",
|
||||
"@radix-ui/react-toggle-group": "^1.1.0",
|
||||
"@radix-ui/react-tooltip": "^1.1.4",
|
||||
"@tanstack/react-query": "^5.60.2",
|
||||
"@tanstack/react-table": "^8.20.5",
|
||||
"axios": "^1.7.7",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
@ -52,7 +53,7 @@
|
||||
"input-otp": "^1.4.1",
|
||||
"lightweight-charts": "^4.2.1",
|
||||
"lucide-react": "^0.395.0",
|
||||
"next": "14.2.10",
|
||||
"next": "14.2.18",
|
||||
"next-themes": "^0.3.0",
|
||||
"react": "^18.3.1",
|
||||
"react-day-picker": "^8.10.1",
|
||||
|
108
pnpm-lock.yaml
generated
108
pnpm-lock.yaml
generated
@ -95,6 +95,9 @@ importers:
|
||||
'@radix-ui/react-tooltip':
|
||||
specifier: ^1.1.4
|
||||
version: 1.1.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@tanstack/react-query':
|
||||
specifier: ^5.60.2
|
||||
version: 5.60.2(react@18.3.1)
|
||||
'@tanstack/react-table':
|
||||
specifier: ^8.20.5
|
||||
version: 8.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
@ -129,8 +132,8 @@ importers:
|
||||
specifier: ^0.395.0
|
||||
version: 0.395.0(react@18.3.1)
|
||||
next:
|
||||
specifier: 14.2.10
|
||||
version: 14.2.10(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
specifier: 14.2.18
|
||||
version: 14.2.18(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
next-themes:
|
||||
specifier: ^0.3.0
|
||||
version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
@ -542,59 +545,59 @@ packages:
|
||||
'@jridgewell/trace-mapping@0.3.25':
|
||||
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
|
||||
|
||||
'@next/env@14.2.10':
|
||||
resolution: {integrity: sha512-dZIu93Bf5LUtluBXIv4woQw2cZVZ2DJTjax5/5DOs3lzEOeKLy7GxRSr4caK9/SCPdaW6bCgpye6+n4Dh9oJPw==}
|
||||
'@next/env@14.2.18':
|
||||
resolution: {integrity: sha512-2vWLOUwIPgoqMJKG6dt35fVXVhgM09tw4tK3/Q34GFXDrfiHlG7iS33VA4ggnjWxjiz9KV5xzfsQzJX6vGAekA==}
|
||||
|
||||
'@next/swc-darwin-arm64@14.2.10':
|
||||
resolution: {integrity: sha512-V3z10NV+cvMAfxQUMhKgfQnPbjw+Ew3cnr64b0lr8MDiBJs3eLnM6RpGC46nhfMZsiXgQngCJKWGTC/yDcgrDQ==}
|
||||
'@next/swc-darwin-arm64@14.2.18':
|
||||
resolution: {integrity: sha512-tOBlDHCjGdyLf0ube/rDUs6VtwNOajaWV+5FV/ajPgrvHeisllEdymY/oDgv2cx561+gJksfMUtqf8crug7sbA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@next/swc-darwin-x64@14.2.10':
|
||||
resolution: {integrity: sha512-Y0TC+FXbFUQ2MQgimJ/7Ina2mXIKhE7F+GUe1SgnzRmwFY3hX2z8nyVCxE82I2RicspdkZnSWMn4oTjIKz4uzA==}
|
||||
'@next/swc-darwin-x64@14.2.18':
|
||||
resolution: {integrity: sha512-uJCEjutt5VeJ30jjrHV1VIHCsbMYnEqytQgvREx+DjURd/fmKy15NaVK4aR/u98S1LGTnjq35lRTnRyygglxoA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@next/swc-linux-arm64-gnu@14.2.10':
|
||||
resolution: {integrity: sha512-ZfQ7yOy5zyskSj9rFpa0Yd7gkrBnJTkYVSya95hX3zeBG9E55Z6OTNPn1j2BTFWvOVVj65C3T+qsjOyVI9DQpA==}
|
||||
'@next/swc-linux-arm64-gnu@14.2.18':
|
||||
resolution: {integrity: sha512-IL6rU8vnBB+BAm6YSWZewc+qvdL1EaA+VhLQ6tlUc0xp+kkdxQrVqAnh8Zek1ccKHlTDFRyAft0e60gteYmQ4A==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@next/swc-linux-arm64-musl@14.2.10':
|
||||
resolution: {integrity: sha512-n2i5o3y2jpBfXFRxDREr342BGIQCJbdAUi/K4q6Env3aSx8erM9VuKXHw5KNROK9ejFSPf0LhoSkU/ZiNdacpQ==}
|
||||
'@next/swc-linux-arm64-musl@14.2.18':
|
||||
resolution: {integrity: sha512-RCaENbIZqKKqTlL8KNd+AZV/yAdCsovblOpYFp0OJ7ZxgLNbV5w23CUU1G5On+0fgafrsGcW+GdMKdFjaRwyYA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@next/swc-linux-x64-gnu@14.2.10':
|
||||
resolution: {integrity: sha512-GXvajAWh2woTT0GKEDlkVhFNxhJS/XdDmrVHrPOA83pLzlGPQnixqxD8u3bBB9oATBKB//5e4vpACnx5Vaxdqg==}
|
||||
'@next/swc-linux-x64-gnu@14.2.18':
|
||||
resolution: {integrity: sha512-3kmv8DlyhPRCEBM1Vavn8NjyXtMeQ49ID0Olr/Sut7pgzaQTo4h01S7Z8YNE0VtbowyuAL26ibcz0ka6xCTH5g==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@next/swc-linux-x64-musl@14.2.10':
|
||||
resolution: {integrity: sha512-opFFN5B0SnO+HTz4Wq4HaylXGFV+iHrVxd3YvREUX9K+xfc4ePbRrxqOuPOFjtSuiVouwe6uLeDtabjEIbkmDA==}
|
||||
'@next/swc-linux-x64-musl@14.2.18':
|
||||
resolution: {integrity: sha512-mliTfa8seVSpTbVEcKEXGjC18+TDII8ykW4a36au97spm9XMPqQTpdGPNBJ9RySSFw9/hLuaCMByluQIAnkzlw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@next/swc-win32-arm64-msvc@14.2.10':
|
||||
resolution: {integrity: sha512-9NUzZuR8WiXTvv+EiU/MXdcQ1XUvFixbLIMNQiVHuzs7ZIFrJDLJDaOF1KaqttoTujpcxljM/RNAOmw1GhPPQQ==}
|
||||
'@next/swc-win32-arm64-msvc@14.2.18':
|
||||
resolution: {integrity: sha512-J5g0UFPbAjKYmqS3Cy7l2fetFmWMY9Oao32eUsBPYohts26BdrMUyfCJnZFQkX9npYaHNDOWqZ6uV9hSDPw9NA==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@next/swc-win32-ia32-msvc@14.2.10':
|
||||
resolution: {integrity: sha512-fr3aEbSd1GeW3YUMBkWAu4hcdjZ6g4NBl1uku4gAn661tcxd1bHs1THWYzdsbTRLcCKLjrDZlNp6j2HTfrw+Bg==}
|
||||
'@next/swc-win32-ia32-msvc@14.2.18':
|
||||
resolution: {integrity: sha512-Ynxuk4ZgIpdcN7d16ivJdjsDG1+3hTvK24Pp8DiDmIa2+A4CfhJSEHHVndCHok6rnLUzAZD+/UOKESQgTsAZGg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@next/swc-win32-x64-msvc@14.2.10':
|
||||
resolution: {integrity: sha512-UjeVoRGKNL2zfbcQ6fscmgjBAS/inHBh63mjIlfPg/NG8Yn2ztqylXt5qilYb6hoHIwaU2ogHknHWWmahJjgZQ==}
|
||||
'@next/swc-win32-x64-msvc@14.2.18':
|
||||
resolution: {integrity: sha512-dtRGMhiU9TN5nyhwzce+7c/4CCeykYS+ipY/4mIrGzJ71+7zNo55ZxCB7cAVuNqdwtYniFNR2c9OFQ6UdFIMcg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
@ -1246,6 +1249,14 @@ packages:
|
||||
'@swc/helpers@0.5.5':
|
||||
resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==}
|
||||
|
||||
'@tanstack/query-core@5.59.20':
|
||||
resolution: {integrity: sha512-e8vw0lf7KwfGe1if4uPFhvZRWULqHjFcz3K8AebtieXvnMOz5FSzlZe3mTLlPuUBcydCnBRqYs2YJ5ys68wwLg==}
|
||||
|
||||
'@tanstack/react-query@5.60.2':
|
||||
resolution: {integrity: sha512-JhpJNxIAPuE0YCpP1Py4zAsgx+zY0V531McRMtQbwVlJF8+mlZwcOPrzGmPV248K8IP+mPbsfxXToVNMNwjUcw==}
|
||||
peerDependencies:
|
||||
react: ^18 || ^19
|
||||
|
||||
'@tanstack/react-table@8.20.5':
|
||||
resolution: {integrity: sha512-WEHopKw3znbUZ61s9i0+i9g8drmDo6asTWbrQh8Us63DAk/M0FkmIqERew6P71HI75ksZ2Pxyuf4vvKh9rAkiA==}
|
||||
engines: {node: '>=12'}
|
||||
@ -2111,8 +2122,8 @@ packages:
|
||||
react: ^16.8 || ^17 || ^18
|
||||
react-dom: ^16.8 || ^17 || ^18
|
||||
|
||||
next@14.2.10:
|
||||
resolution: {integrity: sha512-sDDExXnh33cY3RkS9JuFEKaS4HmlWmDKP1VJioucCG6z5KuA008DPsDZOzi8UfqEk3Ii+2NCQSJrfbEWtZZfww==}
|
||||
next@14.2.18:
|
||||
resolution: {integrity: sha512-H9qbjDuGivUDEnK6wa+p2XKO+iMzgVgyr9Zp/4Iv29lKa+DYaxJGjOeEA+5VOvJh/M7HLiskehInSa0cWxVXUw==}
|
||||
engines: {node: '>=18.17.0'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@ -3126,33 +3137,33 @@ snapshots:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.4.15
|
||||
|
||||
'@next/env@14.2.10': {}
|
||||
'@next/env@14.2.18': {}
|
||||
|
||||
'@next/swc-darwin-arm64@14.2.10':
|
||||
'@next/swc-darwin-arm64@14.2.18':
|
||||
optional: true
|
||||
|
||||
'@next/swc-darwin-x64@14.2.10':
|
||||
'@next/swc-darwin-x64@14.2.18':
|
||||
optional: true
|
||||
|
||||
'@next/swc-linux-arm64-gnu@14.2.10':
|
||||
'@next/swc-linux-arm64-gnu@14.2.18':
|
||||
optional: true
|
||||
|
||||
'@next/swc-linux-arm64-musl@14.2.10':
|
||||
'@next/swc-linux-arm64-musl@14.2.18':
|
||||
optional: true
|
||||
|
||||
'@next/swc-linux-x64-gnu@14.2.10':
|
||||
'@next/swc-linux-x64-gnu@14.2.18':
|
||||
optional: true
|
||||
|
||||
'@next/swc-linux-x64-musl@14.2.10':
|
||||
'@next/swc-linux-x64-musl@14.2.18':
|
||||
optional: true
|
||||
|
||||
'@next/swc-win32-arm64-msvc@14.2.10':
|
||||
'@next/swc-win32-arm64-msvc@14.2.18':
|
||||
optional: true
|
||||
|
||||
'@next/swc-win32-ia32-msvc@14.2.10':
|
||||
'@next/swc-win32-ia32-msvc@14.2.18':
|
||||
optional: true
|
||||
|
||||
'@next/swc-win32-x64-msvc@14.2.10':
|
||||
'@next/swc-win32-x64-msvc@14.2.18':
|
||||
optional: true
|
||||
|
||||
'@nodelib/fs.scandir@2.1.5':
|
||||
@ -3850,6 +3861,13 @@ snapshots:
|
||||
'@swc/counter': 0.1.3
|
||||
tslib: 2.6.3
|
||||
|
||||
'@tanstack/query-core@5.59.20': {}
|
||||
|
||||
'@tanstack/react-query@5.60.2(react@18.3.1)':
|
||||
dependencies:
|
||||
'@tanstack/query-core': 5.59.20
|
||||
react: 18.3.1
|
||||
|
||||
'@tanstack/react-table@8.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@tanstack/table-core': 8.20.5
|
||||
@ -4860,9 +4878,9 @@ snapshots:
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
|
||||
next@14.2.10(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
next@14.2.18(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
'@next/env': 14.2.10
|
||||
'@next/env': 14.2.18
|
||||
'@swc/helpers': 0.5.5
|
||||
busboy: 1.6.0
|
||||
caniuse-lite: 1.0.30001636
|
||||
@ -4872,15 +4890,15 @@ snapshots:
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
styled-jsx: 5.1.1(@babel/core@7.24.7)(react@18.3.1)
|
||||
optionalDependencies:
|
||||
'@next/swc-darwin-arm64': 14.2.10
|
||||
'@next/swc-darwin-x64': 14.2.10
|
||||
'@next/swc-linux-arm64-gnu': 14.2.10
|
||||
'@next/swc-linux-arm64-musl': 14.2.10
|
||||
'@next/swc-linux-x64-gnu': 14.2.10
|
||||
'@next/swc-linux-x64-musl': 14.2.10
|
||||
'@next/swc-win32-arm64-msvc': 14.2.10
|
||||
'@next/swc-win32-ia32-msvc': 14.2.10
|
||||
'@next/swc-win32-x64-msvc': 14.2.10
|
||||
'@next/swc-darwin-arm64': 14.2.18
|
||||
'@next/swc-darwin-x64': 14.2.18
|
||||
'@next/swc-linux-arm64-gnu': 14.2.18
|
||||
'@next/swc-linux-arm64-musl': 14.2.18
|
||||
'@next/swc-linux-x64-gnu': 14.2.18
|
||||
'@next/swc-linux-x64-musl': 14.2.18
|
||||
'@next/swc-win32-arm64-msvc': 14.2.18
|
||||
'@next/swc-win32-ia32-msvc': 14.2.18
|
||||
'@next/swc-win32-x64-msvc': 14.2.18
|
||||
transitivePeerDependencies:
|
||||
- '@babel/core'
|
||||
- babel-plugin-macros
|
||||
|
@ -10,7 +10,6 @@ import { useEffect, useState } from "react";
|
||||
export default function DashboardPage() {
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [cryptosList, setCryptosList] = useState<ICryptoInWalletInfo[]>([]);
|
||||
//FIX the loop
|
||||
|
||||
useEffect(() => {
|
||||
ApiRequest.authenticated.get
|
||||
|
@ -14,7 +14,7 @@ import type React from "react";
|
||||
export const metadata: Metadata = {
|
||||
title: "Neptune Crypto",
|
||||
description: "A fictive app",
|
||||
icons: "neptune.svg",
|
||||
icons: "/neptune.svg",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
@ -27,18 +27,18 @@ export default function RootLayout({
|
||||
<body className={"w-full min-h-screen flex flex-col items-center justify-between"}>
|
||||
<Providers>
|
||||
<Header>
|
||||
<div className={"flex flex-row flex-wrap md:flex-nowrap gap-2"}>
|
||||
<Button asChild variant={"light"}>
|
||||
<Link href={"/wallet"}><p className={"lg:text-lg"}>Wallet</p></Link>
|
||||
<nav className="flex flex-row flex-wrap md:flex-nowrap gap-2">
|
||||
<Button asChild variant="light">
|
||||
<Link href="/wallet" title={"Go to your wallet"}><p className="lg:text-lg">Wallet</p></Link>
|
||||
</Button>
|
||||
<Button asChild variant={"light"}>
|
||||
<Link href={"/dashboard"}><p className={"lg:text-lg"}>Explore cryptos</p></Link>
|
||||
<Button asChild variant="light">
|
||||
<Link href="/dashboard" title={"Go to the crypto explorer"}><p className="lg:text-lg">Explore cryptos</p></Link>
|
||||
</Button>
|
||||
</div>
|
||||
</nav>
|
||||
</Header>
|
||||
{children}
|
||||
<Toaster />
|
||||
<Footer />
|
||||
<Toaster/>
|
||||
<Footer/>
|
||||
</Providers>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,18 +1,18 @@
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Bitcoin, DollarSign, LineChart, Lock, Zap } from "lucide-react"
|
||||
import Link from "next/link"
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Bitcoin, DollarSign, LineChart, Lock, Zap } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<div className="flex flex-col min-h-screen">
|
||||
<main className="flex-1">
|
||||
<section className="w-full py-12 md:py-16 lg:py-24xl:py-32">
|
||||
<section className="w-full py-12 md:py-16 lg:py-24 xl:py-32">
|
||||
<div className="container px-4 md:px-6">
|
||||
<div className="flex flex-col items-center space-y-4 text-center">
|
||||
<div className="space-y-2">
|
||||
<h1 className="text-3xl font-bold tracking-tighter sm:text-4xl md:text-5xl lg:text-6xl/none">
|
||||
<h1 className="text-3xl font-bold tracking-tighter sm:text-4xl md:text-5xl lg:text-6xl">
|
||||
Welcome to Neptune Crypto
|
||||
</h1>
|
||||
<p className="mx-auto max-w-[700px] text-gray-500 md:text-xl dark:text-gray-400">
|
||||
@ -23,30 +23,34 @@ export default function HomePage() {
|
||||
<Button>Get Started</Button>
|
||||
<Button variant="outline">Learn More</Button>
|
||||
</div>
|
||||
<p className="mx-auto max-w-[700px] text-destructive/50 md:text-lg">
|
||||
This website is fictional and for demonstration purposes only.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="w-full py-12 md:py-16 lg:py-20 bg-card rounded">
|
||||
<div className="container px-4 md:px-6 rounded">
|
||||
<h2 className="text-3xl font-bold tracking-tighter sm:text-5xl text-center mb-12">Our Features</h2>
|
||||
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Bitcoin className="h-10 w-10 mb-2" />
|
||||
<Bitcoin className="h-10 w-10 mb-2"/>
|
||||
<CardTitle>Multiple Cryptocurrencies</CardTitle>
|
||||
<CardDescription>Trade a wide variety of popular cryptocurrencies.</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<Lock className="h-10 w-10 mb-2" />
|
||||
<Lock className="h-10 w-10 mb-2"/>
|
||||
<CardTitle>Secure Storage</CardTitle>
|
||||
<CardDescription>Your assets are protected with state-of-the-art security measures.</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<LineChart className="h-10 w-10 mb-2" />
|
||||
<LineChart className="h-10 w-10 mb-2"/>
|
||||
<CardTitle>Advanced Trading Tools</CardTitle>
|
||||
<CardDescription>Access powerful analytics and trading features.</CardDescription>
|
||||
</CardHeader>
|
||||
@ -54,24 +58,25 @@ export default function HomePage() {
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="w-full py-12 md:py-24 lg:py-32">
|
||||
<div className="container px-4 md:px-6">
|
||||
<div className="flex flex-col items-center justify-center space-y-4 text-center">
|
||||
<div className="space-y-2">
|
||||
<h2 className="text-3xl font-bold tracking-tighter sm:text-5xl">Start Trading Today</h2>
|
||||
<p className="mx-auto max-w-[600px] text-gray-500 md:text-xl/relaxed lg:text-base/relaxed xl:text-xl/relaxed dark:text-gray-400">
|
||||
<p className="mx-auto max-w-[600px] text-gray-500 md:text-xl lg:text-base xl:text-xl dark:text-gray-400">
|
||||
Join thousands of traders and investors on our platform. Get started with as little as $10.
|
||||
</p>
|
||||
</div>
|
||||
<div className="w-full max-w-sm space-y-2">
|
||||
<Button>
|
||||
Sign Up
|
||||
<DollarSign className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
<Button>
|
||||
Sign Up
|
||||
<DollarSign className="ml-2 h-4 w-4"/>
|
||||
</Button>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
||||
By signing up, you agree to our{" "}
|
||||
<Link className="underline underline-offset-2" href="#">
|
||||
Terms & Conditions
|
||||
<Link title="Go to legal notice" className="underline underline-offset-2" href="/legal">
|
||||
Legal Notice
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
@ -80,5 +85,5 @@ export default function HomePage() {
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
24
src/app/sell/[cryptoId]/page.tsx
Normal file
24
src/app/sell/[cryptoId]/page.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
"use client"
|
||||
|
||||
import {OfferList} from "@/components/sub/OfferList";
|
||||
import {SellForm} from "@/components/sell-form";
|
||||
|
||||
|
||||
interface SellCryptoPageProps {
|
||||
params: IParams;
|
||||
}
|
||||
|
||||
interface IParams {
|
||||
cryptoId: string;
|
||||
}
|
||||
|
||||
export default function SellCryptoPage({ params }: SellCryptoPageProps) {
|
||||
console.log(params);
|
||||
return (
|
||||
<main className="flex flex-col sm:flex-row items-center justify-center w-full h-full gap-4 p-2 sm:p-4">
|
||||
<section className={"w-full lg:w-1/3 h-full p-0.5 sm:p-2 flex flex-col items-center justify-center gap-4"}>
|
||||
<SellForm defaultCryptoId={params.cryptoId}/>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
@ -14,7 +14,6 @@ export default function WalletPage() {
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [cryptosList, setCryptosList] = useState<ICryptoInWalletInfo[]>([]);
|
||||
const userContext = useContext(UserDataContext);
|
||||
//FIX the loop
|
||||
|
||||
useEffect(() => {
|
||||
console.log(userContext?.userData);
|
||||
|
@ -1,4 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
@ -9,30 +10,55 @@ import {
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import type { IUserData, IUserWallet } from "@/interfaces/userdata.interface";
|
||||
|
||||
import { CopyButton } from "@/components/ui/copy-button";
|
||||
import {
|
||||
type ICryptoInUserWalletInfo,
|
||||
ICryptoInWalletInfo,
|
||||
import type {
|
||||
ICryptoInUserWalletInfo,
|
||||
} from "@/interfaces/crypto.interface";
|
||||
import { doDisconnect, getWallet } from "@/services/account.handler";
|
||||
import { Bitcoin, Fingerprint, Key, Landmark, Unplug, User, Wallet } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import type React from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useState, useCallback, useMemo } from "react";
|
||||
import { EReturnState, type IStandardisedReturn } from "@/interfaces/general.interface";
|
||||
import type { IApiUserAssetsRes } from "@/interfaces/api.interface";
|
||||
import ApiRequest from "@/services/apiRequest";
|
||||
|
||||
export function AccountInfo({
|
||||
userData,
|
||||
setUserData,
|
||||
isDisconnected,
|
||||
}: {
|
||||
userData: IUserData;
|
||||
setUserData: React.Dispatch<React.SetStateAction<IUserData | undefined>>;
|
||||
isDisconnected: boolean;
|
||||
}) {
|
||||
export function doDisconnect() {
|
||||
if (typeof window !== "undefined") {
|
||||
window.localStorage.removeItem("sub");
|
||||
//Redirect to homepage
|
||||
window.location.href = "/";
|
||||
return true;
|
||||
}
|
||||
console.log("Whut ? Why trying to remove an item from the localStorage when running in SSR ?");
|
||||
return false;
|
||||
}
|
||||
|
||||
async function getWallet(): Promise<IStandardisedReturn<IApiUserAssetsRes>> {
|
||||
try {
|
||||
const ReqRes =
|
||||
await ApiRequest.authenticated.get.json<IStandardisedReturn<IApiUserAssetsRes>>(
|
||||
"user/my-assets"
|
||||
);
|
||||
console.log(ReqRes.data);
|
||||
|
||||
if (ReqRes.status !== 200) {
|
||||
return {
|
||||
state: EReturnState.clientError,
|
||||
};
|
||||
}
|
||||
return {
|
||||
state: EReturnState.done,
|
||||
resolved: ReqRes.data,
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
state: EReturnState.serverError,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function useWallet(userData: IUserData, setUserData: React.Dispatch<React.SetStateAction<IUserData | undefined>>) {
|
||||
const [isLoaded, setIsLoaded] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@ -40,52 +66,80 @@ export function AccountInfo({
|
||||
getWallet().then((res) => {
|
||||
const wallet: IUserWallet = {
|
||||
uat: Date.now(),
|
||||
update_interval: 30_000,
|
||||
owned_cryptos:
|
||||
res.resolved?.UserHasCrypto?.map((el): ICryptoInUserWalletInfo => {
|
||||
return {
|
||||
id: el.Crypto.id,
|
||||
name: el.Crypto.name,
|
||||
value: el.Crypto.value,
|
||||
image: el.Crypto.image,
|
||||
quantity: el.Crypto.quantity,
|
||||
owned_amount: el.amount,
|
||||
created_at: el.Crypto.created_at,
|
||||
updated_at: el.Crypto.updated_at,
|
||||
};
|
||||
}) || [],
|
||||
update_interval: 30000,
|
||||
owned_cryptos: res.resolved?.UserHasCrypto?.map((el): ICryptoInUserWalletInfo => ({
|
||||
id: el.Crypto.id,
|
||||
name: el.Crypto.name,
|
||||
value: el.Crypto.value,
|
||||
image: el.Crypto.image,
|
||||
quantity: el.Crypto.quantity,
|
||||
owned_amount: el.amount,
|
||||
created_at: el.Crypto.created_at,
|
||||
updated_at: el.Crypto.updated_at,
|
||||
})) || [],
|
||||
};
|
||||
|
||||
delete res.resolved?.UserHasCrypto;
|
||||
//@ts-ignore
|
||||
setUserData({
|
||||
...userData,
|
||||
setUserData((prev) => ({
|
||||
...prev,
|
||||
...res.resolved,
|
||||
wallet: wallet,
|
||||
});
|
||||
wallet,
|
||||
}) as unknown as IUserData);
|
||||
console.log(userData);
|
||||
setIsLoaded(true);
|
||||
});
|
||||
}
|
||||
}, [isLoaded, userData, setUserData]);
|
||||
|
||||
return isLoaded;
|
||||
}
|
||||
|
||||
export function AccountInfo({
|
||||
userData,
|
||||
setUserData,
|
||||
isDisconnected,
|
||||
}: {
|
||||
userData: IUserData;
|
||||
setUserData: React.Dispatch<React.SetStateAction<IUserData | undefined>>;
|
||||
isDisconnected: boolean;
|
||||
}) {
|
||||
const isLoaded = useWallet(userData, setUserData);
|
||||
|
||||
const walletInfo = useMemo(() => (
|
||||
<div className={"flex flex-col md:flex-row gap-2 justify-center md:justify-evenly items-start md:items-center w-full"}>
|
||||
<div className={"flex gap-1 justify-start md:justify-center items-center mx-auto w-full md:w-fit"}>
|
||||
<Landmark />
|
||||
<p className={"rounded bg-accent text-accent-foreground p-1"}>{userData.dollarAvailables} $</p>
|
||||
</div>
|
||||
<div className={"flex gap-1 justify-start md:justify-center items-center mx-auto w-full md:w-fit"}>
|
||||
<Bitcoin />
|
||||
<p className={"rounded bg-accent text-accent-foreground p-1"}>{`You currently have ${userData.wallet.owned_cryptos.length} crypto(s)`}</p>
|
||||
</div>
|
||||
</div>
|
||||
), [userData.dollarAvailables, userData.wallet.owned_cryptos.length]);
|
||||
|
||||
const userInfo = useMemo(() => (
|
||||
<div className={"flex flex-col gap-3 justify-center items-start mx-auto mt-4"}>
|
||||
<div className={"flex flex-row text-nowrap flex-nowrap gap-1 text-primary"}>
|
||||
<Fingerprint />
|
||||
<h2>Your identity</h2>
|
||||
</div>
|
||||
<div className={"font-light text-xs md:text-sm flex flex-row items-center justify-start gap-1 bg-accent p-2 rounded"}>
|
||||
<p>{userData.id}</p>
|
||||
<CopyButton value={userData.id} />
|
||||
</div>
|
||||
</div>
|
||||
), [userData.id]);
|
||||
|
||||
if (isDisconnected) {
|
||||
return (
|
||||
<div className={"flex flex-col justify-center items-center h-10 p-2 text-xs mt-2"}>
|
||||
<div
|
||||
className={
|
||||
"flex flex-row justify-center items-center gap-1 text-destructive to-red-900 animate-pulse"
|
||||
}
|
||||
>
|
||||
<div className={"flex flex-row justify-center items-center gap-1 text-destructive to-red-900 animate-pulse"}>
|
||||
<Unplug className={"w-4"} />
|
||||
<p>Disconnected</p>
|
||||
</div>
|
||||
<div>
|
||||
<Link
|
||||
href={"/auth"}
|
||||
className={
|
||||
"hover:text-primary flex justify-evenly items-center gap-1 p-1 text-nowrap"
|
||||
}
|
||||
>
|
||||
<Link href={"/auth"} className={"hover:text-primary flex justify-evenly items-center gap-1 p-1 text-nowrap"}>
|
||||
<Key className={"w-3"} /> Link account
|
||||
</Link>
|
||||
</div>
|
||||
@ -107,50 +161,8 @@ export function AccountInfo({
|
||||
<DialogDescription>{userData.pseudo}</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className={"flex flex-col items-center justify-center w-full"}>
|
||||
<div className={"flex flex-col justify-evenly items-center gap-2"}>
|
||||
<div
|
||||
className={
|
||||
"flex flex-col md:flex-row gap-2 justify-center md:justify-evenly items-start md:items-center w-full"
|
||||
}
|
||||
>
|
||||
<div
|
||||
className={
|
||||
"flex gap-1 justify-start md:justify-center items-center mx-auto w-full md:w-fit"
|
||||
}
|
||||
>
|
||||
<Landmark />
|
||||
<p className={"rounded bg-accent text-accent-foreground p-1"}>
|
||||
{userData.dollarAvailables} $
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
"flex gap-1 justify-start md:justify-center items-center mx-auto w-full md:w-fit"
|
||||
}
|
||||
>
|
||||
<Bitcoin />
|
||||
<p className={"rounded bg-accent text-accent-foreground p-1"}>
|
||||
{`You currently have ${userData.wallet.owned_cryptos.length} crypto(s)`}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={"flex flex-col gap-3 justify-center items-start mx-auto mt-4"}
|
||||
>
|
||||
<div className={"flex flex-row text-nowrap flex-nowrap gap-1 text-primary"}>
|
||||
<Fingerprint />
|
||||
<h2>Your identity</h2>
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
"font-light text-xs md:text-sm flex flex-row items-center justify-start gap-1 bg-accent p-2 rounded"
|
||||
}
|
||||
>
|
||||
<p>{userData.id}</p>
|
||||
<CopyButton value={userData.id} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{walletInfo}
|
||||
{userInfo}
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant={"secondary"} className={"gap-2 px-2"} asChild>
|
||||
@ -159,11 +171,7 @@ export function AccountInfo({
|
||||
<p>My wallet</p>
|
||||
</Link>
|
||||
</Button>
|
||||
<Button
|
||||
variant={"destructive"}
|
||||
className={"gap-2 px-2 mb-2"}
|
||||
onClick={() => doDisconnect()}
|
||||
>
|
||||
<Button variant={"destructive"} className={"gap-2 px-2 mb-2"} onClick={() => doDisconnect()}>
|
||||
<Unplug />
|
||||
<p>Disconnect</p>
|
||||
</Button>
|
||||
@ -171,4 +179,4 @@ export function AccountInfo({
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
@ -64,6 +64,9 @@ const registerSchema = z.object({
|
||||
.regex(/[0-9]/, "Password must contain at least one number.")
|
||||
.regex(/[^a-zA-Z0-9]/, "Password must contain at least one special character.")
|
||||
.describe("Your account password."),
|
||||
|
||||
promoCode: z
|
||||
.string().max(255, "Your promotional code is too long.").optional(),
|
||||
});
|
||||
|
||||
export function AuthForms() {
|
||||
@ -176,7 +179,7 @@ export function AuthForms() {
|
||||
}
|
||||
//toast.custom(<ToastBox message={"Login successful ! \n You will be redirected."} type={toastType.success}/>)
|
||||
toast({
|
||||
description: "Login successful ! \n You will be redirected.",
|
||||
description: "Login successful ! \n You will be redirected to the home page.",
|
||||
});
|
||||
setTimeout(() => {
|
||||
setIsLoading(false);
|
||||
@ -245,9 +248,9 @@ export function AuthForms() {
|
||||
<p>Register</p>
|
||||
</AutoFormSubmit>
|
||||
<p className="text-gray-500 text-sm">
|
||||
By submitting this form, you agree to our{" "}
|
||||
<Link href="#" className="text-primary underline">
|
||||
terms and conditions
|
||||
By submitting this form, you agree to the{" "}
|
||||
<Link title={"Go to legal page"} href="/legal" className="text-primary underline">
|
||||
GDPR Compliance Policy & Usage Policy
|
||||
</Link>
|
||||
.
|
||||
</p>
|
||||
|
@ -3,7 +3,6 @@ import type { DefaultValues } from "react-hook-form";
|
||||
import type { z } from "zod";
|
||||
import type { FieldConfig } from "./types";
|
||||
|
||||
// TODO: This should support recursive ZodEffects but TypeScript doesn't allow circular type definitions.
|
||||
export type ZodObjectOrWrapped =
|
||||
| z.ZodObject<any, any>
|
||||
| z.ZodEffects<z.ZodObject<any, any>>;
|
||||
|
@ -4,29 +4,27 @@ import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from "@/components/ui/form";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
Select,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
} from "@/components/ui/select";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { toast } from "@/components/ui/use-toast";
|
||||
import type { IApiAllOffersRes, IApiDoTradeReq } from "@/interfaces/api.interface";
|
||||
import type { IApiAllOffersRes } from "@/interfaces/api.interface";
|
||||
import type { ICryptoInWalletInfo } from "@/interfaces/crypto.interface";
|
||||
import ApiRequest from "@/services/apiRequest";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Ban, DollarSign, RefreshCw } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import * as React from "react";
|
||||
import { Dispatch, SetStateAction, useEffect, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import * as z from "zod";
|
||||
type Props = {
|
||||
@ -43,6 +41,7 @@ export function BuyModal(props: Props) {
|
||||
ApiRequest.authenticated.get
|
||||
.json<IApiAllOffersRes[]>(`offer/crypto/${props.cryptoData.id}`)
|
||||
.then((response) => {
|
||||
console.debug(response)
|
||||
if (response.data) {setOffersList(response.data)}
|
||||
console.log(`Crypto ${props.cryptoData.name} -> ${response.data.length}`);
|
||||
setIsLoaded(true);
|
||||
@ -157,7 +156,10 @@ export function BuyModal(props: Props) {
|
||||
{offersList.length}
|
||||
</p>
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="server">Buy from server</TabsTrigger>
|
||||
<TabsTrigger value="server">Buy from server{" "}<p
|
||||
className={" ml-1 px-1 bg-primary text-primary-foreground rounded"}>
|
||||
{props.cryptoData.quantity}
|
||||
</p></TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent
|
||||
value="user"
|
||||
@ -171,30 +173,28 @@ export function BuyModal(props: Props) {
|
||||
<FormField
|
||||
control={buyFromUserForm.control}
|
||||
name="offerId"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Select an offer</FormLabel>
|
||||
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select an offer to purchase." />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{offersList.map((offer) => {
|
||||
if (!offer) return;
|
||||
return (
|
||||
<SelectItem
|
||||
value={offer.id}
|
||||
key={offer.id}
|
||||
>{`${offer.amount}x ${offer.Crypto.name} - ${offer.User.pseudo}`}</SelectItem>
|
||||
);
|
||||
})}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Select an offer</FormLabel>
|
||||
<Select defaultValue={offersList[0]?.id} onValueChange={(val) => {
|
||||
console.log(val);
|
||||
field.onChange(val);
|
||||
}}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select an offer to purchase."/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{offersList.map((offer) => (
|
||||
<SelectItem
|
||||
value={offer.id}
|
||||
key={offer.id}
|
||||
>{`${offer.amount}x ${offer.Crypto.name}`}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<Button type="submit">Submit</Button>
|
||||
</form>
|
||||
|
10
src/components/cryptos/sell-modal.tsx
Normal file
10
src/components/cryptos/sell-modal.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import type {Row} from "@tanstack/react-table";
|
||||
import type {ICryptoInUserWalletInfo} from "@/interfaces/crypto.interface";
|
||||
|
||||
|
||||
interface SellModalProps {
|
||||
row: Row<ICryptoInUserWalletInfo>
|
||||
}
|
||||
export function SellModal({ row }: SellModalProps) {
|
||||
|
||||
}
|
@ -10,15 +10,12 @@ import { ViewModal } from "@/components/cryptos/view-modal";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { DataTable } from "@/components/ui/data-table";
|
||||
import type { IUserWallet } from "@/interfaces/userdata.interface";
|
||||
import {
|
||||
type ColumnDef,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
useReactTable,
|
||||
import type {
|
||||
ColumnDef,
|
||||
} from "@tanstack/react-table";
|
||||
import { ArrowUpDown, MoreHorizontal } from "lucide-react";
|
||||
import { ArrowUpDown, MoreHorizontal, Receipt } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import Link from "next/link";
|
||||
|
||||
interface DataTableProps<TData, TValue> {
|
||||
columns: ColumnDef<TData, TValue>[];
|
||||
@ -72,8 +69,8 @@ export function WalletTable(props: Props) {
|
||||
const payment = row.original;
|
||||
|
||||
return (
|
||||
<div className={"flex gap-2"}>
|
||||
<p className={"font-light italic text-xs"}>Soon here : Sell, History</p>
|
||||
<div>
|
||||
<Link href={`/sell/${row.original.id}`} className={"flex w-fit gap-2 p-2 justify-center items-center rounded hover:bg-card"}><Receipt /> <p className={"hidden sm:block"}>Sell for USD</p></Link>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@ -11,11 +11,11 @@ export function Header({
|
||||
return (
|
||||
<header
|
||||
className={
|
||||
"flex flex-col md:flex-row justify-between items-center w-full p-1 md:px-3 md:py-2 pb-2 border-b-2"
|
||||
"flex flex-col md:flex-row justify-between items-center w-full p-1 md:px-3 gap-2 md:py-2 pb-2 border-b-2"
|
||||
}
|
||||
>
|
||||
<Link href={"/"} className={"flex flex-row justify-center md:justify-start items-center w-fit gap-2"}>
|
||||
<Image src={"neptune.svg"} alt={"Logo of Neptune"} width={42} height={42} />
|
||||
<Link title={"Return to home page"} href={"/"} className={"flex flex-row justify-center md:justify-start items-center w-fit gap-2"}>
|
||||
<Image src={"/neptune.svg"} alt={"Logo of Neptune"} width={42} height={42} />
|
||||
<h1 className={"font-bold text-xl align-middle text-center text-wrap"}>
|
||||
{title || "Neptune"}
|
||||
</h1>
|
||||
|
@ -4,11 +4,16 @@ import { Header } from "@/components/header";
|
||||
import { ThemeProvider } from "@/components/providers/theme-provider";
|
||||
import { UserDataProvider } from "@/components/providers/userdata-provider";
|
||||
import type React from "react";
|
||||
import {QueryProvider} from "@/components/providers/query-provider";
|
||||
|
||||
export function Providers({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
|
||||
<UserDataProvider>{children}</UserDataProvider>
|
||||
<QueryProvider>
|
||||
<UserDataProvider>
|
||||
{children}
|
||||
</UserDataProvider>
|
||||
</QueryProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
13
src/components/providers/query-provider.tsx
Normal file
13
src/components/providers/query-provider.tsx
Normal file
@ -0,0 +1,13 @@
|
||||
"use client"
|
||||
import type {ReactNode} from "react";
|
||||
import {QueryClient, QueryClientProvider} from "@tanstack/react-query";
|
||||
|
||||
|
||||
interface QueryProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
export function QueryProvider({ children }: QueryProviderProps) {
|
||||
return (<QueryClientProvider client={new QueryClient()}>
|
||||
{ children }
|
||||
</QueryClientProvider>)
|
||||
}
|
199
src/components/sell-form.tsx
Normal file
199
src/components/sell-form.tsx
Normal file
@ -0,0 +1,199 @@
|
||||
"use client";
|
||||
import * as z from "zod";
|
||||
import ApiRequest from "@/services/apiRequest";
|
||||
import { toast } from "@/components/ui/use-toast";
|
||||
import * as React from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useEffect, useState } from "react";
|
||||
import type { IUserWalletCryptos } from "@/interfaces/crypto.interface";
|
||||
import type { IApiUserAssetsRes } from "@/interfaces/api.interface";
|
||||
import { Slider } from "@/components/ui/slider";
|
||||
import { DollarSign, Equal, X } from "lucide-react";
|
||||
import { Card } from "@/components/ui/card";
|
||||
|
||||
interface SellFormProps {
|
||||
defaultCryptoId?: string;
|
||||
}
|
||||
|
||||
export function SellForm({ defaultCryptoId }: SellFormProps) {
|
||||
const [currentWallet, setCurrentWallet] = useState<IUserWalletCryptos[]>();
|
||||
const [sliderValue, setSliderValue] = useState(0);
|
||||
const [quantity, setQuantity] = useState(0);
|
||||
const [selectedCrypto, setSelectedCrypto] = useState<string>();
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchAssets() {
|
||||
const res = await ApiRequest.authenticated.get.json<IApiUserAssetsRes>("user/my-assets");
|
||||
setCurrentWallet(res.data.UserHasCrypto);
|
||||
}
|
||||
fetchAssets();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (defaultCryptoId) {
|
||||
const maxShares = getMaxSharesForCrypto(defaultCryptoId);
|
||||
setSliderValue(maxShares);
|
||||
setSelectedCrypto(defaultCryptoId);
|
||||
setQuantity(1);
|
||||
}
|
||||
}, []);
|
||||
|
||||
function getMaxSharesForCrypto(cryptoId: string) {
|
||||
return currentWallet?.find((crypto) => crypto.Crypto.id === cryptoId)?.amount || 0;
|
||||
}
|
||||
|
||||
function getCryptoValue(cryptoId: string) {
|
||||
return currentWallet?.find((crypto) => crypto.Crypto.id === cryptoId)?.Crypto.value || 0;
|
||||
}
|
||||
|
||||
function getCryptoName(cryptoId: string) {
|
||||
return currentWallet?.find((crypto) => crypto.Crypto.id === cryptoId)?.Crypto.name || "";
|
||||
}
|
||||
|
||||
const sellToUserSchema = z.object({
|
||||
cryptoId: z.string({ required_error: "You should select a crypto from your wallet." }).uuid(),
|
||||
amount: z.number({
|
||||
required_error: "You should select an amount of the assets that you want to sell.",
|
||||
}).max(sliderValue),
|
||||
});
|
||||
|
||||
async function onSellToUserSubmit(data: z.infer<typeof sellToUserSchema>) {
|
||||
const res = await ApiRequest.authenticated.post.json("offer/create", {
|
||||
id_crypto: data.cryptoId,
|
||||
amount: data.amount,
|
||||
});
|
||||
|
||||
if (res.status !== 201) {
|
||||
toast({
|
||||
title: "An error occurred!",
|
||||
description: (
|
||||
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
|
||||
<code className="text-white text-wrap">{JSON.stringify(res.statusText, null, 2)}</code>
|
||||
</pre>
|
||||
),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(res);
|
||||
toast({
|
||||
title: "Transaction accepted.",
|
||||
description: <p>The page is going to reload.</p>,
|
||||
});
|
||||
setTimeout(() => location.reload(), 1_500);
|
||||
|
||||
toast({
|
||||
title: "You submitted the following values:",
|
||||
description: (
|
||||
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
|
||||
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
|
||||
</pre>
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
const sellToUserForm = useForm<z.infer<typeof sellToUserSchema>>({
|
||||
resolver: zodResolver(sellToUserSchema),
|
||||
});
|
||||
|
||||
if (!currentWallet) {
|
||||
return (
|
||||
<Card>
|
||||
<div className="flex items-center justify-center p-2">
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Form {...sellToUserForm}>
|
||||
<form onSubmit={sellToUserForm.handleSubmit(onSellToUserSubmit)} className="w-full space-y-6">
|
||||
<FormField
|
||||
control={sellToUserForm.control}
|
||||
name="cryptoId"
|
||||
render={({ field }) => (
|
||||
<FormItem
|
||||
onChange={(event) => {
|
||||
// @ts-ignore
|
||||
const maxShares = getMaxSharesForCrypto(event.target.value as string);
|
||||
setSliderValue(maxShares);
|
||||
setQuantity(maxShares);
|
||||
// @ts-ignore
|
||||
setSelectedCrypto(event.target.value as string);
|
||||
}}
|
||||
>
|
||||
<FormLabel>Sell shares of your wallet</FormLabel>
|
||||
<Select
|
||||
defaultValue={defaultCryptoId}
|
||||
onValueChange={(val) => {
|
||||
const maxShares = getMaxSharesForCrypto(val);
|
||||
setSliderValue(maxShares);
|
||||
setQuantity(1);
|
||||
setSelectedCrypto(val);
|
||||
field.onChange(val);
|
||||
}}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select a crypto from your wallet." />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{currentWallet?.map((crypto) => (<SelectItem value={crypto.Crypto.id} key={crypto.Crypto.id}>
|
||||
{`${crypto.amount}x ${crypto.Crypto.name} - ${Math.round(crypto.Crypto.value)}$/u`}
|
||||
</SelectItem>))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={sellToUserForm.control}
|
||||
name="amount"
|
||||
render={({ field }) => (
|
||||
<FormItem hidden={!selectedCrypto}>
|
||||
<FormLabel>Quantity</FormLabel>
|
||||
<div className="flex items-center justify-between gap-2 p-2">
|
||||
<p className="bg-accent text-accent-foreground p-1 rounded">{field.value}</p>
|
||||
<Slider
|
||||
form="amount"
|
||||
onValueChange={(val) => {
|
||||
const singleValue = val[0];
|
||||
setQuantity(singleValue as number);
|
||||
field.onChange(singleValue);
|
||||
}}
|
||||
value={[quantity]}
|
||||
min={1}
|
||||
max={sliderValue}
|
||||
step={1}
|
||||
/>
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{selectedCrypto && (
|
||||
<div className="flex items-center justify-center p-2 gap-2">
|
||||
<p>{getCryptoName(selectedCrypto)}</p>
|
||||
<X />
|
||||
<p>{quantity}</p>
|
||||
<Equal />
|
||||
<em className="bg-secondary/35 p-1 rounded">
|
||||
{(quantity * getCryptoValue(selectedCrypto)).toLocaleString("en-US")}
|
||||
</em>
|
||||
<DollarSign />
|
||||
</div>
|
||||
)}
|
||||
<Button type="submit" disabled={!quantity}>
|
||||
Place sell order
|
||||
</Button>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
}
|
0
src/components/sub/GraphSellOffer.tsx
Normal file
0
src/components/sub/GraphSellOffer.tsx
Normal file
50
src/components/sub/OfferList.tsx
Normal file
50
src/components/sub/OfferList.tsx
Normal file
@ -0,0 +1,50 @@
|
||||
import {Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow} from "@/components/ui/table";
|
||||
import {ScrollArea} from "@/components/ui/scroll-area";
|
||||
import type {IAllTrades} from "@/interfaces/crypto.interface";
|
||||
import {cn} from "@/lib/utils";
|
||||
|
||||
interface OfferListProps {
|
||||
className?: string;
|
||||
trades: IAllTrades;
|
||||
}
|
||||
|
||||
interface RenderRowsProps {
|
||||
data?: IAllTrades;
|
||||
}
|
||||
|
||||
function RenderRows({ data }: RenderRowsProps) {
|
||||
if (!data || data?.length === 0) return (<TableRow>
|
||||
<TableCell className="font-medium">{" "}</TableCell>
|
||||
<TableCell>{" "}</TableCell>
|
||||
<TableCell>{"No history..."}</TableCell>
|
||||
<TableCell className="text-right">{" "}</TableCell>
|
||||
</TableRow>);
|
||||
|
||||
return data.map((item, index) => (
|
||||
<TableRow key={item.Crypto.created_at.toString()}>
|
||||
<TableCell className="font-medium">{item.Giver.pseudo}</TableCell>
|
||||
<TableCell>{item.Receiver.pseudo}</TableCell>
|
||||
<TableCell>{item.Crypto.quantity}</TableCell>
|
||||
<TableCell className="text-right">${item.Crypto.value}</TableCell>
|
||||
</TableRow>
|
||||
));
|
||||
}
|
||||
|
||||
export function OfferList({ className, trades}: OfferListProps) {
|
||||
return (<div className={cn("bg-card text-card-foreground rounded p-2", className)}>
|
||||
<Table>
|
||||
<TableCaption>A list of recent sell offers.</TableCaption>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-[100px]">Seller</TableHead>
|
||||
<TableHead>Buyer</TableHead>
|
||||
<TableHead>Crypto share amount</TableHead>
|
||||
<TableHead className="text-right">$</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
<RenderRows data={trades}/>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>)
|
||||
}
|
@ -15,7 +15,6 @@ export interface IUserData {
|
||||
dollarAvailables: number;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
//TODO get on register
|
||||
wallet: IUserWallet;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user