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

update create team form #280

Merged
merged 1 commit into from
Jun 28, 2024
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
2 changes: 2 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import ProtectedRoute from './components/ProtectedRoute'

import CreateTeamForm from './pages/CreateTeamForm/CreateTeamForm.tsx'
import TeamCreated from './pages/CreateTeamForm/TeamCreated.tsx'
import NotFound from './pages/NotFound/NotFound.tsx'
import TeamOverview from './pages/TeamOverview/TeamOverview'
import UserProfile from './pages/UserProfile/UserProfile'
Expand All @@ -20,6 +21,7 @@ const App = () => {
<Route path='/:teamId' element={<TeamDetail />} />
<Route path='/:teamId/:shortName' element={<SharedBucketDetail />} />
<Route path='/opprett-team' element={<CreateTeamForm />} />
<Route path='/opprett-team/suksess' element={<TeamCreated />} />
<Route path='/not-found' element={<NotFound />} />
</Route>
</Routes>
Expand Down
84 changes: 40 additions & 44 deletions src/pages/CreateTeamForm/CreateTeamForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ import {
import * as C from '@statisticsnorway/ssb-component-library'
import { Skeleton } from '@mui/material'
import { useEffect, useState, useMemo } from 'react'
import { useNavigate } from 'react-router-dom'
import { Array as A, Console, Effect, Option as O, pipe } from 'effect'

import FormSubmissionResult, { FormSubmissionResultProps } from './FormSubmissionResult.tsx'
import FormSubmissionError, { FormSubmissionErrorProps } from './FormSubmissionError.tsx'
import PageLayout from '../../components/PageLayout/PageLayout'
import * as Klass from '../../services/klass'
import { AutonomyLevel, CreateTeamRequest, createTeam } from '../../services/createTeam'
import { AutonomyLevel, CreateTeamRequest, CreateTeamResponse, createTeam } from '../../services/createTeam'
import { User } from '../../@types/user'
import * as Utils from '../../utils/utils.ts'

Expand All @@ -41,6 +42,7 @@ interface FormError {
}

const CreateTeamForm = () => {
const navigate = useNavigate()
const uniformNameLengthLimit = 17
// TODO: These should be fetched from the dapla-team-api instead of being hardcoded
const teamAutonomyLevels: DisplayAutonomyLevel[] = [
Expand Down Expand Up @@ -81,21 +83,11 @@ const CreateTeamForm = () => {

const [submitButtonClicked, setSubmitButtonClicked] = useState(false)

const [createTeamResponseLoading, setCreateTeamResponseLoading] = useState(false)

const missingFieldErrorMessage = 'mangler'
const validationErrorMessage = 'har en valideringsfeil'

const resetForm = () => {
setDisplayName('')
setUniformName('')
setOverrideUniformName(false)
setUniformNameErrorMsg('')
setSelectedSection(O.none())

setSelectedAutonomyLevel(teamAutonomyLevels[0])
setAdditionalInformation('')
setSubmitButtonClicked(false)
}

const formErrors: FormError[] = useMemo(
() =>
pipe(
Expand All @@ -117,9 +109,8 @@ const CreateTeamForm = () => {
[displayName, uniformName, selectedSection, uniformNameErrorMsg]
)

const [formSubmissionResult, setFormSubmissionResult] = useState<FormSubmissionResultProps>({
loading: false,
formSubmissionResult: O.none(),
const [formSubmissionError, setFormSubmissionError] = useState<FormSubmissionErrorProps>({
formSubmissionError: O.none(),
})

useEffect(() => {
Expand Down Expand Up @@ -153,50 +144,55 @@ const CreateTeamForm = () => {
event.preventDefault()
// Only submit the form if no form errors are present
if (A.isEmptyArray(formErrors)) {
const userPrincipalName = O.getOrThrow(user).principal_name
const req: CreateTeamRequest = {
teamDisplayName: displayName,
uniformTeamName: uniformName,
sectionCode: O.getOrThrow(selectedSection).id.toString(),
additionalInformation: `This PR was created through Dapla Ctrl. Additional information from user ${userPrincipalName}:\n ${additionalInformation}`,
additionalInformation: `This PR was created through Dapla Ctrl. Additional information from author:\n\n ${additionalInformation}`,
autonomyLevel: selectedAutonomyLevel.id,
features: [],
}

setFormSubmissionResult({ loading: true, formSubmissionResult: O.none() })
setCreateTeamResponseLoading(true)
setFormSubmissionError({ formSubmissionError: O.none() })

Effect.gen(function* () {
const clientResponse = yield* createTeam(req)
yield* Console.log('ClientResponse', clientResponse)
return O.some(
clientResponse.status !== 200
? {
success: false,
message: `Det oppstod en feil ved opprettelse av team. Statuskode: ${clientResponse.status}`,
}
: { success: true, message: 'Opprettelse av team ble registert.' }
)
const createTeamResponse = yield* createTeam(req)
yield* Console.log('CreateTeamResponse:', createTeamResponse)
return {
success: true,
message: 'Opprettelse av team ble registert.',
body: O.some(createTeamResponse),
}
})
.pipe(
Effect.catchTags({
ResponseError: (error) => Effect.succeed(O.some({ success: false, message: error.message })),
RequestError: (error) => Effect.succeed(O.some({ success: false, message: error.message })),
ResponseError: (error) => Effect.succeed({ success: false, message: error.message, body: O.none() }),
RequestError: (error) => Effect.succeed({ success: false, message: error.message, body: O.none() }),
BodyError: (error) =>
Effect.succeed(O.some({ success: false, message: `Failed to parse body: ${error.reason._tag}` })),
Effect.succeed({
success: false,
message: `Failed to parse http request body: ${error.reason._tag}`,
body: O.none(),
}),
ParseError: (error) =>
Effect.succeed({
success: false,
message: `Failed to parse http response: ${error.message}`,
body: O.none(),
}),
}),
Effect.runPromise
)
.then((res: O.Option<{ success: boolean; message: string }>) => {
if (
Utils.option(
res,
() => false,
(r) => r.success
)
) {
resetForm()
.then((result: { success: boolean; message: string; body: O.Option<CreateTeamResponse> }) => {
if (result.success) {
navigate('/opprett-team/suksess', {
replace: true,
state: { kubenPullRequestUrl: O.getOrThrow(result.body).kubenPullRequestUrl },
})
} else {
setFormSubmissionError({ formSubmissionError: O.some(result.message) })
}
setFormSubmissionResult({ loading: false, formSubmissionResult: res })
})
}
}
Expand Down Expand Up @@ -359,7 +355,7 @@ const CreateTeamForm = () => {
</div>
</Dialog>
)}
<FormSubmissionResult {...formSubmissionResult} />
{createTeamResponseLoading && <FormSubmissionError {...formSubmissionError} />}
</form>
)

Expand Down
24 changes: 24 additions & 0 deletions src/pages/CreateTeamForm/FormSubmissionError.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import styles from './createTeamForm.module.scss'
import { Dialog } from '@statisticsnorway/ssb-component-library'
import { Option as O } from 'effect'
import { CircularProgress } from '@mui/material'

export interface FormSubmissionErrorProps {
formSubmissionError: O.Option<string>
}

const FormSubmissionError = ({ formSubmissionError }: FormSubmissionErrorProps) =>
O.match(formSubmissionError, {
onNone: () => (
<div className={styles.center}>
<CircularProgress />
</div>
),
onSome: (errorMessage) => (
<Dialog className={styles.warning} type='warning' title='Feil oppstod ved innsending av skjema'>
{errorMessage}
</Dialog>
),
})

export default FormSubmissionError
37 changes: 0 additions & 37 deletions src/pages/CreateTeamForm/FormSubmissionResult.tsx

This file was deleted.

23 changes: 23 additions & 0 deletions src/pages/CreateTeamForm/TeamCreated.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { Dialog, Link } from '@statisticsnorway/ssb-component-library'
import { useLocation } from 'react-router-dom'

import PageLayout from '../../components/PageLayout/PageLayout'
import styles from './createTeamForm.module.scss'

export interface TeamCreatedProps {
kubenPullRequestUrl: string
}

const TeamCreated = () => {
const { state }: { state: TeamCreatedProps } = useLocation()
const renderContent = () => (
<Dialog className={styles.warning} type={'info'} title={'Skjema ble innsendt'}>
<span>{`Opprettelse av team ble registert. Prosessen kan følges `}</span>
<Link href={state.kubenPullRequestUrl}>her</Link>
</Dialog>
)

return <PageLayout title='Opprett Team' content={renderContent()} />
}

export default TeamCreated
18 changes: 13 additions & 5 deletions src/services/createTeam.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Effect } from 'effect'
import { Schema } from '@effect/schema'
import { ParseResult, Schema } from '@effect/schema'
import * as Http from '@effect/platform/HttpClient'
import { HttpClientError } from '@effect/platform/Http/ClientError'
import { ClientResponse } from '@effect/platform/Http/ClientResponse'
import * as ClientResponse from '@effect/platform/Http/ClientResponse'
import { BodyError } from '@effect/platform/Http/Body'
import { DAPLA_TEAM_API_URL } from '../utils/utils'
import { withKeyEncoding } from '../utils/schema'
Expand All @@ -26,13 +26,21 @@ export type AutonomyLevel = Schema.Schema.Type<typeof AutonomyLevelSchema>
export type Feature = Schema.Schema.Type<typeof FeatureSchema>
export type CreateTeamRequest = Schema.Schema.Type<typeof CreateTeamRequestSchema>

const CreateTeamResponse = Schema.Struct({
uniformTeamName: withKeyEncoding('uniform_team_name', Schema.String),
kubenPullRequestId: withKeyEncoding('kuben_pull_request_id', Schema.Number),
kubenPullRequestUrl: withKeyEncoding('kuben_pull_request_url', Schema.String),
})

export type CreateTeamResponse = Schema.Schema.Type<typeof CreateTeamResponse>

export const createTeam = (
createTeamRequest: CreateTeamRequest
): Effect.Effect<ClientResponse, BodyError | HttpClientError> =>
): Effect.Effect<CreateTeamResponse, BodyError | HttpClientError | ParseResult.ParseError> =>
Http.request
.post(new URL(CREATE_TEAM_URL, window.location.origin))
.pipe(
Http.request.schemaBody(CreateTeamRequestSchema)(createTeamRequest),
Effect.flatMap(Http.client.fetch),
Effect.scoped
Effect.flatMap(Http.client.fetchOk),
ClientResponse.schemaBodyJsonScoped(CreateTeamResponse)
)