Skip to content

Commit 300bb0f

Browse files
authored
Merge pull request #2255 from devtron-labs/feat/manual-chunking
feat: add support for manual chunking and auto generate service worker
2 parents 0267af3 + 37a9607 commit 300bb0f

File tree

26 files changed

+886
-575
lines changed

26 files changed

+886
-575
lines changed

.env

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ LOGIN_DT_LOGO=
4242
SIDEBAR_DT_LOGO=
4343
ENABLE_EXTERNAL_ARGO_CD=false
4444
API_BATCH_SIZE=20
45-
SERVICE_WORKER_TIMEOUT='1'
45+
SERVICE_WORKER_TIMEOUT='3'
4646
ENABLE_RESOURCE_SCAN=false
4747
FEATURE_USER_DEFINED_GITOPS_REPO_ENABLE=false
4848
ENABLE_RESOURCE_SCAN_V2=false

index.html

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,9 @@
3030
<link rel="shortcut icon" href="/favicon.ico" />
3131
<script src="/dashboard/env-config.js"></script>
3232
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
33-
<meta name="theme-color" content="#06c" />
33+
<meta name="theme-color" content="#0066cc" />
3434
<meta name="robots" content="noindex, nofollow" />
35-
<!--
36-
manifest.json provides metadata used when your web app is installed on a
37-
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
38-
-->
39-
<link rel="manifest" href="/manifest.json" />
40-
35+
<meta name="description" content="Easily containerize your application to move it to Kubernetes in the cloud or in your own data center. Build, test, secure, deploy, and manage your applications on Kubernetes using open-source software." />
4136
<title>Devtron</title>
4237
</head>
4338

manifest.json

Lines changed: 0 additions & 15 deletions
This file was deleted.

package.json

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
22
"name": "dashboard",
3-
"version": "1.1.0",
3+
"version": "1.2.0",
44
"private": true,
55
"homepage": "/dashboard",
66
"dependencies": {
7-
"@devtron-labs/devtron-fe-common-lib": "1.1.7",
7+
"@devtron-labs/devtron-fe-common-lib": "1.2.2",
88
"@esbuild-plugins/node-globals-polyfill": "0.2.3",
99
"@rjsf/core": "^5.13.3",
1010
"@rjsf/utils": "^5.13.3",
@@ -57,10 +57,10 @@
5757
"lint": "tsc --noEmit && eslint 'src/**/*.{js,jsx,ts,tsx}' --max-warnings 0",
5858
"lint-fix": "eslint 'src/**/*.{js,jsx,ts,tsx}' --fix",
5959
"start": "vite --open",
60-
"build": "NODE_OPTIONS=--max_old_space_size=4096 vite build",
60+
"build": "NODE_OPTIONS=--max_old_space_size=8192 vite build",
6161
"serve": "vite preview",
62-
"build-light": "NODE_OPTIONS=--max_old_space_size=4096 GENERATE_SOURCEMAP=false vite build",
63-
"build-k8s-app": "NODE_OPTIONS=--max_old_space_size=4096 GENERATE_SOURCEMAP=false VITE_K8S_CLIENT=true vite build",
62+
"build-light": "NODE_OPTIONS=--max_old_space_size=8192 GENERATE_SOURCEMAP=false vite build",
63+
"build-k8s-app": "NODE_OPTIONS=--max_old_space_size=8192 GENERATE_SOURCEMAP=false VITE_K8S_CLIENT=true vite build",
6464
"test": "vitest test",
6565
"test-coverage:watch": "npm run test -- --coverage",
6666
"test-coverage": "npm run test -- --coverage --watchAll=false",
@@ -115,26 +115,21 @@
115115
"husky": "^7.0.4",
116116
"jest-extended": "^2.0.0",
117117
"jest-junit": "^13.0.0",
118-
"jsdom-worker": "^0.2.1",
119118
"lint-staged": "12.5.0",
120119
"mock-socket": "^9.2.1",
121120
"prettier": "^3.1.1",
122121
"react-test-render": "^1.1.2",
123122
"sass": "^1.69.7",
123+
"sharp": "^0.33.5",
124124
"storybook": "^8.4.2",
125+
"svgo": "^3.3.2",
125126
"ts-jest": "29.2.5",
126127
"ts-node": "10.9.2",
127128
"typescript": "5.5.4",
128-
"vite-plugin-pwa": "0.20.5",
129+
"vite-plugin-image-optimizer": "^1.1.8",
130+
"vite-plugin-pwa": "^0.21.1",
129131
"vite-tsconfig-paths": "5.0.1",
130-
"vitest": "2.0.5",
131-
"workbox-core": "7.1.0",
132-
"workbox-navigation-preload": "7.1.0",
133-
"workbox-precaching": "7.1.0",
134-
"workbox-routing": "7.1.0",
135-
"workbox-strategies": "7.1.0",
136-
"workbox-window": "7.1.0",
137-
"worker-loader": "3.0.8"
132+
"vitest": "2.0.5"
138133
},
139134
"jest": {
140135
"collectCoverageFrom": [

src/App.tsx

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
URLS as CommonURLS,
2828
ToastManager,
2929
ToastVariantType,
30+
API_STATUS_CODES,
3031
} from '@devtron-labs/devtron-fe-common-lib'
3132
import { ReactComponent as ICSparkles } from '@Icons/ic-sparkles.svg'
3233
import { ReactComponent as ICArrowClockwise } from '@Icons/ic-arrow-clockwise.svg'
@@ -189,20 +190,25 @@ export default function App() {
189190
return parsedTimeout
190191
}
191192

192-
return 1
193+
return 3
193194
})()
194195

195196
const {
196-
needRefresh: [needRefresh],
197+
needRefresh: [doesNeedRefresh],
197198
updateServiceWorker,
198199
} = useRegisterSW({
199-
onRegisteredSW(swUrl, r) {
200+
onRegisteredSW(swUrl, swRegistration) {
200201
console.log(`Service Worker at: ${swUrl}`)
201-
r &&
202+
swRegistration &&
202203
setInterval(
203204
async () => {
204-
if (!(!r.installing && navigator)) return
205-
if ('connection' in navigator && !navigator.onLine) return
205+
if (
206+
swRegistration.installing ||
207+
!navigator ||
208+
('connection' in navigator && !navigator.onLine)
209+
) {
210+
return
211+
}
206212

207213
try {
208214
const resp = await fetch(swUrl, {
@@ -212,7 +218,9 @@ export default function App() {
212218
'cache-control': 'no-cache',
213219
},
214220
})
215-
if (resp?.status === 200) await r.update()
221+
if (resp?.status === API_STATUS_CODES.OK) {
222+
await swRegistration.update()
223+
}
216224
} catch {
217225
// Do nothing
218226
}
@@ -223,25 +231,20 @@ export default function App() {
223231
onRegisterError(error) {
224232
console.log('SW registration error', error)
225233
},
234+
onNeedRefresh() {
235+
handleNeedRefresh()
236+
},
226237
})
227238

228-
function update() {
239+
function handleAppUpdate() {
240+
if (ToastManager.isToastActive(updateToastRef.current)) {
241+
ToastManager.dismissToast(updateToastRef.current)
242+
}
243+
229244
updateServiceWorker(true)
230245
}
231246

232-
useEffect(() => {
233-
if (window.isSecureContext && navigator.serviceWorker) {
234-
// check for sw updates on page change
235-
navigator.serviceWorker.getRegistrations().then((regs) => regs.forEach((reg) => reg.update()))
236-
if (needRefresh) {
237-
update()
238-
} else if (ToastManager.isToastActive(updateToastRef.current)) {
239-
ToastManager.dismissToast(updateToastRef.current)
240-
}
241-
}
242-
}, [location])
243-
244-
function onUpdate() {
247+
function handleNeedRefresh() {
245248
if (ToastManager.isToastActive(updateToastRef.current)) {
246249
ToastManager.dismissToast(updateToastRef.current)
247250
}
@@ -254,7 +257,7 @@ export default function App() {
254257
buttonProps: {
255258
text: 'Reload',
256259
dataTestId: 'reload-btn',
257-
onClick: update,
260+
onClick: handleAppUpdate,
258261
startIcon: <ICArrowClockwise />,
259262
},
260263
icon: <ICSparkles />,
@@ -270,10 +273,18 @@ export default function App() {
270273
}
271274

272275
useEffect(() => {
273-
if (needRefresh) {
274-
onUpdate()
276+
if (window.isSecureContext && navigator.serviceWorker) {
277+
// check for sw updates on page change
278+
navigator.serviceWorker
279+
.getRegistrations()
280+
.then((registrations) => registrations.forEach((reg) => reg.update()))
281+
if (doesNeedRefresh) {
282+
handleAppUpdate()
283+
} else if (ToastManager.isToastActive(updateToastRef.current)) {
284+
ToastManager.dismissToast(updateToastRef.current)
285+
}
275286
}
276-
}, [needRefresh])
287+
}, [location])
277288

278289
useEffect(() => {
279290
if (!bgUpdated) {

src/components/ApplicationGroup/AppGroup.types.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,10 @@ export interface BulkCDDetailTypeResponse {
6969
uniqueReleaseTags: string[]
7070
}
7171

72-
export interface BulkCDDetailType extends BulkTriggerAppDetailType, Pick<CDMaterialProps, 'isTriggerBlockedDueToPlugin' | 'configurePluginURL' | 'consequence'>, Partial<Pick<CommonNodeAttr, 'showPluginWarning'>> {
72+
export interface BulkCDDetailType
73+
extends BulkTriggerAppDetailType,
74+
Pick<CDMaterialProps, 'isTriggerBlockedDueToPlugin' | 'configurePluginURL' | 'consequence'>,
75+
Partial<Pick<CommonNodeAttr, 'showPluginWarning'>> {
7376
cdPipelineName?: string
7477
cdPipelineId?: string
7578
stageType?: DeploymentNodeType
@@ -113,6 +116,7 @@ export interface BulkCITriggerType extends BulkRuntimeParamsType {
113116
onClickTriggerBulkCI: (appIgnoreCache: Record<number, boolean>, appsToRetry?: Record<string, boolean>) => void
114117
getWebhookPayload: (id, webhookTimeStampOrder: typeof TIME_STAMP_ORDER) => void
115118
webhookPayloads: WebhookPayloadType
119+
setWebhookPayloads: React.Dispatch<React.SetStateAction<WebhookPayloadType>>
116120
isWebhookPayloadLoading: boolean
117121
isShowRegexModal: (_appId: number, ciNodeId: number, inputMaterialList: any[]) => boolean
118122
responseList: ResponseRowType[]
@@ -316,7 +320,7 @@ export interface AppGroupDetailDefaultType {
316320
isVirtualEnv?: boolean
317321
envName?: string
318322
description?: string
319-
getAppListData?: () => Promise<void>
323+
getAppListData?: () => Promise<OptionType[]>
320324
handleSaveDescription?: (description: string) => Promise<void>
321325
}
322326

@@ -609,11 +613,4 @@ export enum AppGroupUrlFilters {
609613

610614
export interface AppGroupUrlFiltersType extends Record<AppGroupUrlFilters, string[]> {}
611615

612-
export interface SetFiltersInLocalStorageParamsType {
613-
filterParentType: FilterParentType,
614-
resourceId: string,
615-
resourceList: MultiValue<OptionType>,
616-
groupList: MultiValue<GroupOptionType>,
617-
}
618-
619-
export type AppEnvLocalStorageKeyType = `${string}__filter`
616+
export type AppEnvLocalStorageKeyType = `${string}__filter`

src/components/ApplicationGroup/AppGroup.utils.ts

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,8 @@ import {
3232
CDWorkflowStatusType,
3333
CIWorkflowStatusType,
3434
ProcessWorkFlowStatusType,
35-
FilterParentType,
36-
SetFiltersInLocalStorageParamsType,
37-
AppEnvLocalStorageKeyType,
3835
} from './AppGroup.types'
3936
import { getParsedBranchValuesForPlugin } from '@Components/common'
40-
import { APP_GROUP_LOCAL_STORAGE_KEY, ENV_GROUP_LOCAL_STORAGE_KEY } from './Constants'
4137

4238
let timeoutId
4339

@@ -296,24 +292,3 @@ export const processConsequenceData = (data: BlockedStateData): ConsequenceType
296292
export const parseSearchParams = (searchParams: URLSearchParams) => ({
297293
[AppGroupUrlFilters.cluster]: searchParams.getAll(AppGroupUrlFilters.cluster),
298294
})
299-
300-
export const getAppFilterLocalStorageKey = (filterParentType: FilterParentType): AppEnvLocalStorageKeyType =>
301-
filterParentType === FilterParentType.app ? ENV_GROUP_LOCAL_STORAGE_KEY : APP_GROUP_LOCAL_STORAGE_KEY
302-
303-
export const setFilterInLocalStorage = ({
304-
filterParentType,
305-
resourceId,
306-
resourceList,
307-
groupList,
308-
}: SetFiltersInLocalStorageParamsType) => {
309-
const localStorageKey = getAppFilterLocalStorageKey(filterParentType)
310-
try {
311-
const localStorageValue = localStorage.getItem(localStorageKey)
312-
const localStoredMap = new Map(localStorageValue ? JSON.parse(localStorageValue) : null)
313-
localStoredMap.set(resourceId, [resourceList, groupList])
314-
// Set filter in local storage as Array from Map of resourceId vs [selectedAppList, selectedGroupFilter]
315-
localStorage.setItem(localStorageKey, JSON.stringify(Array.from(localStoredMap)))
316-
} catch {
317-
localStorage.setItem(localStorageKey, '')
318-
}
319-
}

src/components/ApplicationGroup/AppGroupAppFilter.components.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { components } from 'react-select'
1919
import { ComponentSizeType, ConditionalWrap, TabGroup } from '@devtron-labs/devtron-fe-common-lib'
2020
import Tippy from '@tippyjs/react'
2121
import { useAppGroupAppFilterContext } from './AppGroupDetailsRoute'
22-
import { getOptionBGClass, setFilterInLocalStorage } from './AppGroup.utils'
22+
import { getOptionBGClass } from './AppGroup.utils'
2323
import { ReactComponent as ShowIconFilter } from '../../assets/icons/ic-group-filter.svg'
2424
import { ReactComponent as ShowIconFilterApplied } from '../../assets/icons/ic-group-filter-applied.svg'
2525
import { ReactComponent as Search } from '../../assets/icons/ic-search.svg'
@@ -31,6 +31,7 @@ import { ReactComponent as CheckIcon } from '../../assets/icons/ic-check.svg'
3131
import { AppGroupAppFilterContextType, FilterParentType } from './AppGroup.types'
3232
import { AppFilterTabs } from './Constants'
3333
import { ShortcutKeyBadge } from '@Components/common/formFields/Widgets/Widgets'
34+
import { setAppGroupFilterInLocalStorage } from '@Components/common'
3435

3536
export const ValueContainer = (props): JSX.Element => {
3637
const {
@@ -183,7 +184,7 @@ export const MenuList = (props: any): JSX.Element => {
183184
const clearSelection = (): void => {
184185
setSelectedAppList([])
185186
setSelectedGroupFilter([])
186-
setFilterInLocalStorage({ filterParentType, resourceId, resourceList: [], groupList: [] })
187+
setAppGroupFilterInLocalStorage({ filterParentType, resourceId, resourceList: [], groupList: [] })
187188
}
188189
const onTabChange = (e): void => {
189190
setSelectedFilterTab(e.currentTarget.dataset.selectedTab)

0 commit comments

Comments
 (0)