Skip to content

Commit

Permalink
Adds Missing Line Item Initiative Management Features (#175)
Browse files Browse the repository at this point in the history
  • Loading branch information
gbdubs authored Jan 24, 2024
1 parent 2cd70d9 commit 4fc2c6b
Show file tree
Hide file tree
Showing 12 changed files with 270 additions and 17 deletions.
15 changes: 12 additions & 3 deletions cmd/server/pactasrv/conv/pacta_to_oapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,18 +69,18 @@ func InitiativeToOAPI(i *pacta.Initiative) (*api.Initiative, error) {

func portfolioInitiativeMembershipToOAPIPortfolio(in *pacta.PortfolioInitiativeMembership) (api.PortfolioInitiativeMembershipPortfolio, error) {
var zero api.PortfolioInitiativeMembershipPortfolio
out := &api.PortfolioInitiativeMembershipPortfolio{
out := api.PortfolioInitiativeMembershipPortfolio{
CreatedAt: in.CreatedAt,
}
if in.AddedBy != nil && in.AddedBy.ID == "" {
if in.AddedBy != nil && in.AddedBy.ID != "" {
out.AddedByUserId = strPtr(in.AddedBy.ID)
}
p, err := PortfolioToOAPI(in.Portfolio)
if err != nil {
return zero, oapierr.Internal("portfolioInitiativeMembershipToOAPI: portfolioToOAPI failed", zap.Error(err))
}
out.Portfolio = *p
return zero, nil
return out, nil
}

func portfolioInitiativeMembershipToOAPIInitiative(in *pacta.PortfolioInitiativeMembership) (api.PortfolioInitiativeMembershipInitiative, error) {
Expand Down Expand Up @@ -253,6 +253,14 @@ func PortfolioToOAPI(p *pacta.Portfolio) (*api.Portfolio, error) {
PortfolioGroup: *pg,
})
}
var blob *api.Blob
if p.Blob != nil {
b, err := BlobToOAPI(p.Blob)
if err != nil {
return nil, oapierr.Internal("portfolioToOAPI: blobToOAPI failed", zap.Error(err))
}
blob = b
}
pims, err := convAll(p.PortfolioInitiativeMemberships, portfolioInitiativeMembershipToOAPIInitiative)
if err != nil {
return nil, oapierr.Internal("portfolioToOAPI: portfolioInitiativeMembershipToOAPIInitiative failed", zap.Error(err))
Expand All @@ -264,6 +272,7 @@ func PortfolioToOAPI(p *pacta.Portfolio) (*api.Portfolio, error) {
return &api.Portfolio{
Id: string(p.ID),
Name: p.Name,
Blob: blob,
Description: p.Description,
CreatedAt: p.CreatedAt,
NumberOfRows: p.NumberOfRows,
Expand Down
5 changes: 4 additions & 1 deletion cmd/server/pactasrv/initiative.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,9 @@ func (s *Server) FindInitiativeById(ctx context.Context, request api.FindInitiat
return nil, oapierr.Internal("failed to load portfolios for initiative", zap.String("initiative_id", string(i.ID)), zap.Error(err))
}
i.PortfolioInitiativeMemberships = portfolios
if err := s.populatePortfoliosInInitiatives(ctx, []*pacta.Initiative{i}); err != nil {
return nil, err
}
relationships, err := s.DB.InitiativeUserRelationshipsByInitiative(s.DB.NoTxn(ctx), i.ID)
if err != nil {
return nil, oapierr.Internal("failed to load initiative user relationships for initiative", zap.String("initiative_id", string(i.ID)), zap.Error(err))
Expand Down Expand Up @@ -296,7 +299,7 @@ func (s *Server) initiativeDoAuthzAndAuditLog(ctx context.Context, iID pacta.Ini
} else {
as.authorizedAsActorType = ptr(pacta.AuditLogActorType_Public)
}
case pacta.AuditLogAction_Delete, pacta.AuditLogAction_Create, pacta.AuditLogAction_Update:
case pacta.AuditLogAction_Delete, pacta.AuditLogAction_Create, pacta.AuditLogAction_Update, pacta.AuditLogAction_Download:
if userIsInitiativeManager {
as.authorizedAsActorType = ptr(pacta.AuditLogActorType_Owner)
as.isAuthorized = true
Expand Down
23 changes: 23 additions & 0 deletions cmd/server/pactasrv/populate.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,29 @@ func (s *Server) populatePortfoliosInPortfolioGroups(
return nil
}

func (s *Server) populatePortfoliosInInitiatives(
ctx context.Context,
ts []*pacta.Initiative,
) error {
getFn := func(i *pacta.Initiative) ([]*pacta.Portfolio, error) {
result := []*pacta.Portfolio{}
for _, member := range i.PortfolioInitiativeMemberships {
result = append(result, member.Portfolio)
}
return result, nil
}
lookupFn := func(ids []pacta.PortfolioID) (map[pacta.PortfolioID]*pacta.Portfolio, error) {
return s.DB.Portfolios(s.DB.NoTxn(ctx), ids)
}
getIDFn := func(p *pacta.Portfolio) pacta.PortfolioID {
return p.ID
}
if err := populateAll(ts, getFn, getIDFn, lookupFn); err != nil {
return oapierr.Internal("populating portfolios in initiatives failed", zap.Error(err))
}
return nil
}

func (s *Server) populateInitiativesInPortfolios(
ctx context.Context,
is []*pacta.Portfolio,
Expand Down
2 changes: 1 addition & 1 deletion db/sqldb/portfolio_initiative.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func rowToPortfolioInitiativeMembership(row rowScanner) (*pacta.PortfolioInitiat
}

func rowsToPortfolioInitiativeMemberships(rows pgx.Rows) ([]*pacta.PortfolioInitiativeMembership, error) {
return mapRows("portfolio_initiaitve_membership", rows, rowToPortfolioInitiativeMembership)
return mapRows("portfolio_initiative_membership", rows, rowToPortfolioInitiativeMembership)
}

func validatePortfolioInitiativeMembershipForCreate(pim *pacta.PortfolioInitiativeMembership) error {
Expand Down
13 changes: 1 addition & 12 deletions frontend/app.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,5 @@
<script setup lang="ts">
import { serializeError } from 'serialize-error'
const { loading: { clearLoading }, error: { errorModalVisible, error } } = useModal()
const handleError = (err: Error) => {
if (process.client) {
console.log(err)
}
error.value = serializeError(err)
errorModalVisible.value = true
clearLoading()
}
const { error: { handleError } } = useModal()
onErrorCaptured((err: unknown, _instance: ComponentPublicInstance | null, _info: string) => {
let error: Error | undefined
Expand Down
48 changes: 48 additions & 0 deletions frontend/components/portfolio/DownloadButton.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<script setup lang="ts">
import { type Portfolio, type AccessBlobContentResp } from '@/openapi/generated/pacta'
const { t } = useI18n()
const pactaClient = usePACTA()
const { error: { handleError } } = useModal()
interface Props {
portfolio: Portfolio
}
const props = defineProps<Props>()
const prefix = 'components/portfolio/DownloadButton'
const statePrefix = `${prefix}[${useStateIDGenerator().id()}]`
const tt = (key: string) => t(`${prefix}.${key}`)
const downloadInProgress = useState<boolean>(`${statePrefix}.downloadInProgress`, () => false)
const doDownload = async () => {
const blob = props.portfolio.blob
if (!blob) {
handleError(new Error(`Portfolio did not have blob associated with it, and could not be downloaded: ${props.portfolio.id}`))
return
}
const blobId = blob.id
downloadInProgress.value = true
const resp: AccessBlobContentResp = await pactaClient.accessBlobContent({ items: [{ blobId }] })
const response = await fetch(resp.items[0].downloadUrl)
const data = await response.blob()
const element = document.createElement('a')
element.href = URL.createObjectURL(data)
const fileName = `${blob.fileName}`
element.download = fileName
document.body.appendChild(element)
element.click()
document.body.removeChild(element)
downloadInProgress.value = false
}
</script>

<template>
<PVButton
:loading="downloadInProgress"
:icon="downloadInProgress ? 'pi pi-spinner pi-spin' : 'pi pi-download'"
class="p-button-secondary p-button-outlined p-button-xs"
:label="tt('Download')"
@click="doDownload"
/>
</template>
10 changes: 10 additions & 0 deletions frontend/composables/useModal.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { type Ref } from 'vue'
import { type ErrorObject } from 'serialize-error'
import { serializeError } from 'serialize-error'

export const useModal = () => {
const prefix = 'useModal'
Expand All @@ -24,6 +25,14 @@ export const useModal = () => {
// error
const errorModalVisible = newModalVisibilityState('errorModalVisible')
const error = useState<ErrorObject>('errorModal.error')
const handleError = (err: Error) => {
if (process.client) {
console.log(err)
}
error.value = serializeError(err)
errorModalVisible.value = true
clearLoading()
}

// loading
const loadingSet = useState<Set<string>>(`${prefix}.loadingSet`, () => new Set<string>())
Expand Down Expand Up @@ -86,6 +95,7 @@ export const useModal = () => {
error: {
error,
errorModalVisible,
handleError,
},
permissionDenied: {
permissionDeniedVisible,
Expand Down
13 changes: 13 additions & 0 deletions frontend/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,17 @@
"this user": "this user's public activities on the platform",
"you": "your public activities on the platform"
},
"pages/initiative/id/portfolios": {
"Added By User": "Added by User",
"Added At": "Added At",
"Details": "Details",
"View User": "View User",
"Portfolio":"Portfolio",
"Remove": "Remove",
"Refresh": "Refresh",
"Download": "Download",
"Download All": "Download All"
},
"components/user/Toolbar": {
"Edit": "Edit",
"Profile": "Profile"
Expand Down Expand Up @@ -448,6 +459,8 @@
"pages/initiative/relationships": {
"Actions": "Actions",
"Make Manager": "Make Manager",
"Remove Manager": "Remove Manager",
"Manager": "Manager",
"Member": "Member",
"Remove From Initiative": "Remove From Initiative",
"Role": "Role",
Expand Down
4 changes: 4 additions & 0 deletions frontend/lib/editor/portfolio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ const createEditorPortfolioFields = (translation: Translation): EditorPortfolioF
name: 'initiatives',
label: tt('Initiatives'),
},
blob: {
name: 'blob',
label: tt('Blob'),
},
}
}

Expand Down
5 changes: 5 additions & 0 deletions frontend/openapi/generated/pacta/models/Portfolio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
/* tslint:disable */
/* eslint-disable */

import type { Blob } from './Blob';
import type { HoldingsDate } from './HoldingsDate';
import type { OptionalBoolean } from './OptionalBoolean';
import type { PortfolioGroupMembershipPortfolioGroup } from './PortfolioGroupMembershipPortfolioGroup';
Expand All @@ -25,6 +26,10 @@ export type Portfolio = {
* The time at which this portfolio was successfully parsed from a raw
*/
createdAt: string;
/**
* Information about the logical portfolio file itself
*/
blob?: Blob;
/**
* Whether the admin debug mode is enabled for this portfolio
*/
Expand Down
Loading

0 comments on commit 4fc2c6b

Please sign in to comment.