mirror of
https://github.com/ryanamay/twinkle.git
synced 2024-09-20 08:20:33 +00:00
Compare commits
2 commits
1f64c881b9
...
43a99125c3
Author | SHA1 | Date | |
---|---|---|---|
43a99125c3 | |||
2e41c46a1d |
14 changed files with 437 additions and 34 deletions
10
package-lock.json
generated
10
package-lock.json
generated
|
@ -12,6 +12,7 @@
|
||||||
"next": "14.2.8",
|
"next": "14.2.8",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
|
"react-icons": "^5.3.0",
|
||||||
"recharts": "^2.12.7"
|
"recharts": "^2.12.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -4452,6 +4453,15 @@
|
||||||
"react": "^18.3.1"
|
"react": "^18.3.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-icons": {
|
||||||
|
"version": "5.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz",
|
||||||
|
"integrity": "sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-is": {
|
"node_modules/react-is": {
|
||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
"next": "14.2.8",
|
"next": "14.2.8",
|
||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
|
"react-icons": "^5.3.0",
|
||||||
"recharts": "^2.12.7"
|
"recharts": "^2.12.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
const Header = () => (
|
|
||||||
<header className="py-4">
|
|
||||||
<h1 className="text-3xl font-bold">
|
|
||||||
Stock Price Application
|
|
||||||
</h1>
|
|
||||||
</header>
|
|
||||||
)
|
|
||||||
|
|
||||||
export default Header
|
|
86
src/components/NavigationBar.tsx
Normal file
86
src/components/NavigationBar.tsx
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
import SearchBar from './SearchBar'
|
||||||
|
import Link from 'next/link'
|
||||||
|
import ThemeSwitcher from './ThemeSwitcher'
|
||||||
|
|
||||||
|
interface NavigationBarProps {
|
||||||
|
onSelectSymbol: (symbol: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const NavigationBar = ({ onSelectSymbol }: NavigationBarProps) => {
|
||||||
|
const [currency, setCurrency] = useState('USD')
|
||||||
|
const [watchlistView, setWatchlistView] = useState('priceChange')
|
||||||
|
const [dropdownOpen, setDropdownOpen] = useState(false)
|
||||||
|
|
||||||
|
const handleCurrencyChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||||
|
setCurrency(event.target.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleWatchlistViewChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||||
|
setWatchlistView(event.target.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav className="bg-base text-text">
|
||||||
|
<div className="container mx-auto max-w-8xl flex items-center justify-between py-4 px-4 lg:px-8">
|
||||||
|
<Link className="text-lg font-bold" href="/">TWL
|
||||||
|
</Link>
|
||||||
|
<div className="flex-grow max-w-md mx-auto lg:max-w-lg w-full">
|
||||||
|
<SearchBar onSelectSymbol={onSelectSymbol} />
|
||||||
|
</div>
|
||||||
|
<div className="relative flex items-center space-x-4">
|
||||||
|
<ThemeSwitcher />
|
||||||
|
<button
|
||||||
|
onClick={() => setDropdownOpen(!dropdownOpen)}
|
||||||
|
className="bg-mantle px-4 py-2 rounded focus:outline-none focus:bg-overlay0"
|
||||||
|
>
|
||||||
|
X
|
||||||
|
</button>
|
||||||
|
{dropdownOpen && (
|
||||||
|
<div className="absolute right-0 mt-2 w-48 bg-surface0 rounded-md shadow-lg z-20">
|
||||||
|
<div className="py-2 px-4">
|
||||||
|
<label
|
||||||
|
htmlFor="currency"
|
||||||
|
className="block text-sm font-medium text-text"
|
||||||
|
>
|
||||||
|
Currency:
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
id="currency"
|
||||||
|
value={currency}
|
||||||
|
onChange={handleCurrencyChange}
|
||||||
|
className="mt-1 block w-full p-1 rounded border border-overlay2"
|
||||||
|
>
|
||||||
|
<option value="USD">USD</option>
|
||||||
|
<option value="EUR">EUR</option>
|
||||||
|
<option value="JPY">JPY</option>
|
||||||
|
<option value="GBP">GBP</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className="py-2 px-4">
|
||||||
|
<label
|
||||||
|
htmlFor="watchlistView"
|
||||||
|
className="block text-sm font-medium text-text"
|
||||||
|
>
|
||||||
|
View:
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
id="watchlistView"
|
||||||
|
value={watchlistView}
|
||||||
|
onChange={handleWatchlistViewChange}
|
||||||
|
className="mt-1 block w-full p-1 rounded border border-overlay2"
|
||||||
|
>
|
||||||
|
<option value="priceChange">Price Change</option>
|
||||||
|
<option value="percentageChange">Percentage Change</option>
|
||||||
|
<option value="marketCap">Market Cap</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NavigationBar
|
|
@ -1,7 +1,11 @@
|
||||||
import { useState, useEffect, useCallback } from 'react'
|
import { useState, useEffect, useCallback } from 'react'
|
||||||
import debounce from 'lodash.debounce'
|
import debounce from 'lodash.debounce'
|
||||||
|
|
||||||
const SearchBar = ({ onSelectSymbol }: { onSelectSymbol: (symbol: string) => void }) => {
|
interface SearchBarProps {
|
||||||
|
onSelectSymbol: (symbol: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const SearchBar = ({ onSelectSymbol }: SearchBarProps) => {
|
||||||
const [query, setQuery] = useState('')
|
const [query, setQuery] = useState('')
|
||||||
const [suggestions, setSuggestions] = useState<{ symbol: string, description: string }[]>([])
|
const [suggestions, setSuggestions] = useState<{ symbol: string, description: string }[]>([])
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
@ -13,7 +17,6 @@ const SearchBar = ({ onSelectSymbol }: { onSelectSymbol: (symbol: string) => voi
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/search?query=${query}`)
|
const res = await fetch(`/api/search?query=${query}`)
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
|
|
||||||
if (data.result && data.result.length > 0) {
|
if (data.result && data.result.length > 0) {
|
||||||
setSuggestions(data.result)
|
setSuggestions(data.result)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
import { useState } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
|
import Ticker from './Ticker'
|
||||||
import SearchBar from './SearchBar'
|
import SearchBar from './SearchBar'
|
||||||
|
|
||||||
const StockPrice = () => {
|
interface StockPriceProps {
|
||||||
const [symbol, setSymbol] = useState('')
|
symbol: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const StockPrice = ({ symbol }: StockPriceProps) => {
|
||||||
const [price, setPrice] = useState<number | null>(null)
|
const [price, setPrice] = useState<number | null>(null)
|
||||||
const [error, setError] = useState('')
|
const [error, setError] = useState('')
|
||||||
|
|
||||||
|
@ -13,18 +17,27 @@ const StockPrice = () => {
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
setError(data.error)
|
setError(data.error)
|
||||||
|
setPrice(null)
|
||||||
} else {
|
} else {
|
||||||
setPrice(data.c)
|
setPrice(data.c)
|
||||||
setSymbol(selectedSymbol)
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setError('Failed to fetch stock price')
|
setError('Failed to fetch stock price')
|
||||||
|
setPrice(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let intervalId: NodeJS.Timeout
|
||||||
|
if (symbol) {
|
||||||
|
fetchStockPrice(symbol)
|
||||||
|
intervalId = setInterval(() => fetchStockPrice(symbol), 300000) // 300000 ms = 5 minutes
|
||||||
|
}
|
||||||
|
return () => clearInterval(intervalId)
|
||||||
|
}, [symbol])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="my-4">
|
<div className="my-4">
|
||||||
<SearchBar onSelectSymbol={fetchStockPrice} />
|
|
||||||
{symbol && (
|
{symbol && (
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-2xl font-bold mt-4">Symbol: {symbol}</h2>
|
<h2 className="text-2xl font-bold mt-4">Symbol: {symbol}</h2>
|
||||||
|
@ -38,6 +51,7 @@ const StockPrice = () => {
|
||||||
<p>{error}</p>
|
<p>{error}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<Ticker symbol={symbol} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
28
src/components/ThemeSwitcher.tsx
Normal file
28
src/components/ThemeSwitcher.tsx
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
const ThemeSwitcher = () => {
|
||||||
|
const [theme, setTheme] = useState('light');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (theme === 'dark') {
|
||||||
|
document.documentElement.classList.add('dark');
|
||||||
|
} else {
|
||||||
|
document.documentElement.classList.remove('dark');
|
||||||
|
}
|
||||||
|
}, [theme]);
|
||||||
|
|
||||||
|
const handleThemeToggle = () => {
|
||||||
|
setTheme(theme === 'light' ? 'dark' : 'light');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={handleThemeToggle}
|
||||||
|
className="px-4 py-2 bg-blue-600 text-white rounded-md"
|
||||||
|
>
|
||||||
|
O
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ThemeSwitcher;
|
112
src/components/Ticker.tsx
Normal file
112
src/components/Ticker.tsx
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { fetchQuote } from '../utils/sparkle'
|
||||||
|
import { tradeConditions } from '../utils/tradeConditions'
|
||||||
|
|
||||||
|
interface Trade {
|
||||||
|
p: number; // Price
|
||||||
|
s: string; // Symbol
|
||||||
|
t: number; // Timestamp
|
||||||
|
v: number; // Volume
|
||||||
|
c?: number[]; // Conditions (DOES NOT EXIST FOR ALL TRADES)
|
||||||
|
}
|
||||||
|
|
||||||
|
const Ticker = ({ symbol }: { symbol: string }) => {
|
||||||
|
const [trades, setTrades] = useState<Trade[]>([])
|
||||||
|
const [bid, setBid] = useState<number | null>(null)
|
||||||
|
const [ask, setAsk] = useState<number | null>(null)
|
||||||
|
const [webSocketInitialized, setWebSocketInitialized] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Fetch initial bid and ask prices
|
||||||
|
const initializePrices = async () => {
|
||||||
|
try {
|
||||||
|
const quote = await fetchQuote(symbol)
|
||||||
|
setBid(quote.b)
|
||||||
|
setAsk(quote.a)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch initial bid/ask prices:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initializePrices()
|
||||||
|
}, [symbol])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const initializeWebSocket = async () => {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/ws/start')
|
||||||
|
const data = await response.json()
|
||||||
|
if (data.status === 'WebSocket server is running') {
|
||||||
|
setWebSocketInitialized(true)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to initialize WebSocket:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeWebSocket()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!webSocketInitialized) return
|
||||||
|
const socket = new WebSocket(`${process.env.NEXT_PUBLIC_SPARKLE_BASE_URL!.replace('http', 'ws')}/ws/trades`)
|
||||||
|
|
||||||
|
socket.onopen = () => {
|
||||||
|
console.log('WebSocket connection established')
|
||||||
|
socket.send(JSON.stringify({ type: 'subscribe', symbol }))
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.onmessage = (event) => {
|
||||||
|
const data = JSON.parse(event.data)
|
||||||
|
if (data.type === 'trade') {
|
||||||
|
setTrades((prevTrades) => [...data.data, ...prevTrades])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.onclose = () => {
|
||||||
|
console.log('WebSocket connection closed')
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.send(JSON.stringify({ type: 'unsubscribe', symbol }))
|
||||||
|
socket.close()
|
||||||
|
}
|
||||||
|
}, [symbol, webSocketInitialized])
|
||||||
|
|
||||||
|
const identifyTradeType = (trade: Trade) => {
|
||||||
|
if (bid !== null && ask !== null) {
|
||||||
|
if (trade.p >= ask) {
|
||||||
|
return 'Buy'
|
||||||
|
} else if (trade.p <= bid) {
|
||||||
|
return 'Sell'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 'Unknown'
|
||||||
|
}
|
||||||
|
|
||||||
|
const getTradeConditions = (trade: Trade) => {
|
||||||
|
if (trade.c && trade.c.length > 0) {
|
||||||
|
return trade.c.map(code => tradeConditions[code] || `Unknown Condition: ${code}`).join(', ')
|
||||||
|
}
|
||||||
|
return 'No conditions'
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mt-4">
|
||||||
|
<h2 className="text-2xl font-bold">Latest Trades for {symbol}</h2>
|
||||||
|
<ul className="mt-2">
|
||||||
|
{trades.slice(0, 5).map((trade, index) => (
|
||||||
|
<li key={index} className="p-2 border-b">
|
||||||
|
<div>Price: ${trade.p}</div>
|
||||||
|
<div>Volume: {trade.v}</div>
|
||||||
|
<div>Time: {new Date(trade.t).toLocaleTimeString()}</div>
|
||||||
|
<div>Type: {identifyTradeType(trade)}</div>
|
||||||
|
<div>Conditions: {getTradeConditions(trade)}</div>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Ticker
|
13
src/pages/api/ws/start.ts
Normal file
13
src/pages/api/ws/start.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { startWebSocket } from '@/utils/sparkle'
|
||||||
|
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||||
|
|
||||||
|
let isWebSocketRunning = false
|
||||||
|
|
||||||
|
export default function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
if (!isWebSocketRunning) {
|
||||||
|
startWebSocket();
|
||||||
|
isWebSocketRunning = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(200).json({ status: 'WebSocket server is running' })
|
||||||
|
}
|
|
@ -1,11 +1,20 @@
|
||||||
import Header from '../components/Header'
|
import { useState } from 'react'
|
||||||
|
import NavigationBar from '../components/NavigationBar'
|
||||||
import StockPrice from '../components/StockPrice'
|
import StockPrice from '../components/StockPrice'
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
|
const [symbol, setSymbol] = useState('')
|
||||||
|
|
||||||
|
const handleSelectSymbol = (selectedSymbol: string) => {
|
||||||
|
setSymbol(selectedSymbol)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container mx-auto p-4">
|
<div>
|
||||||
<Header />
|
<NavigationBar onSelectSymbol={handleSelectSymbol} />
|
||||||
<StockPrice />
|
<div className="container mx-auto p-4">
|
||||||
|
<StockPrice symbol={symbol} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -1,3 +1,42 @@
|
||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--color-base: #ebe6df; /* Latte base color */
|
||||||
|
--color-mantle: #e2ddd6;
|
||||||
|
--color-crust: #f5f4ed;
|
||||||
|
--color-surface0: #f2e7dc;
|
||||||
|
--color-surface1: #ece5df;
|
||||||
|
--color-surface2: #f4ede8;
|
||||||
|
--color-overlay0: #dad5d0;
|
||||||
|
--color-overlay1: #e3ded8;
|
||||||
|
--color-overlay2: #edebe7;
|
||||||
|
--color-text: #4c4f52;
|
||||||
|
--color-subtext1: #55595e;
|
||||||
|
--color-subtext0: #5f6368;
|
||||||
|
--background: var(--color-base);
|
||||||
|
--foreground: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--color-base: #1e1e2e; /* Mocha base color */
|
||||||
|
--color-mantle: #181825;
|
||||||
|
--color-crust: #13111e;
|
||||||
|
--color-surface0: #313244;
|
||||||
|
--color-surface1: #3a3c51;
|
||||||
|
--color-surface2: #4e4f68;
|
||||||
|
--color-overlay0: #6c6f85;
|
||||||
|
--color-overlay1: #898ba5;
|
||||||
|
--color-overlay2: #aabbcc;
|
||||||
|
--color-text: #cdd6f4;
|
||||||
|
--color-subtext1: #bac2de;
|
||||||
|
--color-subtext0: #a6adc8;
|
||||||
|
--background: var(--color-base);
|
||||||
|
--foreground: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--background);
|
||||||
|
color: var(--foreground);
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
const SPARKLE_BASE_URL = process.env.NEXT_PUBLIC_SPARKLE_BASE_URL;
|
const SPARKLE_BASE_URL = process.env.NEXT_PUBLIC_SPARKLE_BASE_URL;
|
||||||
|
|
||||||
export const fetchQuote = async (symbol: string) => {
|
export const fetchQuote = async (symbol: string) => {
|
||||||
const url = `${SPARKLE_BASE_URL}/quote?symbol=${symbol}`
|
const url = `${SPARKLE_BASE_URL}/api/v1/quote?symbol=${symbol}`
|
||||||
const res = await fetch(url)
|
const res = await fetch(url)
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
throw new Error('Error fetching quote')
|
throw new Error('Error fetching quote')
|
||||||
|
@ -10,10 +10,19 @@ export const fetchQuote = async (symbol: string) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchSymbols = async (symbol: string) => {
|
export const fetchSymbols = async (symbol: string) => {
|
||||||
const url = `${SPARKLE_BASE_URL}/search?query=${symbol}`
|
const url = `${SPARKLE_BASE_URL}/api/v1/search?query=${symbol}`
|
||||||
const res = await fetch(url)
|
const res = await fetch(url)
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
throw new Error('Error fetching quote')
|
throw new Error('Error fetching quote')
|
||||||
}
|
}
|
||||||
return res.json()
|
return res.json()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const startWebSocket = async () => {
|
||||||
|
const url = `${SPARKLE_BASE_URL}/ws/start-websocket`
|
||||||
|
const res = await fetch(url)
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error('Error starting WebSocket')
|
||||||
|
}
|
||||||
|
return res.json()
|
||||||
|
}
|
43
src/utils/tradeConditions.ts
Normal file
43
src/utils/tradeConditions.ts
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
export const tradeConditions: Record<number, string> = {
|
||||||
|
1: "Regular",
|
||||||
|
2: "Acquisition",
|
||||||
|
3: "Average Price Trade",
|
||||||
|
4: "Bunched",
|
||||||
|
5: "Cash Sale",
|
||||||
|
6: "Distribution",
|
||||||
|
7: "Automatic Execution",
|
||||||
|
8: "Intermarket Sweep Order",
|
||||||
|
9: "Bunched Sold",
|
||||||
|
10: "Price Variation Trade",
|
||||||
|
11: "Cap Election",
|
||||||
|
12: "Odd Lot Trade",
|
||||||
|
13: "Rule 127",
|
||||||
|
14: "Rule 155",
|
||||||
|
15: "Sold last",
|
||||||
|
16: "Market Center Official Close",
|
||||||
|
17: "Next day",
|
||||||
|
18: "Market Center Opening Trade",
|
||||||
|
19: "Opening Prints",
|
||||||
|
20: "Market Center Official Open",
|
||||||
|
21: "Prior Reference Price",
|
||||||
|
22: "Seller",
|
||||||
|
23: "Split Trade",
|
||||||
|
24: "Form-T Trade",
|
||||||
|
25: "Extended Hours (Sold Out of Sequence)",
|
||||||
|
26: "Contingent Trade",
|
||||||
|
27: "Stock Option Trade",
|
||||||
|
28: "Cross Trade",
|
||||||
|
29: "Yellow Flag",
|
||||||
|
30: "Sold (Out of Sequence)",
|
||||||
|
31: "Stopped Stock",
|
||||||
|
32: "Derivatively Priced",
|
||||||
|
33: "Market Center Re-opening Trade",
|
||||||
|
34: "Re-opening Prints",
|
||||||
|
35: "Market Center Closing Trade",
|
||||||
|
36: "Closing Prints",
|
||||||
|
37: "Qualified Contingent Trade",
|
||||||
|
38: "Placeholder for 611 Exempt",
|
||||||
|
39: "Corrected Consolidated Close",
|
||||||
|
40: "Opened",
|
||||||
|
41: "Trade Through Exempt (TTE)",
|
||||||
|
};
|
|
@ -1,19 +1,64 @@
|
||||||
import type { Config } from "tailwindcss";
|
import type { Config } from 'tailwindcss'
|
||||||
|
|
||||||
|
const latte = {
|
||||||
|
base: '#ebe6df',
|
||||||
|
mantle: '#e2ddd6',
|
||||||
|
crust: '#f5f4ed',
|
||||||
|
surface0: '#f2e7dc',
|
||||||
|
surface1: '#ece5df',
|
||||||
|
surface2: '#f4ede8',
|
||||||
|
overlay0: '#dad5d0',
|
||||||
|
overlay1: '#e3ded8',
|
||||||
|
overlay2: '#edebe7',
|
||||||
|
text: '#4c4f52',
|
||||||
|
subtext1: '#55595e',
|
||||||
|
subtext0: '#5f6368'
|
||||||
|
}
|
||||||
|
|
||||||
|
const mocha = {
|
||||||
|
base: '#1e1e2e',
|
||||||
|
mantle: '#181825',
|
||||||
|
crust: '#13111e',
|
||||||
|
surface0: '#313244',
|
||||||
|
surface1: '#3a3c51',
|
||||||
|
surface2: '#4e4f68',
|
||||||
|
overlay0: '#6c6f85',
|
||||||
|
overlay1: '#898ba5',
|
||||||
|
overlay2: '#aabbcc',
|
||||||
|
text: '#cdd6f4',
|
||||||
|
subtext1: '#bac2de',
|
||||||
|
subtext0: '#a6adc8'
|
||||||
|
}
|
||||||
|
|
||||||
const config: Config = {
|
const config: Config = {
|
||||||
content: [
|
content: [
|
||||||
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
|
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
|
||||||
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
|
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
|
||||||
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
|
'./src/app/**/*.{js,ts,jsx,tsx,mdx}'
|
||||||
],
|
],
|
||||||
|
darkMode: 'class',
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
background: "var(--background)",
|
background: 'var(--background)',
|
||||||
foreground: "var(--foreground)",
|
foreground: 'var(--foreground)',
|
||||||
},
|
base: 'var(--color-base)',
|
||||||
},
|
mantle: 'var(--color-mantle)',
|
||||||
|
crust: 'var(--color-crust)',
|
||||||
|
surface0: 'var(--color-surface0)',
|
||||||
|
surface1: 'var(--color-surface1)',
|
||||||
|
surface2: 'var(--color-surface2)',
|
||||||
|
overlay0: 'var(--color-overlay0)',
|
||||||
|
overlay1: 'var(--color-overlay1)',
|
||||||
|
overlay2: 'var(--color-overlay2)',
|
||||||
|
text: 'var(--color-text)',
|
||||||
|
subtext1: 'var(--color-subtext1)',
|
||||||
|
latte,
|
||||||
|
mocha
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: []
|
||||||
};
|
}
|
||||||
export default config;
|
|
||||||
|
export default config
|
Loading…
Reference in a new issue