Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion packages/console/src/app/space/[did]/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useW3 } from '@storacha/ui-react'
import { DidIcon } from '@/components/DidIcon'
import { Nav, NavLink } from '@/components/Nav'
import { QueueListIcon, ShareIcon, CloudArrowUpIcon } from '@heroicons/react/24/outline'
import Link from 'next/link'

interface LayoutProps extends PropsWithChildren {
params: {
Expand All @@ -23,7 +24,10 @@ export default function Layout ({children, params}: LayoutProps): JSX.Element {
const space = spaces.find(s => s.did() === spaceDID)
if (!space) {
console.warn(`not a known space to this agent: ${spaceDID}`)
return <div />
return <div className='text-center text-2xl text-hot-red flex flex-col gap-4'>
<h1>Space not found: {spaceDID}</h1>
<Link href='/' className="underline">Back to spaces</Link>
</div>
}

return (
Expand Down
13 changes: 11 additions & 2 deletions packages/console/src/components/SidebarLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { SpaceFinder } from './SpaceFinder'
import { usePathname, useRouter } from 'next/navigation'
import { H2 } from './Text'
import { SidebarMigrations } from './SidebarMigrations'
import Link from 'next/link'

const navLinks = [
{ name: 'Terms', href: 'https://docs.storacha.network/terms/' },
Expand Down Expand Up @@ -43,8 +44,16 @@ function Sidebar ({ sidebar = <div></div> }: SidebarComponentProps): JSX.Element
<Logo className='pr-4 block' />
</header>
<div className='my-6'>
<H2 className='text-white'>Spaces</H2>
<SpaceFinder spaces={spaces} selected={space} setSelected={goToSpace} />
<H2 className='text-hot-red font-bold'>Spaces</H2>
<div className='bg-white rounded-2xl p-3 shadow-sm overflow-hidden transition-all duration-200 ease-out max-h-32 focus-within:max-h-96 focus-within:shadow-lg border border-hot-red'>
<SpaceFinder spaces={spaces} selected={space} setSelected={goToSpace} />
<Link
href='/space/create'
className='mt-3 block w-full text-center bg-hot-red text-white rounded-full py-3 text-sm font-bold'
>
+ Create New Space
</Link>
</div>
</div>
<div className='my-6'>
<SidebarMigrations />
Expand Down
2 changes: 1 addition & 1 deletion packages/console/src/components/SidebarMigrations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const SidebarMigrations = () => {
<button type='button' className='float-right' onClick={e => { e.preventDefault(); router.push('/migration/create') }}>
<PlusCircleIcon className='w-9 px-2 hover:text-white' style={{ marginTop: -2 }} title='Start a new migration' />
</button>
<H2 className='text-white'>Migrations</H2>
<H2 className='text-hot-red font-bold'>Migrations</H2>
<MigrationsList migrations={migrations} />
</>
)
Expand Down
126 changes: 60 additions & 66 deletions packages/console/src/components/SpaceFinder.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { Space } from '@storacha/ui-react'

import React, { Fragment, useState } from 'react'
import React, { Fragment, useState, useRef } from 'react'
import { Combobox, Transition } from '@headlessui/react'
import { ChevronUpDownIcon } from '@heroicons/react/20/solid'
import { ChevronUpDownIcon, MagnifyingGlassIcon } from '@heroicons/react/20/solid'
import { LockClosedIcon, GlobeAltIcon } from '@heroicons/react/24/outline'
import { shortenDID } from '@/lib'
import { usePrivateSpacesAccess } from '@/hooks/usePrivateSpacesAccess'
Expand All @@ -21,6 +21,8 @@ export function SpaceFinder ({
className = ''
}: SpaceFinderProps): JSX.Element {
const [query, setQuery] = useState('')
const [isOpen, setIsOpen] = useState(false)
const containerRef = useRef<HTMLDivElement | null>(null)
const { shouldShowPrivateSpacesTab } = usePrivateSpacesAccess()

// First filter by query, then categorize and sort
Expand Down Expand Up @@ -55,21 +57,32 @@ export function SpaceFinder ({

const hasResults = publicSpaces.length > 0 || privateSpaces.length > 0

const handleSelect = (space: Space) => {
setSelected?.(space)
setIsOpen(false)
}

return (
<div className={`${className}`}>
<Combobox
value={selected}
onChange={setSelected}
onChange={handleSelect}
by={(a, b) => a?.did() === b?.did()}
>
<div className='relative mt-1'>
<div className='relative mt-1' ref={containerRef}>
{/* Search input with magnifying glass icon */}
<div className='relative w-full overflow-hidden rounded-md bg-white text-left shadow-md'>
<MagnifyingGlassIcon className='pointer-events-none absolute left-2 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400' />
<Combobox.Input
className='w-full border-none py-2 pl-3 pr-10 text-sm text-gray-900'
className='w-full border-none py-2 pl-8 pr-8 text-sm text-gray-900 placeholder:text-gray-400 focus-visible:outline-none'
displayValue={(space: Space) => space.name || shortenDID(space.did())}
onChange={(event) => { setQuery(event.target.value) }}
onChange={(event) => { setQuery(event.target.value); setIsOpen(true) }}
onFocus={() => setIsOpen(true)}
onBlur={(e) => { const next = e.relatedTarget as Node | null; if (!next || !containerRef.current?.contains(next)) setIsOpen(false) }}
onKeyDown={(e) => { if (e.key === 'Escape') setIsOpen(false) }}
placeholder='Search'
/>
<Combobox.Button className='absolute inset-y-0 right-0 flex items-center pl-1 pr-2'>
<Combobox.Button className='absolute inset-y-0 right-0 flex items-center pl-1 pr-2' onClick={() => setIsOpen(v => !v)}>
<ChevronUpDownIcon
className='h-5 w-5 text-gray-400'
aria-hidden='true'
Expand All @@ -78,18 +91,22 @@ export function SpaceFinder ({
</div>
<Transition
as={Fragment}
leave='transition ease-in duration-100'
leaveFrom='opacity-100'
leaveTo='opacity-0'
show={isOpen}
enter='transition ease-out duration-200'
enterFrom='opacity-0 -translate-y-1 scale-y-75'
enterTo='opacity-100 translate-y-0 scale-y-100'
leave='transition ease-in duration-150'
leaveFrom='opacity-100 translate-y-0 scale-y-100'
leaveTo='opacity-0 -translate-y-1 scale-y-75'
afterLeave={() => { setQuery('') }}
>
{/* Sliding options list */}
<Combobox.Options
className='absolute mt-1 max-h-96 w-full bg-white rounded-md pt-1 shadow-lg overflow-scroll z-10'
static
className='mt-2 w-full rounded-md pt-1 overflow-y-auto max-h-60'
>
{!hasResults && query !== ''
? (
<div className='relative select-non py-2 px-4 font-mono text-sm text-red-500'>
<div className='relative select-none py-2 px-4 font-mono text-sm text-hot-red'>
No results found
</div>
)
Expand All @@ -98,7 +115,7 @@ export function SpaceFinder ({
{/* Public Spaces Section */}
{publicSpaces.length > 0 && (
<>
<div className='px-4 py-2 text-xs font-semibold text-gray-500 uppercase tracking-wide bg-gray-50 border-b border-gray-100'>
<div className='px-2 py-2 text-[10px] font-semibold text-gray-800 uppercase tracking-wide'>
<div className='flex items-center gap-2'>
<GlobeAltIcon className='w-3 h-3' />
Public Spaces
Expand All @@ -108,36 +125,25 @@ export function SpaceFinder ({
<Combobox.Option
key={space.did()}
className={({ active }) =>
`relative select-none py-2 pl-9 pr-4 ${
active ? 'bg-hot-yellow-light cursor-pointer text-hot-red' : 'text-black'
`relative select-none py-1 px-2 ${
active ? 'cursor-pointer' : ''
}`
}
value={space}
>
{({ selected, active }) => (
<>
<div className="flex items-center gap-2">
<GlobeAltIcon className='w-3 h-3 flex-shrink-0 text-gray-400' />
<span
className={`block overflow-hidden text-ellipsis whitespace-nowrap ${
selected ? 'font-medium' : ''
}`}
>
{space.name || shortenDID(space.did())}
</span>
</div>
{selected
? (
<span
className={`absolute inset-y-0 left-0 flex items-center pl-3 ${
active ? '' : ''
}`}
>
</span>
)
: null}
</>
<div className={`flex items-center gap-2 px-2 py-2 rounded-md ${
selected || active ? 'bg-hot-yellow-light' : ''
}`}>
<GlobeAltIcon className='w-4 h-4 flex-shrink-0 text-gray-700' />
<span
className={`block overflow-hidden text-ellipsis whitespace-nowrap ${
selected ? 'font-medium' : ''
}`}
>
{space.name || shortenDID(space.did())}
</span>
</div>
)}
</Combobox.Option>
))}
Expand All @@ -147,7 +153,7 @@ export function SpaceFinder ({
{/* Private Spaces Section */}
{shouldShowPrivateSpacesTab && privateSpaces.length > 0 && (
<>
<div className='px-4 py-2 text-xs font-semibold text-gray-500 uppercase tracking-wide bg-red-50 border-b border-gray-100'>
<div className='px-2 py-2 text-[10px] font-semibold text-gray-800 uppercase tracking-wide'>
<div className='flex items-center gap-2'>
<LockClosedIcon className='w-3 h-3' />
Private Spaces
Expand All @@ -157,37 +163,25 @@ export function SpaceFinder ({
<Combobox.Option
key={space.did()}
className={({ active }) =>
`relative select-none py-2 pl-9 pr-4 bg-red-50/30 ${
active ? 'bg-hot-yellow-light cursor-pointer text-hot-red' : 'text-black'
`relative select-none py-1 px-2 ${
active ? 'cursor-pointer' : ''
}`
}
value={space}
>
{({ selected, active }) => (
<>
<div className="flex items-center gap-2">
<LockClosedIcon className='w-3 h-3 flex-shrink-0 text-red-500' />
<span
className={`block overflow-hidden text-ellipsis whitespace-nowrap ${
selected ? 'font-medium' : ''
}`}
>
{space.name || shortenDID(space.did())}
</span>

</div>
{selected
? (
<span
className={`absolute inset-y-0 left-0 flex items-center pl-3 ${
active ? '' : ''
}`}
>
</span>
)
: null}
</>
<div className={`flex items-center gap-2 px-2 py-2 rounded-md ${
selected || active ? 'bg-hot-yellow-light' : ''
}`}>
<LockClosedIcon className='w-4 h-4 flex-shrink-0 text-gray-800' />
<span
className={`block overflow-hidden text-ellipsis whitespace-nowrap ${
selected ? 'font-medium' : ''
}`}
>
{space.name || shortenDID(space.did())}
</span>
</div>
)}
</Combobox.Option>
))}
Expand Down