diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b8..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -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 diff --git a/.idea/discord.xml b/.idea/discord.xml deleted file mode 100644 index d8e9561..0000000 --- a/.idea/discord.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index c53b70e..0000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 8409330..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/neptune-frontend.iml b/.idea/neptune-frontend.iml deleted file mode 100644 index 24643cc..0000000 --- a/.idea/neptune-frontend.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index 2251898..afb73b1 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,16 @@ # neptune-front -The project to validate my DWWM diploma on the front end. \ No newline at end of file +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 +``` \ No newline at end of file diff --git a/package.json b/package.json index aff2bef..709e093 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx index c4d7a1d..e368c31 100644 --- a/src/app/dashboard/page.tsx +++ b/src/app/dashboard/page.tsx @@ -10,7 +10,6 @@ import { useEffect, useState } from "react"; export default function DashboardPage() { const [isLoading, setIsLoading] = useState(true); const [cryptosList, setCryptosList] = useState([]); - //FIX the loop useEffect(() => { ApiRequest.authenticated.get diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 8177e39..2c6f857 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -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({ diff --git a/src/app/page.tsx b/src/app/page.tsx index 93dfcba..fae29c9 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -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 (
-
+
-

+

Welcome to Neptune Crypto

@@ -26,27 +26,28 @@ export default function HomePage() {

+

Our Features

- + Multiple Cryptocurrencies Trade a wide variety of popular cryptocurrencies. - + Secure Storage Your assets are protected with state-of-the-art security measures. - + Advanced Trading Tools Access powerful analytics and trading features. @@ -54,24 +55,25 @@ export default function HomePage() {
+

Start Trading Today

-

+

Join thousands of traders and investors on our platform. Get started with as little as $10.

- +

By signing up, you agree to our{" "} - - Terms & Conditions + + Legal Notice

@@ -80,5 +82,5 @@ export default function HomePage() {
- ) + ); } \ No newline at end of file diff --git a/src/app/sell/[cryptoId]/page.tsx b/src/app/sell/[cryptoId]/page.tsx new file mode 100644 index 0000000..0683167 --- /dev/null +++ b/src/app/sell/[cryptoId]/page.tsx @@ -0,0 +1,39 @@ +"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) { + return ( +
+
+ {/* Graph demande achat/you/other */} +
+ Graph +
+ {/* stats */} +
+ Stats +
+
+
+ {/* Formulaire */} +
+ Formulaire + +
+ {/* Tab Liste offfres vente existante */} + +
+
+ ); +} \ No newline at end of file diff --git a/src/app/wallet/page.tsx b/src/app/wallet/page.tsx index 5c30e29..b3ee94e 100644 --- a/src/app/wallet/page.tsx +++ b/src/app/wallet/page.tsx @@ -14,7 +14,6 @@ export default function WalletPage() { const [isLoading, setIsLoading] = useState(true); const [cryptosList, setCryptosList] = useState([]); const userContext = useContext(UserDataContext); - //FIX the loop useEffect(() => { console.log(userContext?.userData); diff --git a/src/components/auth-form.tsx b/src/components/auth-form.tsx index 5ceac58..982e56c 100644 --- a/src/components/auth-form.tsx +++ b/src/components/auth-form.tsx @@ -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() { diff --git a/src/components/auto-form/utils.ts b/src/components/auto-form/utils.ts index 4e72616..eaf3af2 100644 --- a/src/components/auto-form/utils.ts +++ b/src/components/auto-form/utils.ts @@ -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 | z.ZodEffects>; diff --git a/src/components/cryptos/sell-modal.tsx b/src/components/cryptos/sell-modal.tsx new file mode 100644 index 0000000..33dc998 --- /dev/null +++ b/src/components/cryptos/sell-modal.tsx @@ -0,0 +1,10 @@ +import type {Row} from "@tanstack/react-table"; +import type {ICryptoInUserWalletInfo} from "@/interfaces/crypto.interface"; + + +interface SellModalProps { + row: Row +} +export function SellModal({ row }: SellModalProps) { + +} \ No newline at end of file diff --git a/src/components/data-tables/wallet-table.tsx b/src/components/data-tables/wallet-table.tsx index 931b200..6efbb8c 100644 --- a/src/components/data-tables/wallet-table.tsx +++ b/src/components/data-tables/wallet-table.tsx @@ -10,15 +10,11 @@ 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 { useRouter } from "next/navigation"; -import { useState } from "react"; interface DataTableProps { columns: ColumnDef[]; diff --git a/src/components/header.tsx b/src/components/header.tsx index 4da4885..e589ebe 100644 --- a/src/components/header.tsx +++ b/src/components/header.tsx @@ -14,8 +14,8 @@ export function Header({ "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" } > - - {"Logo + + {"Logo

{title || "Neptune"}

diff --git a/src/components/sell-form.tsx b/src/components/sell-form.tsx new file mode 100644 index 0000000..fcd1c44 --- /dev/null +++ b/src/components/sell-form.tsx @@ -0,0 +1,187 @@ +"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"; + +export function SellForm() { + const [currentWallet, setCurrentWallet] = useState(); + const [sliderValue, setSliderValue] = useState(0); + const [quantity, setQuantity] = useState(0); + const [selectedCrypto, setSelectedCrypto] = useState(); + + useEffect(() => { + async function fetchAssets() { + const res = await ApiRequest.authenticated.get.json("user/my-assets"); + setCurrentWallet(res.data.UserHasCrypto); + } + fetchAssets(); + }, []); + + 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) { + 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: ( +
+            {JSON.stringify(res.statusText, null, 2)}
+          
+ ), + }); + return; + } + + console.log(res); + toast({ + title: "Transaction accepted.", + description:

The page is going to reload.

, + }); + setTimeout(() => location.reload(), 1_500); + + toast({ + title: "You submitted the following values:", + description: ( +
+          {JSON.stringify(data, null, 2)}
+        
+ ), + }); + } + + const sellToUserForm = useForm>({ + resolver: zodResolver(sellToUserSchema), + }); + + if (!currentWallet) { + return ( + +
+

Loading...

+
+
+ ); + } + + return ( +
+ + ( + { + // @ts-ignore + const maxShares = getMaxSharesForCrypto(event.target.value as string); + setSliderValue(maxShares); + setQuantity(maxShares); + // @ts-ignore + setSelectedCrypto(event.target.value as string); + }} + > + Sell shares of your wallet + + + + )} + /> + ( + + )} + /> + {selectedCrypto && ( +
+

{getCryptoName(selectedCrypto)}

+ +

{quantity}

+ + + {(quantity * getCryptoValue(selectedCrypto)).toLocaleString("en-US")} + + +
+ )} + + + + ); +} \ No newline at end of file diff --git a/src/components/sub/GraphSellOffer.tsx b/src/components/sub/GraphSellOffer.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/sub/OfferList.tsx b/src/components/sub/OfferList.tsx new file mode 100644 index 0000000..72a7a66 --- /dev/null +++ b/src/components/sub/OfferList.tsx @@ -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 ( + {" "} + {" "} + {"No history..."} + {" "} + ); + + return data.map((item, index) => ( + + {item.Giver.pseudo} + {item.Receiver.pseudo} + {item.Crypto.quantity} + ${item.Crypto.value} + + )); +} + +export function OfferList({ className, trades}: OfferListProps) { + return (
+ + A list of recent sell offers. + + + Seller + Buyer + Crypto share amount + $ + + + + + +
+
) +} \ No newline at end of file diff --git a/src/interfaces/userdata.interface.ts b/src/interfaces/userdata.interface.ts index 1575221..3d5c643 100644 --- a/src/interfaces/userdata.interface.ts +++ b/src/interfaces/userdata.interface.ts @@ -15,7 +15,6 @@ export interface IUserData { dollarAvailables: number; created_at: string; updated_at: string; - //TODO get on register wallet: IUserWallet; }