Enhance layout and styling: Update index.html for full-width root div, adjust tailwind.config.js for container padding and screen sizes, refactor NavBar and LogIn components for improved user experience, and apply global styles for consistent layout across the application

This commit is contained in:
2025-11-08 04:08:29 +07:00
parent f16e4e0d48
commit 7b8d435521
11 changed files with 407 additions and 243 deletions

View File

@@ -7,7 +7,7 @@
<title>Managing</title> <title>Managing</title>
</head> </head>
<body> <body>
<div id="root" class="min-h-screen flex flex-col"></div> <div id="root" class="min-h-screen flex flex-col w-full"></div>
<script> <script>
global = globalThis global = globalThis
</script> </script>

View File

@@ -7,7 +7,6 @@ import useApiUrlStore from '../../../app/store/apiStore'
import {UserClient} from '../../../generated/ManagingApi' import {UserClient} from '../../../generated/ManagingApi'
import type {ILoginFormInput} from '../../../global/type.tsx' import type {ILoginFormInput} from '../../../global/type.tsx'
import useCookie from '../../../hooks/useCookie' import useCookie from '../../../hooks/useCookie'
import {SecondaryNavbar} from '../NavBar/NavBar'
import Toast from '../Toast/Toast' import Toast from '../Toast/Toast'
const LogIn = () => { const LogIn = () => {
@@ -82,10 +81,6 @@ const LogIn = () => {
return ( return (
<> <>
<div style={{ display: 'flex', justifyContent: 'right' }}>
<SecondaryNavbar />
</div>
<section className="bg-gray-50 dark:bg-gray-900"> <section className="bg-gray-50 dark:bg-gray-900">
<div className="md:h-screen lg:py-0 flex flex-col items-center justify-center px-6 py-8 mx-auto"> <div className="md:h-screen lg:py-0 flex flex-col items-center justify-center px-6 py-8 mx-auto">
<div className="dark:border md:mt-0 sm:max-w-md xl:p-0 dark:bg-gray-800 dark:border-gray-700 w-full bg-white rounded-lg shadow"> <div className="dark:border md:mt-0 sm:max-w-md xl:p-0 dark:bg-gray-800 dark:border-gray-700 w-full bg-white rounded-lg shadow">

View File

@@ -1,10 +1,8 @@
import {useIsFetching, useQuery} from '@tanstack/react-query' import {useIsFetching, useQuery} from '@tanstack/react-query'
import {usePrivy} from '@privy-io/react-auth' import {usePrivy} from '@privy-io/react-auth'
import type {ReactNode} from 'react'
import {useState} from 'react' import {useState} from 'react'
import {Link} from 'react-router-dom' import {Link, NavLink} from 'react-router-dom'
import {NavItem} from '..'
import useApiUrlStore from '../../../app/store/apiStore' import useApiUrlStore from '../../../app/store/apiStore'
import {UserClient} from '../../../generated/ManagingApi' import {UserClient} from '../../../generated/ManagingApi'
import Logo from '../../../assets/img/logo.png' import Logo from '../../../assets/img/logo.png'
@@ -20,46 +18,51 @@ const navigation = [
{ href: '/admin', name: 'Admin' }, { href: '/admin', name: 'Admin' },
] ]
function navItems(isMobile = false) { // Global Loader Component
return navigation.map((item) => ( const GlobalLoader = () => {
<NavItem key={item.name} href={item.href} isMobile={isMobile}> const isFetching = useIsFetching()
{item.name} return isFetching ? (
</NavItem> <div className="flex items-center gap-2">
)) <Loader size="xs"></Loader>
<span className="text-xs text-base-content/70 hidden lg:inline">Loading...</span>
</div>
) : null
} }
function PrimaryNavbar() { // Environment Badge Component
const EnvironmentBadge = ({ environment }: { environment: 'local' | 'sandbox' | 'production' }) => {
const badgeColors = {
local: 'badge-warning',
sandbox: 'badge-info',
production: 'badge-success',
}
const badgeLabels = {
local: 'Local',
sandbox: 'Sandbox',
production: 'Production',
}
return ( return (
<div className="flex"> <div className={`badge ${badgeColors[environment]} badge-sm font-semibold`}>
<Link className="btn btn-ghost text-xl normal-case" to={'/'}> {badgeLabels[environment]}
<img src={Logo} className="App-logo" alt="logo" />
</Link>
{/* <NavItem href="#" /> */}
<div className="md:flex items-center hidden space-x-1">{navItems()}</div>
</div> </div>
) )
} }
const GlobalLoader = () => { // User Profile Dropdown Component
const isFetching = useIsFetching() const UserProfileDropdown = () => {
return isFetching ? <Loader size="xs"></Loader> : null
}
// Custom Privy wallet button component
const PrivyWalletButton = () => {
const { login, logout, authenticated, user } = usePrivy() const { login, logout, authenticated, user } = usePrivy()
const { apiUrl } = useApiUrlStore() const { apiUrl } = useApiUrlStore()
const { getCookie } = useCookie() const { getCookie } = useCookie()
const api = new UserClient({}, apiUrl) const api = new UserClient({}, apiUrl)
// Get JWT token from cookies
const jwtToken = getCookie('token') const jwtToken = getCookie('token')
// Fetch user information from the API
const { data: userInfo } = useQuery({ const { data: userInfo } = useQuery({
queryKey: ['user'], queryKey: ['user'],
queryFn: () => api.user_GetCurrentUser(), queryFn: () => api.user_GetCurrentUser(),
enabled: authenticated && !!jwtToken, // Only fetch when authenticated AND JWT token exists enabled: authenticated && !!jwtToken,
}) })
if (!authenticated) { if (!authenticated) {
@@ -73,8 +76,7 @@ const PrivyWalletButton = () => {
) )
} }
// Display username (agentName) or fallback to wallet address const displayName = userInfo?.agentName || userInfo?.name || (user?.wallet?.address
const displayName = userInfo?.name || (user?.wallet?.address
? `${user.wallet.address.slice(0, 6)}...${user.wallet.address.slice(-4)}` ? `${user.wallet.address.slice(0, 6)}...${user.wallet.address.slice(-4)}`
: 'Connected') : 'Connected')
@@ -83,7 +85,6 @@ const PrivyWalletButton = () => {
const copyToClipboard = async (text: string) => { const copyToClipboard = async (text: string) => {
try { try {
await navigator.clipboard.writeText(text) await navigator.clipboard.writeText(text)
// You could add a toast notification here if you have a toast system
} catch (err) { } catch (err) {
console.error('Failed to copy to clipboard:', err) console.error('Failed to copy to clipboard:', err)
} }
@@ -91,83 +92,134 @@ const PrivyWalletButton = () => {
return ( return (
<div className="dropdown dropdown-end"> <div className="dropdown dropdown-end">
<label tabIndex={0} className="btn btn-primary btn-sm"> <label tabIndex={0} className="btn btn-ghost btn-sm gap-2 h-auto py-2 px-3">
{displayName} <div className="flex items-center gap-2">
</label> {userInfo?.avatarUrl ? (
<ul tabIndex={0} className="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-52">
<li>
<div className="flex items-center justify-between">
<div className="flex items-center space-x-2">
{userInfo?.avatarUrl && (
<img <img
src={userInfo.avatarUrl} src={userInfo.avatarUrl}
alt="User Avatar" alt="User Avatar"
className="w-6 h-6 rounded-full object-cover" className="w-8 h-8 rounded-full object-cover border-2 border-base-content/20"
onError={(e) => { onError={(e) => {
// Fallback to a default avatar if image fails to load
e.currentTarget.style.display = 'none' e.currentTarget.style.display = 'none'
}} }}
/> />
)} ) : (
<div className="w-8 h-8 rounded-full bg-primary/20 flex items-center justify-center border-2 border-base-content/20">
<span className="text-xs font-semibold">
{userInfo?.name?.[0]?.toUpperCase() || user?.wallet?.address?.[0]?.toUpperCase() || '?'}
</span>
</div> </div>
{userInfo?.agentName && ( )}
<span className="text-xs opacity-70 font-semibold"> <div className="flex flex-col items-start hidden lg:flex">
{userInfo.agentName} <span className="text-sm font-semibold leading-tight">{displayName}</span>
{userInfo?.name && userInfo.name !== displayName && (
<span className="text-xs opacity-60 leading-tight">{userInfo.name}</span>
)}
{walletAddress && (
<span className="text-xs opacity-50 font-mono leading-tight">
{walletAddress.slice(0, 6)}...{walletAddress.slice(-4)}
</span> </span>
)} )}
</div> </div>
</div>
</label>
<ul tabIndex={0} className="dropdown-content z-[1] menu p-3 shadow-lg bg-base-100 rounded-box w-64 mt-2">
<li className="mb-2 pb-2 border-b border-base-300">
<div className="flex items-center gap-3">
{userInfo?.avatarUrl ? (
<img
src={userInfo.avatarUrl}
alt="User Avatar"
className="w-12 h-12 rounded-full object-cover"
onError={(e) => {
e.currentTarget.style.display = 'none'
}}
/>
) : (
<div className="w-12 h-12 rounded-full bg-primary/20 flex items-center justify-center">
<span className="text-lg font-semibold">
{userInfo?.name?.[0]?.toUpperCase() || user?.wallet?.address?.[0]?.toUpperCase() || '?'}
</span>
</div>
)}
<div className="flex flex-col">
{userInfo?.agentName && (
<span className="font-semibold">{userInfo.agentName}</span>
)}
{userInfo?.name && (
<span className="text-sm opacity-70">{userInfo.name}</span>
)}
{walletAddress && (
<div className="flex items-center gap-1 mt-1">
<span className="text-xs opacity-60 font-mono">
{walletAddress.slice(0, 8)}...{walletAddress.slice(-6)}
</span>
<button
onClick={() => copyToClipboard(walletAddress)}
className="btn btn-xs btn-ghost p-0 h-4 w-4 min-h-0"
title="Copy wallet address"
>
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
</svg>
</button>
</div>
)}
</div>
</div>
</li> </li>
{user?.linkedAccounts && user.linkedAccounts.length > 0 && ( {user?.linkedAccounts && user.linkedAccounts.length > 0 && (
<> <>
<li className="text-xs opacity-70 font-semibold">Linked Accounts:</li> <li className="text-xs font-semibold opacity-70 mb-1">Linked Accounts</li>
{user.linkedAccounts.map((account, index) => ( {user.linkedAccounts.map((account, index) => (
<li key={index} className="text-xs opacity-70"> <li key={index}>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between py-1">
<div className="flex items-center space-x-2"> <div className="flex items-center gap-2">
<span className="capitalize">{account.type}</span> <span className="text-xs capitalize font-medium">{account.type.replace('_', ' ')}</span>
{account.type === 'wallet' && account.address && ( {account.type === 'wallet' && account.address && (
<span className="opacity-60"> <span className="text-xs opacity-60 font-mono">
{account.address.slice(0, 4)}...{account.address.slice(-4)} {account.address.slice(0, 6)}...{account.address.slice(-4)}
</span> </span>
)} )}
{account.type === 'twitter_oauth' && (
<span className="opacity-60">Twitter Connected</span>
)}
{account.type === 'google_oauth' && (
<span className="opacity-60">Google Connected</span>
)}
{account.type === 'email' && account.address && ( {account.type === 'email' && account.address && (
<span className="opacity-60">{account.address}</span> <span className="text-xs opacity-60">{account.address}</span>
)}
{(account.type === 'twitter_oauth' || account.type === 'google_oauth') && (
<span className="text-xs opacity-60">Connected</span>
)} )}
</div> </div>
{(account.type === 'wallet' && account.address) || (account.type === 'email' && account.address) ? ( {((account.type === 'wallet' && account.address) || (account.type === 'email' && account.address)) && (
<button <button
onClick={() => copyToClipboard(account.address)} onClick={() => copyToClipboard(account.address)}
className="btn btn-xs btn-ghost" className="btn btn-xs btn-ghost p-1 h-6 w-6 min-h-0"
title="Copy to clipboard" title="Copy to clipboard"
> >
📋 <svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
</svg>
</button> </button>
) : null} )}
</div> </div>
</li> </li>
))} ))}
<li className="divider my-1"></li>
</> </>
)} )}
<li><a onClick={logout}>Disconnect</a></li> <li>
<a onClick={logout} className="text-error font-medium">Disconnect</a>
</li>
</ul> </ul>
</div> </div>
) )
} }
export function SecondaryNavbar() { // Environment Dropdown Component
const EnvironmentDropdown = ({ isInSidebar = false }: { isInSidebar?: boolean }) => {
const { environment, prepareEnvironmentChange } = useApiUrlStore() const { environment, prepareEnvironmentChange } = useApiUrlStore()
const { logout } = usePrivy() const { logout } = usePrivy()
const handleEnvironmentChange = async (newEnv: 'local' | 'sandbox' | 'production') => { const handleEnvironmentChange = async (newEnv: 'local' | 'sandbox' | 'production') => {
prepareEnvironmentChange(newEnv) prepareEnvironmentChange(newEnv)
if (logout) { if (logout) {
await logout() await logout()
} }
@@ -175,83 +227,184 @@ export function SecondaryNavbar() {
} }
return ( return (
<div className="md:flex items-center hidden space-x-3"> <div className={`dropdown ${isInSidebar ? 'dropdown-start w-full' : 'dropdown-end'}`}>
<GlobalLoader /> <label tabIndex={0} className={`cursor-pointer ${isInSidebar ? 'w-full' : ''}`}>
<div className="form-control"> <EnvironmentBadge environment={environment} />
<select </label>
className="select select-bordered select-sm" <ul tabIndex={0} className="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-40">
value={environment} <li>
onChange={(e) => handleEnvironmentChange(e.target.value as 'local' | 'sandbox' | 'production')} <a
onClick={() => handleEnvironmentChange('local')}
className={environment === 'local' ? 'active' : ''}
> >
<option value="local">Local</option> Local
<option value="sandbox">Sandbox</option> </a>
<option value="production">Production</option> </li>
</select> <li>
</div> <a
<PrivyWalletButton /> onClick={() => handleEnvironmentChange('sandbox')}
className={environment === 'sandbox' ? 'active' : ''}
>
Sandbox
</a>
</li>
<li>
<a
onClick={() => handleEnvironmentChange('production')}
className={environment === 'production' ? 'active' : ''}
>
Production
</a>
</li>
</ul>
</div> </div>
) )
} }
type MobileMenuButtonProps = { export default function NavBar() {
onClick: VoidFunction const [sidebarOpen, setSidebarOpen] = useState<boolean>(false)
} const { environment } = useApiUrlStore()
const { authenticated } = usePrivy()
const closeSidebar = () => {
setSidebarOpen(false)
}
const toggleSidebar = () => {
setSidebarOpen(!sidebarOpen)
}
function MobileMenuButton({ onClick }: MobileMenuButtonProps) {
return ( return (
<div className="md:hidden flex items-center"> <>
<button className="mobile-menu-button outline-none" onClick={onClick}> {/* Navbar */}
<div className="navbar bg-base-300 shadow-lg border-b border-base-content/10 w-full relative z-50">
{/* Navbar Start - Mobile Menu Button and Logo */}
<div className="navbar-start">
<button
className="btn btn-ghost lg:hidden"
onClick={toggleSidebar}
aria-label="Open sidebar"
>
<svg <svg
className=" hover:text-primary text-accent w-6 h-6" xmlns="http://www.w3.org/2000/svg"
x-show="!showMenu" className="h-5 w-5"
fill="none" fill="none"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24" viewBox="0 0 24 24"
stroke="currentColor" stroke="currentColor"
> >
<path d="M4 6h16M4 12h16M4 18h16" /> <path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
<Link className="btn btn-ghost text-xl" to="/">
<img src={Logo} className="h-8 w-8 object-contain" alt="logo" />
</Link>
</div>
{/* Navbar Center - Desktop Navigation */}
<div className="navbar-center hidden lg:flex">
<ul className="menu menu-horizontal px-1 gap-1">
{navigation.map((item) => (
<li key={item.name}>
<NavLink
to={item.href}
className={({ isActive }) =>
`px-3 py-2 rounded-md font-medium transition-all duration-200 ${
isActive
? 'text-primary bg-base-200/30 font-semibold'
: 'text-base-content hover:text-primary hover:bg-base-200/50'
}`
}
>
{item.name}
</NavLink>
</li>
))}
</ul>
</div>
{/* Navbar End - Loader, Environment, User */}
<div className="navbar-end gap-2">
<GlobalLoader />
<div className="hidden md:flex items-center gap-2">
<span className="text-xs opacity-70 hidden xl:inline">Environment:</span>
<EnvironmentDropdown isInSidebar={false} />
</div>
{/* Show environment badge on mobile */}
<div className="md:hidden flex items-center">
<EnvironmentBadge environment={environment} />
</div>
<UserProfileDropdown />
</div>
</div>
{/* Mobile Sidebar */}
{sidebarOpen && (
<>
{/* Backdrop */}
<div
className="fixed inset-0 bg-black/50 z-40 lg:hidden"
onClick={closeSidebar}
/>
{/* Sidebar */}
<aside className={`fixed top-0 left-0 h-full w-64 bg-base-200 text-base-content z-50 lg:hidden transform transition-transform duration-300 ease-in-out ${
sidebarOpen ? 'translate-x-0' : '-translate-x-full'
}`}>
{/* Sidebar Header */}
<div className="p-4 border-b border-base-300">
<div className="flex items-center justify-between">
<Link className="flex items-center gap-2" to="/" onClick={closeSidebar}>
<img src={Logo} className="h-8 w-8 object-contain" alt="logo" />
<span className="font-bold text-lg">Managing</span>
</Link>
<button
className="btn btn-sm btn-circle btn-ghost"
onClick={closeSidebar}
aria-label="Close sidebar"
>
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-5 w-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" />
</svg> </svg>
</button> </button>
</div> </div>
)
}
type MobileMenuProps = {
isOpen: boolean
}
function MobileMenu({ isOpen }: MobileMenuProps) {
return (
<div className={isOpen ? 'mobile-menu' : 'hidden mobile-menu'}>
<ul>{navItems(true)}</ul>
</div>
)
}
type NavContainerProps = {
children: ReactNode
isMenuOpen: boolean
}
function NavContainer({ children, isMenuOpen }: NavContainerProps) {
return (
<nav className="bg-base-300 shadow-lg">
<div className="max-w-6xl px-4 mx-auto">
<div className="flex justify-between">{children}</div>
</div> </div>
<MobileMenu isOpen={isMenuOpen} /> {/* Sidebar Menu */}
</nav> <ul className="menu p-4 w-full">
) {navigation.map((item) => (
} <li key={item.name}>
export default function NavBar() { <NavLink
const [isMenuOpen, setIsMenuOpen] = useState<boolean>(false) to={item.href}
className={({ isActive }) =>
isActive ? 'active' : ''
}
onClick={closeSidebar}
>
{item.name}
</NavLink>
</li>
))}
return ( {authenticated && (
<NavContainer isMenuOpen={isMenuOpen}> <li>
<PrimaryNavbar /> <div className="px-4 py-2">
<SecondaryNavbar /> <div className="flex items-center justify-between mb-2">
<MobileMenuButton onClick={() => setIsMenuOpen(!isMenuOpen)} /> <span className="text-sm opacity-70">Environment:</span>
</NavContainer> </div>
<EnvironmentDropdown isInSidebar={true} />
</div>
</li>
)}
</ul>
</aside>
</>
)}
</>
) )
} }

View File

@@ -1,30 +1,32 @@
import { NavLink } from 'react-router-dom' import {NavLink} from 'react-router-dom'
import type { INavItemProps } from '../../../global/interface' import type {INavItemProps} from '../../../global/interface'
function navLinkClasses(isActive: boolean, isMobile: boolean) { function navLinkClasses(isActive: boolean, isMobile: boolean) {
let commonClasses = 'block text-sm px-2 py-4' let commonClasses = 'block text-sm px-3 py-2 rounded-md transition-all duration-200'
if (isMobile) { if (isMobile) {
return `${commonClasses} ${ return `${commonClasses} ${
isActive isActive
? 'text-base-content bg-primary font-semibold' ? 'text-base-content bg-primary font-semibold'
: 'hover:bg-primary transition duration-300' : 'hover:bg-base-200 transition duration-300'
}` }`
} }
commonClasses = commonClasses =
'py-4 px-2 font-semibold hover:text-primary transition duration-300' 'px-3 py-2 rounded-md font-medium hover:text-primary hover:bg-base-200/50 transition-all duration-200'
return `${commonClasses} ${isActive ? 'text-primary' : 'text-base-content'}` return `${commonClasses} ${isActive ? 'text-primary bg-base-200/30 font-semibold' : 'text-base-content'}`
} }
export default function NavItem({ export default function NavItem({
children, children,
href, href,
isMobile = false, isMobile = false,
onClick,
}: INavItemProps) { }: INavItemProps) {
const item = ( const item = (
<NavLink <NavLink
to={href} to={href}
className={({ isActive }) => navLinkClasses(isActive, isMobile)} className={({ isActive }) => navLinkClasses(isActive, isMobile)}
onClick={onClick}
> >
{children} {children}
</NavLink> </NavLink>

View File

@@ -1,26 +1,9 @@
import {ArrowDownIcon, ArrowUpIcon} from '@heroicons/react/solid' import {ArrowDownIcon, ArrowUpIcon} from '@heroicons/react/solid'
import React from 'react' import React from 'react'
import {useExpanded, useFilters, usePagination, useSortBy, useTable,} from 'react-table' import {useExpanded, usePagination, useSortBy, useTable,} from 'react-table'
import type {TableInstanceWithHooks} from '../../../global/type.tsx' import type {TableInstanceWithHooks} from '../../../global/type.tsx'
// Define a default UI for filtering
function DefaultColumnFilter({
column: { filterValue, preFilteredRows, setFilter },
}: any) {
const count = preFilteredRows.length
return (
<input
value={filterValue || ''}
onChange={(e) => {
setFilter(e.target.value || undefined) // Set undefined to remove the filter entirely
}}
placeholder={`Search ${count} records...`}
/>
)
}
export default function Table({ export default function Table({
columns, columns,
data, data,
@@ -29,13 +12,6 @@ export default function Table({
hiddenColumns, hiddenColumns,
showTotal = false, showTotal = false,
}: any) { }: any) {
const defaultColumn = React.useMemo<any>(
() => ({
// Let's set up our default Filter UI
Filter: DefaultColumnFilter,
}),
[]
)
// Use the state and functions returned from useTable to build your UI // Use the state and functions returned from useTable to build your UI
const { const {
getTableProps, getTableProps,
@@ -57,12 +33,10 @@ export default function Table({
{ {
columns, columns,
data, data,
defaultColumn, // Be sure to pass the defaultColumn option
initialState: { initialState: {
hiddenColumns: hiddenColumns ? hiddenColumns : [], hiddenColumns: hiddenColumns ? hiddenColumns : [],
}, },
}, },
useFilters,
useSortBy, useSortBy,
useExpanded, useExpanded,
usePagination usePagination
@@ -101,9 +75,6 @@ export default function Table({
'' ''
)} )}
</span> </span>
<div>
{column.canFilter ? column.render('Filter') : null}
</div>
</th> </th>
))} ))}
</tr> </tr>

View File

@@ -220,6 +220,7 @@ export type INavItemProps = {
children: ReactNode children: ReactNode
href: string href: string
isMobile?: boolean isMobile?: boolean
onClick?: () => void
} }
export type IStatusBadge = { export type IStatusBadge = {

View File

@@ -1,15 +1,14 @@
import { Outlet } from 'react-router-dom' import {Outlet} from 'react-router-dom'
import { ToastContainer } from 'react-toastify'
import '../styles/app.css' import '../styles/app.css'
import { NavBar } from '../components/mollecules' import {NavBar} from '../components/mollecules'
const LayoutMain = (): JSX.Element => { const LayoutMain = (): JSX.Element => {
return ( return (
<> <>
<NavBar></NavBar> <NavBar></NavBar>
<main className="layout mt-6"> <main className="mt-6">
<div className="container mx-auto"> <div className="w-full">
<Outlet /> <Outlet />
</div> </div>
</main> </main>

View File

@@ -12,6 +12,7 @@ const WhitelistSettings: React.FC = () => {
const [pageSize, setPageSize] = useState(20) const [pageSize, setPageSize] = useState(20)
const [searchExternalEthereumAccount, setSearchExternalEthereumAccount] = useState<string>('') const [searchExternalEthereumAccount, setSearchExternalEthereumAccount] = useState<string>('')
const [searchTwitterAccount, setSearchTwitterAccount] = useState<string>('') const [searchTwitterAccount, setSearchTwitterAccount] = useState<string>('')
const [filtersOpen, setFiltersOpen] = useState<boolean>(false)
const whitelistClient = new WhitelistClient({}, apiUrl) const whitelistClient = new WhitelistClient({}, apiUrl)
@@ -60,10 +61,18 @@ const WhitelistSettings: React.FC = () => {
<div> <div>
<div className="container mx-auto"> <div className="container mx-auto">
<div className="mb-4"> <div className="mb-4">
<h2 className="text-2xl font-bold mb-4">Whitelist Accounts</h2> {/* Search Filters Collapsible */}
<div className="collapse collapse-arrow bg-base-200 mb-4">
{/* Search Filters */} <input
<div className="flex gap-4 mb-4"> type="checkbox"
checked={filtersOpen}
onChange={(e) => setFiltersOpen(e.target.checked)}
/>
<div className="collapse-title text-lg font-medium">
Search Filters
</div>
<div className="collapse-content">
<div className="flex gap-4 pt-4">
<div className="form-control w-full max-w-xs"> <div className="form-control w-full max-w-xs">
<label className="label"> <label className="label">
<span className="label-text">Search by Ethereum Account</span> <span className="label-text">Search by Ethereum Account</span>
@@ -90,6 +99,8 @@ const WhitelistSettings: React.FC = () => {
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
<WhitelistTable <WhitelistTable
list={accounts} list={accounts}

View File

@@ -12,6 +12,7 @@ const WhitelistSettings: React.FC = () => {
const [pageSize, setPageSize] = useState(20) const [pageSize, setPageSize] = useState(20)
const [searchExternalEthereumAccount, setSearchExternalEthereumAccount] = useState<string>('') const [searchExternalEthereumAccount, setSearchExternalEthereumAccount] = useState<string>('')
const [searchTwitterAccount, setSearchTwitterAccount] = useState<string>('') const [searchTwitterAccount, setSearchTwitterAccount] = useState<string>('')
const [filtersOpen, setFiltersOpen] = useState<boolean>(false)
const whitelistClient = new WhitelistClient({}, apiUrl) const whitelistClient = new WhitelistClient({}, apiUrl)
@@ -60,10 +61,18 @@ const WhitelistSettings: React.FC = () => {
<div> <div>
<div className="container mx-auto"> <div className="container mx-auto">
<div className="mb-4"> <div className="mb-4">
<h2 className="text-2xl font-bold mb-4">Whitelist Accounts</h2> {/* Search Filters Collapsible */}
<div className="collapse collapse-arrow bg-base-200 mb-4">
{/* Search Filters */} <input
<div className="flex gap-4 mb-4"> type="checkbox"
checked={filtersOpen}
onChange={(e) => setFiltersOpen(e.target.checked)}
/>
<div className="collapse-title text-lg font-medium">
Search Filters
</div>
<div className="collapse-content">
<div className="flex gap-4 pt-4">
<div className="form-control w-full max-w-xs"> <div className="form-control w-full max-w-xs">
<label className="label"> <label className="label">
<span className="label-text">Search by Ethereum Account</span> <span className="label-text">Search by Ethereum Account</span>
@@ -90,6 +99,8 @@ const WhitelistSettings: React.FC = () => {
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
<WhitelistTable <WhitelistTable
list={accounts} list={accounts}

View File

@@ -3,9 +3,15 @@
@tailwind utilities; @tailwind utilities;
@layer base { @layer base {
body { html, body {
margin: 0; margin: 0;
padding: 0;
width: 100%;
max-width: 100%;
overflow-x: hidden;
}
body {
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
@@ -21,3 +27,10 @@
@apply sm:w-11/12 w-10/12 mx-auto; @apply sm:w-11/12 w-10/12 mx-auto;
} }
} }
@layer components {
.container {
max-width: 100% !important;
width: 100%;
}
}

View File

@@ -7,6 +7,14 @@ module.exports = {
theme: { theme: {
container: { container: {
center: true, center: true,
padding: '1rem',
screens: {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
'2xl': '1536px',
},
}, },
}, },
} }