diff --git a/graphql-schema-linter.config.js b/graphql-schema-linter.config.js index 398271dc35..1412998da7 100644 --- a/graphql-schema-linter.config.js +++ b/graphql-schema-linter.config.js @@ -52,7 +52,8 @@ module.exports = { 'MTOInfoTranslation', 'EnumTranslation', 'MTOCategoryTranslation', - 'MTOMilestoneTranslation' + 'MTOMilestoneTranslation', + 'MTOSolutionTranslation' ], 'enum-values-all-caps': ['TableName'] } diff --git a/pkg/graph/generated/generated.go b/pkg/graph/generated/generated.go index a80166ec03..24a2b7368d 100644 --- a/pkg/graph/generated/generated.go +++ b/pkg/graph/generated/generated.go @@ -462,6 +462,13 @@ type ComplexityRoot struct { Type func(childComplexity int) int } + MTOSolutionTranslation struct { + Name func(childComplexity int) int + PocEmail func(childComplexity int) int + PocName func(childComplexity int) int + SolutionType func(childComplexity int) int + } + MTOSubcategory struct { ID func(childComplexity int) int IsUncategorized func(childComplexity int) int @@ -4450,6 +4457,34 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.MTOSolution.Type(childComplexity), true + case "MTOSolutionTranslation.name": + if e.complexity.MTOSolutionTranslation.Name == nil { + break + } + + return e.complexity.MTOSolutionTranslation.Name(childComplexity), true + + case "MTOSolutionTranslation.pocEmail": + if e.complexity.MTOSolutionTranslation.PocEmail == nil { + break + } + + return e.complexity.MTOSolutionTranslation.PocEmail(childComplexity), true + + case "MTOSolutionTranslation.pocName": + if e.complexity.MTOSolutionTranslation.PocName == nil { + break + } + + return e.complexity.MTOSolutionTranslation.PocName(childComplexity), true + + case "MTOSolutionTranslation.solutionType": + if e.complexity.MTOSolutionTranslation.SolutionType == nil { + break + } + + return e.complexity.MTOSolutionTranslation.SolutionType(childComplexity), true + case "MTOSubcategory.id": if e.complexity.MTOSubcategory.ID == nil { break @@ -17192,6 +17227,18 @@ extend type Mutation { updateMTOSolution(id: UUID!, changes: MTOSolutionChanges!): MTOSolution! @hasRole(role: MINT_USER) } +`, BuiltIn: false}, + {Name: "../schema/types/mto_solution_translation.graphql", Input: ` +""" +Represents MTO Custom Solution translation data +""" + +type MTOSolutionTranslation { + name: TranslationField! @goTag(key: "db", value: "name") + pocName: TranslationField! @goTag(key: "db", value: "poc_name") + pocEmail: TranslationField! @goTag(key: "db", value: "poc_email") + solutionType: TranslationFieldWithOptions! @goTag(key: "db", value: "type") +} `, BuiltIn: false}, {Name: "../schema/types/nda_info.graphql", Input: `""" NDAInfo represents whether a user has agreed to an NDA or not. If agreed to previously, there will be a datestamp visible @@ -34859,6 +34906,330 @@ func (ec *executionContext) fieldContext_MTOSolution_commonSolution(ctx context. return fc, nil } +func (ec *executionContext) _MTOSolutionTranslation_name(ctx context.Context, field graphql.CollectedField, obj *model.MTOSolutionTranslation) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_MTOSolutionTranslation_name(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Name, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(models.TranslationField) + fc.Result = res + return ec.marshalNTranslationField2githubᚗcomᚋcmsᚑenterpriseᚋmintᚑappᚋpkgᚋmodelsᚐTranslationField(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_MTOSolutionTranslation_name(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "MTOSolutionTranslation", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "gqlField": + return ec.fieldContext_TranslationField_gqlField(ctx, field) + case "goField": + return ec.fieldContext_TranslationField_goField(ctx, field) + case "dbField": + return ec.fieldContext_TranslationField_dbField(ctx, field) + case "label": + return ec.fieldContext_TranslationField_label(ctx, field) + case "readonlyLabel": + return ec.fieldContext_TranslationField_readonlyLabel(ctx, field) + case "sublabel": + return ec.fieldContext_TranslationField_sublabel(ctx, field) + case "multiSelectLabel": + return ec.fieldContext_TranslationField_multiSelectLabel(ctx, field) + case "isArray": + return ec.fieldContext_TranslationField_isArray(ctx, field) + case "dataType": + return ec.fieldContext_TranslationField_dataType(ctx, field) + case "formType": + return ec.fieldContext_TranslationField_formType(ctx, field) + case "isNote": + return ec.fieldContext_TranslationField_isNote(ctx, field) + case "order": + return ec.fieldContext_TranslationField_order(ctx, field) + case "isOtherType": + return ec.fieldContext_TranslationField_isOtherType(ctx, field) + case "otherParentField": + return ec.fieldContext_TranslationField_otherParentField(ctx, field) + case "parentReferencesLabel": + return ec.fieldContext_TranslationField_parentReferencesLabel(ctx, field) + case "exportLabel": + return ec.fieldContext_TranslationField_exportLabel(ctx, field) + case "tableReference": + return ec.fieldContext_TranslationField_tableReference(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type TranslationField", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _MTOSolutionTranslation_pocName(ctx context.Context, field graphql.CollectedField, obj *model.MTOSolutionTranslation) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_MTOSolutionTranslation_pocName(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.PocName, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(models.TranslationField) + fc.Result = res + return ec.marshalNTranslationField2githubᚗcomᚋcmsᚑenterpriseᚋmintᚑappᚋpkgᚋmodelsᚐTranslationField(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_MTOSolutionTranslation_pocName(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "MTOSolutionTranslation", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "gqlField": + return ec.fieldContext_TranslationField_gqlField(ctx, field) + case "goField": + return ec.fieldContext_TranslationField_goField(ctx, field) + case "dbField": + return ec.fieldContext_TranslationField_dbField(ctx, field) + case "label": + return ec.fieldContext_TranslationField_label(ctx, field) + case "readonlyLabel": + return ec.fieldContext_TranslationField_readonlyLabel(ctx, field) + case "sublabel": + return ec.fieldContext_TranslationField_sublabel(ctx, field) + case "multiSelectLabel": + return ec.fieldContext_TranslationField_multiSelectLabel(ctx, field) + case "isArray": + return ec.fieldContext_TranslationField_isArray(ctx, field) + case "dataType": + return ec.fieldContext_TranslationField_dataType(ctx, field) + case "formType": + return ec.fieldContext_TranslationField_formType(ctx, field) + case "isNote": + return ec.fieldContext_TranslationField_isNote(ctx, field) + case "order": + return ec.fieldContext_TranslationField_order(ctx, field) + case "isOtherType": + return ec.fieldContext_TranslationField_isOtherType(ctx, field) + case "otherParentField": + return ec.fieldContext_TranslationField_otherParentField(ctx, field) + case "parentReferencesLabel": + return ec.fieldContext_TranslationField_parentReferencesLabel(ctx, field) + case "exportLabel": + return ec.fieldContext_TranslationField_exportLabel(ctx, field) + case "tableReference": + return ec.fieldContext_TranslationField_tableReference(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type TranslationField", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _MTOSolutionTranslation_pocEmail(ctx context.Context, field graphql.CollectedField, obj *model.MTOSolutionTranslation) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_MTOSolutionTranslation_pocEmail(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.PocEmail, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(models.TranslationField) + fc.Result = res + return ec.marshalNTranslationField2githubᚗcomᚋcmsᚑenterpriseᚋmintᚑappᚋpkgᚋmodelsᚐTranslationField(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_MTOSolutionTranslation_pocEmail(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "MTOSolutionTranslation", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "gqlField": + return ec.fieldContext_TranslationField_gqlField(ctx, field) + case "goField": + return ec.fieldContext_TranslationField_goField(ctx, field) + case "dbField": + return ec.fieldContext_TranslationField_dbField(ctx, field) + case "label": + return ec.fieldContext_TranslationField_label(ctx, field) + case "readonlyLabel": + return ec.fieldContext_TranslationField_readonlyLabel(ctx, field) + case "sublabel": + return ec.fieldContext_TranslationField_sublabel(ctx, field) + case "multiSelectLabel": + return ec.fieldContext_TranslationField_multiSelectLabel(ctx, field) + case "isArray": + return ec.fieldContext_TranslationField_isArray(ctx, field) + case "dataType": + return ec.fieldContext_TranslationField_dataType(ctx, field) + case "formType": + return ec.fieldContext_TranslationField_formType(ctx, field) + case "isNote": + return ec.fieldContext_TranslationField_isNote(ctx, field) + case "order": + return ec.fieldContext_TranslationField_order(ctx, field) + case "isOtherType": + return ec.fieldContext_TranslationField_isOtherType(ctx, field) + case "otherParentField": + return ec.fieldContext_TranslationField_otherParentField(ctx, field) + case "parentReferencesLabel": + return ec.fieldContext_TranslationField_parentReferencesLabel(ctx, field) + case "exportLabel": + return ec.fieldContext_TranslationField_exportLabel(ctx, field) + case "tableReference": + return ec.fieldContext_TranslationField_tableReference(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type TranslationField", field.Name) + }, + } + return fc, nil +} + +func (ec *executionContext) _MTOSolutionTranslation_solutionType(ctx context.Context, field graphql.CollectedField, obj *model.MTOSolutionTranslation) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_MTOSolutionTranslation_solutionType(ctx, field) + if err != nil { + return graphql.Null + } + ctx = graphql.WithFieldContext(ctx, fc) + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.SolutionType, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + if !graphql.HasFieldError(ctx, fc) { + ec.Errorf(ctx, "must not be null") + } + return graphql.Null + } + res := resTmp.(models.TranslationFieldWithOptions) + fc.Result = res + return ec.marshalNTranslationFieldWithOptions2githubᚗcomᚋcmsᚑenterpriseᚋmintᚑappᚋpkgᚋmodelsᚐTranslationFieldWithOptions(ctx, field.Selections, res) +} + +func (ec *executionContext) fieldContext_MTOSolutionTranslation_solutionType(ctx context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { + fc = &graphql.FieldContext{ + Object: "MTOSolutionTranslation", + Field: field, + IsMethod: false, + IsResolver: false, + Child: func(ctx context.Context, field graphql.CollectedField) (*graphql.FieldContext, error) { + switch field.Name { + case "gqlField": + return ec.fieldContext_TranslationFieldWithOptions_gqlField(ctx, field) + case "goField": + return ec.fieldContext_TranslationFieldWithOptions_goField(ctx, field) + case "dbField": + return ec.fieldContext_TranslationFieldWithOptions_dbField(ctx, field) + case "label": + return ec.fieldContext_TranslationFieldWithOptions_label(ctx, field) + case "readonlyLabel": + return ec.fieldContext_TranslationFieldWithOptions_readonlyLabel(ctx, field) + case "sublabel": + return ec.fieldContext_TranslationFieldWithOptions_sublabel(ctx, field) + case "multiSelectLabel": + return ec.fieldContext_TranslationFieldWithOptions_multiSelectLabel(ctx, field) + case "isArray": + return ec.fieldContext_TranslationFieldWithOptions_isArray(ctx, field) + case "dataType": + return ec.fieldContext_TranslationFieldWithOptions_dataType(ctx, field) + case "formType": + return ec.fieldContext_TranslationFieldWithOptions_formType(ctx, field) + case "isNote": + return ec.fieldContext_TranslationFieldWithOptions_isNote(ctx, field) + case "order": + return ec.fieldContext_TranslationFieldWithOptions_order(ctx, field) + case "isOtherType": + return ec.fieldContext_TranslationFieldWithOptions_isOtherType(ctx, field) + case "otherParentField": + return ec.fieldContext_TranslationFieldWithOptions_otherParentField(ctx, field) + case "parentReferencesLabel": + return ec.fieldContext_TranslationFieldWithOptions_parentReferencesLabel(ctx, field) + case "exportLabel": + return ec.fieldContext_TranslationFieldWithOptions_exportLabel(ctx, field) + case "tableReference": + return ec.fieldContext_TranslationFieldWithOptions_tableReference(ctx, field) + case "options": + return ec.fieldContext_TranslationFieldWithOptions_options(ctx, field) + case "exportOptions": + return ec.fieldContext_TranslationFieldWithOptions_exportOptions(ctx, field) + } + return nil, fmt.Errorf("no field named %q was found under type TranslationFieldWithOptions", field.Name) + }, + } + return fc, nil +} + func (ec *executionContext) _MTOSubcategory_id(ctx context.Context, field graphql.CollectedField, obj *models.MTOSubcategory) (ret graphql.Marshaler) { fc, err := ec.fieldContext_MTOSubcategory_id(ctx, field) if err != nil { @@ -136980,6 +137351,60 @@ func (ec *executionContext) _MTOSolution(ctx context.Context, sel ast.SelectionS return out } +var mTOSolutionTranslationImplementors = []string{"MTOSolutionTranslation"} + +func (ec *executionContext) _MTOSolutionTranslation(ctx context.Context, sel ast.SelectionSet, obj *model.MTOSolutionTranslation) graphql.Marshaler { + fields := graphql.CollectFields(ec.OperationContext, sel, mTOSolutionTranslationImplementors) + + out := graphql.NewFieldSet(fields) + deferred := make(map[string]*graphql.FieldSet) + for i, field := range fields { + switch field.Name { + case "__typename": + out.Values[i] = graphql.MarshalString("MTOSolutionTranslation") + case "name": + out.Values[i] = ec._MTOSolutionTranslation_name(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "pocName": + out.Values[i] = ec._MTOSolutionTranslation_pocName(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "pocEmail": + out.Values[i] = ec._MTOSolutionTranslation_pocEmail(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + case "solutionType": + out.Values[i] = ec._MTOSolutionTranslation_solutionType(ctx, field, obj) + if out.Values[i] == graphql.Null { + out.Invalids++ + } + default: + panic("unknown field " + strconv.Quote(field.Name)) + } + } + out.Dispatch(ctx) + if out.Invalids > 0 { + return graphql.Null + } + + atomic.AddInt32(&ec.deferred, int32(len(deferred))) + + for label, dfs := range deferred { + ec.processDeferredGroup(graphql.DeferredGroup{ + Label: label, + Path: graphql.GetPath(ctx), + FieldSet: dfs, + Context: ctx, + }) + } + + return out +} + var mTOSubcategoryImplementors = []string{"MTOSubcategory"} func (ec *executionContext) _MTOSubcategory(ctx context.Context, sel ast.SelectionSet, obj *models.MTOSubcategory) graphql.Marshaler { diff --git a/pkg/graph/model/models_gen.go b/pkg/graph/model/models_gen.go index fbf73a19af..2ba8e988b5 100644 --- a/pkg/graph/model/models_gen.go +++ b/pkg/graph/model/models_gen.go @@ -77,6 +77,14 @@ type MTOMilestoneTranslation struct { Name models.TranslationField `json:"name" db:"name"` } +// Represents MTO Custom Solution translation data +type MTOSolutionTranslation struct { + Name models.TranslationField `json:"name" db:"name"` + PocName models.TranslationField `json:"pocName" db:"poc_name"` + PocEmail models.TranslationField `json:"pocEmail" db:"poc_email"` + SolutionType models.TranslationFieldWithOptions `json:"solutionType" db:"type"` +} + // Represents model plan base translation data type ModelPlanTranslation struct { ModelName models.TranslationField `json:"modelName" db:"model_name"` diff --git a/pkg/graph/schema/types/mto_solution_translation.graphql b/pkg/graph/schema/types/mto_solution_translation.graphql new file mode 100644 index 0000000000..87ea905547 --- /dev/null +++ b/pkg/graph/schema/types/mto_solution_translation.graphql @@ -0,0 +1,11 @@ + +""" +Represents MTO Custom Solution translation data +""" + +type MTOSolutionTranslation { + name: TranslationField! @goTag(key: "db", value: "name") + pocName: TranslationField! @goTag(key: "db", value: "poc_name") + pocEmail: TranslationField! @goTag(key: "db", value: "poc_email") + solutionType: TranslationFieldWithOptions! @goTag(key: "db", value: "type") +} diff --git a/src/features/ModelPlan/ModelToOperations/Home/index.tsx b/src/features/ModelPlan/ModelToOperations/Home/index.tsx index 0ff6e0ff5e..eb26b12765 100644 --- a/src/features/ModelPlan/ModelToOperations/Home/index.tsx +++ b/src/features/ModelPlan/ModelToOperations/Home/index.tsx @@ -200,6 +200,17 @@ const MTOHome = () => { > Add custom milestone + { + clearMessage(); + setModalType('solution'); + setIsModalOpen(true); + }} + className="margin-bottom-4" + > + Add custom solution + diff --git a/src/features/ModelPlan/ModelToOperations/_components/Modal/MilestoneForm/index.test.tsx b/src/features/ModelPlan/ModelToOperations/_components/Modal/MilestoneForm/index.test.tsx index 66fb7374a8..f4c7b583a6 100644 --- a/src/features/ModelPlan/ModelToOperations/_components/Modal/MilestoneForm/index.test.tsx +++ b/src/features/ModelPlan/ModelToOperations/_components/Modal/MilestoneForm/index.test.tsx @@ -7,7 +7,7 @@ import VerboseMockedProvider from 'tests/MockedProvider'; import MessageProvider from 'contexts/MessageContext'; -import ModelMilestoneForm from './index'; +import MilestoneForm from './index'; const mocks = [ { @@ -60,7 +60,7 @@ describe('Custom Milestone form', () => { - {}} /> + {}} /> diff --git a/src/features/ModelPlan/ModelToOperations/_components/Modal/SolutionForm/__snapshots__/index.test.tsx.snap b/src/features/ModelPlan/ModelToOperations/_components/Modal/SolutionForm/__snapshots__/index.test.tsx.snap new file mode 100644 index 0000000000..921931374c --- /dev/null +++ b/src/features/ModelPlan/ModelToOperations/_components/Modal/SolutionForm/__snapshots__/index.test.tsx.snap @@ -0,0 +1,192 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Custom Solution form > matches snapshot 1`] = ` + + + + + + What type of solution is this? + + * + + + + + - Select - + + + IT System + + + Contract vehicle, contractor, or other contract + + + Cross-cutting group + + + Other + + + + + + Please add a title for your solution + + * + + + + + + + Solution point of contact information + + + Add the name and contact information for the person or team who is the primary point of contact for this solution. + + + + + Point of contact name + + * + + + + + + + Point of contact email address + + * + + + + + + + + + + Please double-check that you aren't creating an operational solution or IT system that already exists in the + + solution library + + . + + + + + + Add solution + + + Cancel + + + +`; diff --git a/src/features/ModelPlan/ModelToOperations/_components/Modal/SolutionForm/index.test.tsx b/src/features/ModelPlan/ModelToOperations/_components/Modal/SolutionForm/index.test.tsx new file mode 100644 index 0000000000..52bf7aa185 --- /dev/null +++ b/src/features/ModelPlan/ModelToOperations/_components/Modal/SolutionForm/index.test.tsx @@ -0,0 +1,87 @@ +import React from 'react'; +import { MemoryRouter, Route } from 'react-router-dom'; +import { render, waitFor } from '@testing-library/react'; +import { GetMtoCategoriesDocument } from 'gql/generated/graphql'; +import { modelID } from 'tests/mock/readonly'; +import VerboseMockedProvider from 'tests/MockedProvider'; + +import MessageProvider from 'contexts/MessageContext'; + +import SolutionForm from './index'; + +const mocks = [ + { + request: { + query: GetMtoCategoriesDocument, + variables: { + id: modelID + } + }, + result: { + data: { + modelPlan: { + __typename: 'ModelPlan', + id: modelID, + mtoMatrix: { + __typename: 'MtoMatrix', + categories: [ + { + __typename: 'MtoCategory', + id: '123', + name: 'Category 1', + subCategories: { + __typename: 'MtoSubCategory', + id: '123', + name: 'SubCategory 1' + } + }, + { + __typename: 'MtoCategory', + id: '456', + name: 'Category 2', + subCategories: { + __typename: 'MtoSubCategory', + id: '123', + name: 'SubCategory 2' + } + } + ] + } + } + } + } + } +]; + +describe('Custom Solution form', () => { + it('matches snapshot', async () => { + const { getAllByTestId, getByTestId, asFragment } = render( + + + + + {}} /> + + + + + ); + + await waitFor(() => { + expect(getByTestId('alert')).toBeInTheDocument(); + const selectSolutionType = getAllByTestId('Select')[0]; + const primaryCategoryOptions = + selectSolutionType.querySelectorAll('option'); + + expect(primaryCategoryOptions).toHaveLength(5); + + expect(primaryCategoryOptions[0].value).toBe('default'); + expect(primaryCategoryOptions[1].value).toBe('IT_SYSTEM'); + expect(primaryCategoryOptions[2].value).toBe('CONTRACTOR'); + expect(primaryCategoryOptions[3].value).toBe('CROSS_CUTTING_GROUP'); + expect(primaryCategoryOptions[4].value).toBe('OTHER'); + }); + + expect(asFragment()).toMatchSnapshot(); + }); +}); diff --git a/src/features/ModelPlan/ModelToOperations/_components/Modal/SolutionForm/index.tsx b/src/features/ModelPlan/ModelToOperations/_components/Modal/SolutionForm/index.tsx new file mode 100644 index 0000000000..a221db1bd1 --- /dev/null +++ b/src/features/ModelPlan/ModelToOperations/_components/Modal/SolutionForm/index.tsx @@ -0,0 +1,299 @@ +import React from 'react'; +import { + Controller, + FormProvider, + SubmitHandler, + useForm +} from 'react-hook-form'; +import { Trans, useTranslation } from 'react-i18next'; +import { useParams } from 'react-router-dom'; +import { + Button, + Fieldset, + Form, + FormGroup, + Label, + Select, + TextInput +} from '@trussworks/react-uswds'; +import { + MtoSolutionType, + useCreateMtoSolutionCustomMutation +} from 'gql/generated/graphql'; + +import Alert from 'components/Alert'; +import useMessage from 'hooks/useMessage'; +import usePlanTranslation from 'hooks/usePlanTranslation'; +import { getKeys } from 'types/translation'; +import { convertCamelCaseToKebabCase } from 'utils/modelPlan'; + +type FormValues = { + solutionType: MtoSolutionType | 'default'; + solutionTitle: string; + pocName: string; + pocEmail: string; +}; + +const SolutionForm = ({ closeModal }: { closeModal: () => void }) => { + const { t } = useTranslation('modelToOperationsMisc'); + const { solutionType: solutionTypeConfig } = + usePlanTranslation('mtoSolution'); + + const { modelID } = useParams<{ modelID: string }>(); + const { message, showMessage, clearMessage } = useMessage(); + + // Variables for the form + const methods = useForm({ + defaultValues: { + solutionType: 'default', + solutionTitle: '', + pocName: '', + pocEmail: '' + } + }); + + const { + control, + handleSubmit, + reset, + formState: { isValid } + } = methods; + + const [create] = useCreateMtoSolutionCustomMutation(); + + const onSubmit: SubmitHandler = formData => { + if (formData.solutionType === 'default') return; + + create({ + variables: { + modelPlanID: modelID, + solutionType: formData.solutionType, + name: formData.solutionTitle, + pocName: formData.pocName, + pocEmail: formData.pocEmail + } + }) + .then(response => { + if (!response?.errors) { + showMessage( + <> + + + + }} + values={{ solution: formData.solutionTitle }} + /> + + + > + ); + closeModal(); + } + }) + .catch(() => { + showMessage( + + {t('modal.solution.alert.error')} + + ); + }); + }; + + return ( + + {message} + + + value !== 'default' + }} + render={({ field: { ref, ...field } }) => ( + + + + }} + /> + + + + - Select - + {getKeys(solutionTypeConfig.options).map(option => { + return ( + + {solutionTypeConfig.options[option]} + + ); + })} + + + )} + /> + + ( + + + + }} + /> + + + + + )} + /> + + + + {t('modal.solution.pocHeading')} + + + {t('modal.solution.pocSubheading')} + + + + ( + + + + }} + /> + + + + + )} + /> + + ( + + + + }} + /> + + + + + )} + /> + + + + // TODO: Add a link to the documentation + }} + /> + + + {t('modal.addButton', { type: 'solution' })} + + { + reset(); + clearMessage(); + closeModal(); + }} + > + {t('modal.cancel')} + + + + ); +}; + +export default SolutionForm; diff --git a/src/features/ModelPlan/ModelToOperations/_components/Modal/index.tsx b/src/features/ModelPlan/ModelToOperations/_components/Modal/index.tsx index 757072f2f9..1e18ffee36 100644 --- a/src/features/ModelPlan/ModelToOperations/_components/Modal/index.tsx +++ b/src/features/ModelPlan/ModelToOperations/_components/Modal/index.tsx @@ -7,6 +7,7 @@ import useMessage from 'hooks/useMessage'; import CategoryForm from './CategoryForm'; import MilestoneForm from './MilestoneForm'; +import SolutionForm from './SolutionForm'; type MTOModalProps = { isOpen: boolean; @@ -59,6 +60,7 @@ const MTOModal = ({ isOpen, closeModal, modalType }: MTOModalProps) => { {/* if type is category, then render CategoryForm */} {modalType === 'category' && } {modalType === 'milestone' && } + {modalType === 'solution' && } ); }; diff --git a/src/gql/generated/graphql.ts b/src/gql/generated/graphql.ts index 5ac7ea964c..37fdc15d41 100644 --- a/src/gql/generated/graphql.ts +++ b/src/gql/generated/graphql.ts @@ -984,6 +984,15 @@ export enum MtoSolutionStatus { ONBOARDING = 'ONBOARDING' } +/** Represents MTO Custom Solution translation data */ +export type MtoSolutionTranslation = { + __typename: 'MTOSolutionTranslation'; + name: TranslationField; + pocEmail: TranslationField; + pocName: TranslationField; + solutionType: TranslationFieldWithOptions; +}; + export enum MtoSolutionType { CONTRACTOR = 'CONTRACTOR', CROSS_CUTTING_GROUP = 'CROSS_CUTTING_GROUP', @@ -5386,6 +5395,17 @@ export type CreateMtoMilestoneCustomMutationVariables = Exact<{ export type CreateMtoMilestoneCustomMutation = { __typename: 'Mutation', createMTOMilestoneCustom: { __typename: 'MTOMilestone', id: UUID, name: string } }; +export type CreateMtoSolutionCustomMutationVariables = Exact<{ + modelPlanID: Scalars['UUID']['input']; + solutionType: MtoSolutionType; + name: Scalars['String']['input']; + pocName: Scalars['String']['input']; + pocEmail: Scalars['String']['input']; +}>; + + +export type CreateMtoSolutionCustomMutation = { __typename: 'Mutation', createMTOSolutionCustom: { __typename: 'MTOSolution', id: UUID, name?: string | null, status: MtoSolutionStatus, pocName?: string | null, pocEmail?: string | null, type?: MtoSolutionType | null } }; + export type GetMtoCategoriesQueryVariables = Exact<{ id: Scalars['UUID']['input']; }>; @@ -11064,6 +11084,54 @@ export function useCreateMtoMilestoneCustomMutation(baseOptions?: Apollo.Mutatio export type CreateMtoMilestoneCustomMutationHookResult = ReturnType; export type CreateMtoMilestoneCustomMutationResult = Apollo.MutationResult; export type CreateMtoMilestoneCustomMutationOptions = Apollo.BaseMutationOptions; +export const CreateMtoSolutionCustomDocument = gql` + mutation CreateMTOSolutionCustom($modelPlanID: UUID!, $solutionType: MTOSolutionType!, $name: String!, $pocName: String!, $pocEmail: String!) { + createMTOSolutionCustom( + modelPlanID: $modelPlanID + solutionType: $solutionType + name: $name + pocName: $pocName + pocEmail: $pocEmail + ) { + id + name + status + pocName + pocEmail + type + } +} + `; +export type CreateMtoSolutionCustomMutationFn = Apollo.MutationFunction; + +/** + * __useCreateMtoSolutionCustomMutation__ + * + * To run a mutation, you first call `useCreateMtoSolutionCustomMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useCreateMtoSolutionCustomMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [createMtoSolutionCustomMutation, { data, loading, error }] = useCreateMtoSolutionCustomMutation({ + * variables: { + * modelPlanID: // value for 'modelPlanID' + * solutionType: // value for 'solutionType' + * name: // value for 'name' + * pocName: // value for 'pocName' + * pocEmail: // value for 'pocEmail' + * }, + * }); + */ +export function useCreateMtoSolutionCustomMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(CreateMtoSolutionCustomDocument, options); + } +export type CreateMtoSolutionCustomMutationHookResult = ReturnType; +export type CreateMtoSolutionCustomMutationResult = Apollo.MutationResult; +export type CreateMtoSolutionCustomMutationOptions = Apollo.BaseMutationOptions; export const GetMtoCategoriesDocument = gql` query GetMTOCategories($id: UUID!) { modelPlan(id: $id) { @@ -14253,6 +14321,7 @@ export const TypedModelPlanSubscriptionDocument = {"kind":"Document","definition export const TypedUnlockModelPlanSectionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UnlockModelPlanSection"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"modelPlanID"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"section"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"LockableSection"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"unlockLockableSection"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"modelPlanID"},"value":{"kind":"Variable","name":{"kind":"Name","value":"modelPlanID"}}},{"kind":"Argument","name":{"kind":"Name","value":"section"},"value":{"kind":"Variable","name":{"kind":"Name","value":"section"}}}]}]}}]} as unknown as DocumentNode; export const TypedCreateMtoCategoryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateMTOCategory"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"parentID"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createMTOCategory"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"modelPlanID"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"parentID"},"value":{"kind":"Variable","name":{"kind":"Name","value":"parentID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"isUncategorized"}}]}}]}}]} as unknown as DocumentNode; export const TypedCreateMtoMilestoneCustomDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateMTOMilestoneCustom"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"mtoCategoryID"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createMTOMilestoneCustom"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"modelPlanID"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"mtoCategoryID"},"value":{"kind":"Variable","name":{"kind":"Name","value":"mtoCategoryID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]} as unknown as DocumentNode; +export const TypedCreateMtoSolutionCustomDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateMTOSolutionCustom"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"modelPlanID"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"solutionType"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"MTOSolutionType"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"pocName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"pocEmail"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createMTOSolutionCustom"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"modelPlanID"},"value":{"kind":"Variable","name":{"kind":"Name","value":"modelPlanID"}}},{"kind":"Argument","name":{"kind":"Name","value":"solutionType"},"value":{"kind":"Variable","name":{"kind":"Name","value":"solutionType"}}},{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"Argument","name":{"kind":"Name","value":"pocName"},"value":{"kind":"Variable","name":{"kind":"Name","value":"pocName"}}},{"kind":"Argument","name":{"kind":"Name","value":"pocEmail"},"value":{"kind":"Variable","name":{"kind":"Name","value":"pocEmail"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"pocName"}},{"kind":"Field","name":{"kind":"Name","value":"pocEmail"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}}]}}]} as unknown as DocumentNode; export const TypedGetMtoCategoriesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetMTOCategories"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"modelPlan"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"mtoMatrix"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"categories"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"subCategories"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const TypedGetMtoMilestonesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetMTOMilestones"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"modelPlan"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"mtoMatrix"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"commonMilestones"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"isAdded"}},{"kind":"Field","name":{"kind":"Name","value":"isSuggested"}},{"kind":"Field","name":{"kind":"Name","value":"categoryName"}},{"kind":"Field","name":{"kind":"Name","value":"subCategoryName"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const TypedGetModelToOperationsMatrixDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetModelToOperationsMatrix"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UUID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"modelPlan"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"mtoMatrix"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"categories"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"isUncategorized"}},{"kind":"Field","name":{"kind":"Name","value":"subCategories"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"isUncategorized"}},{"kind":"Field","name":{"kind":"Name","value":"milestones"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"facilitatedBy"}},{"kind":"Field","name":{"kind":"Name","value":"needBy"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"riskIndicator"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"milestones"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"recentEdit"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","alias":{"kind":"Name","value":"id"},"name":{"kind":"Name","value":"modifiedBy"}},{"kind":"Field","name":{"kind":"Name","value":"modifiedByUserAccount"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"commonName"}}]}},{"kind":"Field","name":{"kind":"Name","value":"modifiedDts"}}]}}]}}]}}]}}]} as unknown as DocumentNode; diff --git a/src/gql/operations/ModelToOperations/CreateMTOSolutionCustom.ts b/src/gql/operations/ModelToOperations/CreateMTOSolutionCustom.ts new file mode 100644 index 0000000000..046167d5a6 --- /dev/null +++ b/src/gql/operations/ModelToOperations/CreateMTOSolutionCustom.ts @@ -0,0 +1,26 @@ +import { gql } from '@apollo/client'; + +export default gql(/* GraphQL */ ` + mutation CreateMTOSolutionCustom( + $modelPlanID: UUID! + $solutionType: MTOSolutionType! + $name: String! + $pocName: String! + $pocEmail: String! + ) { + createMTOSolutionCustom( + modelPlanID: $modelPlanID + solutionType: $solutionType + name: $name + pocName: $pocName + pocEmail: $pocEmail + ) { + id + name + status + pocName + pocEmail + type + } + } +`); diff --git a/src/i18n/en-US/index.ts b/src/i18n/en-US/index.ts index 8c84c5c0df..8fb2901d21 100644 --- a/src/i18n/en-US/index.ts +++ b/src/i18n/en-US/index.ts @@ -35,6 +35,7 @@ import modelToOperations, { } from './modelPlan/modelToOperations'; import mtoCategory from './modelPlan/mtoCategory'; import mtoMilestone from './modelPlan/mtoMilestone'; +import mtoSolution from './modelPlan/mtoSolution'; import operationalNeeds from './modelPlan/operationalNeeds'; import operationalSolutionSubtasks from './modelPlan/operationalSolutionSubtasks'; import { @@ -114,6 +115,7 @@ const enUS = { modelToOperationsMisc, mtoCategory, mtoMilestone, + mtoSolution, operationalNeeds, solutions, operationalSolutionSubtasks, diff --git a/src/i18n/en-US/modelPlan/modelToOperations.ts b/src/i18n/en-US/modelPlan/modelToOperations.ts index 87aa84cb57..b5bbb8d349 100644 --- a/src/i18n/en-US/modelPlan/modelToOperations.ts +++ b/src/i18n/en-US/modelPlan/modelToOperations.ts @@ -291,6 +291,23 @@ export const modelToOperationsMisc: Record = { 'There was an error adding your milestone. Please try again. If the error persists, please try again another time.' } }, + solution: { + label: { + solutionType: 'What type of solution is this? *', + solutionTitle: 'Please add a title for your solution *', + pocName: 'Point of contact name *', + pocEmail: 'Point of contact email address *' + }, + pocHeading: 'Solution point of contact information', + pocSubheading: + 'Add the name and contact information for the person or team who is the primary point of contact for this solution.', + alert: { + info: "Please double-check that you aren't creating an operational solution or IT system that already exists in the solution library.", + success: 'Your solution ({{solution}}) has been added.', + error: + 'There was an error adding your solution. Please try again. If the error persists, please try again another time.' + } + }, addButton: 'Add {{type}}', cancel: 'Cancel' }, diff --git a/src/i18n/en-US/modelPlan/mtoSolution.ts b/src/i18n/en-US/modelPlan/mtoSolution.ts new file mode 100644 index 0000000000..4547853dd8 --- /dev/null +++ b/src/i18n/en-US/modelPlan/mtoSolution.ts @@ -0,0 +1,53 @@ +import { TranslationMTOSolutionCustom } from 'types/translation'; + +import { + TranslationDataType, + TranslationFormType +} from '../../../gql/generated/graphql'; + +export const mtoSolution: TranslationMTOSolutionCustom = { + solutionType: { + gqlField: 'solutionType', + goField: 'SolutionType', + dbField: 'type', + label: 'Milestone title', + dataType: TranslationDataType.ENUM, + formType: TranslationFormType.SELECT, + options: { + IT_SYSTEM: 'IT System', + CONTRACTOR: 'Contract vehicle, contractor, or other contract', + CROSS_CUTTING_GROUP: 'Cross-cutting group', + OTHER: 'Other' + }, + order: 1.0 + }, + name: { + gqlField: 'name', + goField: 'Name', + dbField: 'name', + label: 'Milestone title', + dataType: TranslationDataType.STRING, + formType: TranslationFormType.TEXT, + order: 1.1 + }, + pocName: { + gqlField: 'pocName', + goField: 'PocName', + dbField: 'poc_name', + label: 'Milestone title', + dataType: TranslationDataType.STRING, + formType: TranslationFormType.TEXT, + order: 1.2 + }, + pocEmail: { + gqlField: 'pocEmail', + goField: 'PocEmail', + dbField: 'poc_email', + label: 'Milestone title', + dataType: TranslationDataType.STRING, + formType: TranslationFormType.TEXT, + order: 1.3 + } +}; + +export default mtoSolution; diff --git a/src/types/translation.ts b/src/types/translation.ts index beafce6ccc..d46ee19d1f 100644 --- a/src/types/translation.ts +++ b/src/types/translation.ts @@ -47,6 +47,8 @@ import { MtoCategoryTranslation, MtoInfoTranslation, MtoMilestoneTranslation, + MtoSolutionTranslation, + MtoSolutionType, MultiSourceDataToCollect, NonClaimsBasedPayType, OperationalNeedKey, @@ -1277,6 +1279,27 @@ export type TranslationMTOMilestoneCustom = { [K in keyof TranslationMTOMilestoneCustomGQL]: TranslationMTOMilestoneCustomForm[K]; // FE form type }; +// MTO Solution - Change History purposes only +export type TranslationMTOSolutionCustomForm = { + name: TranslationFieldProperties; + pocName: TranslationFieldProperties; + pocEmail: TranslationFieldProperties; + solutionType: TranslationFieldPropertiesWithOptions; +}; + +type TranslationMTOSolutionCustomGQL = Omit< + MtoSolutionTranslation, // graphql gen type + '__typename' +>; + +/* + Merged keys from graphql gen with FE form types + Create a tighter connection between BE/FE translation types +*/ +export type TranslationMTOSolutionCustom = { + [K in keyof TranslationMTOSolutionCustomGQL]: TranslationMTOSolutionCustomForm[K]; // FE form type +}; + export type TranslationPlan = { modelPlan: TranslationModelPlan; basics: TranslationBasics; @@ -1297,6 +1320,7 @@ export type TranslationPlan = { modelToOperations: TranslationMTOInfo; mtoCategory: TranslationMTOCategory; mtoMilestone: TranslationMTOMilestoneCustom; + mtoSolution: TranslationMTOSolutionCustom; }; export type TranslationPlanSection = @@ -1328,5 +1352,6 @@ export enum PlanSection { DATA_EXCHANGE_APPROACH = 'dataExchangeApproach', MTO_INFO = 'modelToOperations', MTO_CATEGORY = 'mtoCategory', - MTO_MILESTONE = 'mtoMilestone' + MTO_MILESTONE = 'mtoMilestone', + MTO_SOLUTION = 'mtoSolution' }
+ Solution point of contact information +
+ Add the name and contact information for the person or team who is the primary point of contact for this solution. +
+ + Please double-check that you aren't creating an operational solution or IT system that already exists in the + + solution library + + . + +
+ {t('modal.solution.pocHeading')} +
+ {t('modal.solution.pocSubheading')} +