Skip to content

PM-579 Add copilot request UI #995

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

Merged
merged 5 commits into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Integrate save request API
  • Loading branch information
himaniraghav3 committed Jan 28, 2025
commit e3f4bf99a85fd5e265e73ce47aa0e55db13935d1
18 changes: 18 additions & 0 deletions src/apps/copilots/src/models/CopilotRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { UserSkill } from '~/libs/core'

import { ProjectType } from '../constants'

export interface CopilotRequest {
projectId: string,
projectType: ProjectType,
complexity: 'high' | 'medium' | 'low',
copilotUsername: string,
numHoursPerWeek: number,
numWeeks: number,
overview: string,
paymentType: string,
requiresCommunicatn: 'yes' | 'no',
skills: UserSkill[],
startDate: Date,
tzRestrictions: 'yes' | 'no',
}
108 changes: 98 additions & 10 deletions src/apps/copilots/src/pages/copilot-request-form/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { FC, useContext, useState } from 'react'
import { SWRResponse } from 'swr'
import { bind, isEmpty } from 'lodash'
import { toast } from 'react-toastify'
import classNames from 'classnames'

import { profileContext, ProfileContextData } from '~/libs/core'
import { Button, InputDatePicker, InputMultiselectOption,
import { Button, IconSolid, InputDatePicker, InputMultiselectOption,
InputRadio, InputSelect, InputText, InputTextarea } from '~/libs/ui'
import { InputSkillSelector } from '~/libs/shared'

import { useFetchProjects } from '../../services/projects'
import { saveCopilotRequest, useFetchProjects } from '../../services/projects'
import { ProjectTypes } from '../../constants'
import { Project } from '../../models/Project'

Expand Down Expand Up @@ -47,6 +48,11 @@ const CopilotRequestForm: FC<{}> = () => {
...prevFormValues,
paymentType: t,
}))
setFormErrors((prevFormErrors: any) => {
const updatedErrors = { ...prevFormErrors }
delete updatedErrors.paymentType
return updatedErrors
})
setPaymentType(t)
}

Expand All @@ -72,6 +78,24 @@ const CopilotRequestForm: FC<{}> = () => {
...oldFormValues,
[key]: value,
})

// Clear specific field error
setFormErrors((prevFormErrors: any) => {
const updatedErrors = { ...prevFormErrors }
let errorKey: string
switch (key) {
case 'copilotUsername':
errorKey = 'existingCopilot'
break
default:
errorKey = key
break
}

// Remove the error from the updatedErrors object
delete updatedErrors[errorKey]
return updatedErrors
})
setIsFormChanged(true)
}

Expand All @@ -85,22 +109,44 @@ const CopilotRequestForm: FC<{}> = () => {
...prevFormValues,
skills: updatedSkills,
}))

setFormErrors((prevFormErrors: any) => {
const updatedErrors = { ...prevFormErrors }
delete updatedErrors.skills
return updatedErrors
})
setIsFormChanged(true)
}

function handleFormAction(): void {
const updatedFormErrors: { [key: string]: string } = {}

if (!formValues.projectId) {
updatedFormErrors.project = 'Project is required'
updatedFormErrors.projectId = 'Project is required'
}

if (!existingCopilot) {
updatedFormErrors.existingCopilot = 'Selection is required'
}

if (!formValues.complexity) {
updatedFormErrors.complexity = 'Selection is required'
}

if (!formValues.requiresCommunicatn) {
updatedFormErrors.requiresCommunicatn = 'Selection is required'
}

if (!formValues.paymentType) {
updatedFormErrors.paymentType = 'Selection is required'
}

if (!formValues.projectType) {
updatedFormErrors.projectType = 'Selecting project type is required'
}

if (!formValues.projectOverview) {
updatedFormErrors.projectOverview = 'Providing a project overview is required'
if (!formValues.overview) {
updatedFormErrors.overview = 'Providing a project overview is required'
}

if (!formValues.skills) {
Expand All @@ -124,7 +170,19 @@ const CopilotRequestForm: FC<{}> = () => {
}

if (isEmpty(updatedFormErrors)) {
// call the API to update the trait based on action type
saveCopilotRequest(formValues)
.then(() => {
toast.success('Subscription updated successfully')
// Reset form after successful submission
setFormValues({})
setIsFormChanged(false)
setFormErrors({})
setExistingCopilot('')
setPaymentType('')
})
.catch(() => {
toast.error('Error updating subscription')
})
}

setFormErrors(updatedFormErrors)
Expand Down Expand Up @@ -152,7 +210,7 @@ const CopilotRequestForm: FC<{}> = () => {
label='Project'
placeholder='Select the project you wish to request a copilot for'
dirty
error={formErrors.project}
error={formErrors.projectId}
/>
<p className={styles.formRow}>
Are you already working with a copilot that you&apos;d love to work with on this project
Expand Down Expand Up @@ -198,6 +256,12 @@ const CopilotRequestForm: FC<{}> = () => {
</div>
)
}
{formErrors.existingCopilot && (
<p className={styles.error}>
<IconSolid.ExclamationIcon />
{formErrors.existingCopilot}
</p>
)}

<p className={styles.formRow}>What type of project are you working on?</p>
<InputSelect
Expand Down Expand Up @@ -252,6 +316,12 @@ const CopilotRequestForm: FC<{}> = () => {
noCaps
leftAlignText
/>
{formErrors.complexity && (
<p className={styles.error}>
<IconSolid.ExclamationIcon />
{formErrors.complexity}
</p>
)}
</div>
<p className={styles.formRow}>
Please provide an overview of the project the copilot will undertake
Expand All @@ -263,11 +333,11 @@ const CopilotRequestForm: FC<{}> = () => {
type of work and project which is to be undertaken.'
value={formValues.overview}
onChange={bind(handleFormValueChange, this, 'overview')}
error={formErrors.projectOverview}
tabIndex={0}
error={formErrors.overview}
dirty
/>
<p className={styles.formRow}>Any specific skills or technology requirements that come to mind?</p>
<div className={styles.skillsWrapper}>
<div className={formErrors.skills ? styles.skillsError : styles.skillsWrapper}>
<InputSkillSelector
placeholder='Enter skills you are searching for...'
useWrapper={false}
Expand All @@ -276,6 +346,12 @@ const CopilotRequestForm: FC<{}> = () => {
onChange={handleSkillsChange}
/>
</div>
{formErrors.skills && (
<p className={styles.error}>
<IconSolid.ExclamationIcon />
{formErrors.skills}
</p>
)}
<p className={styles.formRow}>What&apos;s the planned start date for the copilot?</p>
<InputDatePicker
label='Copilot Start Date'
Expand Down Expand Up @@ -344,6 +420,12 @@ const CopilotRequestForm: FC<{}> = () => {
onChange={bind(handleFormValueChange, this, 'requiresCommunicatn')}
/>
</div>
{formErrors.requiresCommunicatn && (
<p className={styles.error}>
<IconSolid.ExclamationIcon />
{formErrors.requiresCommunicatn}
</p>
)}
<p className={styles.formRow}>Will this role be standard payments or something else?</p>
<div className={styles.formRadioBtn}>
<InputRadio
Expand Down Expand Up @@ -377,6 +459,12 @@ const CopilotRequestForm: FC<{}> = () => {
/>
)}
</div>
{formErrors.paymentType && (
<p className={styles.error}>
<IconSolid.ExclamationIcon />
{formErrors.paymentType}
</p>
)}
<Button
primary
size='lg'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@import '@libs/ui/styles/includes';

$gradient: linear-gradient(
to top right, // This approximates the diagonal direction
#086093, // Start color
Expand Down Expand Up @@ -50,4 +52,22 @@ $gradient: linear-gradient(
border: 1px solid #aaaaaa;
border-radius: 0.375rem;
}

.skillsError {
border: 2px solid $red-100;
border-radius: 0.375rem;
}

.error {
font-size: 11px;
display: flex;
align-items: center;
color: $red-100;

svg {
@include icon-md;
fill: $red-100;
margin-right: $sp-1;
}
}
}
13 changes: 12 additions & 1 deletion src/apps/copilots/src/services/projects.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import useSWR, { SWRResponse } from 'swr'

import { xhrGetAsync } from '~/libs/core'
import { xhrGetAsync, xhrPostAsync } from '~/libs/core'
import { EnvironmentConfig } from '~/config'

import { Project } from '../models/Project'
import { CopilotRequest } from '../models/CopilotRequest'

const baseUrl = `${EnvironmentConfig.API.V5}/projects`

Expand All @@ -14,3 +15,13 @@ export const useFetchProjects = (): SWRResponse<Project[]> => {
})
return response
}

export const saveCopilotRequest = (request: CopilotRequest)
: Promise<CopilotRequest> => {
const url = `${baseUrl}/${request.projectId}/copilots/request`
const requestData = {
data: request,
}

return xhrPostAsync(url, requestData)
}