Compare commits

...

46 Commits
old ... main

Author SHA1 Message Date
5164255aea
feat(ide): Add Project Default inspection profile
This commit includes a new inspection profile named 'Project Default'. This profile is configured to detect duplicated code in TypeScript with a weak warning level. It is enabled by default.
2024-06-20 15:09:41 +02:00
53be13ef38
feat(components): Add buy-modal component for purchasing crypto
This commit introduces a new component, `buy-modal.tsx`, within the cryptos scope of components. This component allows the option to buy cryptocurrency either from a user or directly from the server. It provides form validation, API integrations, handles states, and displays proper response messages.
2024-06-20 15:09:14 +02:00
49d485c11a
feat(components): Add buy-modal component for purchasing crypto
This commit introduces a new component, `buy-modal.tsx`, within the cryptos scope of components. This component allows the option to buy cryptocurrency either from a user or directly from the server. It provides form validation, API integrations, handles states, and displays proper response messages.
2024-06-20 15:07:15 +02:00
37b7116a69
feat(cryptos): Add ViewModal component
A new ViewModal component was added to the cryptos directory. This React component includes Dialog properties and widgets from ui components such as a Button, Label, and Input. Modifications also include a LineChart from lucide-react library.
2024-06-20 15:05:57 +02:00
2452d8d607
feat: Add new file
This commit introduces a new file to the codebase. No pre-existing files or functionalities have been modified or removed as part of this change.
2024-06-20 15:03:27 +02:00
b188e8573d
feat(component): add DataTable component
This commit introduces a new DataTable component in the UI component library. It includes features like column sorting, pagination, and customizable filtering. This reusable component is designed to handle and display data tables across different parts of the application.
2024-06-20 15:02:44 +02:00
fb4b2bb7c1
feat(components): add CryptosTable and WalletTable data-tables components
Introduce CryptosTable and WalletTable components. CryptosTable is responsible for displaying cryptocurrency information available for purchase, while WalletTable displays the user's current cryptocurrency holdings. Both components utilize react-table for table generation and sorting functionality. Additional "Buy" and "View" modals have been configured for CryptosTable.
2024-06-20 15:00:22 +02:00
f268294b93
feat: Add new dashboard, admin, and wallet pages
Implement new page.tsx files for dashboard, admin, and wallet modules, providing essential UI logic and data-fetching mechanisms. These components handle their respective data retrieval and loading states, and present the information via relevant UI structures such as 'CryptosTable' and 'WalletTable'.
2024-06-20 14:59:28 +02:00
90dd1d0828
feat: add @tanstack/react-table dependency to package
This commit includes the addition of @tanstack/react-table package as a project dependency. Changes were made in both package.json and pnpm-lock.yaml files to reflect this addition. This new package contributes enhanced table functionalities to the project.
2024-06-20 14:58:30 +02:00
2c9fafd802
feat(layout): add Wallet and Dashboard buttons
Imported Link and Button components into layout.tsx, which were used to add Wallet and Dashboard navigational buttons to the header section. The buttons have been set with a 'light' variant and flex features for responsive design.
2024-06-20 14:57:03 +02:00
4e0de3be06
feat(apiRequest): use JSON.parse for accessToken in Authorization header
The Authorization header in the apiRequest now utilizes JSON.parse for the accessToken. This change ensures proper parsing and retrieval of the item from the local storage when the window object is defined.
2024-06-20 14:56:35 +02:00
a2f2996ef9
feat(apiRequest): use JSON.parse for accessToken in Authorization header
The Authorization header in the apiRequest now utilizes JSON.parse for the accessToken. This change ensures proper parsing and retrieval of the item from the local storage when the window object is defined.
2024-06-20 14:55:23 +02:00
0a239f4478
feat(interfaces): Add new interfaces
New interfaces related to referral code and trade request are added to the `api.interface.ts`. Additionally, import statement modified to include `ICryptoInWalletInfo` into the module. This enriches the module with a set of necessary interfaces providing better control over referral codes, trades, and offers operations.
2024-06-20 14:52:07 +02:00
69484cc90f
feat(component): add wallet loading state and user wallet information
The account-info component has been updated to include a loading state for user wallets, a feature that significantly improves the performance of the component by preventing unnecessary re-renders. The IUserWallet type now includes new fields to store wallet data such as cryptocurrencies' details and owned amount. These changes also include display adjustments and improved routing for 'My Wallet' button.
2024-06-20 14:45:12 +02:00
3819a0f338
feat(handler): Update imports and remove excess functions
Updated the import statement within account.handler.ts to feature new, relevant components. Also, this commit includes a significant clean-up of the account.handler service, where a large amount of superfluous function calls were removed to streamline the code.
2024-06-20 14:44:31 +02:00
977b13d46a
feat(button component): Add 'light' style variation to button
This commit introduces a new 'light' style variation for the button component. Now a button can be styled with 'light' variation which changes the button's background and text color on hover state.
2024-06-19 22:26:17 +02:00
aa322d0c8f
test(temp): temporary file from Quentin 2024-06-18 22:49:36 +02:00
87245c702e
feat(service): implement trade and referral code functions in account handler
The commit introduces implementation for functions handling trading and referral codes in account.handler.ts. These includes `createTrade`, `getAllTrade`, `getUserTrade`, `getAllReferralCode`, `createReferralCode` functions. The update improves the overall functionality of trade and promotional related processes. A new interface `IAllReferralCodeRes` is also imported for referral code response.
2024-06-18 22:49:07 +02:00
e7f6de4a29
feat(ide): add discord.xml file
This commit includes the creation of a new discord.xml file under the .idea directory. This config file brings in new settings for the DiscordProject related to its 'show' and 'description' options.
2024-06-18 21:33:43 +02:00
b9d47ba401
style(footer): adjust div spacing in footer component
This commit properly adjusts the spacing in an empty div within the footer section of the React component for adherence to standardized code formatting. Proper spacing contributes to code readability and maintainability.
2024-06-18 21:33:17 +02:00
d54d05403b
feat(auth-form): enhance user registration and login process
This commit introduces some improvements to the user registration and login processes. It standardizes the update interval in the user data upon registration, fixes the missing 'toString' call for 'access_token' and improves code readability by correcting indents and adding extra spaces. Additionally, this commit refactors the redirection logic after successful login or registration, making it more robust and reliable.
2024-06-18 21:32:49 +02:00
1898d554f9
feat(interfaces): add new API interfaces and update existing ones
New interfaces were added to enhance functionality for trade and offer creation requests. Renamed IApiAllTrades interface to IApiAllTradesRes for consistency. Interfaces were also added to manage referral codes and rankings.
2024-06-18 21:22:49 +02:00
50225f1c17
feat(account-info): import and use getWallet from account handler
The 'getWallet' function has been imported from the account handler service and is now used in account-info component. This addition ensures to fetch information when the component renders.
2024-06-18 21:22:34 +02:00
00be94c5a8
feat(account.handler): add new API methods
This commit adds several new async functions to fetch wallet, user trades and all trades in `account.handler.ts`. This provides an interface for interacting with multiple new endpoints. Additionally, several import statements have been updated to reflect the changes.
2024-06-18 21:22:12 +02:00
faba9fa3cb
refactor(layout): rearrange import order
The import statements in layout.tsx have been reordered. React type import is now following Toaster component import to maintain organized import sequence.
2024-06-18 16:36:56 +02:00
397bef5cdf
refactor: move general interface to interfaces directory
This commit relocates the 'general.interface.ts' file from the 'services' directory to the 'interfaces' directory. This structural change is meant to streamline the organization of the code base.
2024-06-18 16:35:46 +02:00
11b4c723fa
chore(dependencies): upgrade packages in pnpm-lock.yaml
Several dependencies have been upgraded across multiple versions including react-hook-form, lightweight-charts, next elements, sonner, lucide-react, embla-carousel-react and others. Reflecting these changes in the pnpm-lock.yaml file is necessary for functional and up-to-date building of the project. This helps maintain compatibility and introduces performance improvements or new features from the updated packages.
2024-06-17 10:02:36 +02:00
1674664980
build: Update package dependencies
Upgrade multiple dependencies to their newer versions in package.json file. This includes libraries like 'embla-carousel-react', 'lightweight-charts', and 'react' among others. It also includes devDependencies like '@types/node', '@types/react', and 'typescript'.
2024-06-17 10:02:09 +02:00
39fc556aca
refactor(app): move Toaster component position in layout
The Toaster component was relocated in the layout file. It's been moved from below the Providers component to a position above the Footer component. This repositioning is aimed at improving the visual hierarchy and flow of the application.
2024-06-17 10:01:32 +02:00
7426f5f642
feat(component): Update footer styling and structure
The footer's class has been updated to improve page layout. Additionally, unnecessary div containing flex properties has been removed for simplicity and improved readability.
2024-06-17 10:01:15 +02:00
01c073e879
feat(account-info): add disconnect handling and improve info display
The commit enhances the account information component with better data handling. It adds disconnect handling to account for cases when user session is terminated. It also improves information display by adding details like crypto availability and user identity, improving the overall user experience.
2024-06-17 09:50:06 +02:00
e6d37ef600
feat(services): add general.interface.ts
This commit introduces a new interface file named 'general.interface.ts' to standardise return types in the services scope. It includes the IStandardisedReturn interface and the EReturnState enum. These additions enhance the consistency of return types across different services.
2024-06-17 09:49:09 +02:00
cc286462f0
feat(components): add authentication forms
This commit introduces authentication forms for both user login and registration. These forms use zod for data validation and context for state management. Login and registration processes have been implemented as asynchronous functions, handling API requests, and error responses.
2024-06-17 09:48:38 +02:00
4c061dc19c
feat(account-dialog): handle case when userContext not present
This commit updates the account-dialog component to properly handle when there's no userContext. Previously, a default user data was set when no userContext was found, this has been replaced with a simple message saying 'No account'. Also, checks for an authentication token in localStorage have been included. These changes aim towards better handling of edge cases and unauthenticated scenarios.
2024-06-17 09:47:57 +02:00
3b1a3e93e0
feat(layout): add Toaster component and update favicon link
This commit introduces the Toaster component to the main layout. In addition, the link to the favicon has been updated to correct its location.
2024-06-17 09:46:21 +02:00
5c81ad917d
feat(auth): Update auth page layout
The width of the authentication page is adjusted to occupy the full width of the viewport to enhance usability. This layout update ensures consistency for different screen sizes.
2024-06-17 09:45:58 +02:00
b5526e5877
"refactor(service): remove register and login functions in account.handler.ts"
This commit involves a significant refactoring in the account.handler.ts file. Specifically, it removed the `doRegister` and `doLogin` functions. Furthermore, replaced `useEncodedLocalStorage` with `useLocalStorage`. The `doDisconnect` function has been refined to redirect to homepage after removing an item from local storage.
2024-06-17 09:45:25 +02:00
41ba50d417
feat(ui): Change input background color to accent
In the 'input.tsx' component of the UI, the background color property of the input field has been updated from 'bg-background' to 'bg-accent' to enhance visibility.
2024-06-17 09:44:47 +02:00
0504692dfb
feat(auto-form): apply full width to form class
The className of the form in the auto-form component has been updated to include "w-full". This change ensures that the form will occupy the full width of its parent container, enhancing layout and visibility.
2024-06-17 09:44:07 +02:00
15eb7addd0
feat(interface): allow message to be string or array
This update to the api.interface now accepts both strings and arrays for the 'message' field inside the IAbstractApiResponse interface. This provides more flexibility for responses returned by the API.
2024-06-17 09:43:26 +02:00
5a905d0608
refactor: moved favicon to app root 2024-06-17 09:35:31 +02:00
72bbe08de0
feat(components): update layout for responsiveness
This commit enhances the layout in header.tsx for better mobile responsiveness. It adjusts flex properties and classes for optimal rendering across various device sizes. It also rearranges elements within the header component for a better mobile appearance.
2024-06-14 12:48:03 +02:00
48adc8be6c
refactor(interface): modify import statement in userdata.interface.ts
This change adjusts the import statement to correctly import the ICryptoInUserWalletInfo. Previously, the incorrect syntax was causing import errors and this revision resolves that issue.
2024-06-14 12:47:41 +02:00
3a8621735e
feat(auth): Add new authentication page
A new authentication page has been added to the application. It includes an AuthForms component and uses a flex display for layout, with specific height and width properties set.
2024-06-14 12:47:17 +02:00
0ead6bd969
refactor(interfaces): add Wallet related properties in User and Crypto interfaces
The change adds Wallet related properties in both User and Crypto interfaces. Specifically, `IUserWallet` interface has been added to `userdata.interface.ts` and 'amount' field has been added to `IUserWalletCryptos` in `crypto.interface.ts`. Also, an extra type `ICryptoInUserWalletInfo` extending `ICryptoInWalletInfo` has been added with `owned_amount` field.
2024-06-14 10:08:44 +02:00
747cc1cdb4
feat(ui-component): add new copy button component
This commit introduces a new CopyButton component in the ui components. It also includes a feature to copy multiple choices. The button changes its icon after copying to clipboard, indicating that the copying has been done.
2024-06-14 10:07:58 +02:00
33 changed files with 1845 additions and 233 deletions

7
.idea/discord.xml generated Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DiscordProjectSettings">
<option name="show" value="APPLICATION" />
<option name="description" value="" />
</component>
</project>

View File

@ -0,0 +1,10 @@
<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>

View File

@ -39,24 +39,25 @@
"@radix-ui/react-toggle": "^1.0.3", "@radix-ui/react-toggle": "^1.0.3",
"@radix-ui/react-toggle-group": "^1.0.4", "@radix-ui/react-toggle-group": "^1.0.4",
"@radix-ui/react-tooltip": "^1.0.7", "@radix-ui/react-tooltip": "^1.0.7",
"@tanstack/react-table": "^8.17.3",
"axios": "^1.7.2", "axios": "^1.7.2",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"cmdk": "^1.0.0", "cmdk": "^1.0.0",
"date-fns": "^3.6.0", "date-fns": "^3.6.0",
"embla-carousel-react": "^8.1.3", "embla-carousel-react": "^8.1.5",
"framer-motion": "^11.2.10", "framer-motion": "^11.2.10",
"input-otp": "^1.2.4", "input-otp": "^1.2.4",
"lightweight-charts": "^4.1.4", "lightweight-charts": "^4.1.5",
"lucide-react": "^0.387.0", "lucide-react": "^0.395.0",
"next": "14.2.3", "next": "14.2.4",
"next-themes": "^0.3.0", "next-themes": "^0.3.0",
"react": "^18", "react": "^18.3.1",
"react-day-picker": "^8.10.1", "react-day-picker": "^8.10.1",
"react-dom": "^18", "react-dom": "^18.3.1",
"react-hook-form": "^7.51.5", "react-hook-form": "^7.52.0",
"react-resizable-panels": "^2.0.19", "react-resizable-panels": "^2.0.19",
"sonner": "^1.4.41", "sonner": "^1.5.0",
"tailwind-merge": "^2.3.0", "tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"vaul": "^0.9.1", "vaul": "^0.9.1",
@ -65,12 +66,12 @@
"devDependencies": { "devDependencies": {
"@biomejs/biome": "1.8.1", "@biomejs/biome": "1.8.1",
"@types/jest": "^29.5.12", "@types/jest": "^29.5.12",
"@types/node": "^20", "@types/node": "^20.14.2",
"@types/react": "^18", "@types/react": "^18.3.3",
"@types/react-dom": "^18", "@types/react-dom": "^18.3.0",
"jest": "^29.7.0", "jest": "^29.7.0",
"postcss": "^8", "postcss": "^8.4.38",
"tailwindcss": "^3.4.1", "tailwindcss": "^3.4.4",
"typescript": "^5" "typescript": "^5.4.5"
} }
} }

256
pnpm-lock.yaml generated
View File

@ -13,7 +13,7 @@ importers:
version: 5.0.3 version: 5.0.3
'@hookform/resolvers': '@hookform/resolvers':
specifier: ^3.6.0 specifier: ^3.6.0
version: 3.6.0(react-hook-form@7.51.5(react@18.3.1)) version: 3.6.0(react-hook-form@7.52.0(react@18.3.1))
'@radix-ui/react-accordion': '@radix-ui/react-accordion':
specifier: ^1.1.2 specifier: ^1.1.2
version: 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) version: 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)
@ -95,6 +95,9 @@ importers:
'@radix-ui/react-tooltip': '@radix-ui/react-tooltip':
specifier: ^1.0.7 specifier: ^1.0.7
version: 1.0.7(@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.0.7(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@tanstack/react-table':
specifier: ^8.17.3
version: 8.17.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
axios: axios:
specifier: ^1.7.2 specifier: ^1.7.2
version: 1.7.2 version: 1.7.2
@ -111,8 +114,8 @@ importers:
specifier: ^3.6.0 specifier: ^3.6.0
version: 3.6.0 version: 3.6.0
embla-carousel-react: embla-carousel-react:
specifier: ^8.1.3 specifier: ^8.1.5
version: 8.1.3(react@18.3.1) version: 8.1.5(react@18.3.1)
framer-motion: framer-motion:
specifier: ^11.2.10 specifier: ^11.2.10
version: 11.2.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 11.2.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@ -120,35 +123,35 @@ importers:
specifier: ^1.2.4 specifier: ^1.2.4
version: 1.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 1.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
lightweight-charts: lightweight-charts:
specifier: ^4.1.4 specifier: ^4.1.5
version: 4.1.4 version: 4.1.5
lucide-react: lucide-react:
specifier: ^0.387.0 specifier: ^0.395.0
version: 0.387.0(react@18.3.1) version: 0.395.0(react@18.3.1)
next: next:
specifier: 14.2.3 specifier: 14.2.4
version: 14.2.3(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next-themes: next-themes:
specifier: ^0.3.0 specifier: ^0.3.0
version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: react:
specifier: ^18 specifier: ^18.3.1
version: 18.3.1 version: 18.3.1
react-day-picker: react-day-picker:
specifier: ^8.10.1 specifier: ^8.10.1
version: 8.10.1(date-fns@3.6.0)(react@18.3.1) version: 8.10.1(date-fns@3.6.0)(react@18.3.1)
react-dom: react-dom:
specifier: ^18 specifier: ^18.3.1
version: 18.3.1(react@18.3.1) version: 18.3.1(react@18.3.1)
react-hook-form: react-hook-form:
specifier: ^7.51.5 specifier: ^7.52.0
version: 7.51.5(react@18.3.1) version: 7.52.0(react@18.3.1)
react-resizable-panels: react-resizable-panels:
specifier: ^2.0.19 specifier: ^2.0.19
version: 2.0.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 2.0.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
sonner: sonner:
specifier: ^1.4.41 specifier: ^1.5.0
version: 1.4.41(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 1.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
tailwind-merge: tailwind-merge:
specifier: ^2.3.0 specifier: ^2.3.0
version: 2.3.0 version: 2.3.0
@ -169,25 +172,25 @@ importers:
specifier: ^29.5.12 specifier: ^29.5.12
version: 29.5.12 version: 29.5.12
'@types/node': '@types/node':
specifier: ^20 specifier: ^20.14.2
version: 20.14.2 version: 20.14.2
'@types/react': '@types/react':
specifier: ^18 specifier: ^18.3.3
version: 18.3.3 version: 18.3.3
'@types/react-dom': '@types/react-dom':
specifier: ^18 specifier: ^18.3.0
version: 18.3.0 version: 18.3.0
jest: jest:
specifier: ^29.7.0 specifier: ^29.7.0
version: 29.7.0(@types/node@20.14.2) version: 29.7.0(@types/node@20.14.2)
postcss: postcss:
specifier: ^8 specifier: ^8.4.38
version: 8.4.38 version: 8.4.38
tailwindcss: tailwindcss:
specifier: ^3.4.1 specifier: ^3.4.4
version: 3.4.4 version: 3.4.4
typescript: typescript:
specifier: ^5 specifier: ^5.4.5
version: 5.4.5 version: 5.4.5
packages: packages:
@ -543,59 +546,59 @@ packages:
'@jridgewell/trace-mapping@0.3.25': '@jridgewell/trace-mapping@0.3.25':
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
'@next/env@14.2.3': '@next/env@14.2.4':
resolution: {integrity: sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA==} resolution: {integrity: sha512-3EtkY5VDkuV2+lNmKlbkibIJxcO4oIHEhBWne6PaAp+76J9KoSsGvNikp6ivzAT8dhhBMYrm6op2pS1ApG0Hzg==}
'@next/swc-darwin-arm64@14.2.3': '@next/swc-darwin-arm64@14.2.4':
resolution: {integrity: sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==} resolution: {integrity: sha512-AH3mO4JlFUqsYcwFUHb1wAKlebHU/Hv2u2kb1pAuRanDZ7pD/A/KPD98RHZmwsJpdHQwfEc/06mgpSzwrJYnNg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
'@next/swc-darwin-x64@14.2.3': '@next/swc-darwin-x64@14.2.4':
resolution: {integrity: sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==} resolution: {integrity: sha512-QVadW73sWIO6E2VroyUjuAxhWLZWEpiFqHdZdoQ/AMpN9YWGuHV8t2rChr0ahy+irKX5mlDU7OY68k3n4tAZTg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
'@next/swc-linux-arm64-gnu@14.2.3': '@next/swc-linux-arm64-gnu@14.2.4':
resolution: {integrity: sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==} resolution: {integrity: sha512-KT6GUrb3oyCfcfJ+WliXuJnD6pCpZiosx2X3k66HLR+DMoilRb76LpWPGb4tZprawTtcnyrv75ElD6VncVamUQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@next/swc-linux-arm64-musl@14.2.3': '@next/swc-linux-arm64-musl@14.2.4':
resolution: {integrity: sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==} resolution: {integrity: sha512-Alv8/XGSs/ytwQcbCHwze1HmiIkIVhDHYLjczSVrf0Wi2MvKn/blt7+S6FJitj3yTlMwMxII1gIJ9WepI4aZ/A==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
'@next/swc-linux-x64-gnu@14.2.3': '@next/swc-linux-x64-gnu@14.2.4':
resolution: {integrity: sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==} resolution: {integrity: sha512-ze0ShQDBPCqxLImzw4sCdfnB3lRmN3qGMB2GWDRlq5Wqy4G36pxtNOo2usu/Nm9+V2Rh/QQnrRc2l94kYFXO6Q==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@next/swc-linux-x64-musl@14.2.3': '@next/swc-linux-x64-musl@14.2.4':
resolution: {integrity: sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==} resolution: {integrity: sha512-8dwC0UJoc6fC7PX70csdaznVMNr16hQrTDAMPvLPloazlcaWfdPogq+UpZX6Drqb1OBlwowz8iG7WR0Tzk/diQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
'@next/swc-win32-arm64-msvc@14.2.3': '@next/swc-win32-arm64-msvc@14.2.4':
resolution: {integrity: sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==} resolution: {integrity: sha512-jxyg67NbEWkDyvM+O8UDbPAyYRZqGLQDTPwvrBBeOSyVWW/jFQkQKQ70JDqDSYg1ZDdl+E3nkbFbq8xM8E9x8A==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
'@next/swc-win32-ia32-msvc@14.2.3': '@next/swc-win32-ia32-msvc@14.2.4':
resolution: {integrity: sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==} resolution: {integrity: sha512-twrmN753hjXRdcrZmZttb/m5xaCBFa48Dt3FbeEItpJArxriYDunWxJn+QFXdJ3hPkm4u7CKxncVvnmgQMY1ag==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [ia32] cpu: [ia32]
os: [win32] os: [win32]
'@next/swc-win32-x64-msvc@14.2.3': '@next/swc-win32-x64-msvc@14.2.4':
resolution: {integrity: sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==} resolution: {integrity: sha512-tkLrjBzqFTP8DVrAAQmZelEahfR9OxWpFR++vAI9FBhCiIxtwHwBHC23SBHCTURBtwB4kc/x44imVOnkKGNVGg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
@ -1238,6 +1241,17 @@ packages:
'@swc/helpers@0.5.5': '@swc/helpers@0.5.5':
resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==} resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==}
'@tanstack/react-table@8.17.3':
resolution: {integrity: sha512-5gwg5SvPD3lNAXPuJJz1fOCEZYk9/GeBFH3w/hCgnfyszOIzwkwgp5I7Q4MJtn0WECp84b5STQUDdmvGi8m3nA==}
engines: {node: '>=12'}
peerDependencies:
react: '>=16.8'
react-dom: '>=16.8'
'@tanstack/table-core@8.17.3':
resolution: {integrity: sha512-mPBodDGVL+fl6d90wUREepHa/7lhsghg2A3vFpakEhrhtbIlgNAZiMr7ccTgak5qbHqF14Fwy+W1yFWQt+WmYQ==}
engines: {node: '>=12'}
'@types/babel__core@7.20.5': '@types/babel__core@7.20.5':
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
@ -1410,8 +1424,8 @@ packages:
resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
engines: {node: '>=10'} engines: {node: '>=10'}
caniuse-lite@1.0.30001629: caniuse-lite@1.0.30001636:
resolution: {integrity: sha512-c3dl911slnQhmxUIT4HhYzT7wnBK/XYpGnYLOj4nJBaRiw52Ibe7YxlDaAeRECvA786zCuExhxIUJ2K7nHMrBw==} resolution: {integrity: sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==}
chalk@2.4.2: chalk@2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
@ -1559,21 +1573,21 @@ packages:
eastasianwidth@0.2.0: eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
electron-to-chromium@1.4.796: electron-to-chromium@1.4.803:
resolution: {integrity: sha512-NglN/xprcM+SHD2XCli4oC6bWe6kHoytcyLKCWXmRL854F0qhPhaYgUswUsglnPxYaNQIg2uMY4BvaomIf3kLA==} resolution: {integrity: sha512-61H9mLzGOCLLVsnLiRzCbc63uldP0AniRYPV3hbGVtONA1pI7qSGILdbofR7A8TMbOypDocEAjH/e+9k1QIe3g==}
embla-carousel-react@8.1.3: embla-carousel-react@8.1.5:
resolution: {integrity: sha512-YrezDPgxPDKa+OKMhSrwuPEU2OgF5147vFW473EWT3bx9DETV3W/RyWTxq0/2pf3M4VXkjqFNbS/W1xM8lTaVg==} resolution: {integrity: sha512-xFmfxgJd7mpWDHQ4iyK1Qs+5BTTwu4bkn+mSROKiUH9nKpPHTeilQ+rpeQDCHRrAPeshD67aBk0/p6FxWxXsng==}
peerDependencies: peerDependencies:
react: ^16.8.0 || ^17.0.1 || ^18.0.0 react: ^16.8.0 || ^17.0.1 || ^18.0.0
embla-carousel-reactive-utils@8.1.3: embla-carousel-reactive-utils@8.1.5:
resolution: {integrity: sha512-D8tAK6NRQVEubMWb+b/BJ3VvGPsbEeEFOBM6cCCwfiyfLzNlacOAt0q2dtUEA9DbGxeWkB8ExgXzFRxhGV2Hig==} resolution: {integrity: sha512-76uZTrSaEGGta+qpiGkMFlLK0I7N04TdjZ2obrBhyggYIFDWlxk1CriIEmt2lisLNsa1IYXM85kr863JoCMSyg==}
peerDependencies: peerDependencies:
embla-carousel: 8.1.3 embla-carousel: 8.1.5
embla-carousel@8.1.3: embla-carousel@8.1.5:
resolution: {integrity: sha512-GiRpKtzidV3v50oVMly8S+D7iE1r96ttt7fSlvtyKHoSkzrAnVcu8fX3c4j8Ol2hZSQlVfDqDIqdrFPs0u5TWQ==} resolution: {integrity: sha512-R6xTf7cNdR2UTNM6/yUPZlJFRmZSogMiRjJ5vXHO65II5MoUlrVYUAP0fHQei/py82Vf15lj+WI+QdhnzBxA2g==}
emittery@0.13.1: emittery@0.13.1:
resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==}
@ -1650,8 +1664,8 @@ packages:
debug: debug:
optional: true optional: true
foreground-child@3.1.1: foreground-child@3.2.1:
resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==}
engines: {node: '>=14'} engines: {node: '>=14'}
form-data@4.0.0: form-data@4.0.0:
@ -1965,8 +1979,8 @@ packages:
node-notifier: node-notifier:
optional: true optional: true
jiti@1.21.3: jiti@1.21.6:
resolution: {integrity: sha512-uy2bNX5zQ+tESe+TiC7ilGRz8AtRGmnJH55NC5S0nSUjvvvM2hJHmefHErugGXN4pNv4Qx7vLsnNw9qJ9mtIsw==} resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==}
hasBin: true hasBin: true
js-tokens@4.0.0: js-tokens@4.0.0:
@ -1997,15 +2011,15 @@ packages:
resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
engines: {node: '>=6'} engines: {node: '>=6'}
lightweight-charts@4.1.4: lightweight-charts@4.1.5:
resolution: {integrity: sha512-jsQOK27a3wiw/Db3Eoo3VX93LGovXA/sOWHVEiEosGOOGtxSSuIWTYVebjRKGK0SWkkUwI8AHQ4j7HZSKm7fxA==} resolution: {integrity: sha512-2ML3CgwKGX3FsvLs+ExvIM+C4/cYaa4dsYUy8BHfdqAgYY+bwjIzSDsv5PpTE2a1rDKQBI5LJRtocELcWeXTWw==}
lilconfig@2.1.0: lilconfig@2.1.0:
resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
lilconfig@3.1.1: lilconfig@3.1.2:
resolution: {integrity: sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==} resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==}
engines: {node: '>=14'} engines: {node: '>=14'}
lines-and-columns@1.2.4: lines-and-columns@1.2.4:
@ -2026,8 +2040,8 @@ packages:
lru-cache@5.1.1: lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
lucide-react@0.387.0: lucide-react@0.395.0:
resolution: {integrity: sha512-NyB4oJZ0pzLHT/QgMpgCPbez6yqvz8QPBocMJBXQCInPpXcQVCUpcU1CDlRG8mT2j0KqodLQYp+F5zn8U86sXg==} resolution: {integrity: sha512-6hzdNH5723A4FLaYZWpK50iyZH8iS2Jq5zuPRRotOFkhu6kxxJiebVdJ72tCR5XkiIeYFOU5NUawFZOac+VeYw==}
peerDependencies: peerDependencies:
react: ^16.5.1 || ^17.0.0 || ^18.0.0 react: ^16.5.1 || ^17.0.0 || ^18.0.0
@ -2092,8 +2106,8 @@ packages:
react: ^16.8 || ^17 || ^18 react: ^16.8 || ^17 || ^18
react-dom: ^16.8 || ^17 || ^18 react-dom: ^16.8 || ^17 || ^18
next@14.2.3: next@14.2.4:
resolution: {integrity: sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==} resolution: {integrity: sha512-R8/V7vugY+822rsQGQCjoLhMuC9oFj9SOi4Cl4b2wjDrseD0LRZ10W7R6Czo4w9ZznVSshKjuIomsRjvm9EKJQ==}
engines: {node: '>=18.17.0'} engines: {node: '>=18.17.0'}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
@ -2270,11 +2284,11 @@ packages:
peerDependencies: peerDependencies:
react: ^18.3.1 react: ^18.3.1
react-hook-form@7.51.5: react-hook-form@7.52.0:
resolution: {integrity: sha512-J2ILT5gWx1XUIJRETiA7M19iXHlG74+6O3KApzvqB/w8S5NQR7AbU8HVZrMALdmDgWpRPYiZJl0zx8Z4L2mP6Q==} resolution: {integrity: sha512-mJX506Xc6mirzLsmXUJyqlAI3Kj9Ph2RhplYhUVffeOQSnubK2uVqBFOBJmvKikvbFV91pxVXmDiR+QMF19x6A==}
engines: {node: '>=12.22.0'} engines: {node: '>=12.22.0'}
peerDependencies: peerDependencies:
react: ^16.8.0 || ^17 || ^18 react: ^16.8.0 || ^17 || ^18 || ^19
react-is@18.3.1: react-is@18.3.1:
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
@ -2390,8 +2404,8 @@ packages:
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
engines: {node: '>=8'} engines: {node: '>=8'}
sonner@1.4.41: sonner@1.5.0:
resolution: {integrity: sha512-uG511ggnnsw6gcn/X+YKkWPo5ep9il9wYi3QJxHsYe7yTZ4+cOd1wuodOUmOpFuXL+/RE3R04LczdNCDygTDgQ==} resolution: {integrity: sha512-FBjhG/gnnbN6FY0jaNnqZOMmB73R+5IiyYAw8yBj7L54ER7HB3fOSE5OFiQiE2iXWxeXKvg6fIP4LtVppHEdJA==}
peerDependencies: peerDependencies:
react: ^18.0.0 react: ^18.0.0
react-dom: ^18.0.0 react-dom: ^18.0.0
@ -2610,8 +2624,8 @@ packages:
yallist@3.1.1: yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
yaml@2.4.3: yaml@2.4.5:
resolution: {integrity: sha512-sntgmxj8o7DE7g/Qi60cqpLBA3HG3STcDA0kO+WfB05jEKhZMbY7umNm2rBpQvsmZ16/lPXCJGW2672dgOUkrg==} resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==}
engines: {node: '>= 14'} engines: {node: '>= 14'}
hasBin: true hasBin: true
@ -2904,9 +2918,9 @@ snapshots:
'@fontsource-variable/kode-mono@5.0.3': {} '@fontsource-variable/kode-mono@5.0.3': {}
'@hookform/resolvers@3.6.0(react-hook-form@7.51.5(react@18.3.1))': '@hookform/resolvers@3.6.0(react-hook-form@7.52.0(react@18.3.1))':
dependencies: dependencies:
react-hook-form: 7.51.5(react@18.3.1) react-hook-form: 7.52.0(react@18.3.1)
'@isaacs/cliui@8.0.2': '@isaacs/cliui@8.0.2':
dependencies: dependencies:
@ -3106,33 +3120,33 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2 '@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.4.15 '@jridgewell/sourcemap-codec': 1.4.15
'@next/env@14.2.3': {} '@next/env@14.2.4': {}
'@next/swc-darwin-arm64@14.2.3': '@next/swc-darwin-arm64@14.2.4':
optional: true optional: true
'@next/swc-darwin-x64@14.2.3': '@next/swc-darwin-x64@14.2.4':
optional: true optional: true
'@next/swc-linux-arm64-gnu@14.2.3': '@next/swc-linux-arm64-gnu@14.2.4':
optional: true optional: true
'@next/swc-linux-arm64-musl@14.2.3': '@next/swc-linux-arm64-musl@14.2.4':
optional: true optional: true
'@next/swc-linux-x64-gnu@14.2.3': '@next/swc-linux-x64-gnu@14.2.4':
optional: true optional: true
'@next/swc-linux-x64-musl@14.2.3': '@next/swc-linux-x64-musl@14.2.4':
optional: true optional: true
'@next/swc-win32-arm64-msvc@14.2.3': '@next/swc-win32-arm64-msvc@14.2.4':
optional: true optional: true
'@next/swc-win32-ia32-msvc@14.2.3': '@next/swc-win32-ia32-msvc@14.2.4':
optional: true optional: true
'@next/swc-win32-x64-msvc@14.2.3': '@next/swc-win32-x64-msvc@14.2.4':
optional: true optional: true
'@nodelib/fs.scandir@2.1.5': '@nodelib/fs.scandir@2.1.5':
@ -3879,6 +3893,14 @@ snapshots:
'@swc/counter': 0.1.3 '@swc/counter': 0.1.3
tslib: 2.6.3 tslib: 2.6.3
'@tanstack/react-table@8.17.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@tanstack/table-core': 8.17.3
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
'@tanstack/table-core@8.17.3': {}
'@types/babel__core@7.20.5': '@types/babel__core@7.20.5':
dependencies: dependencies:
'@babel/parser': 7.24.7 '@babel/parser': 7.24.7
@ -4060,8 +4082,8 @@ snapshots:
browserslist@4.23.1: browserslist@4.23.1:
dependencies: dependencies:
caniuse-lite: 1.0.30001629 caniuse-lite: 1.0.30001636
electron-to-chromium: 1.4.796 electron-to-chromium: 1.4.803
node-releases: 2.0.14 node-releases: 2.0.14
update-browserslist-db: 1.0.16(browserslist@4.23.1) update-browserslist-db: 1.0.16(browserslist@4.23.1)
@ -4083,7 +4105,7 @@ snapshots:
camelcase@6.3.0: {} camelcase@6.3.0: {}
caniuse-lite@1.0.30001629: {} caniuse-lite@1.0.30001636: {}
chalk@2.4.2: chalk@2.4.2:
dependencies: dependencies:
@ -4215,19 +4237,19 @@ snapshots:
eastasianwidth@0.2.0: {} eastasianwidth@0.2.0: {}
electron-to-chromium@1.4.796: {} electron-to-chromium@1.4.803: {}
embla-carousel-react@8.1.3(react@18.3.1): embla-carousel-react@8.1.5(react@18.3.1):
dependencies: dependencies:
embla-carousel: 8.1.3 embla-carousel: 8.1.5
embla-carousel-reactive-utils: 8.1.3(embla-carousel@8.1.3) embla-carousel-reactive-utils: 8.1.5(embla-carousel@8.1.5)
react: 18.3.1 react: 18.3.1
embla-carousel-reactive-utils@8.1.3(embla-carousel@8.1.3): embla-carousel-reactive-utils@8.1.5(embla-carousel@8.1.5):
dependencies: dependencies:
embla-carousel: 8.1.3 embla-carousel: 8.1.5
embla-carousel@8.1.3: {} embla-carousel@8.1.5: {}
emittery@0.13.1: {} emittery@0.13.1: {}
@ -4300,7 +4322,7 @@ snapshots:
follow-redirects@1.15.6: {} follow-redirects@1.15.6: {}
foreground-child@3.1.1: foreground-child@3.2.1:
dependencies: dependencies:
cross-spawn: 7.0.3 cross-spawn: 7.0.3
signal-exit: 4.1.0 signal-exit: 4.1.0
@ -4345,7 +4367,7 @@ snapshots:
glob@10.4.1: glob@10.4.1:
dependencies: dependencies:
foreground-child: 3.1.1 foreground-child: 3.2.1
jackspeak: 3.4.0 jackspeak: 3.4.0
minimatch: 9.0.4 minimatch: 9.0.4
minipass: 7.1.2 minipass: 7.1.2
@ -4780,7 +4802,7 @@ snapshots:
- supports-color - supports-color
- ts-node - ts-node
jiti@1.21.3: {} jiti@1.21.6: {}
js-tokens@4.0.0: {} js-tokens@4.0.0: {}
@ -4799,13 +4821,13 @@ snapshots:
leven@3.1.0: {} leven@3.1.0: {}
lightweight-charts@4.1.4: lightweight-charts@4.1.5:
dependencies: dependencies:
fancy-canvas: 2.1.0 fancy-canvas: 2.1.0
lilconfig@2.1.0: {} lilconfig@2.1.0: {}
lilconfig@3.1.1: {} lilconfig@3.1.2: {}
lines-and-columns@1.2.4: {} lines-and-columns@1.2.4: {}
@ -4823,7 +4845,7 @@ snapshots:
dependencies: dependencies:
yallist: 3.1.1 yallist: 3.1.1
lucide-react@0.387.0(react@18.3.1): lucide-react@0.395.0(react@18.3.1):
dependencies: dependencies:
react: 18.3.1 react: 18.3.1
@ -4879,27 +4901,27 @@ snapshots:
react: 18.3.1 react: 18.3.1
react-dom: 18.3.1(react@18.3.1) react-dom: 18.3.1(react@18.3.1)
next@14.2.3(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): next@14.2.4(@babel/core@7.24.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies: dependencies:
'@next/env': 14.2.3 '@next/env': 14.2.4
'@swc/helpers': 0.5.5 '@swc/helpers': 0.5.5
busboy: 1.6.0 busboy: 1.6.0
caniuse-lite: 1.0.30001629 caniuse-lite: 1.0.30001636
graceful-fs: 4.2.11 graceful-fs: 4.2.11
postcss: 8.4.31 postcss: 8.4.31
react: 18.3.1 react: 18.3.1
react-dom: 18.3.1(react@18.3.1) react-dom: 18.3.1(react@18.3.1)
styled-jsx: 5.1.1(@babel/core@7.24.7)(react@18.3.1) styled-jsx: 5.1.1(@babel/core@7.24.7)(react@18.3.1)
optionalDependencies: optionalDependencies:
'@next/swc-darwin-arm64': 14.2.3 '@next/swc-darwin-arm64': 14.2.4
'@next/swc-darwin-x64': 14.2.3 '@next/swc-darwin-x64': 14.2.4
'@next/swc-linux-arm64-gnu': 14.2.3 '@next/swc-linux-arm64-gnu': 14.2.4
'@next/swc-linux-arm64-musl': 14.2.3 '@next/swc-linux-arm64-musl': 14.2.4
'@next/swc-linux-x64-gnu': 14.2.3 '@next/swc-linux-x64-gnu': 14.2.4
'@next/swc-linux-x64-musl': 14.2.3 '@next/swc-linux-x64-musl': 14.2.4
'@next/swc-win32-arm64-msvc': 14.2.3 '@next/swc-win32-arm64-msvc': 14.2.4
'@next/swc-win32-ia32-msvc': 14.2.3 '@next/swc-win32-ia32-msvc': 14.2.4
'@next/swc-win32-x64-msvc': 14.2.3 '@next/swc-win32-x64-msvc': 14.2.4
transitivePeerDependencies: transitivePeerDependencies:
- '@babel/core' - '@babel/core'
- babel-plugin-macros - babel-plugin-macros
@ -4986,8 +5008,8 @@ snapshots:
postcss-load-config@4.0.2(postcss@8.4.38): postcss-load-config@4.0.2(postcss@8.4.38):
dependencies: dependencies:
lilconfig: 3.1.1 lilconfig: 3.1.2
yaml: 2.4.3 yaml: 2.4.5
optionalDependencies: optionalDependencies:
postcss: 8.4.38 postcss: 8.4.38
@ -5043,7 +5065,7 @@ snapshots:
react: 18.3.1 react: 18.3.1
scheduler: 0.23.2 scheduler: 0.23.2
react-hook-form@7.51.5(react@18.3.1): react-hook-form@7.52.0(react@18.3.1):
dependencies: dependencies:
react: 18.3.1 react: 18.3.1
@ -5140,7 +5162,7 @@ snapshots:
slash@3.0.0: {} slash@3.0.0: {}
sonner@1.4.41(react-dom@18.3.1(react@18.3.1))(react@18.3.1): sonner@1.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies: dependencies:
react: 18.3.1 react: 18.3.1
react-dom: 18.3.1(react@18.3.1) react-dom: 18.3.1(react@18.3.1)
@ -5242,7 +5264,7 @@ snapshots:
fast-glob: 3.3.2 fast-glob: 3.3.2
glob-parent: 6.0.2 glob-parent: 6.0.2
is-glob: 4.0.3 is-glob: 4.0.3
jiti: 1.21.3 jiti: 1.21.6
lilconfig: 2.1.0 lilconfig: 2.1.0
micromatch: 4.0.7 micromatch: 4.0.7
normalize-path: 3.0.0 normalize-path: 3.0.0
@ -5362,7 +5384,7 @@ snapshots:
yallist@3.1.1: {} yallist@3.1.1: {}
yaml@2.4.3: {} yaml@2.4.5: {}
yargs-parser@21.1.1: {} yargs-parser@21.1.1: {}

10
src/app/auth/page.tsx Normal file
View File

@ -0,0 +1,10 @@
import { AuthForms } from "@/components/auth-form";
import Image from "next/image";
export default function AuthPage() {
return (
<main className="flex flex-col items-center justify-start h-full w-full">
<AuthForms />
</main>
);
}

View File

@ -0,0 +1,7 @@
export default function AdminPage() {
return (<>
<section>
<h1>Welcome to the Dashboard</h1>
</section>
</>)
}

View File

@ -0,0 +1,44 @@
"use client"
import type {ICryptoInWalletInfo} from "@/interfaces/crypto.interface";
import {CryptosTable} from "@/components/data-tables/cryptos-table";
import {useEffect, useState} from "react";
import {Skeleton} from "@/components/ui/skeleton";
import ApiRequest from "@/services/apiRequest";
import type {IApiAllOffersRes} from "@/interfaces/api.interface";
export default function DashboardPage() {
const [isLoading, setIsLoading] = useState<boolean>(true)
const [cryptosList, setCryptosList] = useState<ICryptoInWalletInfo[]>([])
//FIX the loop
useEffect(() => {
ApiRequest.authenticated.get.json<ICryptoInWalletInfo[]>(
"crypto/all"
).then((response)=>{
if (response.data.length <= 0) {
setCryptosList([])
setIsLoading(false)
return
}
const resp = response.data
setCryptosList(resp)
setIsLoading(false)
})
}, []);
if (isLoading) {
return (<div className={"container flex flex-col justify-center items-center min-h-64 my-6 gap-4"}>
<h1 className={"text-2xl font-bold"}>Available cryptos</h1>
<Skeleton className={"container flex flex-col justify-center items-center min-h-64 my-6 gap-4"}/>
</div>)
}
return (<>
<section className={"container flex flex-col justify-center items-center min-h-64 my-6 gap-4"}>
<h1 className={"text-2xl font-bold"}>Available cryptos</h1>
<CryptosTable cryptosArray={cryptosList}/>
</section>
</>)
}

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -6,7 +6,10 @@ import { Header } from "@/components/header";
import { PrimaryNavigationMenu } from "@/components/primary-nav"; import { PrimaryNavigationMenu } from "@/components/primary-nav";
import { Providers } from "@/components/providers/providers"; import { Providers } from "@/components/providers/providers";
import { ThemeProvider } from "@/components/providers/theme-provider"; import { ThemeProvider } from "@/components/providers/theme-provider";
import { Toaster } from "@/components/ui/toaster";
import type React from "react"; import type React from "react";
import Link from "next/link";
import {Button} from "@/components/ui/button";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "YeloBit", title: "YeloBit",
@ -22,14 +25,18 @@ export default function RootLayout({
return ( return (
<html lang="en"> <html lang="en">
<head> <head>
<link rel="icon" href="/public/favicon.ico" sizes="any" /> <link rel="icon" href="/favicon.ico" sizes="any" />
</head> </head>
<body className={"w-full min-h-screen flex flex-col items-center justify-between"}> <body className={"w-full min-h-screen flex flex-col items-center justify-between"}>
<Providers> <Providers>
<Header> <Header>
<PrimaryNavigationMenu /> <div className={"flex flex-row flex-wrap md:flex-nowrap gap-2"}>
<Button asChild variant={'light'}><Link href={'/wallet'}>Wallet</Link></Button>
<Button asChild variant={'light'}><Link href={'/dashboard'}>Dashboard</Link></Button>
</div>
</Header> </Header>
{children} {children}
<Toaster />
<Footer /> <Footer />
</Providers> </Providers>
</body> </body>

40
src/app/wallet/page.tsx Normal file
View File

@ -0,0 +1,40 @@
"use client"
import type {ICryptoInWalletInfo} from "@/interfaces/crypto.interface";
import {CryptosTable} from "@/components/data-tables/cryptos-table";
import {useContext, useEffect, useState} from "react";
import {Skeleton} from "@/components/ui/skeleton";
import ApiRequest from "@/services/apiRequest";
import type {IApiAllOffersRes} from "@/interfaces/api.interface";
import {WalletTable} from "@/components/data-tables/wallet-table";
import {UserDataContext} from "@/components/providers/userdata-provider";
import {IUserWallet} from "@/interfaces/userdata.interface";
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)
if (userContext?.userData) {
setIsLoading(false)
}
}, [userContext]);
if (isLoading || !userContext?.userData) {
return (<div className={"container flex flex-col justify-center items-center min-h-64 my-6 gap-4"}>
<h1 className={"text-2xl font-bold"}>Cryptos in your wallet</h1>
<Skeleton className={"container flex flex-col justify-center items-center min-h-64 my-6 gap-4"}/>
</div>)
}
return (<>
<section className={"container flex flex-col justify-center items-center min-h-64 my-6 gap-4"}>
<h1 className={"text-2xl font-bold"}>Cryptos in your wallet</h1>
<WalletTable walletArray={userContext.userData.wallet as unknown as IUserWallet}/>
</section>
</>)
}

View File

@ -4,27 +4,29 @@ import { AccountInfo } from "@/components/account-info";
import { UserDataContext } from "@/components/providers/userdata-provider"; import { UserDataContext } from "@/components/providers/userdata-provider";
import { Skeleton } from "@/components/ui/skeleton"; import { Skeleton } from "@/components/ui/skeleton";
import type { IUserData } from "@/interfaces/userdata.interface"; import type { IUserData } from "@/interfaces/userdata.interface";
import {Dispatch, SetStateAction, useContext, useEffect, useState} from "react"; import {
type Dispatch,
type SetStateAction,
useContext,
useEffect,
useState,
} from "react";
const localStorage = typeof window !== "undefined" ? window.localStorage : null;
export function AccountDialog() { export function AccountDialog() {
const userContext = useContext(UserDataContext); const userContext = useContext(UserDataContext);
const token = localStorage?.getItem("sub") || "";
const haveToken = token.length >= 16 || false;
console.log(haveToken);
const [isLoaded, setIsLoaded] = useState<boolean>(false); const [isLoaded, setIsLoaded] = useState<boolean>(false);
if (!userContext?.userData) { if (!userContext) {
userContext?.setUserData({ return (
age: 0, <div>
city: "Chambéry", <p>No account</p>
created_at: "jaj", </div>
dollarAvailables: 34, );
email: "mherriot@tutanota.com",
id: "",
isActive: false,
lastName: "Herriot",
pseudo: "Avnyr",
roleId: "",
updated_at: "",
firstName: "Mathis",
});
} }
useEffect(() => { useEffect(() => {
@ -39,7 +41,13 @@ export function AccountDialog() {
return ( return (
<div> <div>
<AccountInfo userData={userContext?.userData as IUserData} setUserData={userContext?.setUserData as Dispatch<SetStateAction<IUserData | undefined>>} /> <AccountInfo
userData={userContext?.userData as IUserData}
setUserData={
userContext?.setUserData as Dispatch<SetStateAction<IUserData | undefined>>
}
isDisconnected={!haveToken}
/>
</div> </div>
); );
} }

View File

@ -11,18 +11,85 @@ import {
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import type { IUserData } from "@/interfaces/userdata.interface"; import type {IUserData, IUserWallet} from "@/interfaces/userdata.interface";
import { Landmark, Unplug, User, Wallet } from "lucide-react"; import { CopyButton } from "@/components/ui/copy-button";
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 type React from "react";
import {useEffect, useState} from "react";
import {type ICryptoInUserWalletInfo, ICryptoInWalletInfo} from "@/interfaces/crypto.interface";
export function AccountInfo({ export function AccountInfo({
userData, userData,
setUserData, setUserData,
isDisconnected,
}: { }: {
userData: IUserData; userData: IUserData;
setUserData: React.Dispatch<React.SetStateAction<IUserData | undefined>>; setUserData: React.Dispatch<React.SetStateAction<IUserData | undefined>>;
isDisconnected: boolean;
}) { }) {
const [isLoaded,setIsLoaded] = useState(false)
useEffect(() => {
if (!isLoaded) {
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
}
}) || []
}
delete res.resolved?.UserHasCrypto
//@ts-ignore
setUserData({
...userData,
...res.resolved,
wallet: wallet
})
console.log(userData)
setIsLoaded(true)
});
}
}, [isLoaded]);
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"
}
>
<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"
}
>
<Key className={"w-3"} /> Link account
</Link>
</div>
</div>
);
}
return ( return (
<Dialog> <Dialog>
<DialogTrigger asChild> <DialogTrigger asChild>
@ -31,26 +98,69 @@ export function AccountInfo({
<User /> <User />
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent className="sm:max-w-[425px]"> <DialogContent className="sm:max-w-[425px] md:max-w-[720px]">
<DialogHeader> <DialogHeader>
<DialogTitle>{`Your account - ${userData.firstName} ${userData.lastName}`}</DialogTitle> <DialogTitle>{`Your account - ${userData.firstName} ${userData.lastName}`}</DialogTitle>
<DialogDescription>{userData.city}</DialogDescription> <DialogDescription>{userData.city}</DialogDescription>
</DialogHeader> </DialogHeader>
<div className={"flex flex-col items-center justify-center w-full"}> <div className={"flex flex-col items-center justify-center w-full"}>
<div className={"flex flex-row justify-evenly items-center"}> <div className={"flex flex-col justify-evenly items-center gap-2"}>
<div className={"flex flex-row gap-1 justify-center items-center mx-auto"}> <div
<Landmark /> className={
<p>{userData.dollarAvailables} $</p> "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>
<div></div>
</div> </div>
</div> </div>
<DialogFooter> <DialogFooter>
<Button variant={"secondary"} className={"gap-2 px-2"}> <Button variant={"secondary"} className={"gap-2 px-2"} asChild>
<Wallet /> <Link href={'/wallet'}>
<p>My wallet</p> <Wallet />
<p>My wallet</p>
</Link>
</Button> </Button>
<Button variant={"destructive"} className={"gap-2 px-2"}> <Button
variant={"destructive"}
className={"gap-2 px-2 mb-2"}
onClick={() => doDisconnect()}
>
<Unplug /> <Unplug />
<p>Disconnect</p> <p>Disconnect</p>
</Button> </Button>

View File

@ -0,0 +1,247 @@
"use client";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import AutoForm, { AutoFormSubmit } from "@/components/auto-form";
import { UserDataContext } from "@/components/providers/userdata-provider";
import { ToastBox, toastType } from "@/components/ui/toast-box";
import { useToast } from "@/components/ui/use-toast";
import type {
IApiLoginReq,
IApiLoginRes,
IApiRegisterReq,
IApiRegisterRes,
} from "@/interfaces/api.interface";
import { EReturnState, type IStandardisedReturn } from "@/interfaces/general.interface";
import type { IUserData } from "@/interfaces/userdata.interface";
import ApiRequest from "@/services/apiRequest";
import { useLocalStorage } from "@/services/localStorage";
import { Bug, RefreshCw } from "lucide-react";
import Link from "next/link";
import { type Dispatch, type SetStateAction, useContext, useState } from "react";
import * as z from "zod";
const loginSchema = z.object({
email: z
.string({
required_error: "Email is needed.",
})
.email({
message: "Should be a valid email.",
})
.describe("Your account email."),
password: z
.string({
required_error: "Password is needed.",
})
.describe("Your account password."),
});
const registerSchema = z.object({
firstName: z.string({
required_error: "",
}),
lastName: z.string(),
age: z.number().min(18).max(120),
pseudo: z.string({
required_error: "",
}),
city: z.string({
required_error: "",
}),
email: z
.string({
required_error: "Email is needed.",
})
.email("Should be a valid email."),
password: z
.string({
required_error: "Password is needed.",
})
.describe("Your account password."),
});
export function AuthForms() {
const [isLoading, setIsLoading] = useState(false);
const [sub, setSub] = useLocalStorage<string | undefined>("sub", "");
const userContext = useContext(UserDataContext);
const { toast } = useToast();
async function doRegister(
registerData: IApiRegisterReq,
userDataSetter: Dispatch<SetStateAction<IUserData | null>>,
): Promise<IStandardisedReturn<IApiRegisterRes>> {
console.trace(registerData);
try {
const ReqRes = await ApiRequest.standard.post.json<
IApiRegisterReq,
IApiRegisterRes
>("auth/signup", registerData);
console.trace(ReqRes.data);
if (ReqRes.data.user) {
userDataSetter({
...ReqRes.data.user,
wallet: {
uat: Date.now(),
update_interval: 30_000,
owned_cryptos: [],
},
});
setSub(ReqRes.data.access_token);
}
console.debug(ReqRes.data.message || "Not additional message from request");
return {
state: EReturnState.done,
resolved: ReqRes.data,
};
} catch (error) {
console.error("Error during registration:", error);
return {
state: EReturnState.serverError,
message: error as string,
};
}
}
async function doLogin(
loginData: IApiLoginReq,
): Promise<IStandardisedReturn<IApiLoginRes>> {
try {
const ReqRes = await ApiRequest.standard.post.json<IApiLoginReq, IApiLoginRes>(
"auth/signin",
loginData,
);
console.trace(ReqRes.data);
if (ReqRes.data.access_token) {
setSub(ReqRes.data.access_token);
}
return {
state: EReturnState.done,
};
} catch (err) {
console.error("Error during login:", err);
return {
state: EReturnState.serverError,
message: err as string,
};
}
}
if (!userContext || !userContext.setUserData) {
return (
<div
className={
"bg-destructive text-destructive-foreground p-3 gap-2 border rounded flex flex-row justify-center items-center"
}
>
<Bug />
<p>It seems that the context is missing..</p>
</div>
);
}
return (
<Tabs defaultValue="login" className="w-full p-2 md:w-[400px] my-4">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="login">Login</TabsTrigger>
<TabsTrigger value="register">Register</TabsTrigger>
</TabsList>
<TabsContent value="login">
<AutoForm
// Pass the schema to the form
formSchema={loginSchema}
onSubmit={(data: IApiLoginReq) => {
setIsLoading(true);
doLogin(data).then((res) => {
if (res.state !== EReturnState.done) {
toast({
description: res.message || "An unexpected error occurred..",
variant: "destructive",
});
setIsLoading(false);
return;
}
//toast.custom(<ToastBox message={"Login successful ! \n You will be redirected."} type={toastType.success}/>)
toast({
description: "Login successful ! \n You will be redirected.",
});
setTimeout(() => {
setIsLoading(false);
location.href = "/";
console.log("Moving to home.");
}, 3_000);
});
}}
fieldConfig={{
password: {
inputProps: {
type: "password",
placeholder: "••••••••",
},
},
}}
>
<AutoFormSubmit
disabled={!!isLoading}
className={"gap-2 disabled:bg-secondary"}
>
{/* biome-ignore lint/style/useTemplate: <explanation> */}
<RefreshCw className={"animate-spin" + isLoading && "hidden"} />
<p>Login</p>
</AutoFormSubmit>
</AutoForm>
</TabsContent>
<TabsContent value="register">
<AutoForm
// Pass the schema to the form
formSchema={registerSchema}
onSubmit={(data: IApiRegisterReq) => {
setIsLoading(true);
doRegister(
data,
userContext.setUserData as Dispatch<SetStateAction<IUserData | null>>,
).then((res) => {
if (res.state !== EReturnState.done) {
//toast.custom(<ToastBox message={res.message || "An unexpected error occurred.."} type={toastType.error}/>)
setIsLoading(false);
return;
}
//toast.custom(<ToastBox message={"Register successful ! \n You will be redirected."} type={toastType.success}/>)
setTimeout(() => {
setIsLoading(false);
//location.href = "/"
console.log("Moving to home.");
}, 5_000);
});
}}
fieldConfig={{
password: {
inputProps: {
type: "password",
placeholder: "••••••••",
},
},
}}
>
<AutoFormSubmit
disabled={!!isLoading}
className={"gap-2 disabled:bg-secondary"}
>
{/* biome-ignore lint/style/useTemplate: <explanation> */}
<RefreshCw className={"animate-spin" + !isLoading && "hidden"} />
<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
</Link>
.
</p>
</AutoForm>
</TabsContent>
</Tabs>
);
}

View File

@ -98,7 +98,7 @@ function AutoForm<SchemaType extends ZodObjectOrWrapped>({
onSubmit={(e) => { onSubmit={(e) => {
form.handleSubmit(onSubmit)(e); form.handleSubmit(onSubmit)(e);
}} }}
className={cn("space-y-5", className)} className={cn("space-y-5 w-full", className)}
> >
<AutoFormObject <AutoFormObject
schema={objectFormSchema} schema={objectFormSchema}

View File

@ -0,0 +1,193 @@
// @flow
import * as React from 'react';
import {
Dialog,
DialogContent,
DialogTrigger,
} from "@/components/ui/dialog";
import {Button} from "@/components/ui/button";
import {Ban, DollarSign, RefreshCw} from "lucide-react";
import * as z from "zod";
import {Tabs, TabsContent, TabsList, TabsTrigger} from "@/components/ui/tabs";
import {Dispatch, SetStateAction, useEffect, useState} from "react";
import type {ICryptoInWalletInfo} from "@/interfaces/crypto.interface";
import {toast} from "@/components/ui/use-toast";
import AutoForm, {AutoFormSubmit} from "@/components/auto-form";
import type {IApiAllOffersRes, IApiDoTradeReq} from "@/interfaces/api.interface";
import ApiRequest from "@/services/apiRequest";
import {Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage} from "@/components/ui/form";
import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from "@/components/ui/select";
import Link from "next/link";
import {useForm} from "react-hook-form";
import {zodResolver} from "@hookform/resolvers/zod";
type Props = {
cryptoData: ICryptoInWalletInfo
};
export function BuyModal(props: Props) {
const [isLoading, setIsLoading] = useState<boolean>(false)
const [offersList, setOffersList] = useState<IApiAllOffersRes[]>([])
const [isLoaded,setIsLoaded] = useState(false)
// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
useEffect(() => {
if(!isLoaded) {
ApiRequest.authenticated.get.json<IApiAllOffersRes[]>(`offer/crypto/${props.cryptoData.id}`).then((response) => {
setOffersList(response.data);
console.log(`Crypto ${props.cryptoData.name} -> ${response.data.length}`)
setIsLoaded(true);
return;
})
}
}, [isLoaded]);
const buyFromServerSchema = z.object({
amount: z
.number({
required_error: "An amount is needed.",
})
.min(1)
.max(props.cryptoData.quantity, "You cant buy more that what is available on the server.")
.describe("The amount you want to buy."),
});
const buyFromUserSchema = z.object({
offerId: z
.string({
required_error: "You should select an offer.",
})
.uuid(),
})
function onBuyFromServerSubmit(data: z.infer<typeof buyFromServerSchema>) {
ApiRequest.authenticated.post.json("crypto/buy", {id_crypto: props.cryptoData.id, amount: data.amount}).then((res)=>{
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
}
toast({
title: "Transaction accepted.",
description: (
<p>You will be redirected.</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>
),
})
}
function onBuyFromUserSubmit(data: z.infer<typeof buyFromUserSchema>) {
ApiRequest.authenticated.post.json("trade/create", {id_offer: data.offerId}).then((res)=>{
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
}
toast({
title: "Transaction accepted.",
description: (
<p>You will be redirected.</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 buyFromUserForm = useForm<z.infer<typeof buyFromUserSchema>>({
resolver: zodResolver(buyFromUserSchema),
})
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="light"><DollarSign /></Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px] md:max-w-[800px] flex flex-col justify-start items-center">
<Tabs defaultValue="server" className="w-full p-2 my-4">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="user">Buy from user <p className={" ml-1 px-1 bg-primary text-primary-foreground rounded"}>{offersList.length}</p></TabsTrigger>
<TabsTrigger value="server">Buy from server</TabsTrigger>
</TabsList>
<TabsContent value="user" className={"flex flex-col justify-start items-center"}>
<Form {...buyFromUserForm}>
<form onSubmit={buyFromUserForm.handleSubmit(onBuyFromUserSubmit)} className="w-full space-y-6">
<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)=>{
return (
<SelectItem value={offer.id} key={offer.id}>{`${offer.amount}x ${offer.Crypto.name} - ${offer.User.pseudo}`}</SelectItem>
)
})}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>
</TabsContent>
<TabsContent value="server" className={"flex flex-col justify-start items-center"}>
{!props.cryptoData.quantity && <div
className={"bg-destructive text-destructive-foreground border-destructive rounded p-2 flex justify-start items-center gap-2"}>
<Ban/>
<p>The server dont have stock for the designated cryptos.</p>
</div>}
<div className={"w-full flex justify-center gap-2 items-center p-2"}>
<p>Available quantity on the server :</p>
<p className={"p-1 bg-accent text-accent-foreground rounded m-1"}>{props.cryptoData.quantity}</p>
</div>
{props.cryptoData.quantity && <AutoForm
// Pass the schema to the form
formSchema={buyFromServerSchema}
onSubmit={onBuyFromServerSubmit}
>
<AutoFormSubmit
disabled={!!isLoading}
className={"gap-2 disabled:bg-secondary-foreground"}
>
{/* biome-ignore lint/style/useTemplate: <explanation> */}
<RefreshCw className={`animate-spin ${!isLoading && "hidden"}`}/>
<p>Buy from the server</p>
</AutoFormSubmit>
</AutoForm>}
</TabsContent>
</Tabs>
</DialogContent>
</Dialog>
);
};

View File

@ -0,0 +1,30 @@
// @flow
import * as React from 'react';
import {
Dialog,
DialogContent,
DialogDescription, DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import {Button} from "@/components/ui/button";
import {Label} from "@/components/ui/label";
import {Input} from "@/components/ui/input";
import {LineChart} from "lucide-react";
type Props = {
targetedCryptoId: string
};
export function ViewModal(props: Props) {
return (
<Dialog>
<DialogTrigger asChild>
<Button variant="ghost"><LineChart /></Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px] md:max-w-[800px]">
Test 1,2 !
//From here
</DialogContent>
</Dialog>
);
};

View File

@ -0,0 +1,86 @@
'use client'
import * as React from 'react';
import type {ICryptoInWalletInfo} from "@/interfaces/crypto.interface";
import {
type ColumnDef,
flexRender,
getCoreRowModel,
useReactTable,
} from "@tanstack/react-table"
import {DataTable} from "@/components/ui/data-table";
import {Button} from "@/components/ui/button";
import {ArrowUpDown, MoreHorizontal} from "lucide-react";
import {useRouter} from "next/navigation";
import {useState} from "react";
import {BuyModal} from "@/components/cryptos/buy-modal";
import {ViewModal} from "@/components/cryptos/view-modal";
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[]
data: TData[]
}
type Props = {
cryptosArray: ICryptoInWalletInfo[]
};
export function CryptosTable(props: Props) {
const router = useRouter()
const cryptos= props.cryptosArray
const columns: ColumnDef<ICryptoInWalletInfo, any>[] = [
{
accessorKey: "name",
header: "Name",
},
{
accessorKey: "value",
header: ({ column }) => {
return (
<Button
variant="light"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Value - USD
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
)
},
},
{
accessorKey: "quantity",
header: ({ column }) => {
return (
<Button
variant="light"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Available from server
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
)
},
},
{
id: "actions",
cell: ({ row }) => {
const payment = row.original
return (
<div className={"flex gap-2"}>
<BuyModal cryptoData={row.original}/>
<ViewModal targetedCryptoId={row.original.id}/>
</div>
)
},
},
];
return (
<>
<DataTable columns={columns} data={cryptos} fieldToFilter={"name"}/>
</>
);
}

View File

@ -0,0 +1,86 @@
'use client'
import * as React from 'react';
import type {ICryptoInUserWalletInfo, ICryptoInWalletInfo} from "@/interfaces/crypto.interface";
import {
type ColumnDef,
flexRender,
getCoreRowModel,
useReactTable,
} from "@tanstack/react-table"
import {DataTable} from "@/components/ui/data-table";
import {Button} from "@/components/ui/button";
import {ArrowUpDown, MoreHorizontal} from "lucide-react";
import {useRouter} from "next/navigation";
import {useState} from "react";
import {BuyModal} from "@/components/cryptos/buy-modal";
import {ViewModal} from "@/components/cryptos/view-modal";
import {IUserWallet} from "@/interfaces/userdata.interface";
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[]
data: TData[]
}
type Props = {
walletArray: IUserWallet
};
export function WalletTable(props: Props) {
const router = useRouter()
const wallet= props.walletArray.owned_cryptos
const columns: ColumnDef<ICryptoInUserWalletInfo, any>[] = [
{
accessorKey: "name",
header: "Name",
},
{
accessorKey: "value",
header: ({ column }) => {
return (
<Button
variant="light"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Value - USD
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
)
},
},
{
accessorKey: "owned_amount",
header: ({ column }) => {
return (
<Button
variant="light"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Amount owned
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
)
},
},
{
id: "actions",
cell: ({ row }) => {
const payment = row.original
return (
<div className={"flex gap-2"}>
<p className={"font-light italic text-xs"}>Soon here : Sell, History</p>
</div>
)
},
},
];
return (
<>
<DataTable columns={columns} data={wallet} fieldToFilter={"name"}/>
</>
);
}

View File

@ -5,7 +5,7 @@ export function Footer() {
return ( return (
<footer <footer
className={ className={
"flex flex-col-reverse md:flex-row justify-between gap-2 md:gap-1 items-center p-2 border-t-2 w-full" "flex flex-col-reverse md:flex-row justify-between gap-2 md:gap-1 self-end order-6 items-center p-2 border-t-2 w-full"
} }
> >
<div <div
@ -49,11 +49,7 @@ export function Footer() {
<h3 className={"text-nowrap text-center"}>Support Center</h3> <h3 className={"text-nowrap text-center"}>Support Center</h3>
</Link> </Link>
</div> </div>
<div <div />
className={
"flex flex-row gap-1 items-center justify-center md:justify-end md:w-1/3"
}
></div>
</footer> </footer>
); );
} }

View File

@ -23,9 +23,15 @@ export function Header({
{title || "YeloBit"} {title || "YeloBit"}
</h1> </h1>
</div> </div>
<div className={"w-1/3 flex flex-row justify-center items-center"}>{children}</div>
<div <div
className={"w-1/3 flex flex-row justify-center md:justify-end gap-2 items-center"} className={"w-1/3 flex flex-col md:flex-row w-full justify-center items-center"}
>
{children}
</div>
<div
className={
"w-1/3 flex flex-row justify-center md:justify-end w-full md:w-fit gap-2 items-center"
}
> >
<AccountDialog /> <AccountDialog />
<ThemeBtnSelector /> <ThemeBtnSelector />

View File

@ -15,6 +15,7 @@ const buttonVariants = cva(
"border border-input bg-background hover:bg-accent hover:text-accent-foreground", "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground", ghost: "hover:bg-accent hover:text-accent-foreground",
light: "hover:bg-accent-foreground hover:text-accent",
link: "text-primary underline-offset-4 hover:underline", link: "text-primary underline-offset-4 hover:underline",
}, },
size: { size: {

View File

@ -0,0 +1,112 @@
"use client";
import type { DropdownMenuTriggerProps } from "@radix-ui/react-dropdown-menu";
import { CheckIcon, ClipboardIcon } from "lucide-react";
import { Button, type ButtonProps } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { cn } from "@/lib/utils";
import { useCallback, useEffect, useState } from "react";
interface CopyButtonProps extends ButtonProps {
value: string;
src?: string;
event?: Event["NONE"];
}
interface Value {
data: string;
title: string;
}
export async function copyToClipboardWithMeta(value: string) {
await window?.navigator.clipboard.writeText(value);
}
export function CopyButton({
value,
className,
src,
variant = "ghost",
event,
...props
}: CopyButtonProps) {
const [hasCopied, setHasCopied] = useState(false);
// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
useEffect(() => {
setTimeout(() => {
setHasCopied(false);
}, 2000);
}, [hasCopied]);
return (
<Button
size="icon"
variant={variant}
className={cn(
"relative z-10 h-6 w-6 text-zinc-50 hover:bg-zinc-700 hover:text-zinc-50 [&_svg]:size-3",
className,
)}
onClick={() => {
copyToClipboardWithMeta(value).then(() => setHasCopied(true));
}}
{...props}
>
<span className="sr-only">Copy</span>
{hasCopied ? <CheckIcon /> : <ClipboardIcon />}
</Button>
);
}
interface CopyMultipleChoiceButtonProps extends DropdownMenuTriggerProps {
values: Value[];
}
export function CopyMultipleChoiceButton({
values,
className,
...props
}: CopyMultipleChoiceButtonProps) {
const [hasCopied, setHasCopied] = useState(false);
// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
useEffect(() => {
setTimeout(() => {
setHasCopied(false);
}, 2000);
}, [hasCopied]);
const copyCommand = useCallback((value: string) => {
copyToClipboardWithMeta(value).then(() => setHasCopied(true));
}, []);
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
size="icon"
variant="ghost"
className={cn(
"relative z-10 h-6 w-6 text-zinc-50 hover:bg-zinc-700 hover:text-zinc-50",
className,
)}
>
{hasCopied ? (
<CheckIcon className="h-3 w-3" />
) : (
<ClipboardIcon className="h-3 w-3" />
)}
<span className="sr-only">Copy</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => copyCommand("npm")}>npm</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}

View File

@ -0,0 +1,133 @@
"use client"
import {
type ColumnDef,
flexRender,
getCoreRowModel,
getPaginationRowModel,
type ColumnFiltersState,
getFilteredRowModel,
getSortedRowModel, type SortingState,
useReactTable,
} from "@tanstack/react-table";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"
import {Button} from "@/components/ui/button";
import {useState} from "react";
import {Input} from "@/components/ui/input";
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[]
data: TData[]
fieldToFilter: string
}
export function DataTable<TData, TValue>({
columns,
data,
fieldToFilter,
}: DataTableProps<TData, TValue>) {
const [sorting, setSorting] = useState<SortingState>([])
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>(
[]
)
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
onSortingChange: setSorting,
getSortedRowModel: getSortedRowModel(),
onColumnFiltersChange: setColumnFilters,
getFilteredRowModel: getFilteredRowModel(),
state: {
sorting,
columnFilters,
},
})
return (
<div className={"w-full"}>
<div className="flex items-center py-4">
<Input
placeholder={`Filter ${fieldToFilter}...`}
value={(table.getColumn(fieldToFilter)?.getFilterValue() as string) ?? ""}
onChange={(event) =>
table.getColumn(fieldToFilter)?.setFilterValue(event.target.value)
}
className="max-w-sm"
/>
</div>
<div className="rounded-md border w-full text-md">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id} className={"font-bold text-lg text-center"}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
)
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id} className={"text-center"}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
<div className="flex items-center justify-end space-x-2 py-4">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div>
</div>
)
}

View File

@ -10,7 +10,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
<input <input
type={type} type={type}
className={cn( className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium 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", "flex h-10 w-full rounded-md border border-input bg-accent px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium 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",
className, className,
)} )}
ref={ref} ref={ref}

View File

@ -1,4 +1,4 @@
import { ITrade, type IUserWalletCryptos } from "@/interfaces/crypto.interface"; import {type ICryptoInWalletInfo, ITrade, type IUserWalletCryptos} from "@/interfaces/crypto.interface";
import type { IUserData } from "@/interfaces/userdata.interface"; import type { IUserData } from "@/interfaces/userdata.interface";
// ----- Request ----- // ----- Request -----
@ -18,10 +18,33 @@ export interface IApiLoginReq {
password: string; password: string;
} }
export interface IApiTradeCreateRq {
id_offer: string;
}
export interface IApiOfferCreateReq {
id_crypto: string;
amount: number;
}
export interface IApiCreateReferralCodeReq {
name: string;
value: number;
}
export interface IApiDoTradeReq {
id_offer: string;
}
export interface IApiDoOfferReq {
id_crypto: string;
amount: number;
}
// ----- Response ----- // ----- Response -----
export interface IAbstractApiResponse { export interface IAbstractApiResponse {
message?: Array<string>; message?: Array<string> | string;
error?: string; error?: string;
statusCode?: number; statusCode?: number;
} }
@ -43,4 +66,25 @@ export interface IApiUserAssetsRes extends IAbstractApiResponse {
UserHasCrypto?: IUserWalletCryptos[]; UserHasCrypto?: IUserWalletCryptos[];
} }
export interface IApiAllTrades extends IAbstractApiResponse {} export interface IApiAllTradesRes extends IAbstractApiResponse {}
export interface IAllRankRes extends IAbstractApiResponse {}
export interface IAllReferralCodeRes extends IAbstractApiResponse {}
export interface ICreateReferralCodeRes extends IAbstractApiResponse {}
export interface IReferralCodeUpdateRes extends IAbstractApiResponse {}
export interface IReferralCodeDeleteRes extends IAbstractApiResponse {}
export interface IApiAllOffersRes extends IAbstractApiResponse {
id: string
User: {
pseudo: string
}
amount: number
created_at: string
id_user: string
Crypto: ICryptoInWalletInfo
}

View File

@ -1,5 +1,6 @@
export interface IUserWalletCryptos { export interface IUserWalletCryptos {
Crypto?: ICryptoInWalletInfo; Crypto: ICryptoInWalletInfo;
amount: number;
} }
export interface ICryptoInWalletInfo { export interface ICryptoInWalletInfo {
@ -12,6 +13,10 @@ export interface ICryptoInWalletInfo {
updated_at: string; updated_at: string;
} }
export interface ICryptoInUserWalletInfo extends ICryptoInWalletInfo {
owned_amount: number;
}
export type IAllTrades = ITrade[]; export type IAllTrades = ITrade[];
export interface ITrade { export interface ITrade {

View File

@ -0,0 +1,13 @@
export interface IStandardisedReturn<T> {
state: EReturnState;
message?: string;
resolved?: T;
}
export enum EReturnState {
unauthorized = 0,
clientError = 1,
serverError = 2,
done = 3,
queued = 4,
}

View File

@ -1,3 +1,9 @@
import {
type ICryptoInUserWalletInfo,
type ICryptoInWalletInfo,
IUserWalletCryptos,
} from "@/interfaces/crypto.interface";
export interface IUserData { export interface IUserData {
id: string; id: string;
firstName: string; firstName: string;
@ -11,4 +17,12 @@ export interface IUserData {
age: number; age: number;
created_at: string; created_at: string;
updated_at: string; updated_at: string;
//TODO get on register
wallet: IUserWallet;
}
export interface IUserWallet {
uat: number;
update_interval: number;
owned_cryptos: ICryptoInUserWalletInfo[];
} }

View File

@ -1,73 +1,53 @@
"use client"; "use client";
import type { import type {
IApiLoginReq, IAbstractApiResponse,
IApiLoginRes, IAllReferralCodeRes,
IApiRegisterReq, IApiAllTradesRes, IApiDoTradeReq,
IApiRegisterRes, IApiUserAssetsRes,
ICreateReferralCodeRes,
} from "@/interfaces/api.interface"; } from "@/interfaces/api.interface";
import type { IUserData } from "@/interfaces/userdata.interface"; import {ICryptoInWalletInfo, IUserWalletCryptos} from "@/interfaces/crypto.interface";
import { EReturnState, type IStandardisedReturn } from "@/interfaces/general.interface";
import type { IUserData, IUserWallet } from "@/interfaces/userdata.interface";
import ApiRequest from "@/services/apiRequest"; import ApiRequest from "@/services/apiRequest";
import { useEncodedLocalStorage } from "@/services/localStorage"; import type { Dispatch, SetStateAction } from "react";
import { createContext, useContext, useState } from "react"; import {AxiosResponse} from "axios";
const UserDataContext = createContext<IUserData | null>(null);
const [userData, setUserData] = useEncodedLocalStorage<IUserData | null>(
"user_data",
null,
);
//TODO Run register task
export async function doRegister(
registerData: IApiRegisterReq,
): Promise<IApiRegisterRes | null> {
console.trace(registerData);
try {
const ReqRes = await ApiRequest.standard.post.json<IApiRegisterReq, IApiRegisterRes>(
"auth/signup",
registerData,
);
console.trace(ReqRes.data);
if (ReqRes.data.user) {
setUserData(ReqRes.data.user);
}
ReqRes.data.message?.forEach((err) => console.warn(err));
return ReqRes.data;
} catch (error) {
console.error("Error during registration:", error);
return null;
}
}
//TODO Run login task
export async function doLogin(loginData: IApiLoginReq) {
try {
const ReqRes = await ApiRequest.standard.post.json<IApiLoginReq, IApiLoginRes>(
"auth/login",
loginData,
);
console.trace(ReqRes.data);
//if (ReqRes.data.user) {
// setUserData(ReqRes.data.user)
//}
ReqRes.data.message?.forEach((err) => console.warn(err));
return ReqRes.data;
} catch (err) {
console.error("Error during login:", err);
return null;
}
}
//TODO Run disconnect task //TODO Run disconnect task
export function doDisconnect() { export function doDisconnect() {
if (typeof window !== "undefined") { if (typeof window !== "undefined") {
window.localStorage.removeItem("sub"); window.localStorage.removeItem("sub");
//Redirect to homepage
window.location.href = "/";
return true; return true;
} }
console.log( console.log(
"Whut ? Why trying to remove an item from the localStorage when runner in SSR ?", "Whut ? Why trying to remove an item from the localStorage when running in SSR ?",
); );
return false; return false;
} }
//TODO Run update user data export 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,
};
}
}

View File

@ -10,7 +10,7 @@ const AxiosConfigs = {
return { return {
headers: { headers: {
"content-type": "application/json", "content-type": "application/json",
Authorization: `Bearer ${typeof window !== "undefined" ? window.localStorage.getItem("sub") : "not-ssr"}`, Authorization: `Bearer ${typeof window !== "undefined" ? JSON.parse(window.localStorage.getItem("sub") || "not-ssr") : "not-ssr"}`,
}, },
validateStatus: (status: number) => { validateStatus: (status: number) => {
return status < 500; // Resolve only if the status code is less than 500 return status < 500; // Resolve only if the status code is less than 500

400
temp.ts Normal file
View File

@ -0,0 +1,400 @@
////////apiTypes.ts
export interface ResponseSuccess {
data: any
status: number
statusText: string
}
export interface ResponseFailed {
code: string
message: string
name: string
response: {
data: {
error: string
message: string
statusCode: number
}
status: number
statusText: string
}
}
///////////////cryptoTypes.ts
export enum RoleName {
user = 'user',
admin = 'admin',
}
export type Role = {
id: string
name: RoleName
created_at?: string
updated_at?: string
}
export type PromoCode = {
id: string
name: string
value: number
}
export type CryptoHistory = {
id: string
id_crypto: string
value: number
created_at: string
updated_at: string
}
export interface Offer {
id: string
User: {
pseudo: string
}
amount: number
created_at: string
id_user: string
Crypto: CryptoData
}
export interface UserAssets {
firstName: string
lastName: string
dollarAvailables: number
pseudo: string
age: number
UserHasCrypto: CryptoData[]
}
export interface Signin {
access_token: string
user: UserExtended
Role: Role
}
export interface CryptoData {
id: string
name: string
value: number
image: string
quantity: number
created_at: string
updated_at: string
}
export interface MyCryptoData {
Crypto: CryptoData
amount: number
id: string
}
export interface Trade {
Giver: User
Receiver: User
Crypto: CryptoData
id: string
}
export interface User {
firstName: string
lastName: string
pseudo: string
dollarAvailables: number
}
export interface UserHasCrypto {
id: string
id_user: string
id_crypto: string
amount: number
createdAt: string
updated_at: string
Crypto: CryptoData
}
export interface UserExtended extends User {
id: string
hash: string
email: string
roleId: string
isActive: boolean
city: string
age: number
created_at: string
updated_at: string
UserHasCrypto?: UserHasCrypto
}
export interface MyTrade {
id: string
id_giver: string
id_receiver: string
id_crypto: string
amount_traded: number
created_at: string
updated_at: string
Crypto: CryptoData
Giver: UserExtended
Receiver: UserExtended
}
export interface AuthData {
access_token: string
user: {
id: string
firstName: string
lastName: string
pseudo: string
hash: null | any
email: string
roleId: string
isActive: boolean
city: string
dollarAvailables: number
age: number
created_at: string
updated_at: string
UserHasCrypto: UserHasCrypto[]
Role: Role
}
}
////////////formTypes.ts
export type RegisterInput = {
firstName: string
lastName: string
pseudo: string
city: string
email: string
password: string
confirmPassword: string
promoCode: string
age: number
}
export type LoginInput = {
email: string
password: string
}
export type RoleInput = {
name: string
}
export type PromoCodeInput = {
name: string
value: number
}
export type TradeInput = {
id_offer: string
}
export type OfferInput = {
id_crypto: string
amount: number
}