mirror of
https://github.com/0xShay/ticketchain.git
synced 2026-01-11 13:13:25 +00:00
resolve merge conflicts
This commit is contained in:
@@ -1,2 +1,2 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
npx lint-staged && npm run build
|
npx lint-staged && npm run build
|
||||||
|
|||||||
5
.idea/.gitignore
generated
vendored
Normal file
5
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
12
.idea/Event-Chain.iml
generated
Normal file
12
.idea/Event-Chain.iml
generated
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
65
.idea/codeStyles/Project.xml
generated
Normal file
65
.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<HTMLCodeStyleSettings>
|
||||||
|
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
||||||
|
</HTMLCodeStyleSettings>
|
||||||
|
<JSCodeStyleSettings version="0">
|
||||||
|
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||||
|
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||||
|
<option name="USE_DOUBLE_QUOTES" value="false" />
|
||||||
|
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||||
|
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
|
||||||
|
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||||
|
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||||
|
</JSCodeStyleSettings>
|
||||||
|
<JetCodeStyleSettings>
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
</JetCodeStyleSettings>
|
||||||
|
<TypeScriptCodeStyleSettings version="0">
|
||||||
|
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||||
|
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||||
|
<option name="USE_DOUBLE_QUOTES" value="false" />
|
||||||
|
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||||
|
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
|
||||||
|
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||||
|
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||||
|
</TypeScriptCodeStyleSettings>
|
||||||
|
<VueCodeStyleSettings>
|
||||||
|
<option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
|
||||||
|
<option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
|
||||||
|
</VueCodeStyleSettings>
|
||||||
|
<codeStyleSettings language="HTML">
|
||||||
|
<option name="SOFT_MARGINS" value="80" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="INDENT_SIZE" value="2" />
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||||
|
<option name="TAB_SIZE" value="2" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="JavaScript">
|
||||||
|
<option name="SOFT_MARGINS" value="80" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="INDENT_SIZE" value="2" />
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||||
|
<option name="TAB_SIZE" value="2" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="TypeScript">
|
||||||
|
<option name="SOFT_MARGINS" value="80" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="INDENT_SIZE" value="2" />
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||||
|
<option name="TAB_SIZE" value="2" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="Vue">
|
||||||
|
<option name="SOFT_MARGINS" value="80" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="kotlin">
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
</codeStyleSettings>
|
||||||
|
</code_scheme>
|
||||||
|
</component>
|
||||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||||
|
</state>
|
||||||
|
</component>
|
||||||
6
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
6
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/Event-Chain.iml" filepath="$PROJECT_DIR$/.idea/Event-Chain.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/prettier.xml
generated
Normal file
6
.idea/prettier.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="PrettierConfiguration">
|
||||||
|
<option name="myConfigurationMode" value="AUTOMATIC" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
79
app/Home.tsx
Normal file
79
app/Home.tsx
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
'use client';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import Header from '../components/custom/header';
|
||||||
|
import Footer from '../components/custom/footer';
|
||||||
|
import Test from '../components/scripts/Test';
|
||||||
|
import MetaMask from '../components/scripts/MetaMask';
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
const [isClient, setIsClient] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsClient(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header />
|
||||||
|
<div className="relative min-h-screen overflow-hidden">
|
||||||
|
{/* Video Background */}
|
||||||
|
{isClient && (
|
||||||
|
<video
|
||||||
|
autoPlay
|
||||||
|
loop
|
||||||
|
muted
|
||||||
|
className="absolute inset-0 w-full h-full object-cover z-0"
|
||||||
|
src="BGVid2.mp4"
|
||||||
|
>
|
||||||
|
Your browser does not support the video tag.
|
||||||
|
</video>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Dark Overlay for Enhanced Readability */}
|
||||||
|
<div className="absolute inset-0 bg-black opacity-50 z-10"></div>
|
||||||
|
|
||||||
|
{/* Page Content Over the Video */}
|
||||||
|
<div className="relative z-20 min-h-screen bg-gradient-to-b from-transparent to-gray-900 pt-16">
|
||||||
|
<div className="container mx-auto p-4">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search events..."
|
||||||
|
className="search-bar mt-4 p-2 border border-gray-300 rounded w-full max-w-md mx-auto"
|
||||||
|
/>
|
||||||
|
<main>
|
||||||
|
<section className="mb-8">
|
||||||
|
<h2 className="text-2xl font-semibold text-white mb-4">
|
||||||
|
Featured Events
|
||||||
|
</h2>
|
||||||
|
<p className="text-gray-300">
|
||||||
|
No events available at the moment.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<h2 className="text-2xl font-semibold text-white mb-4">
|
||||||
|
Upcoming Events
|
||||||
|
</h2>
|
||||||
|
<ul className="list-disc list-inside text-gray-300">
|
||||||
|
<li>Event 1 - Date</li>
|
||||||
|
<li>Event 2 - Date</li>
|
||||||
|
<li>Event 3 - Date</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<section className="mb-8">
|
||||||
|
<Test />
|
||||||
|
</section>
|
||||||
|
<section className="mb-8">
|
||||||
|
<MetaMask />
|
||||||
|
</section>
|
||||||
|
<Footer />
|
||||||
|
</main>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
149
app/TicketListings/TicketListings.tsx
Normal file
149
app/TicketListings/TicketListings.tsx
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
'use client';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import Header from '../../components/custom/header';
|
||||||
|
import Footer from '../../components/custom/footer';
|
||||||
|
|
||||||
|
// Define the Event interface including new fields
|
||||||
|
interface Event {
|
||||||
|
EventID: number;
|
||||||
|
name: string;
|
||||||
|
date: string;
|
||||||
|
location: string;
|
||||||
|
ticketPrice: string;
|
||||||
|
description: string;
|
||||||
|
capacity: number;
|
||||||
|
ticketsSold: number;
|
||||||
|
imageUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dummy function to fetch events
|
||||||
|
const fetchEvents = (): Event[] => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
EventID: 1,
|
||||||
|
name: 'Rock Concert',
|
||||||
|
date: '2023-12-01',
|
||||||
|
location: 'New York City',
|
||||||
|
ticketPrice: '$99',
|
||||||
|
description: 'An exhilarating rock concert featuring famous bands.',
|
||||||
|
capacity: 5000,
|
||||||
|
ticketsSold: 4500,
|
||||||
|
imageUrl: 'https://via.placeholder.com/150',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
EventID: 2,
|
||||||
|
name: 'Art Expo',
|
||||||
|
date: '2023-11-15',
|
||||||
|
location: 'San Francisco',
|
||||||
|
ticketPrice: '$55',
|
||||||
|
description: 'A showcase of modern art from around the world.',
|
||||||
|
capacity: 300,
|
||||||
|
ticketsSold: 260,
|
||||||
|
imageUrl: 'https://via.placeholder.com/150',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
EventID: 3,
|
||||||
|
name: 'Tech Summit 2023',
|
||||||
|
date: '2023-12-10',
|
||||||
|
location: 'Chicago',
|
||||||
|
ticketPrice: '$250',
|
||||||
|
description: 'The leading tech summit with top industry speakers.',
|
||||||
|
capacity: 2000,
|
||||||
|
ticketsSold: 1800,
|
||||||
|
imageUrl: 'https://via.placeholder.com/150',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
const TicketListing: React.FC = () => {
|
||||||
|
const [events, setEvents] = useState<Event[]>([]);
|
||||||
|
const [hoveredEventId, setHoveredEventId] = useState<number | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const eventsData = fetchEvents();
|
||||||
|
setEvents(eventsData);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative min-h-screen overflow-hidden">
|
||||||
|
<Header />
|
||||||
|
{/* Video Background */}
|
||||||
|
<video
|
||||||
|
autoPlay
|
||||||
|
loop
|
||||||
|
muted
|
||||||
|
className="absolute inset-0 w-full h-full object-cover z-0"
|
||||||
|
src="BGVid1.mp4"
|
||||||
|
>
|
||||||
|
Your browser does not support the video tag.
|
||||||
|
</video>
|
||||||
|
{/* Overlay for Readability */}
|
||||||
|
<div className="absolute inset-0 bg-black opacity-50 z-10"></div>
|
||||||
|
|
||||||
|
{/* Main Content */}
|
||||||
|
<div className="relative z-20 container mx-auto p-4 pt-16">
|
||||||
|
<div className="mb-6">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search events..."
|
||||||
|
className="search-bar mt-4 p-2 border border-gray-300 rounded w-full max-w-md"
|
||||||
|
/>
|
||||||
|
<div className="flex mt-4 space-x-4">
|
||||||
|
<button className="bg-blue-500 text-white px-4 py-2 rounded">
|
||||||
|
Sort
|
||||||
|
</button>
|
||||||
|
<button className="bg-blue-500 text-white px-4 py-2 rounded">
|
||||||
|
Filter
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<main>
|
||||||
|
<section>
|
||||||
|
<h2 className="text-2xl font-semibold text-white mb-4">
|
||||||
|
Available Events
|
||||||
|
</h2>
|
||||||
|
<div className="grid grid-cols-1 gap-6">
|
||||||
|
{events.map((event) => (
|
||||||
|
<div
|
||||||
|
key={event.EventID}
|
||||||
|
className="relative flex bg-white p-4 rounded-lg shadow-lg"
|
||||||
|
onMouseEnter={() => setHoveredEventId(event.EventID)}
|
||||||
|
onMouseLeave={() => setHoveredEventId(null)}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={event.imageUrl}
|
||||||
|
alt={event.name}
|
||||||
|
className="w-1/4 rounded-lg"
|
||||||
|
/>
|
||||||
|
<div className="ml-4 relative">
|
||||||
|
<h3 className="text-xl font-bold">{event.name}</h3>
|
||||||
|
<p className="text-gray-600">{event.date}</p>
|
||||||
|
<p className="text-gray-600">{event.location}</p>
|
||||||
|
<p className="text-gray-800 font-semibold">
|
||||||
|
{event.ticketPrice}
|
||||||
|
</p>
|
||||||
|
{event.ticketsSold / event.capacity >= 0.9 && (
|
||||||
|
<div className="mt-2 p-2 bg-yellow-300 text-black rounded">
|
||||||
|
Limited Tickets Remaining!
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="absolute top-0 right-0 flex items-center space-x-2">
|
||||||
|
{hoveredEventId === event.EventID && (
|
||||||
|
<div className="top-0 left-4 w-full bg-white p-4 shadow-lg rounded-lg z-10">
|
||||||
|
<p>{event.description}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TicketListing;
|
||||||
9
app/TicketListings/page.tsx
Normal file
9
app/TicketListings/page.tsx
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import TicketListings from './TicketListings';
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<TicketListings />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,21 +1,26 @@
|
|||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
import localFont from 'next/font/local';
|
// import localFont from 'next/font/local';
|
||||||
import './globals.css';
|
import './globals.css';
|
||||||
|
|
||||||
const geistSans = localFont({
|
import { Inter } from 'next/font/google';
|
||||||
src: './fonts/GeistVF.woff',
|
import './globals.css';
|
||||||
variable: '--font-geist-sans',
|
|
||||||
weight: '100 900',
|
const inter = Inter({ subsets: ['latin'] });
|
||||||
});
|
|
||||||
const geistMono = localFont({
|
// const geistSans = localFont({
|
||||||
src: './fonts/GeistMonoVF.woff',
|
// src: './fonts/GeistVF.woff',
|
||||||
variable: '--font-geist-mono',
|
// variable: '--font-geist-sans',
|
||||||
weight: '100 900',
|
// weight: '100 900',
|
||||||
});
|
// });
|
||||||
|
// const geistMono = localFont({
|
||||||
|
// src: './fonts/GeistMonoVF.woff',
|
||||||
|
// variable: '--font-geist-mono',
|
||||||
|
// weight: '100 900',
|
||||||
|
// });
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: 'Create Next App',
|
title: 'Ticket Chain',
|
||||||
description: 'Generated by create next app',
|
description: 'A blockchain-based ticketing system.',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
@@ -25,11 +30,7 @@ export default function RootLayout({
|
|||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<body
|
<body className={inter.className}>{children}</body>
|
||||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
102
app/page.tsx
102
app/page.tsx
@@ -1,101 +1,9 @@
|
|||||||
import Image from 'next/image';
|
import Home from './Home';
|
||||||
|
|
||||||
export default function Home() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
|
<>
|
||||||
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
|
<Home />
|
||||||
<Image
|
</>
|
||||||
className="dark:invert"
|
|
||||||
src="https://nextjs.org/icons/next.svg"
|
|
||||||
alt="Next.js logo"
|
|
||||||
width={180}
|
|
||||||
height={38}
|
|
||||||
priority
|
|
||||||
/>
|
|
||||||
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
|
|
||||||
<li className="mb-2">
|
|
||||||
Get started by editing{' '}
|
|
||||||
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
|
|
||||||
app/page.tsx
|
|
||||||
</code>
|
|
||||||
.
|
|
||||||
</li>
|
|
||||||
<li>Save and see your changes instantly.</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<div className="flex gap-4 items-center flex-col sm:flex-row">
|
|
||||||
<a
|
|
||||||
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
|
|
||||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
className="dark:invert"
|
|
||||||
src="https://nextjs.org/icons/vercel.svg"
|
|
||||||
alt="Vercel logomark"
|
|
||||||
width={20}
|
|
||||||
height={20}
|
|
||||||
/>
|
|
||||||
Deploy now
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
|
|
||||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Read our docs
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
<footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
|
|
||||||
<a
|
|
||||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
|
||||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
aria-hidden
|
|
||||||
src="https://nextjs.org/icons/file.svg"
|
|
||||||
alt="File icon"
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
/>
|
|
||||||
Learn
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
|
||||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
aria-hidden
|
|
||||||
src="https://nextjs.org/icons/window.svg"
|
|
||||||
alt="Window icon"
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
/>
|
|
||||||
Examples
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
|
||||||
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
aria-hidden
|
|
||||||
src="https://nextjs.org/icons/globe.svg"
|
|
||||||
alt="Globe icon"
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
/>
|
|
||||||
Go to nextjs.org →
|
|
||||||
</a>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
64
components/Confirmation.tsx
Normal file
64
components/Confirmation.tsx
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
'use client';
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardHeader,
|
||||||
|
CardContent,
|
||||||
|
CardFooter,
|
||||||
|
} from '@/components/ui/card';
|
||||||
|
import { Badge } from '@/components/ui/badge';
|
||||||
|
import { Separator } from '@/components/ui/separator';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { CheckCircle } from 'lucide-react';
|
||||||
|
|
||||||
|
interface props {
|
||||||
|
ticketTitle: string;
|
||||||
|
eventDate: string;
|
||||||
|
ticketID: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ConfirmationTicket = ({ ticketTitle, eventDate, ticketID }: props) => {
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
className="max-w-md mx-auto bg-blue-900 text-blue-100 rounded-xl shadow-md p-6
|
||||||
|
transition-all duration-300 hover:shadow-[0px_0px_20px_5px_rgba(59,130,246,0.5)]"
|
||||||
|
>
|
||||||
|
<CardHeader className="flex flex-col items-center space-y-4">
|
||||||
|
<CheckCircle className="text-green-400 w-10 h-10" />
|
||||||
|
<h1 className="text-2xl font-semibold text-blue-50">
|
||||||
|
Ticket Confirmed!
|
||||||
|
</h1>
|
||||||
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className="text-blue-200 bg-blue-800 px-3 py-1 rounded-full"
|
||||||
|
>
|
||||||
|
#{ticketID}
|
||||||
|
</Badge>
|
||||||
|
</CardHeader>
|
||||||
|
|
||||||
|
<CardContent className="text-center">
|
||||||
|
<Separator className="my-4 border-blue-700" />
|
||||||
|
<h2 className="text-lg font-medium">{ticketTitle}</h2>
|
||||||
|
<p className="text-blue-300 mt-2">Event Date: {eventDate}</p>
|
||||||
|
<Separator className="my-4 border-blue-700" />
|
||||||
|
</CardContent>
|
||||||
|
|
||||||
|
<CardFooter className="flex flex-col items-center space-y-3">
|
||||||
|
<Button
|
||||||
|
variant="default"
|
||||||
|
className="w-full bg-blue-700 text-blue-100 font-semibold rounded-lg hover:bg-blue-600"
|
||||||
|
>
|
||||||
|
View Ticket
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="w-full text-blue-200 border-blue-700 hover:bg-blue-800"
|
||||||
|
>
|
||||||
|
Download PDF
|
||||||
|
</Button>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ConfirmationTicket;
|
||||||
18
components/EventPage.tsx
Normal file
18
components/EventPage.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const EventPage = () => {
|
||||||
|
return (
|
||||||
|
// <>Header</>
|
||||||
|
// <EventDescription>
|
||||||
|
// <Footer>
|
||||||
|
<div
|
||||||
|
className="relative bg-black-100 flex
|
||||||
|
justify-center items-center, flex-col overflow-hidden
|
||||||
|
mx-auto sm:px-10 px-5"
|
||||||
|
>
|
||||||
|
<div className="max-w-7xl w-full"></div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EventPage;
|
||||||
93
components/Profile.tsx
Normal file
93
components/Profile.tsx
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
'use client';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardFooter,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from '@/components/ui/card';
|
||||||
|
import { Input } from '@/components/ui/input';
|
||||||
|
import { Label } from '@/components/ui/label';
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
|
|
||||||
|
// Dark theme and animation setup
|
||||||
|
const cardVariants = {
|
||||||
|
hidden: { opacity: 0, y: 10 },
|
||||||
|
visible: { opacity: 1, y: 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
const Profile = () => {
|
||||||
|
return (
|
||||||
|
<motion.div
|
||||||
|
initial="hidden"
|
||||||
|
animate="visible"
|
||||||
|
variants={cardVariants}
|
||||||
|
transition={{ duration: 0.5, ease: 'easeOut' }}
|
||||||
|
>
|
||||||
|
<Card className="w-[350px] bg-[#1e2a3a] text-white shadow-lg">
|
||||||
|
<CardHeader>
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: -10 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ delay: 0.1, duration: 0.5 }}
|
||||||
|
>
|
||||||
|
<CardTitle className="text-lg font-semibold">Profile</CardTitle>
|
||||||
|
</motion.div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<form>
|
||||||
|
<div className="grid w-full items-center gap-4">
|
||||||
|
<motion.div
|
||||||
|
className="flex flex-col space-y-1.5"
|
||||||
|
initial={{ opacity: 0, x: -10 }}
|
||||||
|
animate={{ opacity: 1, x: 0 }}
|
||||||
|
transition={{ delay: 0.2, duration: 0.4 }}
|
||||||
|
>
|
||||||
|
<Label htmlFor="name" className="text-gray-300">
|
||||||
|
Name
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="name"
|
||||||
|
placeholder="Name"
|
||||||
|
className="bg-[#2b3a4a] text-white placeholder-gray-400"
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
<motion.div
|
||||||
|
className="flex flex-col space-y-1.5"
|
||||||
|
initial={{ opacity: 0, x: -10 }}
|
||||||
|
animate={{ opacity: 1, x: 0 }}
|
||||||
|
transition={{ delay: 0.3, duration: 0.4 }}
|
||||||
|
>
|
||||||
|
<Label htmlFor="framework" className="text-gray-300">
|
||||||
|
MetaMask Public Key
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="name"
|
||||||
|
placeholder="Key"
|
||||||
|
className="bg-[#2b3a4a] text-white placeholder-gray-400"
|
||||||
|
/>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter className="flex justify-between">
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: 10 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ delay: 0.4, duration: 0.4 }}
|
||||||
|
>
|
||||||
|
<Button className="bg-[#365b85] text-white hover:bg-[#2b4a70]">
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</motion.div>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default Profile;
|
||||||
|
// return <EventDescription/>
|
||||||
|
// return <ConfirmationTicket ticketTitle='taylor swift' ticketID='2' eventDate='27th september'/>;
|
||||||
|
// return <Profile/>;
|
||||||
7
components/WalletAdapter.tsx
Normal file
7
components/WalletAdapter.tsx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const WalletAdapter = () => {
|
||||||
|
return <div>WalletAdapter</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default WalletAdapter;
|
||||||
7
components/custom/BuyTicket.tsx
Normal file
7
components/custom/BuyTicket.tsx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const BuyTicket = () => {
|
||||||
|
return <div>BuyTicket</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BuyTicket;
|
||||||
61
components/custom/EventDescription.tsx
Normal file
61
components/custom/EventDescription.tsx
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
'use client';
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardHeader,
|
||||||
|
CardContent,
|
||||||
|
CardFooter,
|
||||||
|
} from '@/components/ui/card';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Badge } from '@/components/ui/badge';
|
||||||
|
import { Separator } from '@/components/ui/separator';
|
||||||
|
import ImageCarousel from './ImageCarousel';
|
||||||
|
import TicketButton from './TicketButton';
|
||||||
|
|
||||||
|
const EventDescription = () => {
|
||||||
|
return (
|
||||||
|
<Card className="pt-10 pb-16 px-6 bg-gradient-to-r from-blue-50 to-gray-50 rounded-xl shadow-lg max-w-4xl mx-auto">
|
||||||
|
<CardHeader className="flex flex-col items-start space-y-4">
|
||||||
|
<h1 className="text-3xl font-semibold text-gray-800">TicketTitle</h1>
|
||||||
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className="text-blue-600 bg-blue-100 px-3 py-1 rounded-full"
|
||||||
|
>
|
||||||
|
Price: $99
|
||||||
|
</Badge>
|
||||||
|
</CardHeader>
|
||||||
|
|
||||||
|
<CardContent className="flex flex-col md:flex-row items-start space-y-8 md:space-y-0 md:space-x-10">
|
||||||
|
<div className="md:w-1/2 flex justify-center">
|
||||||
|
<ImageCarousel />
|
||||||
|
</div>
|
||||||
|
<div className="md:w-1/2 text-gray-700">
|
||||||
|
<Separator className="my-4" />
|
||||||
|
<p className="leading-relaxed">
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
|
||||||
|
eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem
|
||||||
|
ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod
|
||||||
|
tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum
|
||||||
|
dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor
|
||||||
|
incididunt ut labore et dolore magna aliqua.
|
||||||
|
</p>
|
||||||
|
<Separator className="my-4" />
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
|
||||||
|
<CardFooter className="flex flex-col md:flex-row items-center justify-between space-y-4 md:space-y-0">
|
||||||
|
<Button
|
||||||
|
variant="default"
|
||||||
|
className="w-full md:w-auto bg-gradient-to-r from-blue-500 to-purple-600 text-white font-semibold rounded-lg hover:from-blue-600 hover:to-purple-700"
|
||||||
|
>
|
||||||
|
Buy now Using MetaMask
|
||||||
|
</Button>
|
||||||
|
<div className="relative md:left-5">
|
||||||
|
<TicketButton />
|
||||||
|
</div>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EventDescription;
|
||||||
33
components/custom/ImageCarousel.tsx
Normal file
33
components/custom/ImageCarousel.tsx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
|
import {
|
||||||
|
Carousel,
|
||||||
|
CarouselContent,
|
||||||
|
CarouselItem,
|
||||||
|
CarouselNext,
|
||||||
|
CarouselPrevious,
|
||||||
|
} from '@/components/ui/carousel';
|
||||||
|
|
||||||
|
export default function ImageCarousel() {
|
||||||
|
return (
|
||||||
|
<Carousel className="w-full max-w-xs">
|
||||||
|
<CarouselContent>
|
||||||
|
{/* map your images here from the page */}
|
||||||
|
{Array.from({ length: 5 }).map((_, index) => (
|
||||||
|
<CarouselItem key={index}>
|
||||||
|
<div className="p-1">
|
||||||
|
<Card>
|
||||||
|
<CardContent className="flex aspect-square items-center justify-center p-6">
|
||||||
|
<span className="text-4xl font-semibold">{index + 1}</span>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</CarouselItem>
|
||||||
|
))}
|
||||||
|
</CarouselContent>
|
||||||
|
<CarouselPrevious />
|
||||||
|
<CarouselNext />
|
||||||
|
</Carousel>
|
||||||
|
);
|
||||||
|
}
|
||||||
53
components/custom/TicketButton.tsx
Normal file
53
components/custom/TicketButton.tsx
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
'use client';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Button } from '../ui/button'; // Adjust import path to where your shadcn Button component is located
|
||||||
|
|
||||||
|
interface NumberPickerProps {
|
||||||
|
initialCount?: number;
|
||||||
|
min?: number;
|
||||||
|
max?: number;
|
||||||
|
onChange?: (count: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const NumberPicker: React.FC<NumberPickerProps> = ({
|
||||||
|
initialCount = 1,
|
||||||
|
min = 1,
|
||||||
|
max = 10,
|
||||||
|
onChange,
|
||||||
|
}) => {
|
||||||
|
const [count, setCount] = useState<number>(initialCount);
|
||||||
|
|
||||||
|
const increment = () => {
|
||||||
|
if (count < max) {
|
||||||
|
const newCount = count + 1;
|
||||||
|
setCount(newCount);
|
||||||
|
if (onChange) {
|
||||||
|
onChange(newCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const decrement = () => {
|
||||||
|
if (count > min) {
|
||||||
|
const newCount = count - 1;
|
||||||
|
setCount(newCount);
|
||||||
|
if (onChange) {
|
||||||
|
onChange(newCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Button variant="outline" onClick={decrement} disabled={count === min}>
|
||||||
|
-
|
||||||
|
</Button>
|
||||||
|
<span className="text-center w-8">{count}</span>
|
||||||
|
<Button variant="outline" onClick={increment} disabled={count === max}>
|
||||||
|
+
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NumberPicker;
|
||||||
12
components/custom/footer.tsx
Normal file
12
components/custom/footer.tsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const Footer = () => {
|
||||||
|
return (
|
||||||
|
<footer className="text-center mt-8">
|
||||||
|
<p className="text-gray-500">
|
||||||
|
© 2024 Ticket Chain. All rights reserved.
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default Footer;
|
||||||
72
components/custom/header.tsx
Normal file
72
components/custom/header.tsx
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
'use client';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import MetaMask from '../scripts/MetaMask';
|
||||||
|
|
||||||
|
const Header = () => {
|
||||||
|
const [mouseX, setMouseX] = useState(-1);
|
||||||
|
const [mouseY, setMouseY] = useState(-1);
|
||||||
|
|
||||||
|
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
setMouseX(e.clientX);
|
||||||
|
setMouseY(e.clientY);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseLeave = () => {
|
||||||
|
setMouseX(-1);
|
||||||
|
setMouseY(-1);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="fixed top-0 left-0 right-0 backdrop-blur-md bg-opacity-60 z-50"
|
||||||
|
onMouseMove={handleMouseMove}
|
||||||
|
onMouseLeave={handleMouseLeave}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="absolute inset-0 pointer-events-none"
|
||||||
|
style={{
|
||||||
|
border: '1px solid transparent',
|
||||||
|
background:
|
||||||
|
mouseX >= 0 && mouseY >= 0
|
||||||
|
? `radial-gradient(circle at ${mouseX}px ${mouseY}px, rgba(255, 255, 255, 0.4), transparent 20%)`
|
||||||
|
: 'none',
|
||||||
|
backgroundClip: 'padding-box, border-box',
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
<div className="container mx-auto px-6 py-4 flex justify-between items-center">
|
||||||
|
<h1 className="text-2xl font-Ariel-bold text-white">TicketChain</h1>
|
||||||
|
<nav className="nav">
|
||||||
|
<ul className="flex space-x-6">
|
||||||
|
<li>
|
||||||
|
<Link href="/" legacyBehavior>
|
||||||
|
<a className="text-white hover:text-blue-500 transition-colors duration-300">
|
||||||
|
Home
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link href="/TicketListings" legacyBehavior>
|
||||||
|
<a className="text-white hover:text-blue-500 transition-colors duration-300">
|
||||||
|
Events
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<Link href="/contact" legacyBehavior>
|
||||||
|
<a className="text-white hover:text-blue-500 transition-colors duration-300">
|
||||||
|
Contact
|
||||||
|
</a>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<MetaMask />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Header;
|
||||||
117
components/scripts/MetaMask.tsx
Normal file
117
components/scripts/MetaMask.tsx
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { useSDK, MetaMaskProvider } from '@metamask/sdk-react';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import {
|
||||||
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
|
} from '@/components/ui/popover';
|
||||||
|
|
||||||
|
function WalletIcon(props: React.SVGProps<SVGSVGElement>) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
{...props}
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M21 12V7H5a2 2 0 0 1 0-4h14v4" />
|
||||||
|
<path d="M3 5v14a2 2 0 0 0 2 2h16v-5" />
|
||||||
|
<path d="M18 12a2 2 0 0 0 0 4h4v-4Z" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatAddress(address: string | undefined): string {
|
||||||
|
if (!address) return '';
|
||||||
|
return `${address.slice(0, 6)}...${address.slice(-4)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function MetaMaskConnect() {
|
||||||
|
const { sdk, connected, connecting, account } = useSDK();
|
||||||
|
const [metaMaskInstalled, setMetaMaskInstalled] = useState(false);
|
||||||
|
|
||||||
|
const isMetaMaskInstalled = () =>
|
||||||
|
typeof window !== 'undefined' && typeof window.ethereum !== 'undefined';
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isMetaMaskInstalled()) {
|
||||||
|
setMetaMaskInstalled(true);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const connect = async () => {
|
||||||
|
try {
|
||||||
|
await sdk?.connect();
|
||||||
|
} catch (err) {
|
||||||
|
console.warn(`No accounts found`, err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const disconnect = () => {
|
||||||
|
if (sdk) {
|
||||||
|
sdk.terminate();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!metaMaskInstalled) {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
window.open('https://metamask.io/download.html', '_blank')
|
||||||
|
}
|
||||||
|
className="bg-gradient-to-r from-blue-500 to-indigo-700"
|
||||||
|
>
|
||||||
|
Install MetaMask
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative">
|
||||||
|
{connected ? (
|
||||||
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button>{formatAddress(account)}</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-44">
|
||||||
|
<button
|
||||||
|
onClick={disconnect}
|
||||||
|
className="w-full px-4 py-2 text-left text-destructive hover:bg-muted"
|
||||||
|
>
|
||||||
|
Disconnect
|
||||||
|
</button>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
) : (
|
||||||
|
<Button disabled={connecting} onClick={connect}>
|
||||||
|
<WalletIcon className="mr-2 h-4 w-4" /> Connect Wallet
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function MetaMaskConnectWrapper() {
|
||||||
|
return (
|
||||||
|
<MetaMaskProvider
|
||||||
|
debug={false}
|
||||||
|
sdkOptions={{
|
||||||
|
dappMetadata: {
|
||||||
|
name: 'My dApp',
|
||||||
|
url: typeof window !== 'undefined' ? window.location.href : '',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MetaMaskConnect />
|
||||||
|
</MetaMaskProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
16
components/scripts/Test.tsx
Normal file
16
components/scripts/Test.tsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
'use client';
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
|
||||||
|
const Test = () => {
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('Print some shit');
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p>Hellao!</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Test;
|
||||||
36
components/ui/badge.tsx
Normal file
36
components/ui/badge.tsx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { cva, type VariantProps } from 'class-variance-authority';
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const badgeVariants = cva(
|
||||||
|
'inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default:
|
||||||
|
'border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80',
|
||||||
|
secondary:
|
||||||
|
'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
||||||
|
destructive:
|
||||||
|
'border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80',
|
||||||
|
outline: 'text-foreground',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: 'default',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export interface BadgeProps
|
||||||
|
extends React.HTMLAttributes<HTMLDivElement>,
|
||||||
|
VariantProps<typeof badgeVariants> {}
|
||||||
|
|
||||||
|
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||||
|
return (
|
||||||
|
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Badge, badgeVariants };
|
||||||
@@ -5,7 +5,7 @@ import { cva, type VariantProps } from 'class-variance-authority';
|
|||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
const buttonVariants = cva(
|
const buttonVariants = cva(
|
||||||
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50',
|
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
|
|||||||
83
components/ui/card.tsx
Normal file
83
components/ui/card.tsx
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const Card = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
'rounded-xl border bg-card text-card-foreground shadow',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
Card.displayName = 'Card';
|
||||||
|
|
||||||
|
const CardHeader = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn('flex flex-col space-y-1.5 p-6', className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
CardHeader.displayName = 'CardHeader';
|
||||||
|
|
||||||
|
const CardTitle = React.forwardRef<
|
||||||
|
HTMLParagraphElement,
|
||||||
|
React.HTMLAttributes<HTMLHeadingElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<h3
|
||||||
|
ref={ref}
|
||||||
|
className={cn('font-semibold leading-none tracking-tight', className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
CardTitle.displayName = 'CardTitle';
|
||||||
|
|
||||||
|
const CardDescription = React.forwardRef<
|
||||||
|
HTMLParagraphElement,
|
||||||
|
React.HTMLAttributes<HTMLParagraphElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<p
|
||||||
|
ref={ref}
|
||||||
|
className={cn('text-sm text-muted-foreground', className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
CardDescription.displayName = 'CardDescription';
|
||||||
|
|
||||||
|
const CardContent = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
|
||||||
|
));
|
||||||
|
CardContent.displayName = 'CardContent';
|
||||||
|
|
||||||
|
const CardFooter = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn('flex items-center p-6 pt-0', className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
CardFooter.displayName = 'CardFooter';
|
||||||
|
|
||||||
|
export {
|
||||||
|
Card,
|
||||||
|
CardHeader,
|
||||||
|
CardFooter,
|
||||||
|
CardTitle,
|
||||||
|
CardDescription,
|
||||||
|
CardContent,
|
||||||
|
};
|
||||||
262
components/ui/carousel.tsx
Normal file
262
components/ui/carousel.tsx
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
import { ArrowLeftIcon, ArrowRightIcon } from '@radix-ui/react-icons';
|
||||||
|
import useEmblaCarousel, {
|
||||||
|
type UseEmblaCarouselType,
|
||||||
|
} from 'embla-carousel-react';
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
|
||||||
|
type CarouselApi = UseEmblaCarouselType[1];
|
||||||
|
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
|
||||||
|
type CarouselOptions = UseCarouselParameters[0];
|
||||||
|
type CarouselPlugin = UseCarouselParameters[1];
|
||||||
|
|
||||||
|
type CarouselProps = {
|
||||||
|
opts?: CarouselOptions;
|
||||||
|
plugins?: CarouselPlugin;
|
||||||
|
orientation?: 'horizontal' | 'vertical';
|
||||||
|
setApi?: (api: CarouselApi) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CarouselContextProps = {
|
||||||
|
carouselRef: ReturnType<typeof useEmblaCarousel>[0];
|
||||||
|
api: ReturnType<typeof useEmblaCarousel>[1];
|
||||||
|
scrollPrev: () => void;
|
||||||
|
scrollNext: () => void;
|
||||||
|
canScrollPrev: boolean;
|
||||||
|
canScrollNext: boolean;
|
||||||
|
} & CarouselProps;
|
||||||
|
|
||||||
|
const CarouselContext = React.createContext<CarouselContextProps | null>(null);
|
||||||
|
|
||||||
|
function useCarousel() {
|
||||||
|
const context = React.useContext(CarouselContext);
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('useCarousel must be used within a <Carousel />');
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Carousel = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement> & CarouselProps
|
||||||
|
>(
|
||||||
|
(
|
||||||
|
{
|
||||||
|
orientation = 'horizontal',
|
||||||
|
opts,
|
||||||
|
setApi,
|
||||||
|
plugins,
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
},
|
||||||
|
ref
|
||||||
|
) => {
|
||||||
|
const [carouselRef, api] = useEmblaCarousel(
|
||||||
|
{
|
||||||
|
...opts,
|
||||||
|
axis: orientation === 'horizontal' ? 'x' : 'y',
|
||||||
|
},
|
||||||
|
plugins
|
||||||
|
);
|
||||||
|
const [canScrollPrev, setCanScrollPrev] = React.useState(false);
|
||||||
|
const [canScrollNext, setCanScrollNext] = React.useState(false);
|
||||||
|
|
||||||
|
const onSelect = React.useCallback((api: CarouselApi) => {
|
||||||
|
if (!api) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCanScrollPrev(api.canScrollPrev());
|
||||||
|
setCanScrollNext(api.canScrollNext());
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const scrollPrev = React.useCallback(() => {
|
||||||
|
api?.scrollPrev();
|
||||||
|
}, [api]);
|
||||||
|
|
||||||
|
const scrollNext = React.useCallback(() => {
|
||||||
|
api?.scrollNext();
|
||||||
|
}, [api]);
|
||||||
|
|
||||||
|
const handleKeyDown = React.useCallback(
|
||||||
|
(event: React.KeyboardEvent<HTMLDivElement>) => {
|
||||||
|
if (event.key === 'ArrowLeft') {
|
||||||
|
event.preventDefault();
|
||||||
|
scrollPrev();
|
||||||
|
} else if (event.key === 'ArrowRight') {
|
||||||
|
event.preventDefault();
|
||||||
|
scrollNext();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[scrollPrev, scrollNext]
|
||||||
|
);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!api || !setApi) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setApi(api);
|
||||||
|
}, [api, setApi]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!api) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelect(api);
|
||||||
|
api.on('reInit', onSelect);
|
||||||
|
api.on('select', onSelect);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
api?.off('select', onSelect);
|
||||||
|
};
|
||||||
|
}, [api, onSelect]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<CarouselContext.Provider
|
||||||
|
value={{
|
||||||
|
carouselRef,
|
||||||
|
api: api,
|
||||||
|
opts,
|
||||||
|
orientation:
|
||||||
|
orientation || (opts?.axis === 'y' ? 'vertical' : 'horizontal'),
|
||||||
|
scrollPrev,
|
||||||
|
scrollNext,
|
||||||
|
canScrollPrev,
|
||||||
|
canScrollNext,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
onKeyDownCapture={handleKeyDown}
|
||||||
|
className={cn('relative', className)}
|
||||||
|
role="region"
|
||||||
|
aria-roledescription="carousel"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</CarouselContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Carousel.displayName = 'Carousel';
|
||||||
|
|
||||||
|
const CarouselContent = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => {
|
||||||
|
const { carouselRef, orientation } = useCarousel();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={carouselRef} className="overflow-hidden">
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
'flex',
|
||||||
|
orientation === 'horizontal' ? '-ml-4' : '-mt-4 flex-col',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
CarouselContent.displayName = 'CarouselContent';
|
||||||
|
|
||||||
|
const CarouselItem = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => {
|
||||||
|
const { orientation } = useCarousel();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
role="group"
|
||||||
|
aria-roledescription="slide"
|
||||||
|
className={cn(
|
||||||
|
'min-w-0 shrink-0 grow-0 basis-full',
|
||||||
|
orientation === 'horizontal' ? 'pl-4' : 'pt-4',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
CarouselItem.displayName = 'CarouselItem';
|
||||||
|
|
||||||
|
const CarouselPrevious = React.forwardRef<
|
||||||
|
HTMLButtonElement,
|
||||||
|
React.ComponentProps<typeof Button>
|
||||||
|
>(({ className, variant = 'outline', size = 'icon', ...props }, ref) => {
|
||||||
|
const { orientation, scrollPrev, canScrollPrev } = useCarousel();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
ref={ref}
|
||||||
|
variant={variant}
|
||||||
|
size={size}
|
||||||
|
className={cn(
|
||||||
|
'absolute h-8 w-8 rounded-full',
|
||||||
|
orientation === 'horizontal'
|
||||||
|
? '-left-12 top-1/2 -translate-y-1/2'
|
||||||
|
: '-top-12 left-1/2 -translate-x-1/2 rotate-90',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
disabled={!canScrollPrev}
|
||||||
|
onClick={scrollPrev}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ArrowLeftIcon className="h-4 w-4" />
|
||||||
|
<span className="sr-only">Previous slide</span>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
CarouselPrevious.displayName = 'CarouselPrevious';
|
||||||
|
|
||||||
|
const CarouselNext = React.forwardRef<
|
||||||
|
HTMLButtonElement,
|
||||||
|
React.ComponentProps<typeof Button>
|
||||||
|
>(({ className, variant = 'outline', size = 'icon', ...props }, ref) => {
|
||||||
|
const { orientation, scrollNext, canScrollNext } = useCarousel();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
ref={ref}
|
||||||
|
variant={variant}
|
||||||
|
size={size}
|
||||||
|
className={cn(
|
||||||
|
'absolute h-8 w-8 rounded-full',
|
||||||
|
orientation === 'horizontal'
|
||||||
|
? '-right-12 top-1/2 -translate-y-1/2'
|
||||||
|
: '-bottom-12 left-1/2 -translate-x-1/2 rotate-90',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
disabled={!canScrollNext}
|
||||||
|
onClick={scrollNext}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ArrowRightIcon className="h-4 w-4" />
|
||||||
|
<span className="sr-only">Next slide</span>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
CarouselNext.displayName = 'CarouselNext';
|
||||||
|
|
||||||
|
export {
|
||||||
|
type CarouselApi,
|
||||||
|
Carousel,
|
||||||
|
CarouselContent,
|
||||||
|
CarouselItem,
|
||||||
|
CarouselPrevious,
|
||||||
|
CarouselNext,
|
||||||
|
};
|
||||||
22
components/ui/input.tsx
Normal file
22
components/ui/input.tsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const Input = React.forwardRef<
|
||||||
|
HTMLInputElement,
|
||||||
|
React.InputHTMLAttributes<HTMLInputElement>
|
||||||
|
>(({ className, type, ...props }, ref) => {
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type={type}
|
||||||
|
className={cn(
|
||||||
|
'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
Input.displayName = 'Input';
|
||||||
|
|
||||||
|
export { Input };
|
||||||
26
components/ui/label.tsx
Normal file
26
components/ui/label.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
import * as LabelPrimitive from '@radix-ui/react-label';
|
||||||
|
import { cva, type VariantProps } from 'class-variance-authority';
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const labelVariants = cva(
|
||||||
|
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70'
|
||||||
|
);
|
||||||
|
|
||||||
|
const Label = React.forwardRef<
|
||||||
|
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
||||||
|
VariantProps<typeof labelVariants>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<LabelPrimitive.Root
|
||||||
|
ref={ref}
|
||||||
|
className={cn(labelVariants(), className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
Label.displayName = LabelPrimitive.Root.displayName;
|
||||||
|
|
||||||
|
export { Label };
|
||||||
33
components/ui/popover.tsx
Normal file
33
components/ui/popover.tsx
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
import * as PopoverPrimitive from '@radix-ui/react-popover';
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const Popover = PopoverPrimitive.Root;
|
||||||
|
|
||||||
|
const PopoverTrigger = PopoverPrimitive.Trigger;
|
||||||
|
|
||||||
|
const PopoverAnchor = PopoverPrimitive.Anchor;
|
||||||
|
|
||||||
|
const PopoverContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof PopoverPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
|
||||||
|
>(({ className, align = 'center', sideOffset = 4, ...props }, ref) => (
|
||||||
|
<PopoverPrimitive.Portal>
|
||||||
|
<PopoverPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
align={align}
|
||||||
|
sideOffset={sideOffset}
|
||||||
|
className={cn(
|
||||||
|
'z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</PopoverPrimitive.Portal>
|
||||||
|
));
|
||||||
|
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
||||||
|
|
||||||
|
export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };
|
||||||
164
components/ui/select.tsx
Normal file
164
components/ui/select.tsx
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
import {
|
||||||
|
CaretSortIcon,
|
||||||
|
CheckIcon,
|
||||||
|
ChevronDownIcon,
|
||||||
|
ChevronUpIcon,
|
||||||
|
} from '@radix-ui/react-icons';
|
||||||
|
import * as SelectPrimitive from '@radix-ui/react-select';
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const Select = SelectPrimitive.Root;
|
||||||
|
|
||||||
|
const SelectGroup = SelectPrimitive.Group;
|
||||||
|
|
||||||
|
const SelectValue = SelectPrimitive.Value;
|
||||||
|
|
||||||
|
const SelectTrigger = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
||||||
|
>(({ className, children, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Trigger
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
'flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<SelectPrimitive.Icon asChild>
|
||||||
|
<CaretSortIcon className="h-4 w-4 opacity-50" />
|
||||||
|
</SelectPrimitive.Icon>
|
||||||
|
</SelectPrimitive.Trigger>
|
||||||
|
));
|
||||||
|
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
||||||
|
|
||||||
|
const SelectScrollUpButton = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.ScrollUpButton
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
'flex cursor-default items-center justify-center py-1',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ChevronUpIcon />
|
||||||
|
</SelectPrimitive.ScrollUpButton>
|
||||||
|
));
|
||||||
|
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
||||||
|
|
||||||
|
const SelectScrollDownButton = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.ScrollDownButton
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
'flex cursor-default items-center justify-center py-1',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ChevronDownIcon />
|
||||||
|
</SelectPrimitive.ScrollDownButton>
|
||||||
|
));
|
||||||
|
SelectScrollDownButton.displayName =
|
||||||
|
SelectPrimitive.ScrollDownButton.displayName;
|
||||||
|
|
||||||
|
const SelectContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
||||||
|
>(({ className, children, position = 'popper', ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Portal>
|
||||||
|
<SelectPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||||
|
position === 'popper' &&
|
||||||
|
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
position={position}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<SelectScrollUpButton />
|
||||||
|
<SelectPrimitive.Viewport
|
||||||
|
className={cn(
|
||||||
|
'p-1',
|
||||||
|
position === 'popper' &&
|
||||||
|
'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</SelectPrimitive.Viewport>
|
||||||
|
<SelectScrollDownButton />
|
||||||
|
</SelectPrimitive.Content>
|
||||||
|
</SelectPrimitive.Portal>
|
||||||
|
));
|
||||||
|
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
||||||
|
|
||||||
|
const SelectLabel = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Label>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Label
|
||||||
|
ref={ref}
|
||||||
|
className={cn('px-2 py-1.5 text-sm font-semibold', className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
||||||
|
|
||||||
|
const SelectItem = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Item>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
||||||
|
>(({ className, children, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Item
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||||
|
<SelectPrimitive.ItemIndicator>
|
||||||
|
<CheckIcon className="h-4 w-4" />
|
||||||
|
</SelectPrimitive.ItemIndicator>
|
||||||
|
</span>
|
||||||
|
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||||
|
</SelectPrimitive.Item>
|
||||||
|
));
|
||||||
|
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
||||||
|
|
||||||
|
const SelectSeparator = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Separator>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Separator
|
||||||
|
ref={ref}
|
||||||
|
className={cn('-mx-1 my-1 h-px bg-muted', className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
||||||
|
|
||||||
|
export {
|
||||||
|
Select,
|
||||||
|
SelectGroup,
|
||||||
|
SelectValue,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectContent,
|
||||||
|
SelectLabel,
|
||||||
|
SelectItem,
|
||||||
|
SelectSeparator,
|
||||||
|
SelectScrollUpButton,
|
||||||
|
SelectScrollDownButton,
|
||||||
|
};
|
||||||
31
components/ui/separator.tsx
Normal file
31
components/ui/separator.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import * as React from 'react';
|
||||||
|
import * as SeparatorPrimitive from '@radix-ui/react-separator';
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const Separator = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SeparatorPrimitive.Root>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
|
||||||
|
>(
|
||||||
|
(
|
||||||
|
{ className, orientation = 'horizontal', decorative = true, ...props },
|
||||||
|
ref
|
||||||
|
) => (
|
||||||
|
<SeparatorPrimitive.Root
|
||||||
|
ref={ref}
|
||||||
|
decorative={decorative}
|
||||||
|
orientation={orientation}
|
||||||
|
className={cn(
|
||||||
|
'shrink-0 bg-border',
|
||||||
|
orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
Separator.displayName = SeparatorPrimitive.Root.displayName;
|
||||||
|
|
||||||
|
export { Separator };
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0
|
|
||||||
|
|
||||||
pragma solidity >=0.8.2 <0.9.0;
|
|
||||||
|
|
||||||
|
|
||||||
contract Escrow {
|
|
||||||
|
|
||||||
struct EscrowContract {
|
|
||||||
address buyer;
|
|
||||||
address seller;
|
|
||||||
uint256 amount;
|
|
||||||
bool buyerApproved;
|
|
||||||
bool sellerApproved;
|
|
||||||
bool isComplete;
|
|
||||||
}
|
|
||||||
|
|
||||||
mapping(uint256 => EscrowContract) public escrows;
|
|
||||||
uint256 public escrowCounter;
|
|
||||||
|
|
||||||
function createEscrow(address _seller) public payable {
|
|
||||||
escrows[escrowCounter] = EscrowContract(msg.sender, _seller, msg.value, false, false, false);
|
|
||||||
escrowCounter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
function approveEscrow(uint256 escrowId) public {
|
|
||||||
require(escrowId < escrowCounter, "Invalid escrow ID");
|
|
||||||
require(msg.sender == escrows[escrowId].buyer || msg.sender == escrows[escrowId].seller, "Unauthorized");
|
|
||||||
|
|
||||||
if (msg.sender == escrows[escrowId].buyer) {
|
|
||||||
escrows[escrowId].buyerApproved = true;
|
|
||||||
} else {
|
|
||||||
escrows[escrowId].sellerApproved = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (escrows[escrowId].buyerApproved && escrows[escrowId].sellerApproved) {
|
|
||||||
escrows[escrowId].isComplete = true;
|
|
||||||
(bool sent, ) = escrows[escrowId].seller.call{value: escrows[escrowId].amount}("");
|
|
||||||
require(sent, "Failed to send FLR to seller");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getEscrowAmount(uint256 escrowId) public view returns (uint256) {
|
|
||||||
return escrows[escrowId].amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getEscrowBuyer(uint256 escrowId) public view returns (address) {
|
|
||||||
return escrows[escrowId].buyer;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getEscrowSeller(uint256 escrowId) public view returns (address) {
|
|
||||||
return escrows[escrowId].seller;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getEscrowBuyerApproved(uint256 escrowId) public view returns (bool) {
|
|
||||||
return escrows[escrowId].buyerApproved;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getEscrowSellerApproved(uint256 escrowId) public view returns (bool) {
|
|
||||||
return escrows[escrowId].sellerApproved;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getEscrowIsComplete(uint256 escrowId) public view returns (bool) {
|
|
||||||
return escrows[escrowId].isComplete;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
|
||||||
pragma solidity >=0.8.0 <0.9.0;
|
|
||||||
|
|
||||||
import {ContractRegistry} from "@flarenetwork/flare-periphery-contracts/coston2/ContractRegistry.sol";
|
|
||||||
/* THIS IS A TEST IMPORT, in production use: import {FtsoV2Interface} from "@flarenetwork/flare-periphery-contracts/coston2/FtsoV2Interface.sol"; */
|
|
||||||
import {TestFtsoV2Interface} from "@flarenetwork/flare-periphery-contracts/coston2/TestFtsoV2Interface.sol";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* THIS IS AN EXAMPLE CONTRACT.
|
|
||||||
* DO NOT USE THIS CODE IN PRODUCTION.
|
|
||||||
*/
|
|
||||||
contract FtsoV2FeedConsumer {
|
|
||||||
TestFtsoV2Interface internal ftsoV2;
|
|
||||||
// Feed IDs, see https://dev.flare.network/ftso/feeds for full list
|
|
||||||
bytes21[] public feedIds = [
|
|
||||||
bytes21(0x01464c522f55534400000000000000000000000000) // FLR/USD
|
|
||||||
// bytes21(0x014254432f55534400000000000000000000000000), // BTC/USD
|
|
||||||
// bytes21(0x014554482f55534400000000000000000000000000) // ETH/USD
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor initializes the FTSOv2 contract.
|
|
||||||
* The contract registry is used to fetch the FtsoV2 contract address.
|
|
||||||
*/
|
|
||||||
constructor() {
|
|
||||||
/* THIS IS A TEST METHOD, in production use: ftsoV2 = ContractRegistry.getFtsoV2(); */
|
|
||||||
ftsoV2 = ContractRegistry.getTestFtsoV2();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current value of the feeds.
|
|
||||||
*/
|
|
||||||
function getFtsoV2CurrentFeedValues()
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (
|
|
||||||
uint256[] memory _feedValues,
|
|
||||||
int8[] memory _decimals,
|
|
||||||
uint64 _timestamp
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return ftsoV2.getFeedsById(feedIds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
9428
package-lock.json
generated
9428
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
@@ -7,19 +7,28 @@
|
|||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"test": "hardhat test",
|
"test": "npx hardhat --tsconfig tsconfig.hardhat.json test",
|
||||||
"compile": "hardhat compile",
|
"compile": "hardhat compile",
|
||||||
"prepare": "husky"
|
"prepare": "husky"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@flarenetwork/flare-periphery-contracts": "^0.1.16",
|
||||||
|
"@metamask/sdk-react": "^0.30.0",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
|
"@radix-ui/react-label": "^2.1.0",
|
||||||
|
"@radix-ui/react-popover": "^1.1.2",
|
||||||
|
"@radix-ui/react-select": "^2.1.2",
|
||||||
|
"@radix-ui/react-separator": "^1.1.0",
|
||||||
"@radix-ui/react-slot": "^1.1.0",
|
"@radix-ui/react-slot": "^1.1.0",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"embla-carousel-react": "^8.3.0",
|
||||||
|
"ethers": "^5.7.2",
|
||||||
|
"framer-motion": "^11.11.10",
|
||||||
"lucide-react": "^0.446.0",
|
"lucide-react": "^0.446.0",
|
||||||
"next": "14.2.13",
|
"next": "14.2.13",
|
||||||
"react": "^18",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18.3.1",
|
||||||
"tailwind-merge": "^2.5.2",
|
"tailwind-merge": "^2.5.2",
|
||||||
"tailwindcss-animate": "^1.0.7"
|
"tailwindcss-animate": "^1.0.7"
|
||||||
},
|
},
|
||||||
@@ -30,6 +39,7 @@
|
|||||||
"@nomiclabs/hardhat-waffle": "^2.0.0",
|
"@nomiclabs/hardhat-waffle": "^2.0.0",
|
||||||
"@typechain/hardhat": "^6.1.6",
|
"@typechain/hardhat": "^6.1.6",
|
||||||
"@types/mocha": "^10.0.9",
|
"@types/mocha": "^10.0.9",
|
||||||
|
"@types/ethereum-protocol": "^1.0.5",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
|
|||||||
BIN
public/BGVid1.mp4
Normal file
BIN
public/BGVid1.mp4
Normal file
Binary file not shown.
BIN
public/BGVid2.mp4
Normal file
BIN
public/BGVid2.mp4
Normal file
Binary file not shown.
@@ -10,6 +10,10 @@ const config: Config = {
|
|||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
|
black: {
|
||||||
|
DEFAULT: '#000',
|
||||||
|
100: '#000319',
|
||||||
|
},
|
||||||
background: 'hsl(var(--background))',
|
background: 'hsl(var(--background))',
|
||||||
foreground: 'hsl(var(--foreground))',
|
foreground: 'hsl(var(--foreground))',
|
||||||
card: {
|
card: {
|
||||||
|
|||||||
Reference in New Issue
Block a user