mirror of
https://github.com/0xShay/ticketchain.git
synced 2026-01-11 21:23:24 +00:00
fixing changes
This commit is contained in:
@@ -1,23 +1,27 @@
|
||||
'use client';
|
||||
import React from 'react';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useParams } from 'next/navigation';
|
||||
import Header from '../../../components/custom/header';
|
||||
import Footer from '../../../components/custom/footer';
|
||||
import EventDescription from '../../../components/custom/EventDescription';
|
||||
|
||||
// Dummy function to simulate a GET request
|
||||
const fetchEventDetails = (eventID: number) => {
|
||||
alert(`Fetching details for event ID: ${eventID}`);
|
||||
// Simulated JSON response for the event
|
||||
return {
|
||||
EventID: eventID,
|
||||
const ListingPage: React.FC = () => {
|
||||
const { eventId } = useParams();
|
||||
const [eventDetails, setEventDetails] = useState<any>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchEventDetails = async (id: number) => {
|
||||
alert(`Fetching details for event ID: ${id}`);
|
||||
// Dummy Response
|
||||
const details = {
|
||||
EventID: id,
|
||||
name: 'Example Event Name',
|
||||
date: '2023-12-01',
|
||||
location: 'Example Location',
|
||||
ticketPrice: 100,
|
||||
description: 'Detailed description of the event.',
|
||||
capacity: 300,
|
||||
ticketsSold: 150,
|
||||
ticketsSold: 295,
|
||||
imageUrl: [
|
||||
'https://via.placeholder.com/150',
|
||||
'https://via.placeholder.com/150',
|
||||
@@ -25,22 +29,27 @@ const fetchEventDetails = (eventID: number) => {
|
||||
host: 'Example Host',
|
||||
tickets: [1, 2, 3, 4],
|
||||
};
|
||||
};
|
||||
return details;
|
||||
};
|
||||
|
||||
const ListingPage: React.FC = () => {
|
||||
const searchParams = useSearchParams();
|
||||
const eventID = searchParams.get('eventID');
|
||||
|
||||
// Simulate fetching data from backend
|
||||
if (eventID) {
|
||||
const eventDetails = fetchEventDetails(Number(eventID));
|
||||
console.log('Event Details:', eventDetails);
|
||||
const getEventDetails = async () => {
|
||||
if (eventId) {
|
||||
const details = await fetchEventDetails(Number(eventId));
|
||||
setEventDetails(details);
|
||||
}
|
||||
};
|
||||
|
||||
getEventDetails();
|
||||
}, [eventId]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<EventDescription eventId={eventID!} />
|
||||
{eventDetails ? (
|
||||
<EventDescription eventDetails={eventDetails} />
|
||||
) : (
|
||||
<p>Loading...</p>
|
||||
)}
|
||||
<Footer />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -18,7 +18,6 @@ interface Event {
|
||||
host: string;
|
||||
}
|
||||
|
||||
// Dummy function to fetch events
|
||||
const fetchEvents = (): Event[] => {
|
||||
return [
|
||||
{
|
||||
@@ -161,7 +160,7 @@ const EventsPage: React.FC = () => {
|
||||
}, []);
|
||||
|
||||
const handleEventClick = (eventID: number) => {
|
||||
router.push(`/events/${eventID}`); // You may replace this with a Link from Next.js
|
||||
router.push(`/events/${eventID}`); // Route to the specific event page
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
14
app/host/page.tsx
Normal file
14
app/host/page.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
'use client';
|
||||
|
||||
import EventForm from '@/components/custom/EventForm';
|
||||
import React from 'react';
|
||||
|
||||
const page = () => {
|
||||
return (
|
||||
<>
|
||||
<EventForm onSubmit={() => {}} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default page;
|
||||
@@ -20,8 +20,8 @@ const inter = Inter({ subsets: ['latin'] });
|
||||
// });
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Ticket Chain',
|
||||
description: 'A blockchain-based ticketing system.',
|
||||
title: 'TicketChain',
|
||||
description: 'A verifiable and immutable ticketing platform.',
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
|
||||
@@ -57,11 +57,11 @@ export default function Home() {
|
||||
<div className="relative z-20 min-h-screen bg-gradient-to-b from-transparent to-gray-900 pt-20">
|
||||
<div className="container mx-auto p-4">
|
||||
<div className="container mx-auto justify-center items-center p-4">
|
||||
<div className="text-4xl font-bold text-white text-shadow-lg">
|
||||
<div className="text-6xl font-bold text-white text-center text-shadow-lg">
|
||||
Book your next
|
||||
<FlipWords
|
||||
words={words}
|
||||
className="text-light-purple text-opacity-75"
|
||||
className="text-pink-500 text-opacity-75 pl-3.5"
|
||||
/>
|
||||
on the Flare blockchain.
|
||||
</div>
|
||||
|
||||
@@ -9,43 +9,63 @@ 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';
|
||||
import { buyHandler } from '@/lib/buyHandler';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
import NumberPicker from './TicketButton';
|
||||
|
||||
const EventDescription = ({ eventId }: { eventId: string }) => {
|
||||
interface EventDescriptionProps {
|
||||
eventDetails: {
|
||||
EventID: number;
|
||||
name: string;
|
||||
date: string;
|
||||
location: string;
|
||||
ticketPrice: number;
|
||||
description: string;
|
||||
capacity: number;
|
||||
ticketsSold: number;
|
||||
imageUrl: string[];
|
||||
host: string;
|
||||
};
|
||||
}
|
||||
|
||||
const EventDescription: React.FC<EventDescriptionProps> = ({
|
||||
eventDetails,
|
||||
}) => {
|
||||
const { toast } = useToast();
|
||||
const handleBuyNow = () => {
|
||||
buyHandler(Number(eventId), toast);
|
||||
buyHandler(eventDetails.EventID, toast);
|
||||
};
|
||||
|
||||
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>
|
||||
<h1 className="text-3xl font-semibold text-gray-800">
|
||||
{eventDetails.name}
|
||||
</h1>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="text-blue-600 bg-blue-100 px-3 py-1 rounded-full"
|
||||
>
|
||||
Price: $99
|
||||
Price: ${eventDetails.ticketPrice}
|
||||
</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 />
|
||||
<ImageCarousel images={eventDetails.imageUrl} />
|
||||
</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>
|
||||
<p className="leading-relaxed">{eventDetails.description}</p>
|
||||
<Separator className="my-4" />
|
||||
<p>Location: {eventDetails.location}</p>
|
||||
<p>Date: {eventDetails.date}</p>
|
||||
<p>Host: {eventDetails.host}</p>
|
||||
{eventDetails.ticketsSold / eventDetails.capacity >= 0.9 && (
|
||||
<div className="mt-2 p-2 bg-yellow-300 text-black rounded">
|
||||
Limited Tickets Remaining!
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
@@ -58,7 +78,9 @@ const EventDescription = ({ eventId }: { eventId: string }) => {
|
||||
Buy now Using MetaMask
|
||||
</Button>
|
||||
<div className="relative md:left-5">
|
||||
<TicketButton />
|
||||
<NumberPicker
|
||||
max={eventDetails.capacity - eventDetails.ticketsSold}
|
||||
/>
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import {
|
||||
Carousel,
|
||||
@@ -9,17 +8,25 @@ import {
|
||||
CarouselPrevious,
|
||||
} from '@/components/ui/carousel';
|
||||
|
||||
export default function ImageCarousel() {
|
||||
interface ImageCarouselProps {
|
||||
images: string[];
|
||||
}
|
||||
|
||||
const ImageCarousel: React.FC<ImageCarouselProps> = ({ images }) => {
|
||||
return (
|
||||
<Carousel className="w-full max-w-xs">
|
||||
<CarouselContent>
|
||||
{/* map your images here from the page */}
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
{/* Map over the images array to create CarouselItems */}
|
||||
{images.map((imageUrl, 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>
|
||||
<img
|
||||
src={imageUrl}
|
||||
alt={`Event image ${index + 1}`}
|
||||
className="w-full h-full object-cover rounded-lg"
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
@@ -30,4 +37,6 @@ export default function ImageCarousel() {
|
||||
<CarouselNext />
|
||||
</Carousel>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default ImageCarousel;
|
||||
|
||||
@@ -62,6 +62,16 @@ const Header = () => {
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/host" legacyBehavior>
|
||||
<a
|
||||
className="text-white hover:text-light-purple hover:text-opacity-75 transition-colors duration-300"
|
||||
style={{ textShadow: '1px 1px 2px rgba(0, 0, 0, 0.5)' }}
|
||||
>
|
||||
Host Event
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/contact" legacyBehavior>
|
||||
<a
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useSDK, MetaMaskProvider } from '@metamask/sdk-react';
|
||||
import { WalletIcon } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Popover,
|
||||
@@ -9,54 +9,87 @@ import {
|
||||
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)}`;
|
||||
declare global {
|
||||
interface Window {
|
||||
ethereum?: {
|
||||
isMetaMask?: boolean;
|
||||
request: (request: {
|
||||
method: string;
|
||||
params?: Array<unknown>; // Use `unknown` instead of `any`
|
||||
}) => Promise<unknown>; // Specify a more accurate return type if possible
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function MetaMaskConnect() {
|
||||
const { sdk, connected, connecting, account } = useSDK();
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const [isConnected, setIsConnected] = useState<boolean>(false);
|
||||
const [account, setAccount] = useState<string | null>(null);
|
||||
|
||||
// Initial check on load
|
||||
useEffect(() => {
|
||||
setIsConnected(connected);
|
||||
}, [connected]);
|
||||
|
||||
const connect = async () => {
|
||||
const checkConnection = async () => {
|
||||
if (typeof window !== 'undefined' && window.ethereum) {
|
||||
try {
|
||||
await sdk?.connect();
|
||||
// Check if there are any accounts already connected
|
||||
const accounts = (await window.ethereum.request({
|
||||
method: 'eth_accounts',
|
||||
})) as string[];
|
||||
if (accounts && accounts.length > 0) {
|
||||
setIsConnected(true);
|
||||
} catch (err) {
|
||||
console.warn(`No accounts found`, err);
|
||||
setAccount(accounts[0]);
|
||||
localStorage.setItem('isConnected', JSON.stringify(true));
|
||||
localStorage.setItem('account', accounts[0]);
|
||||
} else {
|
||||
// No connected accounts found; check `localStorage`
|
||||
const storedIsConnected = JSON.parse(
|
||||
localStorage.getItem('isConnected') || 'false'
|
||||
);
|
||||
const storedAccount = localStorage.getItem('account') || null;
|
||||
setIsConnected(storedIsConnected);
|
||||
setAccount(storedAccount);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking MetaMask connection:', error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const disconnect = () => {
|
||||
if (sdk) {
|
||||
sdk.terminate();
|
||||
setIsConnected(false);
|
||||
checkConnection();
|
||||
}, []);
|
||||
|
||||
// Update localStorage whenever connection state changes
|
||||
useEffect(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
localStorage.setItem('isConnected', JSON.stringify(isConnected));
|
||||
localStorage.setItem('account', account || '');
|
||||
}
|
||||
}, [isConnected, account]);
|
||||
|
||||
const connect = async () => {
|
||||
try {
|
||||
const accounts = (await window.ethereum?.request({
|
||||
method: 'eth_requestAccounts',
|
||||
})) as string[];
|
||||
if (accounts && accounts.length > 0) {
|
||||
setIsConnected(true);
|
||||
setAccount(accounts[0]);
|
||||
localStorage.setItem('isConnected', JSON.stringify(true));
|
||||
localStorage.setItem('account', accounts[0]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('MetaMask connection failed:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const disconnect = async () => {
|
||||
setIsConnected(false);
|
||||
setAccount(null);
|
||||
localStorage.setItem('isConnected', JSON.stringify(false));
|
||||
localStorage.removeItem('account');
|
||||
await window.ethereum?.request({
|
||||
method: 'wallet_revokePermissions',
|
||||
params: [{ eth_accounts: {} }],
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -65,7 +98,7 @@ function MetaMaskConnect() {
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button variant="link" className="text-white">
|
||||
{formatAddress(account)}
|
||||
{account && `${account.slice(0, 6)}...${account.slice(-4)}`}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-44">
|
||||
@@ -80,7 +113,6 @@ function MetaMaskConnect() {
|
||||
</Popover>
|
||||
) : (
|
||||
<Button
|
||||
disabled={connecting}
|
||||
onClick={connect}
|
||||
className="bg-light-purple bg-opacity-75 hover:bg-purple border-0 hover:border-0"
|
||||
>
|
||||
@@ -91,18 +123,4 @@ function MetaMaskConnect() {
|
||||
);
|
||||
}
|
||||
|
||||
export default function MetaMaskConnectWrapper() {
|
||||
return (
|
||||
<MetaMaskProvider
|
||||
debug={false}
|
||||
sdkOptions={{
|
||||
dappMetadata: {
|
||||
name: 'My dApp',
|
||||
url: typeof window !== 'undefined' ? window.location.href : '',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<MetaMaskConnect />
|
||||
</MetaMaskProvider>
|
||||
);
|
||||
}
|
||||
export default MetaMaskConnect;
|
||||
|
||||
@@ -33,7 +33,6 @@ export const buyHandler = async (
|
||||
return;
|
||||
}
|
||||
|
||||
// @ts-expect-error: window.ethereum might not match ExternalProvider exactly
|
||||
const provider = new ethers.providers.Web3Provider(window.ethereum);
|
||||
const signer = provider.getSigner();
|
||||
const contract = getContract().connect(signer);
|
||||
|
||||
3
lib/createEvent.ts
Normal file
3
lib/createEvent.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const createEvent = async (event: Event) => {
|
||||
console.log('HELLO');
|
||||
};
|
||||
10
package-lock.json
generated
10
package-lock.json
generated
@@ -6794,6 +6794,7 @@
|
||||
"integrity": "sha512-bdM5cEGCOhDSwminryHJbRmXc1x7dPKg6Pqns3qyTwFlxsqUgxE29lsERS3PlIW1HTjoIGMUqsk1zQQwST1Yxw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"node-gyp-build": "4.3.0"
|
||||
},
|
||||
@@ -6806,6 +6807,7 @@
|
||||
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz",
|
||||
"integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"bin": {
|
||||
"node-gyp-build": "bin.js",
|
||||
"node-gyp-build-optional": "optional.js",
|
||||
@@ -19793,6 +19795,7 @@
|
||||
"resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-5.0.1.tgz",
|
||||
"integrity": "sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA==",
|
||||
"hasInstallScript": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"elliptic": "^6.5.7",
|
||||
"node-addon-api": "^5.0.0",
|
||||
@@ -19805,12 +19808,14 @@
|
||||
"node_modules/secp256k1/node_modules/bn.js": {
|
||||
"version": "4.12.0",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
|
||||
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
|
||||
"integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/secp256k1/node_modules/elliptic": {
|
||||
"version": "6.6.0",
|
||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.0.tgz",
|
||||
"integrity": "sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"bn.js": "^4.11.9",
|
||||
"brorand": "^1.1.0",
|
||||
@@ -19824,7 +19829,8 @@
|
||||
"node_modules/secp256k1/node_modules/node-addon-api": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
|
||||
"integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA=="
|
||||
"integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/seedrandom": {
|
||||
"version": "3.0.5",
|
||||
|
||||
Reference in New Issue
Block a user