diff --git a/CHANGELOG.md b/CHANGELOG.md index 5564a7248149..e7088d5cc2b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,308 @@ All notable changes to this project will be documented in this file. +## [6.3.0] - 2024-10-01 + +### Bug Fixes + +- Update dependency @slack/web-api to v6.12.1 ([#8103](https://github.com/Unleash/unleash/issues/8103)) + +- Also display strategy variant information on default strategies ([#8115](https://github.com/Unleash/unleash/issues/8115)) + +- Give project_default_strategy_write the ability to update the default strategy ([#8112](https://github.com/Unleash/unleash/issues/8112)) + +- Allow you to see default strategies if you have write access ([#8113](https://github.com/Unleash/unleash/issues/8113)) + +- Allow you to save default strategies with the right permissions ([#8114](https://github.com/Unleash/unleash/issues/8114)) + +- User projects should exclude archived ones ([#8118](https://github.com/Unleash/unleash/issues/8118)) + +- Update dependency express-rate-limit to v7.4.0 ([#8127](https://github.com/Unleash/unleash/issues/8127)) + +- Update dependency unleash-client to v5.6.1 ([#8129](https://github.com/Unleash/unleash/issues/8129)) + +- Update dependency git-url-parse to v14.1.0 ([#8128](https://github.com/Unleash/unleash/issues/8128)) + +- Invert logic ([#8135](https://github.com/Unleash/unleash/issues/8135)) + +- Update dependency express to v4.20.0 [security] ([#8138](https://github.com/Unleash/unleash/issues/8138)) + +- Docker warnings ([#8148](https://github.com/Unleash/unleash/issues/8148)) + +- Express upgrade to 4.21 ([#8169](https://github.com/Unleash/unleash/issues/8169)) + +- Upgrade openapi to drop dompurify and fix path-to-regexp ([#8170](https://github.com/Unleash/unleash/issues/8170)) + +- Connect sdk description bg color ([#8171](https://github.com/Unleash/unleash/issues/8171)) + +- Archive project curl ([#8181](https://github.com/Unleash/unleash/issues/8181)) + +- Update playground SDK to increase the possible random numbers used for stickiness id ([#8182](https://github.com/Unleash/unleash/issues/8182)) + +- Typo and improved example in latest ADR ([#8063](https://github.com/Unleash/unleash/issues/8063)) + +- Now onboarding will show up in correct times ([#8214](https://github.com/Unleash/unleash/issues/8214)) + +- Onboarding img asset path ([#8213](https://github.com/Unleash/unleash/issues/8213)) + +- Update dependency nodemailer to v6.9.15 ([#8224](https://github.com/Unleash/unleash/issues/8224)) + +- Trim messages longer than 3000 chars ([#8219](https://github.com/Unleash/unleash/issues/8219)) + +- SDK snippets import ([#8235](https://github.com/Unleash/unleash/issues/8235)) + +- Adjust welcome dialog size ([#8244](https://github.com/Unleash/unleash/issues/8244)) + +- Layout content width on large screens ([#8267](https://github.com/Unleash/unleash/issues/8267)) + +- Trim role names before validation ([#8277](https://github.com/Unleash/unleash/issues/8277)) + +- Trim name and description before validation ([#8275](https://github.com/Unleash/unleash/issues/8275)) + +- Now only one onboarding screen will be shown at time ([#8290](https://github.com/Unleash/unleash/issues/8290)) + +- Event timeline should unmount when hidden and be closed by default ([#8294](https://github.com/Unleash/unleash/issues/8294)) + +- Some integrations only counted errors not all results ([#8295](https://github.com/Unleash/unleash/issues/8295)) + +- Extend feature_toggle_update counter with details about action ([#8202](https://github.com/Unleash/unleash/issues/8202)) + +- Handle empty strings on permissions gracefully in access service ([#8306](https://github.com/Unleash/unleash/issues/8306)) + +- Add missing awaits on events ([#8320](https://github.com/Unleash/unleash/issues/8320)) + + +### Documentation + +- Add in gradual rollout use case doc ([#8172](https://github.com/Unleash/unleash/issues/8172)) + +- Remove extra diagram ([#8203](https://github.com/Unleash/unleash/issues/8203)) + +- Demo is pro ([#8270](https://github.com/Unleash/unleash/issues/8270)) + +- Recommend PG v13 or later ([#8276](https://github.com/Unleash/unleash/issues/8276)) + +- Feature lifecycle availability ([#8288](https://github.com/Unleash/unleash/issues/8288)) + +- A/B Testing Use Case Tutorial ([#8257](https://github.com/Unleash/unleash/issues/8257)) + + +### Features + +- Check flag status snippet ([#8097](https://github.com/Unleash/unleash/issues/8097)) + +- Change sdk action ([#8098](https://github.com/Unleash/unleash/issues/8098)) + +- Connection status bubble ([#8099](https://github.com/Unleash/unleash/issues/8099)) + +- Onboarding stepper ([#8100](https://github.com/Unleash/unleash/issues/8100)) + +- Onboarding step badges ([#8102](https://github.com/Unleash/unleash/issues/8102)) + +- New onboarding welcome screen logic ([#8110](https://github.com/Unleash/unleash/issues/8110)) + +- Make node example more consistent ([#8111](https://github.com/Unleash/unleash/issues/8111)) + +- Explain complete feature name ([#8120](https://github.com/Unleash/unleash/issues/8120)) + +- Swift and flutter sdk snippets ([#8149](https://github.com/Unleash/unleash/issues/8149)) + +- Personal dashboard route ([#8173](https://github.com/Unleash/unleash/issues/8173)) + +- My projects ui stub ([#8185](https://github.com/Unleash/unleash/issues/8185)) + +- Personal dashboard project selection ([#8188](https://github.com/Unleash/unleash/issues/8188)) + +- Personal dashboard connect sdk ([#8190](https://github.com/Unleash/unleash/issues/8190)) + +- Placeholder flag metrics chart ([#8197](https://github.com/Unleash/unleash/issues/8197)) + +- Onboarding flow will not break ([#8198](https://github.com/Unleash/unleash/issues/8198)) + +- Welcome dialog with unleash concepts ([#8199](https://github.com/Unleash/unleash/issues/8199)) + +- Onboarding can be now closed ([#8215](https://github.com/Unleash/unleash/issues/8215)) + +- Personal dashboard api ([#8218](https://github.com/Unleash/unleash/issues/8218)) + +- Personal flags UI component ([#8221](https://github.com/Unleash/unleash/issues/8221)) + +- Complete java example ([#8227](https://github.com/Unleash/unleash/issues/8227)) + +- Only show sdk button to specific roles ([#8231](https://github.com/Unleash/unleash/issues/8231)) + +- Sdk snippets in files ([#8233](https://github.com/Unleash/unleash/issues/8233)) + +- Personal flag metrics display ([#8232](https://github.com/Unleash/unleash/issues/8232)) + +- Add your projects (with roles) to personal dashboard api ([#8236](https://github.com/Unleash/unleash/issues/8236)) + +- Highlighting flags chart ([#8237](https://github.com/Unleash/unleash/issues/8237)) + +- Add group project roles to project roles ([#8245](https://github.com/Unleash/unleash/issues/8245)) + +- Flag exposure in personal dashboard ([#8247](https://github.com/Unleash/unleash/issues/8247)) + +- Rust SDK snippet ([#8239](https://github.com/Unleash/unleash/issues/8239)) + +- Add project owners to personal dashboard project payload ([#8248](https://github.com/Unleash/unleash/issues/8248)) + +- Nodejs snippet for production ([#8256](https://github.com/Unleash/unleash/issues/8256)) + +- Display new completed dialog ([#8255](https://github.com/Unleash/unleash/issues/8255)) + +- Sdk connected using production snippet ([#8266](https://github.com/Unleash/unleash/issues/8266)) + +- Get projects by ids ([#8269](https://github.com/Unleash/unleash/issues/8269)) + +- Show user's roles and project owners ([#8253](https://github.com/Unleash/unleash/issues/8253)) + +- Project details for personal dashboard ([#8274](https://github.com/Unleash/unleash/issues/8274)) + +- Vue and Svelte snippets ([#8250](https://github.com/Unleash/unleash/issues/8250)) + +- Read projects from personal dashboard API ([#8279](https://github.com/Unleash/unleash/issues/8279)) + +- After onboarding show success box with resources ([#8278](https://github.com/Unleash/unleash/issues/8278)) + +- Personal dashboard project details API stub ([#8282](https://github.com/Unleash/unleash/issues/8282)) + +- Now code examples are joined into one ([#8284](https://github.com/Unleash/unleash/issues/8284)) + +- Create page for when you have no projects ([#8285](https://github.com/Unleash/unleash/issues/8285)) + +- Add production snippets and resources ([#8286](https://github.com/Unleash/unleash/issues/8286)) + +- Return latest project events ([#8287](https://github.com/Unleash/unleash/issues/8287)) + +- Android snippet ([#8281](https://github.com/Unleash/unleash/issues/8281)) + +- Display basic list of project events ([#8291](https://github.com/Unleash/unleash/issues/8291)) + +- Adjust search query for personal project ([#8296](https://github.com/Unleash/unleash/issues/8296)) + +- Add project owners to personal dashboard ([#8293](https://github.com/Unleash/unleash/issues/8293)) + +- Last project events ui polishing ([#8298](https://github.com/Unleash/unleash/issues/8298)) + +- Add Unleash admins to API payload ([#8299](https://github.com/Unleash/unleash/issues/8299)) + +- Open unleash concepts ([#8301](https://github.com/Unleash/unleash/issues/8301)) + +- Add onboarding status to personal dashboard api ([#8302](https://github.com/Unleash/unleash/issues/8302)) + +- Use onboarding status to conditionally show badge and message ([#8304](https://github.com/Unleash/unleash/issues/8304)) + +- Hook up admin / owner data to UI ([#8300](https://github.com/Unleash/unleash/issues/8300)) + +- Personal dashboard flag created ([#8305](https://github.com/Unleash/unleash/issues/8305)) + +- Include favorite projects in personal dashboard ([#8309](https://github.com/Unleash/unleash/issues/8309)) + +- Skeleton loaders for personal dashboard ([#8313](https://github.com/Unleash/unleash/issues/8313)) + +- Return project owners ([#8312](https://github.com/Unleash/unleash/issues/8312)) + +- Return project roles ([#8314](https://github.com/Unleash/unleash/issues/8314)) + +- Add transactional support for access service ([#8311](https://github.com/Unleash/unleash/issues/8311)) + +- Add links to examples ([#8308](https://github.com/Unleash/unleash/issues/8308)) + +- Redirect to personal dashboard when no last project ([#8318](https://github.com/Unleash/unleash/issues/8318)) + +- .NET snippet ([#8307](https://github.com/Unleash/unleash/issues/8307)) + + +### Miscellaneous Tasks + +- Bump version to 6.2.0 + +- Remove unused SCIM setting - assumeControlOfExisting ([#8101](https://github.com/Unleash/unleash/issues/8101)) + +- Adjust language names ([#8117](https://github.com/Unleash/unleash/issues/8117)) + +- Add logging to new code path ([#8133](https://github.com/Unleash/unleash/issues/8133)) + +- Add stringified logs ([#8134](https://github.com/Unleash/unleash/issues/8134)) + +- Bump version to 6.2.1 + +- Bump version to 6.2.2 + +- Update go sdk examples ([#8145](https://github.com/Unleash/unleash/issues/8145)) + +- PHP SDK example fix ([#8146](https://github.com/Unleash/unleash/issues/8146)) + +- Remove debug logs ([#8147](https://github.com/Unleash/unleash/issues/8147)) + +- Upgrade unleash-client and mime libraries ([#8158](https://github.com/Unleash/unleash/issues/8158)) + +- Add eventTimeline feature flag ([#8159](https://github.com/Unleash/unleash/issues/8159)) + +- Update docker yarnlock ([#8174](https://github.com/Unleash/unleash/issues/8174)) + +- Bump version to 6.2.3 + +- Make count column bigint. ([#8183](https://github.com/Unleash/unleash/issues/8183)) + +- Event timeline ([#8176](https://github.com/Unleash/unleash/issues/8176)) + +- Finalize python sdk example ([#8186](https://github.com/Unleash/unleash/issues/8186)) + +- Enables github docker cache ([#8187](https://github.com/Unleash/unleash/issues/8187)) + +- Update project onboarding UI text ([#8189](https://github.com/Unleash/unleash/issues/8189)) + +- Orval gen ([#8220](https://github.com/Unleash/unleash/issues/8220)) + +- Event timeline tooltips ([#8205](https://github.com/Unleash/unleash/issues/8205)) + +- Full js sdk example ([#8229](https://github.com/Unleash/unleash/issues/8229)) + +- Feature event formatter md format style ([#8222](https://github.com/Unleash/unleash/issues/8222)) + +- Send full message without trimming to the addon event ([#8230](https://github.com/Unleash/unleash/issues/8230)) + +- Event timeline header placement ([#8234](https://github.com/Unleash/unleash/issues/8234)) + +- Event timeline persistent state ([#8240](https://github.com/Unleash/unleash/issues/8240)) + +- Update orval for personal dashboards ([#8268](https://github.com/Unleash/unleash/issues/8268)) + +- Remove manual anonymization of outgoing project owners ([#8252](https://github.com/Unleash/unleash/issues/8252)) + +- Move onboarding flow and dialog under same location ([#8272](https://github.com/Unleash/unleash/issues/8272)) + +- Implement event grouping in the event timeline ([#8254](https://github.com/Unleash/unleash/issues/8254)) + +- Fix search events endpoint description ([#8289](https://github.com/Unleash/unleash/issues/8289)) + +- Event timeline signals ([#8310](https://github.com/Unleash/unleash/issues/8310)) + + +### Refactor + +- Move getProjectsByUser to read model ([#8262](https://github.com/Unleash/unleash/issues/8262)) + +- Composition root for personal dashboard service ([#8280](https://github.com/Unleash/unleash/issues/8280)) + +- Extract my projects component ([#8317](https://github.com/Unleash/unleash/issues/8317)) + + +### Testing + +- Onboarding test with existing key ([#8116](https://github.com/Unleash/unleash/issues/8116)) + +- Improve onboarding test ([#8121](https://github.com/Unleash/unleash/issues/8121)) + +- Reduce noise from test warnings ([#8251](https://github.com/Unleash/unleash/issues/8251)) + +- Skip vercel toolbar in e2e tests ([#8273](https://github.com/Unleash/unleash/issues/8273)) + +- Test the dashboard admins property ([#8303](https://github.com/Unleash/unleash/issues/8303)) + + ## [6.2.3] - 2024-09-18 ### Bug Fixes diff --git a/frontend/src/component/InitialRedirect.tsx b/frontend/src/component/InitialRedirect.tsx index 4377385f5261..f65ba0e7186f 100644 --- a/frontend/src/component/InitialRedirect.tsx +++ b/frontend/src/component/InitialRedirect.tsx @@ -1,11 +1,13 @@ import { useCallback, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; -import useProjects from '../hooks/api/getters/useProjects/useProjects'; -import { useLastViewedProject } from '../hooks/useLastViewedProject'; +import useProjects from 'hooks/api/getters/useProjects/useProjects'; +import { useLastViewedProject } from 'hooks/useLastViewedProject'; import Loader from './common/Loader/Loader'; -import { getSessionStorageItem, setSessionStorageItem } from '../utils/storage'; +import { getSessionStorageItem, setSessionStorageItem } from 'utils/storage'; +import { useUiFlag } from 'hooks/useUiFlag'; export const InitialRedirect = () => { + const personalDashboardUiEnabled = useUiFlag('personalDashboardUI'); const { lastViewed } = useLastViewedProject(); const { projects, loading } = useProjects(); const navigate = useNavigate(); @@ -17,12 +19,16 @@ export const InitialRedirect = () => { return `/projects/${lastViewed}`; } + if (personalDashboardUiEnabled) { + return '/personal'; + } + if (projects && !lastViewed && projects.length === 1) { return `/projects/${projects[0].id}`; } return '/projects'; - }, [lastViewed, projects]); + }, [lastViewed, projects, personalDashboardUiEnabled]); const redirect = () => { navigate(sessionRedirect ?? getRedirect(), { replace: true }); diff --git a/frontend/src/component/commandBar/CommandBar.tsx b/frontend/src/component/commandBar/CommandBar.tsx index 9701022191e5..e7434139e370 100644 --- a/frontend/src/component/commandBar/CommandBar.tsx +++ b/frontend/src/component/commandBar/CommandBar.tsx @@ -21,7 +21,10 @@ import { CommandPageSuggestions } from './CommandPageSuggestions'; import { useRoutes } from 'component/layout/MainLayout/NavigationSidebar/useRoutes'; import { useAsyncDebounce } from 'react-table'; import useProjects from 'hooks/api/getters/useProjects/useProjects'; -import { CommandSearchFeatures } from './CommandSearchFeatures'; +import { + type CommandQueryCounter, + CommandSearchFeatures, +} from './CommandSearchFeatures'; import { usePlausibleTracker } from 'hooks/usePlausibleTracker'; import { CommandQuickSuggestions } from './CommandQuickSuggestions'; import { CommandSearchPages } from './CommandSearchPages'; @@ -105,7 +108,8 @@ export const CommandBar = () => { const [searchedPages, setSearchedPages] = useState< CommandResultGroupItem[] >([]); - const [searchedFlagCount, setSearchedFlagCount] = useState(0); + const [searchedFlagCount, setSearchedFlagCount] = + useState({ query: '', count: 0 }); const [hasNoResults, setHasNoResults] = useState(false); const [value, setValue] = useState(''); const { routes } = useRoutes(); @@ -155,7 +159,8 @@ export const CommandBar = () => { query.length !== 0 && mappedProjects.length === 0 && mappedPages.length === 0 && - searchedFlagCount === 0; + searchedFlagCount.count === 0 && + searchedFlagCount.query === query; if (noResultsFound) { trackEvent('command-bar', { props: { @@ -169,7 +174,7 @@ export const CommandBar = () => { useEffect(() => { debouncedSetSearchState(value); - }, [searchedFlagCount]); + }, [JSON.stringify(searchedFlagCount)]); const onSearchChange = (value: string) => { debouncedSetSearchState(value); diff --git a/frontend/src/component/commandBar/CommandSearchFeatures.tsx b/frontend/src/component/commandBar/CommandSearchFeatures.tsx index cb009fa75585..a75c3e79f7c9 100644 --- a/frontend/src/component/commandBar/CommandSearchFeatures.tsx +++ b/frontend/src/component/commandBar/CommandSearchFeatures.tsx @@ -6,9 +6,14 @@ import { useFeatureSearch } from 'hooks/api/getters/useFeatureSearch/useFeatureS import { useEffect } from 'react'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; +export type CommandQueryCounter = { + query: string; + count: number; +}; + interface ICommandBar { searchString: string; - setSearchedFlagCount: (count: number) => void; + setSearchedFlagCount: (count: CommandQueryCounter) => void; onClick: () => void; setSearchLoading: (loading: boolean) => void; } @@ -36,8 +41,8 @@ export const CommandSearchFeatures = ({ })); useEffect(() => { - setSearchedFlagCount(flags.length); - }, [JSON.stringify(flags)]); + setSearchedFlagCount({ count: flags.length, query: searchString }); + }, [loading]); useEffect(() => { setSearchLoading(loading); diff --git a/frontend/src/component/events/EventTimeline/EventTimeline.tsx b/frontend/src/component/events/EventTimeline/EventTimeline.tsx index dda221500a41..296f35ff06df 100644 --- a/frontend/src/component/events/EventTimeline/EventTimeline.tsx +++ b/frontend/src/component/events/EventTimeline/EventTimeline.tsx @@ -4,11 +4,11 @@ import { startOfDay, sub } from 'date-fns'; import { useEventSearch } from 'hooks/api/getters/useEventSearch/useEventSearch'; import { EventTimelineEventGroup } from './EventTimelineEventGroup/EventTimelineEventGroup'; import { EventTimelineHeader } from './EventTimelineHeader/EventTimelineHeader'; -import { useEventTimeline } from './useEventTimeline'; import { useMemo } from 'react'; import { useSignalQuery } from 'hooks/api/getters/useSignalQuery/useSignalQuery'; import type { ISignalQuerySignal } from 'interfaces/signal'; import type { IEnvironment } from 'interfaces/environments'; +import { useEventTimelineContext } from './EventTimelineContext'; export type TimelineEventType = 'signal' | EventSchemaType; @@ -30,6 +30,12 @@ const StyledRow = styled('div')({ justifyContent: 'space-between', }); +const StyledTimelineBody = styled('div')(({ theme }) => ({ + display: 'flex', + flexDirection: 'column', + padding: theme.spacing(1, 0), +})); + const StyledTimelineContainer = styled('div')(({ theme }) => ({ position: 'relative', height: theme.spacing(1), @@ -152,9 +158,7 @@ const getTimelineEvent = ( }; export const EventTimeline = () => { - const { timeSpan, environment, setTimeSpan, setEnvironment } = - useEventTimeline(); - + const { timeSpan, environment } = useEventTimelineContext(); const endDate = new Date(); const startDate = sub(endDate, timeSpan.value); const endTime = endDate.getTime(); @@ -229,37 +233,33 @@ export const EventTimeline = () => { return ( <> - - - - - - {groups.map((group) => ( - - ))} - - - - {timeSpan.markers[0]} - {timeSpan.markers.slice(1).map((marker) => ( - - - {marker} - - ))} - now + + + + + + {groups.map((group) => ( + + ))} + + + + {timeSpan.markers[0]} + {timeSpan.markers.slice(1).map((marker) => ( + + + {marker} + + ))} + now + + ); }; diff --git a/frontend/src/component/events/EventTimeline/EventTimelineContext.tsx b/frontend/src/component/events/EventTimeline/EventTimelineContext.tsx new file mode 100644 index 000000000000..85e5cd1154e1 --- /dev/null +++ b/frontend/src/component/events/EventTimeline/EventTimelineContext.tsx @@ -0,0 +1,18 @@ +import { createContext, useContext } from 'react'; +import type { IEventTimelineContext } from './EventTimelineProvider'; + +export const EventTimelineContext = createContext< + IEventTimelineContext | undefined +>(undefined); + +export const useEventTimelineContext = (): IEventTimelineContext => { + const context = useContext(EventTimelineContext); + + if (!context) { + throw new Error( + 'useEventTimelineContext must be used within a EventTimelineProvider', + ); + } + + return context; +}; diff --git a/frontend/src/component/events/EventTimeline/EventTimelineEventGroup/EventTimelineEventCircle.tsx b/frontend/src/component/events/EventTimeline/EventTimelineEventGroup/EventTimelineEventCircle.tsx index 5d3969f56d66..0b5e2fd761c5 100644 --- a/frontend/src/component/events/EventTimeline/EventTimelineEventGroup/EventTimelineEventCircle.tsx +++ b/frontend/src/component/events/EventTimeline/EventTimelineEventGroup/EventTimelineEventCircle.tsx @@ -47,7 +47,11 @@ const getEventIcon = (type: TimelineEventType) => { return ; } if (type.startsWith('strategy-') || type.startsWith('feature-strategy-')) { - return ; + return ( + + ); } if (type.startsWith('feature-')) { return ; @@ -77,7 +81,10 @@ export const EventTimelineEventCircle = ({ group, ...props }: IEventTimelineEventCircleProps) => { - if (group.length === 1) { + if ( + group.length === 1 || + !group.some(({ type }) => type !== group[0].type) + ) { const event = group[0]; return ( diff --git a/frontend/src/component/events/EventTimeline/EventTimelineEventGroup/EventTimelineEventGroup.tsx b/frontend/src/component/events/EventTimeline/EventTimelineEventGroup/EventTimelineEventGroup.tsx index 9c458a4cbd69..14e628783710 100644 --- a/frontend/src/component/events/EventTimeline/EventTimelineEventGroup/EventTimelineEventGroup.tsx +++ b/frontend/src/component/events/EventTimeline/EventTimelineEventGroup/EventTimelineEventGroup.tsx @@ -36,7 +36,7 @@ export const EventTimelineEventGroup = ({ } - maxWidth={320} + maxWidth={350} arrow > ({ whiteSpace: 'nowrap', })); +const StyledTooltipItemList = styled('div')(({ theme }) => ({ + marginTop: theme.spacing(1), + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(2), +})); + const StyledTooltipItem = styled('div')(({ theme }) => ({ display: 'flex', gap: theme.spacing(1), - marginBottom: theme.spacing(1), })); const StyledEventTimelineEventCircle = styled(EventTimelineEventCircle)( ({ theme }) => ({ - marginTop: theme.spacing(0.5), + marginTop: theme.spacing(0.125), height: theme.spacing(2.5), width: theme.spacing(2.5), transition: 'none', '& > svg': { - height: theme.spacing(2), + height: theme.spacing(1.75), }, '&:hover': { transform: 'none', @@ -56,6 +63,8 @@ const StyledEventTimelineEventCircle = styled(EventTimelineEventCircle)( }), ); +const BoldToNormal = ({ children }: HTMLAttributes) => children; + interface IEventTimelineEventTooltipProps { group: TimelineEventGroup; } @@ -78,7 +87,9 @@ export const EventTimelineEventTooltip = ({ {event.label} {eventDateTime} - {event.summary} + + {event.summary} + ); } @@ -97,20 +108,24 @@ export const EventTimelineEventTooltip = ({ {eventDate} - {group.map((event) => ( - - -
- - {formatDateHMS( - event.timestamp, - locationSettings?.locale, - )} - - {event.summary} -
-
- ))} + + {group.map((event) => ( + + +
+ + {formatDateHMS( + event.timestamp, + locationSettings?.locale, + )} + + + {event.summary} + +
+
+ ))} +
); }; diff --git a/frontend/src/component/events/EventTimeline/EventTimelineHeader/EventTimelineHeader.tsx b/frontend/src/component/events/EventTimeline/EventTimelineHeader/EventTimelineHeader.tsx index d7c29ea76d4b..e8e571fb5370 100644 --- a/frontend/src/component/events/EventTimeline/EventTimelineHeader/EventTimelineHeader.tsx +++ b/frontend/src/component/events/EventTimeline/EventTimelineHeader/EventTimelineHeader.tsx @@ -1,9 +1,16 @@ -import { MenuItem, styled, TextField } from '@mui/material'; +import { + IconButton, + MenuItem, + styled, + TextField, + Tooltip, +} from '@mui/material'; import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender'; import { useEnvironments } from 'hooks/api/getters/useEnvironments/useEnvironments'; -import type { IEnvironment } from 'interfaces/environments'; import { useEffect, useMemo } from 'react'; -import { type TimeSpanOption, timeSpanOptions } from '../useEventTimeline'; +import { timeSpanOptions } from '../EventTimelineProvider'; +import CloseIcon from '@mui/icons-material/Close'; +import { useEventTimelineContext } from '../EventTimelineContext'; const StyledCol = styled('div')(({ theme }) => ({ display: 'flex', @@ -12,9 +19,9 @@ const StyledCol = styled('div')(({ theme }) => ({ })); const StyledFilter = styled(TextField)(({ theme }) => ({ - color: theme.palette.text.secondary, '& > div': { background: 'transparent', + color: theme.palette.text.secondary, '& > .MuiSelect-select': { padding: theme.spacing(0.5, 4, 0.5, 1), background: 'transparent', @@ -23,21 +30,19 @@ const StyledFilter = styled(TextField)(({ theme }) => ({ }, })); +const StyledTimelineEventsCount = styled('span')(({ theme }) => ({ + marginTop: theme.spacing(0.25), +})); + interface IEventTimelineHeaderProps { totalEvents: number; - timeSpan: TimeSpanOption; - setTimeSpan: (timeSpan: TimeSpanOption) => void; - environment: IEnvironment | undefined; - setEnvironment: (environment: IEnvironment) => void; } export const EventTimelineHeader = ({ totalEvents, - timeSpan, - setTimeSpan, - environment, - setEnvironment, }: IEventTimelineHeaderProps) => { + const { timeSpan, environment, setOpen, setTimeSpan, setEnvironment } = + useEventTimelineContext(); const { environments } = useEnvironments(); const activeEnvironments = useMemo( @@ -57,10 +62,10 @@ export const EventTimelineHeader = ({ return ( <> - + {totalEvents} event {totalEvents === 1 ? '' : 's'} - + )} /> + + setOpen(false)} + > + + + ); diff --git a/frontend/src/component/events/EventTimeline/useEventTimeline.ts b/frontend/src/component/events/EventTimeline/EventTimelineProvider.tsx similarity index 69% rename from frontend/src/component/events/EventTimeline/useEventTimeline.ts rename to frontend/src/component/events/EventTimeline/EventTimelineProvider.tsx index 66367ea2ab06..680bd7bfed55 100644 --- a/frontend/src/component/events/EventTimeline/useEventTimeline.ts +++ b/frontend/src/component/events/EventTimeline/EventTimelineProvider.tsx @@ -1,13 +1,33 @@ +import type { ReactNode } from 'react'; +import { EventTimelineContext } from './EventTimelineContext'; import { useLocalStorageState } from 'hooks/useLocalStorageState'; import type { IEnvironment } from 'interfaces/environments'; -export type TimeSpanOption = { +type TimeSpanOption = { key: string; label: string; value: Duration; markers: string[]; }; +type EventTimelineState = { + open: boolean; + timeSpan: TimeSpanOption; + environment?: IEnvironment; + signalsAlertSeen?: boolean; +}; + +type EventTimelineStateSetters = { + setOpen: (open: boolean) => void; + setTimeSpan: (timeSpan: TimeSpanOption) => void; + setEnvironment: (environment: IEnvironment) => void; + setSignalsAlertSeen: (seen: boolean) => void; +}; + +export interface IEventTimelineContext + extends EventTimelineState, + EventTimelineStateSetters {} + export const timeSpanOptions: TimeSpanOption[] = [ { key: '30m', @@ -57,18 +77,18 @@ export const timeSpanOptions: TimeSpanOption[] = [ }, ]; -type EventTimelineState = { - open: boolean; - timeSpan: TimeSpanOption; - environment?: IEnvironment; -}; - const defaultState: EventTimelineState = { open: false, timeSpan: timeSpanOptions[0], }; -export const useEventTimeline = () => { +interface IEventTimelineProviderProps { + children: ReactNode; +} + +export const EventTimelineProvider = ({ + children, +}: IEventTimelineProviderProps) => { const [state, setState] = useLocalStorageState( 'event-timeline:v1', defaultState, @@ -81,12 +101,20 @@ export const useEventTimeline = () => { setState((prevState) => ({ ...prevState, [key]: value })); }; - return { + const contextValue: IEventTimelineContext = { ...state, setOpen: (open: boolean) => setField('open', open), setTimeSpan: (timeSpan: TimeSpanOption) => setField('timeSpan', timeSpan), setEnvironment: (environment: IEnvironment) => setField('environment', environment), + setSignalsAlertSeen: (seen: boolean) => + setField('signalsAlertSeen', seen), }; + + return ( + + {children} + + ); }; diff --git a/frontend/src/component/layout/MainLayout/MainLayout.tsx b/frontend/src/component/layout/MainLayout/MainLayout.tsx index ab8af57676bf..88460c8ebbe3 100644 --- a/frontend/src/component/layout/MainLayout/MainLayout.tsx +++ b/frontend/src/component/layout/MainLayout/MainLayout.tsx @@ -17,8 +17,8 @@ import { DraftBanner } from './DraftBanner/DraftBanner'; import { ThemeMode } from 'component/common/ThemeMode/ThemeMode'; import { NavigationSidebar } from './NavigationSidebar/NavigationSidebar'; import { useUiFlag } from 'hooks/useUiFlag'; -import { useEventTimeline } from 'component/events/EventTimeline/useEventTimeline'; import { MainLayoutEventTimeline } from './MainLayoutEventTimeline'; +import { EventTimelineProvider } from 'component/events/EventTimeline/EventTimelineProvider'; interface IMainLayoutProps { children: ReactNode; @@ -112,14 +112,11 @@ const MainLayoutContentContainer = styled('div')(({ theme }) => ({ export const MainLayout = forwardRef( ({ children }, ref) => { - const { uiConfig, isOss } = useUiConfig(); + const { uiConfig } = useUiConfig(); const projectId = useOptionalPathParam('projectId'); const { isChangeRequestConfiguredInAnyEnv } = useChangeRequestsEnabled( projectId || '', ); - const eventTimeline = useUiFlag('eventTimeline') && !isOss(); - const { open: showTimeline, setOpen: setShowTimeline } = - useEventTimeline(); const sidebarNavigationEnabled = useUiFlag('navigationSidebar'); const StyledMainLayoutContent = sidebarNavigationEnabled @@ -129,22 +126,12 @@ export const MainLayout = forwardRef( const isSmallScreen = useMediaQuery(theme.breakpoints.down('lg')); return ( - <> + - } - elseShow={ - - } + show={
} + elseShow={} /> @@ -179,9 +166,7 @@ export const MainLayout = forwardRef( minWidth: 0, }} > - + @@ -211,7 +196,7 @@ export const MainLayout = forwardRef(