Skip to content

Commit

Permalink
Merge pull request #846 from oslokommune/item-about-drawer
Browse files Browse the repository at this point in the history
Add drawer for editing item info
  • Loading branch information
petterhj authored Sep 5, 2023
2 parents c2059a0 + 4c8514d commit 98260b3
Show file tree
Hide file tree
Showing 13 changed files with 457 additions and 319 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ All notable changes to this project will be documented in this file. The format

- Fixed create/edit/delete rights for organization admins.

### Changed

- Organization, department and product details are now edited from within the
current item's about page.

## [3.9.0] 2023-09-01

### Added
Expand Down
5 changes: 2 additions & 3 deletions src/components/ArchivedRestore.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<pkt-alert :title="$t('archived.heading')" skin="warning" class="archived-alert">
<div class="archived-alert__body">
<p>{{ $t('archived.body', { objectType: objectType }) }}</p>
<p>{{ $t(`archived.body.${objectType}`) }} {{ $t('archived.restoreText') }}</p>

<pkt-button
v-if="hasEditRights"
Expand Down Expand Up @@ -35,8 +35,7 @@ export default {
},
objectType: {
type: String,
default: 'objekt',
required: false,
required: true,
},
},
Expand Down
1 change: 1 addition & 0 deletions src/components/FormComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
:options="selectOptions"
:clearable="selectClearable"
:reduce="selectReduce"
:disabled="disabled"
:data-cy="dataCy"
:append-to-body="true"
@input="$emit('select', $event)"
Expand Down
343 changes: 343 additions & 0 deletions src/components/drawers/EditItemDrawer.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,343 @@
<template>
<paged-drawer-wrapper
ref="drawer"
:visible="visible"
:page-count="pageCount"
@close="$emit('close')"
>
<template #title="{ isDone, isSuccess }">
<template v-if="!isDone">
{{ $t('admin.item.change', { name: item.name }) }}
</template>
<template v-else-if="isSuccess">
{{ $t('admin.item.updated', { name: thisItem.name }) }}
</template>
<template v-else>{{ $t('toaster.error.save') }}</template>
</template>

<template #page="{ pageIndex, prev }">
<form-section :hide-errors="true">
<template v-if="pageIndex === 1">
<form-component
v-model="thisItem.name"
input-type="input"
name="name"
:disabled="thisItem?.archived"
:label="$t('fields.name')"
rules="required"
/>
<pkt-alert v-if="thisItem.name !== item.name" skin="info" class="mb-size-16">
{{ $t('admin.item.slugChangeInfo', { name: item.name }) }}
</pkt-alert>
<form-component
v-model="thisItem.missionStatement"
input-type="textarea"
name="missionStatement"
:disabled="thisItem?.archived"
:rows="5"
:label="$t('fields.missionStatement')"
rules="required"
/>
<form-component
v-model="thisItem.targetAudience"
input-type="textarea"
name="targetAudience"
:disabled="thisItem?.archived"
:rows="4"
:label="$t('dashboard.targetAudience')"
/>
</template>

<template v-else-if="pageIndex === 2">
<form-component
v-if="type === 'department'"
v-model="thisItem.organization"
input-type="select"
name="parent"
:disabled="thisItem?.archived"
:label="$t('admin.department.parentOrganisation')"
rules="required"
:select-options="organizations"
/>

<form-component
v-else-if="type === 'product'"
v-model="thisItem.department"
input-type="select"
name="parent"
:disabled="thisItem?.archived"
:label="$t('admin.product.parentDepartment')"
rules="required"
:select-options="departments"
/>

<div class="pkt-form-group">
<span class="pkt-form-label" for="teamMembers">
{{ $t('general.teamMembers') }}
<span class="pkt-badge">{{ $t('validation.optional') }}</span>
</span>
<v-select
id="teamMembers"
v-model="thisItem.team"
multiple
:disabled="thisItem?.archived"
:options="users"
:get-option-label="(option) => option.displayName || option.id"
>
<template #option="option">
{{ option.displayName || option.id }}
<span v-if="option.displayName !== option.id">({{ option.id }})</span>
</template>
</v-select>
</div>

<form-component
v-model="thisItem.secret"
input-type="input"
name="secret"
:disabled="thisItem?.archived"
:label="$t('fields.secret')"
>
<template #help><span v-html="$t('admin.apiSecret')" /></template>
</form-component>
</template>

<template v-if="!thisItem?.archived" #actions="{ handleSubmit }">
<pkt-button
v-if="pageIndex === 1"
:text="$t('btn.cancel')"
skin="tertiary"
:disabled="loading"
@onClick="$emit('close')"
/>
<pkt-button
v-else
:text="$t('btn.back')"
skin="tertiary"
:disabled="loading"
@onClick="prev"
/>

<btn-save
:label="pageIndex === pageCount ? $t('btn.complete') : $t('btn.continue')"
:disabled="loading"
variant="label-only"
@click="handleSubmit(save)"
/>
</template>
</form-section>
</template>

<template #done>
<em v-if="redirectTimer">
{{ $t('admin.item.redirectInfo', { count: redirectCounter }) }}
</em>
</template>

<template #footer="{ isDone }">
<template v-if="!isDone">
<archived-restore
v-if="thisItem.archived"
:restore="restore"
:object-type="type"
/>
<div v-else class="button-row">
<btn-delete :disabled="loading" @click="archive" />
</div>
</template>
</template>
</paged-drawer-wrapper>
</template>

<script>
import { mapState } from 'vuex';
import Organization from '@/db/Organization';
import Department from '@/db/Department';
import Product from '@/db/Product';
import { db } from '@/config/firebaseConfig';
import { PktAlert, PktButton } from '@oslokommune/punkt-vue2';
import { FormSection, BtnSave, BtnDelete } from '@/components/generic/form';
import PagedDrawerWrapper from '@/components/drawers/PagedDrawerWrapper.vue';
export default {
name: 'EditItemDrawer',
components: {
ArchivedRestore: () => import('@/components/ArchivedRestore.vue'),
PktAlert,
PktButton,
PagedDrawerWrapper,
FormSection,
BtnSave,
BtnDelete,
},
props: {
visible: {
type: Boolean,
required: true,
default: false,
},
item: {
type: Object,
required: true,
},
},
data: () => ({
thisItem: null,
pageCount: 2,
loading: false,
redirectCounter: 3,
redirectTimer: null,
}),
computed: {
...mapState(['organizations', 'departments', 'users']),
type() {
const { department, organization } = this.item;
if (organization && department) {
return 'product';
}
if (organization) {
return 'department';
}
return 'organization';
},
},
watch: {
visible: {
immediate: true,
async handler(visible) {
if (visible) {
this.$refs.drawer.reset();
this.redirectTimer = null;
this.redirectCounter = 3;
}
this.loading = true;
this.thisItem = { ...this.item };
this.loading = false;
},
},
item(item, prevItem) {
// Watch for slug changes and redirect if needed.
if (item.id === prevItem.id && item.slug !== prevItem.slug && !this.redirectTimer) {
this.redirectTimer = setInterval(() => {
this.redirectCounter -= 1;
if (this.redirectCounter === 0) {
clearInterval(this.redirectTimer);
const { name, params, query } = this.$route;
this.$router.replace({
name,
params: { ...params, slug: item.slug },
query,
});
}
}, 1000);
}
},
},
methods: {
async save() {
const { pageIndex, next } = this.$refs.drawer;
if (pageIndex < this.pageCount) {
next();
} else {
this.loading = true;
try {
const { id: itemId } = this.item;
const { name, missionStatement, targetAudience, secret } = this.thisItem;
const team = this.thisItem.team.map((user) =>
db.collection('users').doc(user.id)
);
const data = {
name,
team,
missionStatement,
targetAudience: targetAudience === undefined ? '' : targetAudience,
secret: secret === undefined ? '' : secret,
};
if (this.type === 'organization') {
await Organization.update(itemId, data);
} else if (this.type === 'department') {
const organization = await db
.collection('organizations')
.doc(this.thisItem.organization.id);
await Department.update(itemId, {
...data,
organization,
});
} else {
const department = db
.collection('departments')
.doc(this.thisItem.department.id);
await Product.update(itemId, {
...data,
department,
});
}
this.$refs.drawer.next();
} catch (error) {
this.$refs.drawer.next(false);
this.$toasted.error(this.$t('toaster.error.save'));
} finally {
this.loading = false;
}
}
},
async archive() {
this.loading = true;
try {
await this.getObjectType().archive(this.item.id);
this.thisItem.archived = true;
} catch (error) {
this.$toasted.error(
this.$t('toaster.error.archive', { document: this.item.name })
);
} finally {
this.loading = false;
}
},
async restore() {
try {
await this.getObjectType().restore(this.item.id);
this.thisItem.archived = false;
} catch {
this.$toasted.error(
this.$t('toaster.error.restore', { document: this.thisKeyResult.id })
);
}
},
getObjectType() {
if (this.type === 'organization') {
return Organization;
}
if (this.type === 'department') {
return Department;
}
if (this.type === 'product') {
return Product;
}
throw new Error(`Unknown object type ${this.type}`);
},
},
};
</script>
2 changes: 1 addition & 1 deletion src/components/drawers/EditKeyResult.vue
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@
<archived-restore
v-if="keyResult.archived"
:restore="restore"
:object-type="$t('archived.keyResult')"
object-type="keyResult"
/>
<div v-else class="button-row">
<btn-delete :disabled="loading" @click="archive" />
Expand Down
Loading

0 comments on commit 98260b3

Please sign in to comment.