mirror of
https://github.com/0xShay/ticketchain.git
synced 2026-01-11 21:23:24 +00:00
fixed metamask button
This commit is contained in:
2
.env.example
Normal file
2
.env.example
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
NEXT_PUBLIC_RPC_URL=
|
||||||
|
NEXT_PUBLIC_CONTRACT_ADDRESS=
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"extends": ["next/core-web-vitals", "next/typescript"],
|
"extends": ["next/core-web-vitals", "next/typescript"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"@next/next/no-img-element": "off"
|
"@next/next/no-img-element": "off",
|
||||||
|
"@typescript-eslint/no-explicit-any": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -26,7 +26,7 @@ yarn-debug.log*
|
|||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
|
||||||
# local env files
|
# local env files
|
||||||
.env*
|
.env
|
||||||
.local
|
.local
|
||||||
|
|
||||||
# vercel
|
# vercel
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React, { useEffect, useState, useRef } from 'react';
|
import React, { useEffect, useState, useRef, Suspense } from 'react';
|
||||||
import { useRouter } from 'next/navigation'; // Import useRouter for routing
|
import { useRouter } from 'next/navigation'; // Import useRouter for routing
|
||||||
import Header from '../../components/custom/header';
|
import Header from '../../components/custom/header';
|
||||||
import Footer from '../../components/custom/footer';
|
import Footer from '../../components/custom/footer';
|
||||||
|
import { useSearchParams } from 'next/navigation';
|
||||||
|
|
||||||
interface Event {
|
interface Event {
|
||||||
EventID: number;
|
EventID: number;
|
||||||
@@ -80,6 +81,19 @@ const EventsPage: React.FC = () => {
|
|||||||
setFilteredEvents(eventsData);
|
setFilteredEvents(eventsData);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const SearchBox = () => {
|
||||||
|
setSearchQuery(useSearchParams().get('q') || '');
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search events..."
|
||||||
|
value={searchQuery}
|
||||||
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
|
className="search-bar mt-4 p-2 border border-gray-300 rounded w-full max-w-md"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let filtered = events.filter((event) =>
|
let filtered = events.filter((event) =>
|
||||||
['name', 'date', 'location', 'description', 'host'].some((key) =>
|
['name', 'date', 'location', 'description', 'host'].some((key) =>
|
||||||
@@ -166,13 +180,20 @@ const EventsPage: React.FC = () => {
|
|||||||
|
|
||||||
<div className="relative z-20 container mx-auto p-4 pt-16">
|
<div className="relative z-20 container mx-auto p-4 pt-16">
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
|
<Suspense
|
||||||
|
fallback={
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search events..."
|
placeholder="Search events..."
|
||||||
value={searchQuery}
|
disabled={true}
|
||||||
|
value="loading..."
|
||||||
onChange={(e) => setSearchQuery(e.target.value)}
|
onChange={(e) => setSearchQuery(e.target.value)}
|
||||||
className="search-bar mt-4 p-2 border border-gray-300 rounded w-full max-w-md"
|
className="search-bar mt-4 p-2 border border-gray-300 rounded w-full max-w-md"
|
||||||
/>
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SearchBox />
|
||||||
|
</Suspense>
|
||||||
<div className="flex mt-4 space-x-4">
|
<div className="flex mt-4 space-x-4">
|
||||||
{/* Sort Button and Dropdown */}
|
{/* Sort Button and Dropdown */}
|
||||||
<div className="relative" ref={sortRef}>
|
<div className="relative" ref={sortRef}>
|
||||||
|
|||||||
99
app/page.tsx
99
app/page.tsx
@@ -1,16 +1,38 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
import Header from '../components/custom/header';
|
import Header from '../components/custom/header';
|
||||||
import Footer from '../components/custom/footer';
|
import Footer from '../components/custom/footer';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
|
import FeaturedEvent from '@/components/custom/FeaturedEvent';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { FlipWords } from '@/components/ui/flip-words';
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
|
const router = useRouter();
|
||||||
const [isClient, setIsClient] = useState(false);
|
const [isClient, setIsClient] = useState(false);
|
||||||
|
const inputRef: any = useRef(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsClient(true);
|
setIsClient(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
function searchForEvents() {
|
||||||
|
if (inputRef.current.value == '') return;
|
||||||
|
router.replace('/events?q=' + encodeURIComponent(inputRef.current.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
const words = [
|
||||||
|
'adventure',
|
||||||
|
'concert',
|
||||||
|
'outing',
|
||||||
|
'journey',
|
||||||
|
'hackathon',
|
||||||
|
'conference',
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header />
|
<Header />
|
||||||
@@ -32,34 +54,65 @@ export default function Home() {
|
|||||||
<div className="absolute inset-0 bg-black opacity-50 z-10"></div>
|
<div className="absolute inset-0 bg-black opacity-50 z-10"></div>
|
||||||
|
|
||||||
{/* Page Content Over the Video */}
|
{/* 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="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 p-4">
|
||||||
<div className="flex items-center justify-center">
|
<div className="container mx-auto justify-center items-center p-4">
|
||||||
|
<div className="text-4xl font-bold text-white text-shadow-lg">
|
||||||
|
Book your next
|
||||||
|
<FlipWords
|
||||||
|
words={words}
|
||||||
|
className="text-light-purple text-opacity-75"
|
||||||
|
/>
|
||||||
|
on the Flare blockchain.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-center mt-6 flex-col gap-4">
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search events ..."
|
placeholder="Search for your next experience..."
|
||||||
className="search-bar mt-4 p-2 border bg-white bg-opacity-25 border-gray-300 rounded-xl w-full max-w-5xl text-white"
|
className="flex w-full rounded-md border border-input bg-white bg-opacity-50 placeholder:text-black px-4 py-6 text-lg shadow-sm"
|
||||||
|
ref={inputRef}
|
||||||
/>
|
/>
|
||||||
|
<Button
|
||||||
|
className="bg-pink-600 bg-opacity-50 text-white px-4 py-6 text-lg w-full hover:bg-pink-500"
|
||||||
|
onClick={searchForEvents}
|
||||||
|
>
|
||||||
|
Search for events
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<main>
|
<main>
|
||||||
<br></br>
|
<section className="mb-8 mt-4 mx-auto grid grid-cols-4 gap-4">
|
||||||
<section className="mb-8">
|
<FeaturedEvent
|
||||||
<h2 className="text-2xl font-semibold text-white mb-4">
|
name="FAB XO Halloween"
|
||||||
Featured Events
|
description="Halloween is upon us and is one of the biggest nights of the FAB XO calendar. Fancy dress is encouraged! So have your fancy dress ready and we look forward to seeing who have the best fancy dress on the night! As a special treat we will be serving our very own witches brew!!!"
|
||||||
</h2>
|
location="Birmingham, UK"
|
||||||
<p className="text-gray-300">
|
eventStartDate={1729980000}
|
||||||
No events available at the moment.
|
eventHost="0x225C73C8c536C4F5335a2C1abECa95b0f221eeF6"
|
||||||
</p>
|
imageURL={
|
||||||
</section>
|
'https://www.guildofstudents.com/asset/Event/7572/Halloween-Fab-XO-Web-Event.jpg'
|
||||||
<section>
|
}
|
||||||
<h2 className="text-2xl font-semibold text-white mb-4">
|
/>
|
||||||
Upcoming Events
|
<FeaturedEvent
|
||||||
</h2>
|
name="Halls Halloween Spooktacular"
|
||||||
<ul className="list-disc list-inside text-gray-300">
|
description="Put on your spookiest costume and head on down to Pritchatts Park and join your Event Activators for our ResLife SPOOKTACULAR on Wednesday 30th October 5-8pm."
|
||||||
<li>Event 1 - Date</li>
|
location="Birmingham, UK"
|
||||||
<li>Event 2 - Date</li>
|
eventStartDate={1730307600}
|
||||||
<li>Event 3 - Date</li>
|
eventHost="0x225C73C8c536C4F5335a2C1abECa95b0f221eeF6"
|
||||||
</ul>
|
imageURL={
|
||||||
|
'https://www.guildofstudents.com/asset/Event/41187/Spooktacular-Web-Event-2024.png'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<FeaturedEvent
|
||||||
|
name="Housing Fair"
|
||||||
|
description="We’re hosting a Housing Fair, so make sure you save the date! Whether you’re living in student accommodation or the local community, this will be a great place to start as you begin thinking about where you’ll be living next year."
|
||||||
|
location="Birmingham, UK"
|
||||||
|
eventStartDate={1730804400}
|
||||||
|
eventHost="0x225C73C8c536C4F5335a2C1abECa95b0f221eeF6"
|
||||||
|
imageURL={
|
||||||
|
'https://www.guildofstudents.com/asset/Event/41111/Housing-Fair-Web-Event.png'
|
||||||
|
}
|
||||||
|
/>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
<Footer />
|
<Footer />
|
||||||
|
|||||||
51
components/custom/FeaturedEvent.tsx
Normal file
51
components/custom/FeaturedEvent.tsx
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
'use client';
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardHeader,
|
||||||
|
CardFooter,
|
||||||
|
CardTitle,
|
||||||
|
CardDescription,
|
||||||
|
CardContent,
|
||||||
|
} from '@/components/ui/card';
|
||||||
|
|
||||||
|
interface props {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
location: string;
|
||||||
|
eventStartDate: number;
|
||||||
|
eventHost: string;
|
||||||
|
imageURL: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FeaturedEvent = ({
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
location,
|
||||||
|
eventStartDate,
|
||||||
|
eventHost,
|
||||||
|
imageURL,
|
||||||
|
}: props) => {
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
{imageURL && <img src={imageURL} alt={name}></img>}
|
||||||
|
<CardTitle>{name}</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
{location}
|
||||||
|
<br />
|
||||||
|
{new Date(eventStartDate * 1000).toLocaleString()}
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>{description}</CardContent>
|
||||||
|
<CardFooter>
|
||||||
|
<i>
|
||||||
|
Host: {eventHost.substring(0, 8)}...
|
||||||
|
{eventHost.substring(eventHost.length - 3)}
|
||||||
|
</i>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FeaturedEvent;
|
||||||
@@ -3,7 +3,7 @@ import React from 'react';
|
|||||||
const Footer = () => {
|
const Footer = () => {
|
||||||
return (
|
return (
|
||||||
<footer className="text-center mt-8">
|
<footer className="text-center mt-8">
|
||||||
<p className="text-gray-500">
|
<p className="text-light-purple text-opacity-75">
|
||||||
© 2024 TicketChain. All rights reserved.
|
© 2024 TicketChain. All rights reserved.
|
||||||
</p>
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -36,14 +36,16 @@ const Header = () => {
|
|||||||
></div>
|
></div>
|
||||||
<div className="container mx-auto px-6 py-4 flex justify-between items-center">
|
<div className="container mx-auto px-6 py-4 flex justify-between items-center">
|
||||||
<Link href="/" legacyBehavior>
|
<Link href="/" legacyBehavior>
|
||||||
<a className="text-2xl font-semibold text-white">TicketChain</a>
|
<a className="text-2xl font-semibold text-white hover:text-light-purple hover:text-opacity-75 transition-colors duration-300">
|
||||||
|
TicketChain
|
||||||
|
</a>
|
||||||
</Link>
|
</Link>
|
||||||
<nav className="nav">
|
<nav className="nav">
|
||||||
<ul className="flex space-x-6">
|
<ul className="flex space-x-6">
|
||||||
<li>
|
<li>
|
||||||
<Link href="/" legacyBehavior>
|
<Link href="/" legacyBehavior>
|
||||||
<a
|
<a
|
||||||
className="text-white hover:text-blue-500 transition-colors duration-300"
|
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)' }}
|
style={{ textShadow: '1px 1px 2px rgba(0, 0, 0, 0.5)' }}
|
||||||
>
|
>
|
||||||
Home
|
Home
|
||||||
@@ -53,7 +55,7 @@ const Header = () => {
|
|||||||
<li>
|
<li>
|
||||||
<Link href="/events" legacyBehavior>
|
<Link href="/events" legacyBehavior>
|
||||||
<a
|
<a
|
||||||
className="text-white hover:text-blue-500 transition-colors duration-300"
|
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)' }}
|
style={{ textShadow: '1px 1px 2px rgba(0, 0, 0, 0.5)' }}
|
||||||
>
|
>
|
||||||
Events
|
Events
|
||||||
@@ -63,7 +65,7 @@ const Header = () => {
|
|||||||
<li>
|
<li>
|
||||||
<Link href="/contact" legacyBehavior>
|
<Link href="/contact" legacyBehavior>
|
||||||
<a
|
<a
|
||||||
className="text-white hover:text-blue-500 transition-colors duration-300"
|
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)' }}
|
style={{ textShadow: '1px 1px 2px rgba(0, 0, 0, 0.5)' }}
|
||||||
>
|
>
|
||||||
Contact
|
Contact
|
||||||
|
|||||||
98
components/ui/flip-words.tsx
Normal file
98
components/ui/flip-words.tsx
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
'use client';
|
||||||
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
export const FlipWords = ({
|
||||||
|
words,
|
||||||
|
duration = 3000,
|
||||||
|
className,
|
||||||
|
}: {
|
||||||
|
words: string[];
|
||||||
|
duration?: number;
|
||||||
|
className?: string;
|
||||||
|
}) => {
|
||||||
|
const [currentWord, setCurrentWord] = useState(words[0]);
|
||||||
|
const [isAnimating, setIsAnimating] = useState<boolean>(false);
|
||||||
|
|
||||||
|
// thanks for the fix Julian - https://github.com/Julian-AT
|
||||||
|
const startAnimation = useCallback(() => {
|
||||||
|
const word = words[words.indexOf(currentWord) + 1] || words[0];
|
||||||
|
setCurrentWord(word);
|
||||||
|
setIsAnimating(true);
|
||||||
|
}, [currentWord, words]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isAnimating)
|
||||||
|
setTimeout(() => {
|
||||||
|
startAnimation();
|
||||||
|
}, duration);
|
||||||
|
}, [isAnimating, duration, startAnimation]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AnimatePresence
|
||||||
|
onExitComplete={() => {
|
||||||
|
setIsAnimating(false);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<motion.div
|
||||||
|
initial={{
|
||||||
|
opacity: 0,
|
||||||
|
y: 10,
|
||||||
|
}}
|
||||||
|
animate={{
|
||||||
|
opacity: 1,
|
||||||
|
y: 0,
|
||||||
|
}}
|
||||||
|
transition={{
|
||||||
|
type: 'spring',
|
||||||
|
stiffness: 100,
|
||||||
|
damping: 10,
|
||||||
|
}}
|
||||||
|
exit={{
|
||||||
|
opacity: 0,
|
||||||
|
y: -40,
|
||||||
|
x: 40,
|
||||||
|
filter: 'blur(8px)',
|
||||||
|
scale: 2,
|
||||||
|
position: 'absolute',
|
||||||
|
}}
|
||||||
|
className={cn(
|
||||||
|
'z-10 inline-block relative text-left text-neutral-900 dark:text-neutral-100 px-2',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
key={currentWord}
|
||||||
|
>
|
||||||
|
{/* edit suggested by Sajal: https://x.com/DewanganSajal */}
|
||||||
|
{currentWord.split(' ').map((word, wordIndex) => (
|
||||||
|
<motion.span
|
||||||
|
key={word + wordIndex}
|
||||||
|
initial={{ opacity: 0, y: 10, filter: 'blur(8px)' }}
|
||||||
|
animate={{ opacity: 1, y: 0, filter: 'blur(0px)' }}
|
||||||
|
transition={{
|
||||||
|
delay: wordIndex * 0.3,
|
||||||
|
duration: 0.3,
|
||||||
|
}}
|
||||||
|
className="inline-block whitespace-nowrap"
|
||||||
|
>
|
||||||
|
{word.split('').map((letter, letterIndex) => (
|
||||||
|
<motion.span
|
||||||
|
key={word + letterIndex}
|
||||||
|
initial={{ opacity: 0, y: 10, filter: 'blur(8px)' }}
|
||||||
|
animate={{ opacity: 1, y: 0, filter: 'blur(0px)' }}
|
||||||
|
transition={{
|
||||||
|
delay: wordIndex * 0.3 + letterIndex * 0.05,
|
||||||
|
duration: 0.2,
|
||||||
|
}}
|
||||||
|
className="inline-block"
|
||||||
|
>
|
||||||
|
{letter}
|
||||||
|
</motion.span>
|
||||||
|
))}
|
||||||
|
<span className="inline-block"> </span>
|
||||||
|
</motion.span>
|
||||||
|
))}
|
||||||
|
</motion.div>
|
||||||
|
</AnimatePresence>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -37,7 +37,8 @@ export const buyHandler = async (
|
|||||||
const signer = provider.getSigner();
|
const signer = provider.getSigner();
|
||||||
const contract = getContract().connect(signer);
|
const contract = getContract().connect(signer);
|
||||||
|
|
||||||
const ticketCost = await contract.getEventPriceFlare(eventId);
|
let ticketCost = await contract.getEventPriceFlare(eventId);
|
||||||
|
ticketCost = ticketCost.mul(105).div(100);
|
||||||
const balance = await provider.getBalance(await signer.getAddress());
|
const balance = await provider.getBalance(await signer.getAddress());
|
||||||
|
|
||||||
if (balance.lt(ticketCost)) {
|
if (balance.lt(ticketCost)) {
|
||||||
|
|||||||
10
package-lock.json
generated
10
package-lock.json
generated
@@ -25,7 +25,7 @@
|
|||||||
"next": "14.2.13",
|
"next": "14.2.13",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"tailwind-merge": "^2.5.2",
|
"tailwind-merge": "^2.5.4",
|
||||||
"tailwindcss-animate": "^1.0.7"
|
"tailwindcss-animate": "^1.0.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -39,6 +39,7 @@
|
|||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
|
"@types/tailwindcss": "^3.0.11",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
"eslint": "^8",
|
"eslint": "^8",
|
||||||
"eslint-config-next": "14.2.13",
|
"eslint-config-next": "14.2.13",
|
||||||
@@ -7495,6 +7496,13 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/tailwindcss": {
|
||||||
|
"version": "3.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/tailwindcss/-/tailwindcss-3.0.11.tgz",
|
||||||
|
"integrity": "sha512-PR+BOIrI+rxteHwFvkfIOty+PDJwTG4ute3alxSSXpF/xKpryO1room265m46Auyae0VwqUYs3PuVEOF9Oil3w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/yargs": {
|
"node_modules/@types/yargs": {
|
||||||
"version": "17.0.33",
|
"version": "17.0.33",
|
||||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz",
|
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz",
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
"next": "14.2.13",
|
"next": "14.2.13",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"tailwind-merge": "^2.5.2",
|
"tailwind-merge": "^2.5.4",
|
||||||
"tailwindcss-animate": "^1.0.7"
|
"tailwindcss-animate": "^1.0.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -43,6 +43,7 @@
|
|||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
|
"@types/tailwindcss": "^3.0.11",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
"eslint": "^8",
|
"eslint": "^8",
|
||||||
"eslint-config-next": "14.2.13",
|
"eslint-config-next": "14.2.13",
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
import type { Config } from 'tailwindcss';
|
import type { Config } from 'tailwindcss';
|
||||||
|
import tailwindcssAnimate from 'tailwindcss-animate';
|
||||||
|
|
||||||
|
type ColorValue = string | ColorDictionary;
|
||||||
|
interface ColorDictionary {
|
||||||
|
[colorName: string]: ColorValue;
|
||||||
|
}
|
||||||
|
|
||||||
const config: Config = {
|
const config: Config = {
|
||||||
darkMode: ['class'],
|
darkMode: ['class'],
|
||||||
@@ -14,6 +20,12 @@ const config: Config = {
|
|||||||
DEFAULT: '#000',
|
DEFAULT: '#000',
|
||||||
100: '#000319',
|
100: '#000319',
|
||||||
},
|
},
|
||||||
|
'darkest-purple': '#240046',
|
||||||
|
'darker-purple': '#3C096C',
|
||||||
|
'dark-purple': '#5A189A',
|
||||||
|
purple: '#7B2CBF',
|
||||||
|
'light-purple': '#9D4EDD',
|
||||||
|
|
||||||
background: 'hsl(var(--background))',
|
background: 'hsl(var(--background))',
|
||||||
foreground: 'hsl(var(--foreground))',
|
foreground: 'hsl(var(--foreground))',
|
||||||
card: {
|
card: {
|
||||||
@@ -62,6 +74,44 @@ const config: Config = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [require('tailwindcss-animate')],
|
plugins: [tailwindcssAnimate, addVariablesForColors],
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|
||||||
|
// Plugin to add each Tailwind color as a CSS variable
|
||||||
|
function addVariablesForColors({
|
||||||
|
addBase,
|
||||||
|
theme,
|
||||||
|
}: {
|
||||||
|
addBase: (styles: Record<string, Record<string, string>>) => void;
|
||||||
|
theme: (path: string) => unknown;
|
||||||
|
}) {
|
||||||
|
const colors = theme('colors') as ColorDictionary;
|
||||||
|
const flattenedColors = flattenColors(colors);
|
||||||
|
const newVars = Object.fromEntries(
|
||||||
|
Object.entries(flattenedColors).map(([key, val]) => [`--${key}`, val])
|
||||||
|
);
|
||||||
|
|
||||||
|
addBase({
|
||||||
|
':root': newVars,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursive function to flatten nested color objects
|
||||||
|
function flattenColors(
|
||||||
|
colors: ColorDictionary,
|
||||||
|
prefix = ''
|
||||||
|
): Record<string, string> {
|
||||||
|
const result: Record<string, string> = {};
|
||||||
|
for (const [key, value] of Object.entries(colors)) {
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
// If the value is a string, add it to the result
|
||||||
|
result[prefix + key] = value;
|
||||||
|
} else if (typeof value === 'object' && value !== null) {
|
||||||
|
// If the value is an object, recursively flatten it
|
||||||
|
Object.assign(result, flattenColors(value, `${prefix}${key}-`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user