From a399391cf7af033d8c7547acaaa9f456f3f83706 Mon Sep 17 00:00:00 2001 From: Grady Berry Ward Date: Tue, 19 Dec 2023 08:15:24 -0700 Subject: [PATCH] Frontend for Portfolio Groups (#85) --- frontend/components/form/EditorField.vue | 2 +- frontend/components/form/Field.vue | 4 +- .../components/modal/MissingTranslations.vue | 74 ++++-- frontend/components/portfolio/ListView.vue | 224 ++++++++++++++++ .../components/portfolio/group/Editor.vue | 44 ++++ .../components/portfolio/group/ListView.vue | 200 +++++++++++++++ .../components/portfolio/group/NewModal.vue | 80 ++++++ .../portfolio/group/membership/MenuButton.vue | 177 +++++++++++++ frontend/components/standard/Debug.vue | 17 +- frontend/components/standard/Modal.vue | 3 + frontend/composables/useModal.ts | 6 + frontend/lang/en.json | 64 ++++- frontend/lib/editor/incomplete_upload.ts | 4 +- frontend/lib/editor/index.ts | 5 +- frontend/lib/editor/initiative.ts | 2 +- frontend/lib/editor/pacta_version.ts | 4 +- frontend/lib/editor/portfolio.ts | 13 +- frontend/lib/editor/portfolio_group.ts | 39 +++ frontend/lib/editor/user.ts | 2 +- frontend/lib/editor/utils.ts | 4 +- frontend/openapi/generated/pacta/index.ts | 8 + .../pacta/models/ListPortfolioGroupsReq.ts | 6 + .../pacta/models/ListPortfolioGroupsResp.ts | 11 + .../generated/pacta/models/Portfolio.ts | 5 + .../generated/pacta/models/PortfolioGroup.ts | 30 +++ .../pacta/models/PortfolioGroupChanges.ts | 16 ++ .../pacta/models/PortfolioGroupCreate.ts | 16 ++ .../models/PortfolioGroupMembershipIds.ts | 16 ++ .../PortfolioGroupMembershipPortfolio.ts | 15 ++ .../PortfolioGroupMembershipPortfolioGroup.ts | 15 ++ .../pacta/services/DefaultService.ts | 132 ++++++++++ frontend/pages/portfolios.vue | 241 +++++++----------- 32 files changed, 1290 insertions(+), 189 deletions(-) create mode 100644 frontend/components/portfolio/ListView.vue create mode 100644 frontend/components/portfolio/group/Editor.vue create mode 100644 frontend/components/portfolio/group/ListView.vue create mode 100644 frontend/components/portfolio/group/NewModal.vue create mode 100644 frontend/components/portfolio/group/membership/MenuButton.vue create mode 100644 frontend/lib/editor/portfolio_group.ts create mode 100644 frontend/openapi/generated/pacta/models/ListPortfolioGroupsReq.ts create mode 100644 frontend/openapi/generated/pacta/models/ListPortfolioGroupsResp.ts create mode 100644 frontend/openapi/generated/pacta/models/PortfolioGroup.ts create mode 100644 frontend/openapi/generated/pacta/models/PortfolioGroupChanges.ts create mode 100644 frontend/openapi/generated/pacta/models/PortfolioGroupCreate.ts create mode 100644 frontend/openapi/generated/pacta/models/PortfolioGroupMembershipIds.ts create mode 100644 frontend/openapi/generated/pacta/models/PortfolioGroupMembershipPortfolio.ts create mode 100644 frontend/openapi/generated/pacta/models/PortfolioGroupMembershipPortfolioGroup.ts diff --git a/frontend/components/form/EditorField.vue b/frontend/components/form/EditorField.vue index 5b08ea8..7b0d172 100644 --- a/frontend/components/form/EditorField.vue +++ b/frontend/components/form/EditorField.vue @@ -30,7 +30,7 @@ const slots = useSlots() const helpTextSlotExists = computed(() => slots['help-text'] !== undefined) const valid = computed(() => isValid(props.editorField, props.editorValue)) const hasValidation = computed(() => (props.editorField.validation ?? []).length > 0) -const loadingLabel = computed(() => props.editorField.loadingLabel ?? tt('Loading...')) +const loadingLabel = computed(() => props.editorField.loadingLabel ?? tt('Loading')) const invalidLabel = computed(() => props.editorField.invalidLabel ?? tt('Needs Attention')) const validLabel = computed(() => props.editorField.validLabel ?? '') const startHelpTextExpanded = computed(() => props.editorField.startHelpTextExpanded ?? false) diff --git a/frontend/components/form/Field.vue b/frontend/components/form/Field.vue index 80e3ded..dac9221 100644 --- a/frontend/components/form/Field.vue +++ b/frontend/components/form/Field.vue @@ -22,10 +22,10 @@ const props = withDefaults(defineProps(), { helpText: '', startHelpTextExpanded: true, isLoading: false, - loadingLabel: 'Loading...', + loadingLabel: undefined, hasValidation: false, isValid: false, - invalidLabel: 'Needs Attention', + invalidLabel: undefined, validLabel: '', }) diff --git a/frontend/components/modal/MissingTranslations.vue b/frontend/components/modal/MissingTranslations.vue index 93f710b..a1bc165 100644 --- a/frontend/components/modal/MissingTranslations.vue +++ b/frontend/components/modal/MissingTranslations.vue @@ -1,6 +1,6 @@ + + diff --git a/frontend/components/portfolio/group/Editor.vue b/frontend/components/portfolio/group/Editor.vue new file mode 100644 index 0000000..c4e9fee --- /dev/null +++ b/frontend/components/portfolio/group/Editor.vue @@ -0,0 +1,44 @@ + + + diff --git a/frontend/components/portfolio/group/ListView.vue b/frontend/components/portfolio/group/ListView.vue new file mode 100644 index 0000000..44b5419 --- /dev/null +++ b/frontend/components/portfolio/group/ListView.vue @@ -0,0 +1,200 @@ + + + diff --git a/frontend/components/portfolio/group/NewModal.vue b/frontend/components/portfolio/group/NewModal.vue new file mode 100644 index 0000000..f7bacec --- /dev/null +++ b/frontend/components/portfolio/group/NewModal.vue @@ -0,0 +1,80 @@ + + + diff --git a/frontend/components/portfolio/group/membership/MenuButton.vue b/frontend/components/portfolio/group/membership/MenuButton.vue new file mode 100644 index 0000000..7b66a41 --- /dev/null +++ b/frontend/components/portfolio/group/membership/MenuButton.vue @@ -0,0 +1,177 @@ + + + + + diff --git a/frontend/components/standard/Debug.vue b/frontend/components/standard/Debug.vue index 66a4a67..81377a2 100644 --- a/frontend/components/standard/Debug.vue +++ b/frontend/components/standard/Debug.vue @@ -12,6 +12,21 @@ interface Props { } const props = withDefaults(defineProps(), { always: false, label: undefined }) const label = computed(() => props.label ?? tt('Debugging Information')) +const valueAsStr = computed(() => JSON.stringify(props.value, createCircularReplacer(), 2)) + +function createCircularReplacer (): (this: any, key: string, value: any) => any { + const seen = new WeakSet() + return function (this: any, key: string, value: any) { + if (typeof value === 'object' && value !== null) { + if (seen.has(value)) { + return '#REF' + } + seen.add(value) + } + return value + } +} +