Skip to content

Commit

Permalink
Merge branch 'ui-button-permissions' into 'main'
Browse files Browse the repository at this point in the history
Ui button permissions

See merge request reportcreator/reportcreator!367
  • Loading branch information
MWedl committed Dec 13, 2023
2 parents 956d492 + e7a5929 commit 68f95d8
Show file tree
Hide file tree
Showing 30 changed files with 274 additions and 114 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## TBD
* Design and UI rework
* Dark mode
* Disable buttons and menu entries when user does not have permissions
* Fix save error for user fields
* Ensure custom fonts are loaded before rendering charts and diagrams

Expand Down
7 changes: 7 additions & 0 deletions api/src/reportcreator_api/api_utils/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ def settings_endpoint(self, *args, **kwargs):
'private_designs': settings.ENABLE_PRIVATE_DESIGNS,
'spellcheck': bool(settings.SPELLCHECK_URL and license.is_professional()),
'archiving': license.is_professional(),
'permissions': license.is_professional(),
},
'guest_permissions': {
'import_projects': settings.GUEST_USERS_CAN_IMPORT_PROJECTS,
'create_projects': settings.GUEST_USERS_CAN_CREATE_PROJECTS,
'delete_projects': settings.GUEST_USERS_CAN_DELETE_PROJECTS,
'update_project_settings': settings.GUEST_USERS_CAN_UPDATE_PROJECT_SETTINGS,
},
})

Expand Down
5 changes: 2 additions & 3 deletions frontend/src/components/Design/CreateDesignDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
import { ProjectTypeScope } from "~/utils/types";
const auth = useAuth();
const apiSettings = useApiSettings();
const projectTypeStore = useProjectTypeStore();
const dialogVisible = ref(false);
Expand All @@ -63,8 +62,8 @@ watch(actionInProgress, () => { currentDesign.value = null });
const scopeItems = computed(() => {
return [
{ value: ProjectTypeScope.GLOBAL, title: 'Global Design', props: { subtitle: 'Available for all users', disabled: !auth.hasScope('designer') } },
{ value: ProjectTypeScope.PRIVATE, title: 'Private Design', props: { subtitle: 'Available only for you', disabled: !apiSettings.settings?.features.private_designs } },
{ value: ProjectTypeScope.GLOBAL, title: 'Global Design', props: { subtitle: 'Available for all users', disabled: !auth.permissions.designer } },
{ value: ProjectTypeScope.PRIVATE, title: 'Private Design', props: { subtitle: 'Available only for you', disabled: !auth.permissions.private_designs } },
];
});
const currentScope = ref<ProjectTypeScope>(scopeItems.value.find(item => !item.props.disabled)?.value || ProjectTypeScope.GLOBAL);
Expand Down
5 changes: 2 additions & 3 deletions frontend/src/components/Design/ImportDesignDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,11 @@
import { ProjectTypeScope } from "~/utils/types";
const auth = useAuth();
const apiSettings = useApiSettings();
const scopeItems = computed(() => {
return [
{ value: ProjectTypeScope.GLOBAL, title: 'Global Design', props: { subtitle: 'Available for all users', disabled: !auth.hasScope('designer') } },
{ value: ProjectTypeScope.PRIVATE, title: 'Private Design', props: { subtitle: 'Available only for you', disabled: !apiSettings.settings?.features.private_designs } },
{ value: ProjectTypeScope.GLOBAL, title: 'Global Design', props: { subtitle: 'Available for all users', disabled: !auth.permissions.designer } },
{ value: ProjectTypeScope.PRIVATE, title: 'Private Design', props: { subtitle: 'Available only for you', disabled: !auth.permissions.private_designs } },
];
});
const currentScope = ref<ProjectTypeScope>(scopeItems.value.find(item => !item.props.disabled)?.value || ProjectTypeScope.GLOBAL);
Expand Down
22 changes: 15 additions & 7 deletions frontend/src/components/EditToolbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
<slot></slot>

<s-btn-icon
v-if="canSave"
v-if="showSave"
:loading="savingInProgress"
:disabled="savingInProgress"
:disabled="!canSave"
@click="performSave"
>
<v-badge v-if="props.data !== undefined" dot :color="hasChanges? 'error' : 'success'">
Expand All @@ -31,14 +31,15 @@
/>
</s-btn-icon>

<s-btn-icon v-if="(canSave && props.canAutoSave) || canDelete || slots['context-menu']">
<s-btn-icon v-if="(showSave && props.canAutoSave) || showDelete || slots['context-menu']">
<v-icon icon="mdi-dots-vertical" />

<v-menu activator="parent" :close-on-content-click="false" location="bottom left" class="context-menu">
<v-list>
<v-list-item
v-if="canSave && props.canAutoSave"
v-if="showSave && props.canAutoSave"
@click="autoSaveEnabled = !autoSaveEnabled"
:disabled="!canSave"
link
title="Auto Save"
>
Expand All @@ -53,10 +54,11 @@
</v-list-item>
<slot name="context-menu" />
<btn-delete
v-if="canDelete"
v-if="showDelete"
:delete="performDelete"
:confirm="true"
:confirm-input="props.deleteConfirmInput"
:disabled="!canDelete"
button-variant="list-item"
color="error"
/>
Expand Down Expand Up @@ -100,8 +102,10 @@ import { EditMode } from '@/utils/types';
const props = withDefaults(defineProps<{
data?: T,
form?: VForm,
canSave?: boolean,
canAutoSave?: boolean,
save?:((data: T) => Promise<void>),
canDelete?: boolean,
delete?: ((data: T) => Promise<void>),
deleteConfirmInput?: string,
lockUrl?: string,
Expand All @@ -111,8 +115,10 @@ const props = withDefaults(defineProps<{
}>(), {
data: undefined,
form: undefined,
canSave: true,
canAutoSave: false,
save: undefined,
canDelete: undefined,
delete: undefined,
deleteConfirmInput: undefined,
lockUrl: undefined,
Expand All @@ -130,8 +136,10 @@ const slots = useSlots();
const auth = useAuth();
const localSettings = useLocalSettings();
const canDelete = computed(() => props.delete !== undefined && props.editMode === EditMode.EDIT);
const canSave = computed(() => props.save !== undefined && props.editMode === EditMode.EDIT);
const showDelete = computed(() => props.delete !== undefined);
const canDelete = computed(() => props.canDelete || (props.canDelete === undefined && props.delete !== undefined && props.editMode === EditMode.EDIT));
const showSave = computed(() => props.save !== undefined);
const canSave = computed(() => showSave.value && props.canSave && props.editMode === EditMode.EDIT);
const hasChangesValue = ref(false);
const hasChanges = computed(() => hasChangesValue.value || props.data === undefined);
const autoSaveEnabled = computed({
Expand Down
12 changes: 8 additions & 4 deletions frontend/src/components/HistoryTimeline.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<div class="history-timeline-header">
<v-list-item>
<v-list-item-title class="text-h6">
Version History
<pro-info>Version History</pro-info>
</v-list-item-title>
<template #append>
<v-btn icon variant="text" @click="emit('update:modelValue', false)">
Expand All @@ -22,9 +22,9 @@
</div>

<v-list-item v-if="!apiSettings.isProfessionalLicense">
Version history is not available <br>
in community edition.<br><br>
See <a href="https://docs.sysreptor.com/features-and-pricing/" target="_blank">https://docs.sysreptor.com/features-and-pricing/</a>
Version history is available<br>
in SysReptor Professional.<br><br>
See <a href="https://docs.sysreptor.com/features-and-pricing/" target="_blank" class="text-primary">https://docs.sysreptor.com/features-and-pricing/</a>
</v-list-item>
<div v-else>
<v-timeline
Expand Down Expand Up @@ -87,6 +87,10 @@ watch(() => props.url, () => {
.history-sidebar {
width: 25em !important;
z-index: 5001 !important;
}
.history-sidebar + .v-navigation-drawer__scrim {
z-index: 5000 !important;
}
.history-timeline-header {
Expand Down
5 changes: 2 additions & 3 deletions frontend/src/components/LicenseInfoMenuItem.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<v-list-item to="/license/" title="License" :disabled="!showLicense">
<v-list-item to="/license/" title="License" :disabled="!auth.permissions.view_license">
<template #prepend>
<v-badge :color="licenseError ? 'error' : licenseWarning ? 'warning' : 'transparent'" dot>
<v-icon icon="mdi-check-decagram" />
Expand All @@ -12,10 +12,9 @@
const auth = useAuth();
const apiSettings = useApiSettings();
const showLicense = computed(() => auth.hasScope('user_manager') || auth.user.value!.is_superuser);
const licenseError = computed(() => apiSettings.settings!.license.error !== null);
const { data: licenseInfo } = useLazyAsyncData(async () => {
if (showLicense.value && licenseError.value) {
if (auth.permissions.view_license && licenseError.value) {
await nextTick();
return await $fetch<LicenseInfoDetails>('/api/v1/utils/license/', { method: 'GET' });
} else {
Expand Down
29 changes: 29 additions & 0 deletions frontend/src/components/ProInfo.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<template>
<a
v-if="!apiSettings.isProfessionalLicense"
href="https://docs.sysreptor.com/features-and-pricing/"
target="_blank"
class="pro-link"
>
<slot />
<span class="pro-badge">/PRO</span>
<s-tooltip activator="parent" text="Available in SysReptor Professional" />
</a>
<span v-else><slot /></span>
</template>

<script setup lang="ts">
const apiSettings = useApiSettings();
</script>

<style lang="scss" scoped>
.pro-link {
pointer-events: auto;
text-decoration: none;
color: inherit;
}
.pro-badge {
color: rgb(var(--v-theme-primary));
font-weight: 800;
}
</style>
Loading

0 comments on commit 68f95d8

Please sign in to comment.