Skip to content

Commit 908dc35

Browse files
dennisvankekemsvcAPLBotCopilot
authored andcommitted
feat: Git Configure Edit modal (#806)
Co-authored-by: svcAPLBot <174728082+svcAPLBot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent d30d474 commit 908dc35

5 files changed

Lines changed: 146 additions & 23 deletions

File tree

src/components/InformationBanner.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export default function InformationBanner({ message, children, small, sx }: Prop
2424
return (
2525
<StyledInfoBanner small={small} sx={{ ...sx }}>
2626
<Iconify icon='material-symbols:info' width={40} height={28} color='#c7d030d9' />
27-
<Typography sx={{ color: theme.palette.text.primary }}>{message}</Typography>
27+
<Typography sx={{ color: theme.palette.text.primary, ml: 1 }}>{message}</Typography>
2828
{children}
2929
</StyledInfoBanner>
3030
)

src/components/Setting.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,7 @@ export const getSettingUiSchema = (settings: GetSettingsInfoApiResponse, setting
5858
adminPassword: { 'ui:widget': 'hidden' },
5959
useORCS: { 'ui:widget': 'hidden' },
6060
aiEnabled: { 'ui:widget': 'hidden' },
61-
git: {
62-
password: { 'ui:widget': 'password' },
63-
},
61+
git: { 'ui:widget': 'hidden' },
6462
},
6563
kms: {
6664
sops: {

src/components/modals/ConfigureGitModal.tsx

Lines changed: 128 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
/* eslint-disable no-nested-ternary */
2+
import ContentCopyIcon from '@mui/icons-material/ContentCopy'
23
import { yupResolver } from '@hookform/resolvers/yup'
34
import { LoadingButton } from '@mui/lab'
4-
import { Box, Button, Modal, Typography, styled } from '@mui/material'
5+
import { Box, Button, IconButton, Modal, Tooltip, Typography, styled } from '@mui/material'
56
import { FetchBaseQueryError } from '@reduxjs/toolkit/query'
67
import InformationBanner from 'components/InformationBanner'
78
import { TextField } from 'components/forms/TextField'
89
import { useEffect, useMemo, useState } from 'react'
910
import { Controller, useForm } from 'react-hook-form'
1011
import { useLocalStorage } from 'react-use'
1112
import { useSession } from 'providers/Session'
12-
import { useMigrateGitMutation } from 'redux/otomiApi'
13+
import { useGetGitSettingsQuery, useMigrateGitMutation } from 'redux/otomiApi'
1314
import { GitSettingsFormValues, gitSettingsSchema } from './gitSettingsValidator'
1415

1516
const MODAL_TITLE = 'Configure Git Repository'
@@ -88,6 +89,25 @@ const IntroParagraph = styled(BodyText)({
8889
marginBottom: '24px',
8990
})
9091

92+
const DefaultGitUrlBlock = styled(Box)(({ theme }) => ({
93+
marginTop: '24px',
94+
padding: '14px 16px',
95+
borderRadius: 8,
96+
border: '1px solid rgba(145, 158, 171, 0.24)',
97+
backgroundColor: theme.palette.cm.rowAlter,
98+
display: 'flex',
99+
alignItems: 'center',
100+
justifyContent: 'space-between',
101+
gap: '16px',
102+
}))
103+
104+
const DefaultGitUrlText = styled(Typography)({
105+
overflow: 'hidden',
106+
textOverflow: 'ellipsis',
107+
whiteSpace: 'nowrap',
108+
fontFamily: 'monospace',
109+
})
110+
91111
const SectionTitle = styled(Typography)({
92112
marginBottom: '4px',
93113
fontWeight: 550,
@@ -216,15 +236,45 @@ function getErrorMessage(error: unknown): string {
216236
return 'Something went wrong while migrating Git settings.'
217237
}
218238

239+
const emptyGitFormValues: GitSettingsFormValues = {
240+
repoUrl: '',
241+
branch: '',
242+
username: '',
243+
password: '',
244+
email: '',
245+
}
246+
219247
export default function ConfigureGitModal({ open, onClose }: ConfigureGitModalProps) {
220248
const {
221249
user: { isPlatformAdmin },
222250
settings: {
251+
cluster: { domainSuffix },
223252
otomi: { isPreInstalled },
224253
},
225254
} = useSession()
226255

227256
const [showGitWizard, setShowGitWizard] = useLocalStorage<boolean>('showGitConfigureWizard', true)
257+
258+
const isControlled = typeof open === 'boolean'
259+
const actualOpen = useMemo(() => (isControlled ? !!open : !!showGitWizard), [isControlled, open, showGitWizard])
260+
261+
const { data: gitSettings, isFetching: isFetchingGitSettings } = useGetGitSettingsQuery(undefined, {
262+
skip: !isPlatformAdmin || !isPreInstalled || !actualOpen,
263+
})
264+
265+
const defaultGitUrl = gitSettings?.repoUrl || ''
266+
const isDefaultGitConfiguration = gitSettings?.repoUrl?.includes('git-server.git-server.svc.cluster.local') ?? false
267+
const hasGitConfiguration = !!gitSettings?.repoUrl && !isDefaultGitConfiguration
268+
const displayedRepoUrl = isDefaultGitConfiguration && domainSuffix ? `https://git.${domainSuffix}/otomi/values` : ''
269+
270+
const getGitFormValues = (): GitSettingsFormValues => ({
271+
repoUrl: hasGitConfiguration ? gitSettings?.repoUrl || '' : '',
272+
branch: hasGitConfiguration ? gitSettings?.branch || '' : '',
273+
username: hasGitConfiguration ? gitSettings?.username || '' : '',
274+
password: hasGitConfiguration ? gitSettings?.password || '' : '',
275+
email: hasGitConfiguration ? gitSettings?.email || '' : '',
276+
})
277+
228278
const [showFormStep, setShowFormStep] = useState(false)
229279
const [isTransitioning, setIsTransitioning] = useState(false)
230280
const [submitError, setSubmitError] = useState('')
@@ -235,29 +285,21 @@ export default function ConfigureGitModal({ open, onClose }: ConfigureGitModalPr
235285
const {
236286
control,
237287
handleSubmit,
288+
getValues,
238289
formState: { errors },
239290
reset,
240291
} = useForm<GitSettingsFormValues>({
241292
resolver: yupResolver(gitSettingsSchema),
242-
defaultValues: {
243-
repoUrl: '',
244-
branch: '',
245-
username: '',
246-
password: '',
247-
email: '',
248-
},
293+
defaultValues: emptyGitFormValues,
249294
mode: 'onBlur',
250295
})
251296

252-
const isControlled = typeof open === 'boolean'
253-
const actualOpen = useMemo(() => (isControlled ? !!open : !!showGitWizard), [isControlled, open, showGitWizard])
254-
255297
const resetModalState = () => {
256-
setShowFormStep(false)
298+
setShowFormStep(hasGitConfiguration)
257299
setSubmitError('')
258300
setMigrationSucceeded(false)
259301
setIsTransitioning(false)
260-
reset()
302+
reset(getGitFormValues())
261303
}
262304

263305
useEffect(() => {
@@ -269,9 +311,18 @@ export default function ConfigureGitModal({ open, onClose }: ConfigureGitModalPr
269311
}, [isPreInstalled, isControlled, setShowGitWizard])
270312

271313
useEffect(() => {
272-
if (!actualOpen) resetModalState()
314+
if (!actualOpen) {
315+
resetModalState()
316+
return
317+
}
318+
319+
if (!gitSettings) return
320+
321+
reset(getGitFormValues())
322+
setShowFormStep(hasGitConfiguration)
323+
273324
// eslint-disable-next-line react-hooks/exhaustive-deps
274-
}, [actualOpen])
325+
}, [actualOpen, gitSettings, hasGitConfiguration])
275326

276327
const handleClose = () => {
277328
resetModalState()
@@ -284,6 +335,19 @@ export default function ConfigureGitModal({ open, onClose }: ConfigureGitModalPr
284335
setShowGitWizard(false)
285336
}
286337

338+
const handleCopyDefaultGitUrl = async () => {
339+
if (!displayedRepoUrl) return
340+
await navigator.clipboard.writeText(displayedRepoUrl)
341+
}
342+
343+
const handleCopyRepoUrl = async () => {
344+
const repoUrl = getValues('repoUrl')
345+
346+
if (!repoUrl) return
347+
348+
await navigator.clipboard.writeText(repoUrl)
349+
}
350+
287351
const goToFormStep = () => {
288352
setIsTransitioning(true)
289353

@@ -356,7 +420,12 @@ export default function ConfigureGitModal({ open, onClose }: ConfigureGitModalPr
356420
>
357421
<ModalBox>
358422
<AnimatedContainer isTransitioning={isTransitioning}>
359-
{!showFormStep ? (
423+
{isFetchingGitSettings ? (
424+
<ModalContent>
425+
<ModalTitle variant='h4'>{MODAL_TITLE}</ModalTitle>
426+
<BodyText variant='body1'>Loading Git settings...</BodyText>
427+
</ModalContent>
428+
) : !showFormStep ? (
360429
<>
361430
<ModalContent>
362431
<ModalTitle variant='h4'>{MODAL_TITLE}</ModalTitle>
@@ -370,6 +439,25 @@ export default function ConfigureGitModal({ open, onClose }: ConfigureGitModalPr
370439
<BodyText variant='body1'>
371440
Configuring an external Git Repo is recommended for installing App Platform.
372441
</BodyText>
442+
443+
{!!defaultGitUrl && (
444+
<DefaultGitUrlBlock>
445+
<Box sx={{ minWidth: 0 }}>
446+
<Typography variant='subtitle2'>Current internal Git repository</Typography>
447+
<DefaultGitUrlText variant='body2'>{displayedRepoUrl}</DefaultGitUrlText>
448+
</Box>
449+
450+
<Tooltip title='Copy Git repository URL'>
451+
<IconButton
452+
aria-label='Copy Git repository URL'
453+
color='primary'
454+
onClick={handleCopyDefaultGitUrl}
455+
>
456+
<ContentCopyIcon fontSize='small' />
457+
</IconButton>
458+
</Tooltip>
459+
</DefaultGitUrlBlock>
460+
)}
373461
</ModalContent>
374462

375463
<ModalFooter>
@@ -413,6 +501,10 @@ export default function ConfigureGitModal({ open, onClose }: ConfigureGitModalPr
413501
<ModalContent>
414502
<ModalTitle variant='h4'>{MODAL_TITLE}</ModalTitle>
415503

504+
{hasGitConfiguration && (
505+
<InformationBanner message='Changing the Git repository URL will migrate App Platform to the new repository. Updating credentials only will not trigger a migration.' />
506+
)}
507+
416508
{!!submitError && <InformationBanner message={submitError} />}
417509

418510
<RepoFieldBlock>
@@ -427,6 +519,24 @@ export default function ConfigureGitModal({ open, onClose }: ConfigureGitModalPr
427519
fullWidth
428520
error={!!errors.repoUrl}
429521
helperText={errors.repoUrl?.message}
522+
InputProps={
523+
hasGitConfiguration
524+
? {
525+
endAdornment: (
526+
<Tooltip title='Copy Git repository URL'>
527+
<IconButton
528+
edge='end'
529+
sx={{ mr: '0px' }}
530+
color='primary'
531+
onClick={handleCopyRepoUrl}
532+
>
533+
<ContentCopyIcon fontSize='small' />
534+
</IconButton>
535+
</Tooltip>
536+
),
537+
}
538+
: undefined
539+
}
430540
/>
431541
)}
432542
/>
@@ -513,7 +623,7 @@ export default function ConfigureGitModal({ open, onClose }: ConfigureGitModalPr
513623

514624
<ModalFooter>
515625
<Button variant='outlined' color='primary' onClick={handleClose} disabled={isMigrating}>
516-
Configure later
626+
{hasGitConfiguration ? 'Cancel' : 'Configure later'}
517627
</Button>
518628

519629
<LoadingButton type='submit' variant='contained' color='primary' loading={isMigrating}>

src/pages/SettingsOverview.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export default function SettingsOverview() {
3535
{ title: 'Backup', path: '/settings/platformBackups', icon: getIcon('backup_icon.svg'), id: 'backup' },
3636
{ title: 'Object Storage', path: '/settings/obj', icon: getIcon('cloud_upload.svg'), id: 'objectStorage' },
3737
{
38-
title: 'Git',
38+
title: 'GitOps',
3939
icon: getIcon('git_icon.svg'),
4040
id: 'git',
4141
onClick: () => setOpenGitModal(true),

src/redux/otomiApi.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,9 @@ const injectedRtkApi = api.injectEndpoints({
382382
body: queryArg.body,
383383
}),
384384
}),
385+
getGitSettings: build.query<GetGitSettingsApiResponse, GetGitSettingsApiArg>({
386+
query: () => ({ url: `/v2/git` }),
387+
}),
385388
migrateGit: build.mutation<MigrateGitApiResponse, MigrateGitApiArg>({
386389
query: (queryArg) => ({ url: `/v2/git`, method: 'PUT', body: queryArg.body }),
387390
}),
@@ -4251,6 +4254,9 @@ export type GetSettingsInfoApiResponse = /** status 200 The request is successfu
42514254
git?: {
42524255
repoUrl?: string
42534256
branch?: string
4257+
username?: string
4258+
password?: string
4259+
email?: string
42544260
}
42554261
}
42564262
ingressClassNames?: string[]
@@ -4854,6 +4860,14 @@ export type EditAppApiArg = {
48544860
}
48554861
}
48564862
}
4863+
export type GetGitSettingsApiResponse = /** status 200 Current Git settings */ {
4864+
repoUrl: string
4865+
username?: string
4866+
password: string
4867+
email: string
4868+
branch: string
4869+
}
4870+
export type GetGitSettingsApiArg = void
48574871
export type MigrateGitApiResponse = /** status 200 Migration successful. API is now locked. */ undefined
48584872
export type MigrateGitApiArg = {
48594873
/** New git configuration to migrate to. */
@@ -4968,6 +4982,7 @@ export const {
49684982
useToggleAppsMutation,
49694983
useGetTeamAppQuery,
49704984
useEditAppMutation,
4985+
useGetGitSettingsQuery,
49714986
useMigrateGitMutation,
49724987
useGetApiStatusQuery,
49734988
} = injectedRtkApi

0 commit comments

Comments
 (0)