Skip to content

Commit c79e69f

Browse files
committed
Merge branch 'main' of github.com:devtron-labs/dashboard into feat/user-status-p2
2 parents 1f3fcf3 + 1f65f25 commit c79e69f

File tree

11 files changed

+149
-53
lines changed

11 files changed

+149
-53
lines changed

.env

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,5 @@ GLOBAL_API_TIMEOUT=60000
4141
TRIGGER_API_TIMEOUT=60000
4242
LOGIN_DT_LOGO=
4343
SIDEBAR_DT_LOGO=
44+
USE_V2=
45+
ENABLE_EXTERNAL_ARGO_CD=false

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,9 @@
121121
"workbox-window": "7.0.0",
122122
"workbox-core": "^7.0.0",
123123
"workbox-precaching": "^7.0.0",
124-
"workbox-routing": "^7.0.0"
124+
"workbox-routing": "^7.0.0",
125+
"workbox-navigation-preload": "7.0.0",
126+
"workbox-strategies": "7.0.0"
125127
},
126128
"jest": {
127129
"collectCoverageFrom": [

src/App.tsx

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ export default function App() {
4747
const updateToastRef = useRef(null)
4848
const [errorPage, setErrorPage] = useState<boolean>(false)
4949
const isOnline = useOnline()
50+
const refreshing = useRef(false)
51+
const [bgUpdated, setBGUpdated] = useState(false)
5052
const [validating, setValidating] = useState(true)
5153
const [approvalToken, setApprovalToken] = useState<string>('')
5254
const [approvalType, setApprovalType] = useState<APPROVAL_MODAL_TYPE>(APPROVAL_MODAL_TYPE.CONFIG)
@@ -129,7 +131,22 @@ export default function App() {
129131
}
130132
}
131133

134+
function handleControllerChange() {
135+
if (refreshing.current) {
136+
return
137+
}
138+
if (document.visibilityState === 'visible') {
139+
window.location.reload()
140+
refreshing.current = true
141+
} else {
142+
setBGUpdated(true)
143+
}
144+
}
145+
132146
useEffect(() => {
147+
if (navigator.serviceWorker) {
148+
navigator.serviceWorker.addEventListener('controllerchange', handleControllerChange)
149+
}
133150
// If not K8S_CLIENT then validateToken otherwise directly redirect
134151
if (!window._env_.K8S_CLIENT) {
135152
// By Passing validations for direct email approval notifications
@@ -142,6 +159,9 @@ export default function App() {
142159
setValidating(false)
143160
defaultRedirection()
144161
}
162+
return () => {
163+
navigator.serviceWorker.removeEventListener('controllerchange', handleControllerChange)
164+
}
145165
}, [])
146166

147167
const {
@@ -150,9 +170,20 @@ export default function App() {
150170
} = useRegisterSW({
151171
onRegisteredSW(swUrl, r) {
152172
console.log(`Service Worker at: ${swUrl}`)
153-
if (r) {
154-
r.update()
155-
}
173+
r &&
174+
setInterval(async () => {
175+
if (!(!r.installing && navigator)) return
176+
if ('connection' in navigator && !navigator.onLine) return
177+
const resp = await fetch(swUrl, {
178+
cache: 'no-store',
179+
headers: {
180+
cache: 'no-store',
181+
'cache-control': 'no-cache',
182+
},
183+
})
184+
185+
if (resp?.status === 200) await r.update()
186+
}, 1000 * 60)
156187
},
157188
onRegisterError(error) {
158189
console.log('SW registration error', error)
@@ -169,10 +200,11 @@ export default function App() {
169200
if (window.isSecureContext && navigator.serviceWorker) {
170201
// check for sw updates on page change
171202
navigator.serviceWorker.getRegistrations().then((regs) => regs.forEach((reg) => reg.update()))
172-
if (!needRefresh) {
173-
return
203+
if (needRefresh) {
204+
update()
205+
} else if (toast.isActive(updateToastRef.current)) {
206+
toast.dismiss(updateToastRef.current)
174207
}
175-
update()
176208
}
177209
}, [location])
178210

@@ -200,6 +232,24 @@ export default function App() {
200232
}
201233
}, [needRefresh])
202234

235+
useEffect(() => {
236+
if (!bgUpdated) {
237+
return
238+
}
239+
const bgUpdatedToastBody = (
240+
<UpdateToast
241+
onClick={() => window.location.reload()}
242+
text="This page has been updated. Please save any unsaved changes and refresh."
243+
buttonText="Reload"
244+
/>
245+
)
246+
if (toast.isActive(updateToastRef.current)) {
247+
toast.update(updateToastRef.current, { render: bgUpdatedToastBody })
248+
} else {
249+
updateToastRef.current = toast.info(bgUpdatedToastBody, { autoClose: false, closeButton: false })
250+
}
251+
}, [bgUpdated])
252+
203253
return (
204254
<Suspense fallback={null}>
205255
{validating ? (

src/Pages/GlobalConfigurations/Authorization/authorization.service.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,16 @@ export const getUserById = async (userId: User['id']): Promise<User> => {
3838
}
3939
}
4040

41-
export const createOrUpdateUser = (data: UserCreateOrUpdatePayload) => {
42-
const isUpdate = !!data.id
41+
export const createOrUpdateUser = ({ emailId, ...data }: UserCreateOrUpdatePayload) => {
42+
const _data: UserDto = {
43+
...data,
44+
email_id: emailId,
45+
}
46+
const isUpdate = !!_data.id
4347
const options: APIOptions = {
4448
timeout: window._env_.CONFIGURABLE_TIMEOUT ? parseInt(window._env_.CONFIGURABLE_TIMEOUT, 10) : null,
4549
}
46-
return isUpdate ? put(Routes.USER, data, options) : post(Routes.USER, data, options)
50+
return isUpdate ? put(Routes.USER, _data, options) : post(Routes.USER, _data, options)
4751
}
4852

4953
export const deleteUser = (userId: User['id']) => trash(`${Routes.USER}/${userId}`)
@@ -58,7 +62,9 @@ export const getUserList = async (
5862
try {
5963
const {
6064
result: { users, totalCount },
61-
} = (await get(getUrlWithSearchParams(Routes.USER, queryParams ?? {}), { signal })) as ResponseType<{
65+
} = (await get(getUrlWithSearchParams(`${Routes.USER}/${Routes.API_VERSION_V2}`, queryParams ?? {}), {
66+
signal,
67+
})) as ResponseType<{
6268
users: UserDto[]
6369
totalCount: number
6470
}>
@@ -115,7 +121,10 @@ export const getPermissionGroupList = async (
115121
try {
116122
const {
117123
result: { roleGroups: permissionGroups, totalCount },
118-
} = (await get(getUrlWithSearchParams(Routes.USER_ROLE_GROUP, queryParams ?? {}), { signal })) as ResponseType<{
124+
} = (await get(
125+
getUrlWithSearchParams(`${Routes.USER_ROLE_GROUP}/${Routes.API_VERSION_V2}`, queryParams ?? {}),
126+
{ signal },
127+
)) as ResponseType<{
119128
roleGroups: PermissionGroupDto[]
120129
totalCount: number
121130
}>

src/Pages/GlobalConfigurations/Authorization/types.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export interface UserDto {
5050
/**
5151
* Email of the user
5252
*/
53-
emailId: string
53+
email_id: string
5454
/**
5555
* Status of the user
5656
*
@@ -87,14 +87,15 @@ export interface UserDto {
8787
roleGroups?: Pick<PermissionGroup, 'id' | 'name' | 'description'>
8888
}
8989

90-
export interface User extends Omit<UserDto, 'timeoutWindowExpression'> {
90+
export interface User extends Omit<UserDto, 'timeoutWindowExpression' | 'email_id'> {
9191
/**
9292
* Time until which the user is active
9393
* Note: Only a user with status 'active' can have 'timeToLive'
9494
*
9595
* @default ''
9696
*/
9797
timeToLive?: string
98+
emailId: UserDto['email_id']
9899
}
99100

100101
export type UserCreateOrUpdatePayload = Pick<

src/Pages/GlobalConfigurations/Authorization/utils.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ import { LAST_LOGIN_TIME_NULL_STATE } from './UserPermissions/constants'
77
import { useAuthorizationBulkSelection } from './shared/components/BulkSelection'
88

99
export const transformUserResponse = (_user: UserDto): User => {
10-
const { lastLoginTime, timeoutWindowExpression, ...user } = _user
10+
const { lastLoginTime, timeoutWindowExpression, email_id: emailId, ...user } = _user
1111

1212
return {
1313
...user,
14+
emailId,
1415
lastLoginTime:
1516
lastLoginTime === ZERO_TIME_STRING || !lastLoginTime
1617
? LAST_LOGIN_TIME_NULL_STATE

src/components/app/list-new/AppList.tsx

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -915,17 +915,19 @@ export default function AppList({ isSuperAdmin, appListCount, isArgoInstalled }:
915915
Helm Apps
916916
</a>
917917
</li>
918-
<li className="tab-list__tab">
919-
<a
920-
className={`tab-list__tab-link ${
921-
currentTab === AppListConstants.AppTabs.ARGO_APPS ? 'active' : ''
922-
}`}
923-
onClick={() => changeAppTab(AppListConstants.AppTabs.ARGO_APPS)}
924-
data-testid="helm-app-list-button"
925-
>
926-
{AppListConstants.AppTabs.ARGO_APPS}
927-
</a>
928-
</li>
918+
{window._env_?.ENABLE_EXTERNAL_ARGO_CD && (
919+
<li className="tab-list__tab">
920+
<a
921+
className={`tab-list__tab-link ${
922+
currentTab === AppListConstants.AppTabs.ARGO_APPS ? 'active' : ''
923+
}`}
924+
onClick={() => changeAppTab(AppListConstants.AppTabs.ARGO_APPS)}
925+
data-testid="helm-app-list-button"
926+
>
927+
{AppListConstants.AppTabs.ARGO_APPS}
928+
</a>
929+
</li>
930+
)}
929931
</ul>
930932
<div className="app-tabs-sync fs-13">
931933
{lastDataSyncTimeString &&
@@ -1048,7 +1050,7 @@ export default function AppList({ isSuperAdmin, appListCount, isArgoInstalled }:
10481050
</>
10491051
)
10501052
}
1051-
if (params.appType === AppListConstants.AppType.ARGO_APPS) {
1053+
if (params.appType === AppListConstants.AppType.ARGO_APPS && window?._env_?.ENABLE_EXTERNAL_ARGO_CD) {
10521054
return (
10531055
<>
10541056
<ExternalArgoList

src/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ interface customEnv {
5454
NODE_ENV?: string
5555
LOGIN_DT_LOGO?: string
5656
SIDEBAR_DT_LOGO?: string
57+
ENABLE_EXTERNAL_ARGO_CD: boolean
5758
}
5859
declare global {
5960
interface Window {
@@ -169,6 +170,7 @@ if (!window || !window._env_) {
169170
TRIGGER_API_TIMEOUT: 60000,
170171
LOGIN_DT_LOGO: '',
171172
SIDEBAR_DT_LOGO: '',
173+
ENABLE_EXTERNAL_ARGO_CD: false,
172174
}
173175
}
174176

src/prompt-sw.ts

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

src/service-worker.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { precacheAndRoute, cleanupOutdatedCaches } from 'workbox-precaching'
2+
import { NavigationRoute, registerRoute, Route } from 'workbox-routing'
3+
import * as navigationPreload from 'workbox-navigation-preload'
4+
import { NetworkFirst, StaleWhileRevalidate } from 'workbox-strategies'
5+
import { clientsClaim } from 'workbox-core'
6+
7+
declare let self: ServiceWorkerGlobalScope
8+
9+
self.addEventListener('message', (event) => {
10+
if (event.data && event.data.type === 'SKIP_WAITING') {
11+
// eslint-disable-next-line no-void
12+
void self.skipWaiting()
13+
}
14+
})
15+
16+
clientsClaim()
17+
18+
// Precache the manifest
19+
precacheAndRoute(self.__WB_MANIFEST)
20+
21+
// clean old assets
22+
cleanupOutdatedCaches()
23+
24+
// Enable navigation preload
25+
navigationPreload.enable()
26+
27+
// Create a new navigation route that uses the Network-first, falling back to
28+
// cache strategy for navigation requests with its own cache. This route will be
29+
// handled by navigation preload. The NetworkOnly strategy will work as well.
30+
const navigationRoute = new NavigationRoute(
31+
new NetworkFirst({
32+
cacheName: 'navigations',
33+
}),
34+
)
35+
36+
// Register the navigation route
37+
registerRoute(navigationRoute)
38+
39+
// Create a route for image, script, or style requests that use a
40+
// stale-while-revalidate strategy. This route will be unaffected
41+
// by navigation preload.
42+
const staticAssetsRoute = new Route(
43+
({ request }) => {
44+
return ['image', 'script', 'style'].includes(request.destination)
45+
},
46+
new StaleWhileRevalidate({
47+
cacheName: 'static-assets',
48+
}),
49+
)
50+
51+
// Register the route handling static assets
52+
registerRoute(staticAssetsRoute)

0 commit comments

Comments
 (0)