diff --git a/cmd/server/pactasrv/conv/oapi_to_pacta.go b/cmd/server/pactasrv/conv/oapi_to_pacta.go index ae6b1b5..193be02 100644 --- a/cmd/server/pactasrv/conv/oapi_to_pacta.go +++ b/cmd/server/pactasrv/conv/oapi_to_pacta.go @@ -103,12 +103,12 @@ func AnalysisTypeFromOAPI(at api.AnalysisType) (pacta.AnalysisType, error) { return "", oapierr.BadRequest("analysisTypeFromOAPI: unknown analysis type", zap.String("analysis_type", string(at))) } -func HoldingsDateFromOAPI(hd *api.HoldingsDate) (*pacta.HoldingsDate, error) { - if hd == nil { +func HoldingsDateFromOAPI(hd api.HoldingsDate) (*pacta.HoldingsDate, error) { + if hd.Time == nil { return nil, nil } return &pacta.HoldingsDate{ - Time: hd.Time, + Time: *hd.Time, }, nil } diff --git a/cmd/server/pactasrv/conv/pacta_to_oapi.go b/cmd/server/pactasrv/conv/pacta_to_oapi.go index 5c1f32a..7a93c88 100644 --- a/cmd/server/pactasrv/conv/pacta_to_oapi.go +++ b/cmd/server/pactasrv/conv/pacta_to_oapi.go @@ -177,12 +177,12 @@ func InitiativeUserRelationshipToOAPI(i *pacta.InitiativeUserRelationship) (*api }, nil } -func HoldingsDateToOAPI(hd *pacta.HoldingsDate) (*api.HoldingsDate, error) { +func HoldingsDateToOAPI(hd *pacta.HoldingsDate) (api.HoldingsDate, error) { if hd == nil { - return nil, nil + return api.HoldingsDate{}, nil } - return &api.HoldingsDate{ - Time: hd.Time, + return api.HoldingsDate{ + Time: &hd.Time, }, nil } @@ -208,12 +208,9 @@ func IncompleteUploadToOAPI(iu *pacta.IncompleteUpload) (*api.IncompleteUpload, if err != nil { return nil, oapierr.Internal("incompleteUploadToOAPI: failureCodeToOAPI failed", zap.Error(err)) } - var hd *api.HoldingsDate - if iu.Properties.HoldingsDate != nil { - hd, err = HoldingsDateToOAPI(iu.Properties.HoldingsDate) - if err != nil { - return nil, oapierr.Internal("incompleteUploadToOAPI: holdingsDateToOAPI failed", zap.Error(err)) - } + hd, err := HoldingsDateToOAPI(iu.Properties.HoldingsDate) + if err != nil { + return nil, oapierr.Internal("incompleteUploadToOAPI: holdingsDateToOAPI failed", zap.Error(err)) } return &api.IncompleteUpload{ Id: string(iu.ID), @@ -255,12 +252,9 @@ func PortfolioToOAPI(p *pacta.Portfolio) (*api.Portfolio, error) { if err != nil { return nil, oapierr.Internal("initiativeToOAPI: portfolioInitiativeMembershipToOAPIInitiative failed", zap.Error(err)) } - var hd *api.HoldingsDate - if p.Properties.HoldingsDate != nil { - hd, err = HoldingsDateToOAPI(p.Properties.HoldingsDate) - if err != nil { - return nil, oapierr.Internal("portfolioToOAPI: holdingsDateToOAPI failed", zap.Error(err)) - } + hd, err := HoldingsDateToOAPI(p.Properties.HoldingsDate) + if err != nil { + return nil, oapierr.Internal("portfolioToOAPI: holdingsDateToOAPI failed", zap.Error(err)) } return &api.Portfolio{ Id: string(p.ID), diff --git a/cmd/server/pactasrv/portfolio.go b/cmd/server/pactasrv/portfolio.go index 41adde6..594acbd 100644 --- a/cmd/server/pactasrv/portfolio.go +++ b/cmd/server/pactasrv/portfolio.go @@ -91,7 +91,7 @@ func (s *Server) UpdatePortfolio(ctx context.Context, request api.UpdatePortfoli mutations = append(mutations, db.SetPortfolioDescription(*request.Body.Description)) } if request.Body.PropertyHoldingsDate != nil { - hd, err := conv.HoldingsDateFromOAPI(request.Body.PropertyHoldingsDate) + hd, err := conv.HoldingsDateFromOAPI(*request.Body.PropertyHoldingsDate) if err != nil { return nil, err } diff --git a/cmd/server/pactasrv/upload.go b/cmd/server/pactasrv/upload.go index 2f4a59a..08ee393 100644 --- a/cmd/server/pactasrv/upload.go +++ b/cmd/server/pactasrv/upload.go @@ -26,11 +26,9 @@ func (s *Server) StartPortfolioUpload(ctx context.Context, request api.StartPort } owner := &pacta.Owner{ID: actorInfo.OwnerID} properties := pacta.PortfolioProperties{} - if request.Body.PropertyHoldingsDate != nil { - properties.HoldingsDate, err = conv.HoldingsDateFromOAPI(request.Body.PropertyHoldingsDate) - if err != nil { - return nil, err - } + properties.HoldingsDate, err = conv.HoldingsDateFromOAPI(request.Body.PropertyHoldingsDate) + if err != nil { + return nil, err } properties.ESG = conv.OptionalBoolFromOAPI(request.Body.PropertyESG) properties.External = conv.OptionalBoolFromOAPI(request.Body.PropertyExternal) diff --git a/frontend/components/analysis/ContextualListView.vue b/frontend/components/analysis/ContextualListView.vue index 87b75bf..f7830ab 100644 --- a/frontend/components/analysis/ContextualListView.vue +++ b/frontend/components/analysis/ContextualListView.vue @@ -127,6 +127,7 @@ const reportButtonClasses = computed(() => { :portfolio-id="props.portfolioId" :portfolio-group-id="props.portfolioGroupId" :initiative-id="props.initiativeId" + :warn-for-duplicate="hasAnyAudits" class="p-button-sm" @started="() => emit('refresh')" /> @@ -137,6 +138,7 @@ const reportButtonClasses = computed(() => { :portfolio-id="props.portfolioId" :portfolio-group-id="props.portfolioGroupId" :initiative-id="props.initiativeId" + :warn-for-duplicate="hasAnyReports" class="p-button-sm" @started="() => emit('refresh')" /> diff --git a/frontend/components/analysis/RunButton.vue b/frontend/components/analysis/RunButton.vue index c51e8f6..76d8c5f 100644 --- a/frontend/components/analysis/RunButton.vue +++ b/frontend/components/analysis/RunButton.vue @@ -1,7 +1,9 @@ diff --git a/frontend/components/modal/Group.vue b/frontend/components/modal/Group.vue index ebee878..d1e1f94 100644 --- a/frontend/components/modal/Group.vue +++ b/frontend/components/modal/Group.vue @@ -9,5 +9,9 @@ + diff --git a/frontend/lang/en.json b/frontend/lang/en.json index 477d065..f6c2586 100644 --- a/frontend/lang/en.json +++ b/frontend/lang/en.json @@ -220,7 +220,11 @@ "components/analysis/RunButton": { "Run": "Run", "AnalysisTypeAUDIT": "Audit", - "AnalysisTypeREPORT": "Report" + "AnalysisTypeREPORT": "Report", + "ConfirmationHeader": "Run Duplicate Analysis?", + "ConfirmationMessage": "It looks like there is already a run for this analysis - if you have changed the settings or underlying data for this analysis, a new run may be warranted, but otherwise this will just return the same analysis as above.", + "Run Anyway": "Run Anyway", + "Cancel Run": "Nevermind" }, "components/portfolio/group/ListView": { "Created At": "Created At", diff --git a/frontend/openapi/generated/pacta/models/HoldingsDate.ts b/frontend/openapi/generated/pacta/models/HoldingsDate.ts index 11c37ae..37c9d35 100644 --- a/frontend/openapi/generated/pacta/models/HoldingsDate.ts +++ b/frontend/openapi/generated/pacta/models/HoldingsDate.ts @@ -7,6 +7,6 @@ export type HoldingsDate = { /** * The time at which the holdings are represented at */ - time: string; + time?: string; }; diff --git a/frontend/openapi/generated/pacta/models/IncompleteUpload.ts b/frontend/openapi/generated/pacta/models/IncompleteUpload.ts index d02ba35..9e5146c 100644 --- a/frontend/openapi/generated/pacta/models/IncompleteUpload.ts +++ b/frontend/openapi/generated/pacta/models/IncompleteUpload.ts @@ -20,7 +20,7 @@ export type IncompleteUpload = { * Description of the upload */ description: string; - propertyHoldingsDate?: HoldingsDate; + propertyHoldingsDate: HoldingsDate; /** * If set, this portfolio represents ESG data */ diff --git a/frontend/openapi/generated/pacta/models/Portfolio.ts b/frontend/openapi/generated/pacta/models/Portfolio.ts index 729d843..2240c63 100644 --- a/frontend/openapi/generated/pacta/models/Portfolio.ts +++ b/frontend/openapi/generated/pacta/models/Portfolio.ts @@ -41,7 +41,7 @@ export type Portfolio = { * The list of initiatives that this portfolio is a member of */ initiatives?: Array; - propertyHoldingsDate?: HoldingsDate; + propertyHoldingsDate: HoldingsDate; /** * If set, this portfolio represents ESG data */ diff --git a/frontend/openapi/generated/pacta/models/StartPortfolioUploadReq.ts b/frontend/openapi/generated/pacta/models/StartPortfolioUploadReq.ts index ce128a1..8489ed3 100644 --- a/frontend/openapi/generated/pacta/models/StartPortfolioUploadReq.ts +++ b/frontend/openapi/generated/pacta/models/StartPortfolioUploadReq.ts @@ -9,7 +9,10 @@ import type { StartPortfolioUploadReqItem } from './StartPortfolioUploadReqItem' export type StartPortfolioUploadReq = { items: Array; - propertyHoldingsDate?: HoldingsDate; + /** + * The holdings date of the portfolio, if set + */ + propertyHoldingsDate: HoldingsDate; /** * If set, this portfolio represents ESG data */ diff --git a/frontend/pages/upload.vue b/frontend/pages/upload.vue index f2d2235..9639b9b 100644 --- a/frontend/pages/upload.vue +++ b/frontend/pages/upload.vue @@ -39,7 +39,7 @@ interface FileStateDetail extends FileState { effectiveError?: string | undefined } -const holdingsDate = useState(`${prefix}.holdingsDate`, () => undefined) +const holdingsDate = useState(`${prefix}.holdingsDate`, () => ({ time: undefined })) const esg = useState(`${prefix}.esg`, () => OptionalBoolean.OPTIONAL_BOOLEAN_UNSET) const external = useState(`${prefix}.external`, () => OptionalBoolean.OPTIONAL_BOOLEAN_UNSET) const engagementStrategy = useState(`${prefix}.engagementStrategy`, () => OptionalBoolean.OPTIONAL_BOOLEAN_UNSET) @@ -50,7 +50,7 @@ const isProcessing = useState(`${prefix}.isProcessing`, () => false) const fileStates = useState(`${prefix}.fileState`, () => []) const reset = () => { - holdingsDate.value = undefined + holdingsDate.value = { time: undefined } esg.value = OptionalBoolean.OPTIONAL_BOOLEAN_UNSET external.value = OptionalBoolean.OPTIONAL_BOOLEAN_UNSET engagementStrategy.value = OptionalBoolean.OPTIONAL_BOOLEAN_UNSET diff --git a/frontend/plugins/primevue.ts b/frontend/plugins/primevue.ts index 1034938..e80e3ee 100644 --- a/frontend/plugins/primevue.ts +++ b/frontend/plugins/primevue.ts @@ -9,6 +9,8 @@ import Calendar from 'primevue/calendar' import Card from 'primevue/card' import Chips from 'primevue/chips' import Column from 'primevue/column' +import ConfirmDialog from 'primevue/confirmdialog' +import ConfirmationService from 'primevue/confirmationservice' import DataTable from 'primevue/datatable' import Dialog from 'primevue/dialog' import Dropdown from 'primevue/dropdown' @@ -42,6 +44,7 @@ export default defineNuxtPlugin(({ vueApp }) => { vueApp.component('PVCard', Card) vueApp.component('PVChips', Chips) vueApp.component('PVColumn', Column) + vueApp.component('PVConfirmDialog', ConfirmDialog) vueApp.component('PVDataTable', DataTable) vueApp.component('PVDialog', Dialog) vueApp.component('PVDropdown', Dropdown) @@ -62,4 +65,5 @@ export default defineNuxtPlugin(({ vueApp }) => { vueApp.component('PVTriStateCheckbox', TriStateCheckbox) vueApp.directive('tooltip', Tooltip) + vueApp.use(ConfirmationService) }) diff --git a/openapi/pacta.yaml b/openapi/pacta.yaml index a1d8501..a703956 100644 --- a/openapi/pacta.yaml +++ b/openapi/pacta.yaml @@ -1508,6 +1508,7 @@ components: type: object required: - items + - propertyHoldingsDate - propertyESG - propertyExternal - propertyEngagementStrategy @@ -1517,6 +1518,7 @@ components: items: $ref: '#/components/schemas/StartPortfolioUploadReqItem' propertyHoldingsDate: + description: The holdings date of the portfolio, if set $ref: '#/components/schemas/HoldingsDate' propertyESG: description: If set, this portfolio represents ESG data @@ -1626,8 +1628,6 @@ components: description: The time at which the signed URL will expire. HoldingsDate: type: object - required: - - time properties: time: type: string @@ -1641,6 +1641,7 @@ components: - description - createdAt - adminDebugEnabled + - propertyHoldingsDate - propertyESG - propertyExternal - propertyEngagementStrategy @@ -1708,6 +1709,7 @@ components: - createdAt - adminDebugEnabled - numberOfRows + - propertyHoldingsDate - propertyESG - propertyExternal - propertyEngagementStrategy