diff --git a/.gitignore b/.gitignore
index 5ef6a52..fcadec0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -39,3 +39,6 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
+
+package-lock.json
+bun.lock
diff --git a/app/globals.css b/app/globals.css
index a2dc41e..4a8ef3b 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -1,5 +1,7 @@
@import "tailwindcss";
+@custom-variant dark (&:where(.dark, .dark *));
+
:root {
--background: #ffffff;
--foreground: #171717;
@@ -8,8 +10,54 @@
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
- --font-sans: var(--font-geist-sans);
- --font-mono: var(--font-geist-mono);
+ --font-sans: var(--font-inter);
+ --font-display: var(--font-anton);
+
+ /* Custom Colors */
+ --color-primary: #3B82F6;
+ --color-background-light: #FDFDFD;
+ --color-background-dark: #050505;
+ --color-slush-yellow: #FFD600;
+ --color-slush-orange: #FF5C00;
+ --color-slush-purple: #9747FF;
+ --color-slush-green: #00D68F;
+ --color-slush-dark: #1A1A1A;
+
+ /* Border Radius */
+ --radius: 0.5rem;
+ --radius-large: 2rem;
+
+ /* Animations */
+ --animate-marquee: marquee 25s linear infinite;
+ --animate-marquee-reverse: marquee-reverse 25s linear infinite;
+ --animate-float: float 6s ease-in-out infinite;
+}
+
+@keyframes marquee {
+ 0% {
+ transform: translateX(0%);
+ }
+ 100% {
+ transform: translateX(-100%);
+ }
+}
+
+@keyframes marquee-reverse {
+ 0% {
+ transform: translateX(-100%);
+ }
+ 100% {
+ transform: translateX(0%);
+ }
+}
+
+@keyframes float {
+ 0%, 100% {
+ transform: translateY(0);
+ }
+ 50% {
+ transform: translateY(-20px);
+ }
}
@media (prefers-color-scheme: dark) {
@@ -22,5 +70,98 @@
body {
background: var(--background);
color: var(--foreground);
- font-family: Arial, Helvetica, sans-serif;
+ font-family: var(--font-inter), Arial, Helvetica, sans-serif;
+}
+
+.text-outline-light {
+ -webkit-text-stroke: 2px black;
+ color: transparent;
+}
+
+.text-outline-dark {
+ -webkit-text-stroke: 2px white;
+ color: transparent;
+}
+
+::-webkit-scrollbar {
+ width: 8px;
+}
+
+::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+::-webkit-scrollbar-thumb {
+ background: #888;
+ border-radius: 4px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: #555;
+}
+
+/* Custom Sui Wallet Button Styles */
+.sui-wallet-button button {
+ background-color: #000;
+ color: #fff;
+ padding: 0.5rem 1.25rem;
+ border-radius: 9999px;
+ font-weight: 700;
+ font-size: 0.875rem;
+ text-transform: uppercase;
+ transition: transform 0.2s;
+ border: none;
+ cursor: pointer;
+}
+
+.sui-wallet-button button:hover {
+ transform: scale(1.05);
+}
+
+:is(.dark *) .sui-wallet-button button {
+ background-color: #fff;
+ color: #000;
+}
+
+/* Style cho account menu dropdown */
+[data-radix-popper-content-wrapper] {
+ z-index: 9999 !important;
+}
+
+/* Style cho các nút trong account menu (Copy, Disconnect, v.v.) */
+.dapp-kit-account-dropdown-menu-item,
+[role="menuitem"] {
+ padding: 0.75rem 1rem !important;
+ font-size: 0.875rem !important;
+ font-weight: 600 !important;
+ text-transform: uppercase !important;
+ transition: all 0.2s !important;
+ border-radius: 0.5rem !important;
+ margin: 0.25rem !important;
+}
+
+.dapp-kit-account-dropdown-menu-item:hover,
+[role="menuitem"]:hover {
+ background-color: #f3f4f6 !important;
+ transform: translateX(4px) !important;
+}
+
+:is(.dark *) .dapp-kit-account-dropdown-menu-item:hover,
+:is(.dark *) [role="menuitem"]:hover {
+ background-color: #374151 !important;
+}
+
+/* Style cho dropdown menu container */
+[role="menu"] {
+ background: white !important;
+ border: 2px solid #e5e7eb !important;
+ border-radius: 1rem !important;
+ padding: 0.5rem !important;
+ box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1) !important;
+ min-width: 200px !important;
+}
+
+:is(.dark *) [role="menu"] {
+ background: #1f2937 !important;
+ border-color: #374151 !important;
}
diff --git a/app/layout.tsx b/app/layout.tsx
index f7fa87e..077172e 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -1,20 +1,24 @@
import type { Metadata } from "next";
-import { Geist, Geist_Mono } from "next/font/google";
+import { Anton, Inter } from "next/font/google";
+import { SuiProviders } from "@/components/SuiProviders";
import "./globals.css";
+import "@mysten/dapp-kit/dist/index.css";
-const geistSans = Geist({
- variable: "--font-geist-sans",
+const anton = Anton({
+ weight: "400",
+ variable: "--font-anton",
subsets: ["latin"],
});
-const geistMono = Geist_Mono({
- variable: "--font-geist-mono",
+const inter = Inter({
+ weight: ["400", "600", "800"],
+ variable: "--font-inter",
subsets: ["latin"],
});
export const metadata: Metadata = {
- title: "Create Next App",
- description: "Generated by create next app",
+ title: "Data Exchange - Unlock Your Data",
+ description: "Monetize your insights securely. Built on Walrus, Seal, and Nautilus for unstoppable decentralized data trading.",
};
export default function RootLayout({
@@ -23,11 +27,36 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
-
+
+
+
-
-
-
-
- To get started, edit the page.tsx file.
-
-
- Looking for a starting point or more instructions? Head over to{" "}
-
- Templates
- {" "}
- or the{" "}
-
- Learning
- {" "}
- center.
-
-
-
-
+
+
+
+
+
+
+
+
);
}
diff --git a/bun.lock b/bun.lock
index 9badfd0..50605bd 100644
--- a/bun.lock
+++ b/bun.lock
@@ -1,5 +1,6 @@
{
"lockfileVersion": 1,
+ "configVersion": 0,
"workspaces": {
"": {
"name": "datex",
diff --git a/components/CardsSection.tsx b/components/CardsSection.tsx
new file mode 100644
index 0000000..4cf0907
--- /dev/null
+++ b/components/CardsSection.tsx
@@ -0,0 +1,64 @@
+export default function CardsSection() {
+ return (
+
+
+ Data for humans.
+
+ Not just bots.
+
+
+
+
+ Frictionless
+
+ Onboarding
+
+
+
+
+
+
+ swipe
+
+
+
+
+
+ For Data
+
+ Power Users
+
+
+
+
+
+
+ analytics
+
+
+
+
+
+ For
+
+ Developers
+
+
+
+
+
+
+ code
+
+
+
+
+
+ );
+}
diff --git a/components/FeaturesSection.tsx b/components/FeaturesSection.tsx
new file mode 100644
index 0000000..5673d77
--- /dev/null
+++ b/components/FeaturesSection.tsx
@@ -0,0 +1,139 @@
+export default function FeaturesSection() {
+ return (
+
+
+
+ Your Shortcut to
+ DeFi Data
+
+
+
+
+
+
+ Explore
+
+ Data
+
+ Opportunities
+
+
+ Discover vetted datasets to put your algorithms to work through
+ Sui DeFi protocols.
+
+
+
+
+

+
+
+ monetization_on
+
+
+
+
+
+ touch_app
+
+
+ Simple, Direct
+
+ Execution
+
+
+ Buy and sell insights in a few taps. Withdraw rewards in the
+ asset you choose.
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+ Powered by Sui
+
+
+
+ Built for
+ Speed & Scale
+
+
+ Leveraging the power of Nautilus for transaction speed, Walrus
+ for decentralized storage, and Seal for top-tier encryption.
+
+
+
+ water
+ Walrus
+
+
+
+ verified_user
+
+ Seal
+
+
+
+ rocket_launch
+
+ Nautilus
+
+
+
+
+
+
+
+
+ cloud_queue
+
+
+ Storage
+
+
+
+
+ enhanced_encryption
+
+
+ Security
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/components/Footer.tsx b/components/Footer.tsx
new file mode 100644
index 0000000..1d3ee61
--- /dev/null
+++ b/components/Footer.tsx
@@ -0,0 +1,83 @@
+export default function Footer() {
+ return (
+
+ );
+}
diff --git a/components/Hero.tsx b/components/Hero.tsx
new file mode 100644
index 0000000..44ae221
--- /dev/null
+++ b/components/Hero.tsx
@@ -0,0 +1,60 @@
+export default function Hero() {
+ return (
+
+ );
+}
diff --git a/components/MarqueeSection.tsx b/components/MarqueeSection.tsx
new file mode 100644
index 0000000..65bffc9
--- /dev/null
+++ b/components/MarqueeSection.tsx
@@ -0,0 +1,49 @@
+interface MarqueeSectionProps {
+ variant?: "default" | "reverse";
+ text: string;
+ bgColor?: string;
+}
+
+export default function MarqueeSection({
+ variant = "default",
+ text,
+ bgColor = "bg-black",
+}: MarqueeSectionProps) {
+ const animationClass =
+ variant === "reverse" ? "animate-marquee-reverse" : "animate-marquee";
+
+ return (
+
+
+
+ {text}
+
+
+ {text}
+
+
+ {text}
+
+
+ {text}
+
+
+ {text}
+
+
+ {text}
+
+
+ {text}
+
+
+ {text}
+
+
+
+ );
+}
diff --git a/components/Navbar.tsx b/components/Navbar.tsx
new file mode 100644
index 0000000..7aa7cc7
--- /dev/null
+++ b/components/Navbar.tsx
@@ -0,0 +1,32 @@
+"use client";
+
+import { WalletConnectButton } from "./WalletConnectButton";
+
+export default function Navbar() {
+ return (
+
+ );
+}
diff --git a/components/SuiProviders.tsx b/components/SuiProviders.tsx
new file mode 100644
index 0000000..956fea5
--- /dev/null
+++ b/components/SuiProviders.tsx
@@ -0,0 +1,34 @@
+"use client";
+
+import { createNetworkConfig, SuiClientProvider, WalletProvider } from "@mysten/dapp-kit";
+import { getFullnodeUrl } from "@mysten/sui/client";
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+import { ReactNode, useState } from "react";
+
+// Config cho testnet
+const { networkConfig } = createNetworkConfig({
+ testnet: { url: getFullnodeUrl("testnet") },
+ mainnet: { url: getFullnodeUrl("mainnet") },
+});
+
+export function SuiProviders({ children }: { children: ReactNode }) {
+ // Tạo QueryClient trong component để tránh shared state giữa requests
+ const [queryClient] = useState(() => new QueryClient({
+ defaultOptions: {
+ queries: {
+ refetchOnWindowFocus: false,
+ retry: false,
+ },
+ },
+ }));
+
+ return (
+
+
+
+ {children}
+
+
+
+ );
+}
diff --git a/components/WalletConnectButton.tsx b/components/WalletConnectButton.tsx
new file mode 100644
index 0000000..eaa6e95
--- /dev/null
+++ b/components/WalletConnectButton.tsx
@@ -0,0 +1,26 @@
+"use client";
+
+import { ConnectButton, useCurrentAccount } from "@mysten/dapp-kit";
+
+export function WalletConnectButton() {
+ const account = useCurrentAccount();
+
+ // Format địa chỉ: 0x1234...5678
+ const formatAddress = (address: string) => {
+ return `${address.slice(0, 6)}...${address.slice(-4)}`;
+ };
+
+ return (
+
+ {account && (
+
+ account_balance_wallet
+ {formatAddress(account.address)}
+
+ )}
+
+ );
+}
diff --git a/next.config.ts b/next.config.ts
index e9ffa30..5a2c26e 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -1,7 +1,14 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
- /* config options here */
+ images: {
+ remotePatterns: [
+ {
+ protocol: "https",
+ hostname: "lh3.googleusercontent.com",
+ },
+ ],
+ },
};
export default nextConfig;
diff --git a/package.json b/package.json
index af699d6..64ff2ad 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,9 @@
"lint": "eslint"
},
"dependencies": {
+ "@mysten/dapp-kit": "^0.20.0",
+ "@mysten/sui": "^1.45.2",
+ "@tanstack/react-query": "^5.90.18",
"next": "16.1.3",
"react": "19.2.3",
"react-dom": "19.2.3"
diff --git a/tailwind.config.ts b/tailwind.config.ts
new file mode 100644
index 0000000..3cd444f
--- /dev/null
+++ b/tailwind.config.ts
@@ -0,0 +1,54 @@
+import type { Config } from "tailwindcss";
+
+const config: Config = {
+ darkMode: "class",
+ content: [
+ "./pages/**/*.{js,ts,jsx,tsx,mdx}",
+ "./components/**/*.{js,ts,jsx,tsx,mdx}",
+ "./app/**/*.{js,ts,jsx,tsx,mdx}",
+ ],
+ theme: {
+ extend: {
+ colors: {
+ primary: "#3B82F6", // Electric Blue
+ "background-light": "#FDFDFD",
+ "background-dark": "#050505",
+ "slush-yellow": "#FFD600",
+ "slush-orange": "#FF5C00",
+ "slush-purple": "#9747FF",
+ "slush-green": "#00D68F",
+ "slush-dark": "#1A1A1A",
+ },
+ fontFamily: {
+ display: ["var(--font-anton)", "sans-serif"],
+ body: ["var(--font-inter)", "sans-serif"],
+ },
+ borderRadius: {
+ DEFAULT: "0.5rem",
+ large: "2rem",
+ },
+ animation: {
+ marquee: "marquee 25s linear infinite",
+ "marquee-reverse": "marquee-reverse 25s linear infinite",
+ float: "float 6s ease-in-out infinite",
+ },
+ keyframes: {
+ marquee: {
+ "0%": { transform: "translateX(0%)" },
+ "100%": { transform: "translateX(-100%)" },
+ },
+ "marquee-reverse": {
+ "0%": { transform: "translateX(-100%)" },
+ "100%": { transform: "translateX(0%)" },
+ },
+ float: {
+ "0%, 100%": { transform: "translateY(0)" },
+ "50%": { transform: "translateY(-20px)" },
+ },
+ },
+ },
+ },
+ plugins: [],
+};
+
+export default config;