Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new notification system to add notifications from anywhere #342

Merged
merged 1 commit into from
Aug 30, 2023
Merged
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
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