Skip to content

Commit

Permalink
Add new notification system to add notifications from anywhere
Browse files Browse the repository at this point in the history
  • Loading branch information
wesbillman committed Aug 30, 2023
1 parent 2165140 commit 6be8d60
Show file tree
Hide file tree
Showing 5 changed files with 315 additions and 156 deletions.
229 changes: 118 additions & 111 deletions console/client/src/layout/IDELayout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {Tab} from '@headlessui/react'
import {InformationCircleIcon} from '@heroicons/react/20/solid'
import {XMarkIcon} from '@heroicons/react/24/outline'
import React from 'react'
import {useSearchParams} from 'react-router-dom'
Expand All @@ -9,6 +8,10 @@ import {Timeline} from '../features/timeline/Timeline'
import {VerbTab} from '../features/verbs/VerbTab'
import {useClient} from '../hooks/use-client'
import {ConsoleService} from '../protos/xyz/block/ftl/v1/console/console_connect'
import {
NotificationType,
NotificationsContext,
} from '../providers/notifications-provider'
import {
TabSearchParams,
TabType,
Expand All @@ -32,9 +35,9 @@ const unselectedTabStyle = `text-gray-300 bg-slate-100 dark:bg-slate-600`
export function IDELayout() {
const client = useClient(ConsoleService)
const {tabs, activeTab, setActiveTab, setTabs} = React.useContext(TabsContext)
const {showNotification} = React.useContext(NotificationsContext)
const [searchParams, setSearchParams] = useSearchParams()
const [activeIndex, setActiveIndex] = React.useState(0)
const [invalidTabMessage, setInvalidTabMessage] = React.useState<string>()
const id = searchParams.get(TabSearchParams.id) as string
const type = searchParams.get(TabSearchParams.type) as string
// Set active tab index whenever our activeTab context changes
Expand Down Expand Up @@ -76,7 +79,11 @@ export function IDELayout() {
setActiveTab({id: timelineTab.id, type: timelineTab.type})
// On intial mount we have no query params set for tabs so we want to skip setting invalidTabMessage
if (type === null && id === null) return
return setInvalidTabMessage(msg)
return showNotification({
title: 'Invalid Tab',
message: msg,
type: NotificationType.Error,
})
}
const inTabsList = tabs.some(
({id: tabId, type: tabType}) => tabId === id && tabType === type
Expand Down Expand Up @@ -104,128 +111,128 @@ export function IDELayout() {
return setTabs(nextTabs)
}
if (moduleExist && !verbExist) {
setInvalidTabMessage(`Verb ${verbId} does not exist on ${moduleId}`)
return showNotification({
title: 'Verb not found',
message: `Verb '${verbId}' does not exist on module '${moduleId}'`,
type: NotificationType.Error,
})
}
if (!moduleExist) {
setInvalidTabMessage(`Module ${moduleId} does not exist`)
return showNotification({
title: 'Module not found',
message: `Module '${moduleId}' does not exist`,
type: NotificationType.Error,
})
}
setActiveTab({id: timelineTab.id, type: timelineTab.type})
}
void validateTabs()
}, [id, type])

return (
<div className={`h-screen flex flex-col ${bgColor} ${textColor}`}>
<Navigation />
<>
<div className={`h-screen flex flex-col ${bgColor} ${textColor}`}>
<Navigation />

<div className='flex-grow flex overflow-hidden p-1'>
{/* Left Column */}
<aside className={`w-80 flex flex-col`}>
{/* Top Section */}
<div
className={`flex flex-col h-1/2 overflow-hidden rounded-t ${panelColor}`}
>
<header className={`px-4 py-2 ${headerTextColor} ${headerColor}`}>
Modules
</header>
<section className={`${panelColor} p-4 overflow-y-auto`}>
<ModulesList />
</section>
</div>
<div className='flex-grow flex overflow-hidden p-1'>
{/* Left Column */}
<aside className={`w-80 flex flex-col`}>
{/* Top Section */}
<div
className={`flex flex-col h-1/2 overflow-hidden rounded-t ${panelColor}`}
>
<header className={`px-4 py-2 ${headerTextColor} ${headerColor}`}>
Modules
</header>
<section className={`${panelColor} p-4 overflow-y-auto`}>
<ModulesList />
</section>
</div>

{/* Bottom Section */}
<div
className={`flex flex-col h-1/2 overflow-hidden mt-1 rounded-t ${panelColor}`}
>
<header className={`px-4 py-2 ${headerTextColor} ${headerColor}`}>
Module Details
</header>
<section className={`${panelColor} p-4 overflow-y-auto`}>
<ModuleDetails />
</section>
</div>
</aside>
{/* Bottom Section */}
<div
className={`flex flex-col h-1/2 overflow-hidden mt-1 rounded-t ${panelColor}`}
>
<header className={`px-4 py-2 ${headerTextColor} ${headerColor}`}>
Module Details
</header>
<section className={`${panelColor} p-4 overflow-y-auto`}>
<ModuleDetails />
</section>
</div>
</aside>

{/* Main Content */}
<main className='flex-grow flex flex-col overflow-hidden pl-1'>
<section className='flex-grow overflow-y-auto'>
<div className='flex flex-grow overflow-hidden h-full'>
<div className={`flex-1 flex flex-col rounded`}>
<Tab.Group
selectedIndex={activeIndex}
onChange={handleChangeTab}
>
<div>
<Tab.List
className={`flex items-center rounded-t ${headerTextColor}`}
>
{tabs.map(({label, id}, i) => {
return (
<Tab
key={id}
className='flex items-center mr-1 relative'
as='span'
>
<span
className={`px-4 py-2 rounded-t ${
id !== 'timeline' ? 'pr-8' : ''
} ${
activeIndex === i
? `${selectedTabStyle}`
: `${unselectedTabStyle}`
}`}
{/* Main Content */}
<main className='flex-grow flex flex-col overflow-hidden pl-1'>
<section className='flex-grow overflow-y-auto'>
<div className='flex flex-grow overflow-hidden h-full'>
<div className={`flex-1 flex flex-col rounded`}>
<Tab.Group
selectedIndex={activeIndex}
onChange={handleChangeTab}
>
<div>
<Tab.List
className={`flex items-center rounded-t ${headerTextColor}`}
>
{tabs.map(({label, id}, i) => {
return (
<Tab
key={id}
className='flex items-center mr-1 relative'
as='span'
>
{label}
</span>
{i !== 0 && (
<button
onClick={e => {
e.stopPropagation()
handleCloseTab(id, i)
}}
className='absolute right-0 mr-2 text-gray-400 hover:text-white'
<span
className={`px-4 py-2 rounded-t ${
id !== 'timeline' ? 'pr-8' : ''
} ${
activeIndex === i
? `${selectedTabStyle}`
: `${unselectedTabStyle}`
}`}
>
<XMarkIcon className={`h-5 w-5`} />
</button>
)}
</Tab>
)
})}
</Tab.List>
<div className='flex-grow'></div>
</div>
<div className={`flex-1 overflow-y-scroll ${panelColor}`}>
<Tab.Panels>
{tabs.map(({id}, i) => {
return i === 0 ? (
<Tab.Panel key={id}>
<Timeline />
</Tab.Panel>
) : (
<Tab.Panel key={id}>
<VerbTab id={id} />
</Tab.Panel>
)
})}
</Tab.Panels>
</div>
</Tab.Group>
{label}
</span>
{i !== 0 && (
<button
onClick={e => {
e.stopPropagation()
handleCloseTab(id, i)
}}
className='absolute right-0 mr-2 text-gray-400 hover:text-white'
>
<XMarkIcon className={`h-5 w-5`} />
</button>
)}
</Tab>
)
})}
</Tab.List>
<div className='flex-grow'></div>
</div>
<div className={`flex-1 overflow-y-scroll ${panelColor}`}>
<Tab.Panels>
{tabs.map(({id}, i) => {
return i === 0 ? (
<Tab.Panel key={id}>
<Timeline />
</Tab.Panel>
) : (
<Tab.Panel key={id}>
<VerbTab id={id} />
</Tab.Panel>
)
})}
</Tab.Panels>
</div>
</Tab.Group>
</div>
<SidePanel />
</div>
<SidePanel />
</div>
</section>
</main>
</section>
</main>
</div>
</div>
{invalidTabMessage && (
<Notification
color='text-red-400'
icon={
<InformationCircleIcon className='flex-shrink-0 inline w-4 h-4 mr-3' />
}
title='Alert!'
message={invalidTabMessage}
/>
)}
</div>
<Notification />
</>
)
}
Loading

0 comments on commit 6be8d60

Please sign in to comment.