Merge branch 'main' into room-cleaning

This commit is contained in:
Nitwel
2020-09-08 16:22:07 +02:00
38 changed files with 631 additions and 297 deletions

View File

@@ -0,0 +1,165 @@
<template>
<v-list-item exact :to="bookmark.to" class="bookmark" @contextmenu.native.prevent.stop="$refs.contextMenu.activate">
<v-list-item-icon><v-icon name="bookmark" /></v-list-item-icon>
<v-list-item-content>{{ bookmark.title }}</v-list-item-content>
<v-list-item-icon v-if="bookmark.scope !== 'user'" class="bookmark-scope">
<v-icon :name="bookmark.scope === 'role' ? 'people' : 'public'" />
</v-list-item-icon>
<v-menu ref="contextMenu" show-arrow placement="bottom-start">
<v-list dense>
<v-list-item @click="renameActive = true" :disabled="isMine === false">
<v-list-item-icon>
<v-icon name="edit" outline />
</v-list-item-icon>
<v-list-item-content>
{{ $t('rename_bookmark') }}
</v-list-item-content>
</v-list-item>
<v-list-item @click="deleteActive = true" class="danger" :disabled="isMine === false">
<v-list-item-icon>
<v-icon name="delete" outline />
</v-list-item-icon>
<v-list-item-content>
{{ $t('delete_bookmark') }}
</v-list-item-content>
</v-list-item>
</v-list>
</v-menu>
<v-dialog v-model="renameActive" persistent>
<v-card>
<v-card-title>{{ $t('rename_bookmark') }}</v-card-title>
<v-card-text>
<v-input v-model="renameValue" autofocus @keyup.enter="renameSave" />
</v-card-text>
<v-card-actions>
<v-button secondary @click="renameActive = false">{{ $t('cancel') }}</v-button>
<v-button @click="renameSave" :loading="renameSaving">{{ $t('save') }}</v-button>
</v-card-actions>
</v-card>
</v-dialog>
<v-dialog v-model="deleteActive" persistent>
<v-card>
<v-card-title>{{ $t('delete_bookmark_copy', { bookmark: bookmark.title }) }}</v-card-title>
<v-card-actions>
<v-button secondary @click="deleteActive = false">{{ $t('cancel') }}</v-button>
<v-button @click="deleteSave" :loading="deleteSaving" class="action-delete">
{{ $t('delete') }}
</v-button>
</v-card-actions>
</v-card>
</v-dialog>
</v-list-item>
</template>
<script lang="ts">
import { defineComponent, PropType, ref, computed } from '@vue/composition-api';
import { Preset } from '@/types';
import { useUserStore, usePresetsStore } from '@/stores';
export default defineComponent({
props: {
bookmark: {
type: Object as PropType<Preset>,
required: true,
},
},
setup(props) {
const contextMenu = ref();
const userStore = useUserStore();
const presetsStore = usePresetsStore();
const isMine = computed(() => props.bookmark.user === userStore.state.currentUser!.id);
const { renameActive, renameValue, renameSave, renameSaving } = useRenameBookmark();
const { deleteActive, deleteValue, deleteSave, deleteSaving } = useDeleteBookmark();
return {
contextMenu,
isMine,
renameActive,
renameValue,
renameSave,
renameSaving,
deleteActive,
deleteValue,
deleteSave,
deleteSaving,
};
function useRenameBookmark() {
const renameActive = ref(false);
const renameValue = ref(props.bookmark.title);
const renameSaving = ref(false);
return { renameActive, renameValue, renameSave, renameSaving };
async function renameSave() {
renameSaving.value = true;
try {
await presetsStore.savePreset({
...props.bookmark,
title: renameValue.value,
});
renameActive.value = false;
} catch (error) {
console.error(error);
} finally {
renameSaving.value = false;
}
}
}
function useDeleteBookmark() {
const deleteActive = ref(false);
const deleteValue = ref(props.bookmark.title);
const deleteSaving = ref(false);
return { deleteActive, deleteValue, deleteSave, deleteSaving };
async function deleteSave() {
deleteSaving.value = true;
try {
await presetsStore.savePreset(props.bookmark.id);
deleteActive.value = false;
} catch (error) {
console.error(error);
} finally {
deleteSaving.value = false;
}
}
}
},
});
</script>
<style lang="scss" scoped>
.bookmark-scope {
--v-icon-color: var(--foreground-subdued);
opacity: 0;
transition: opacity var(--fast) var(--transition);
}
.bookmark:hover .bookmark-scope {
opacity: 1;
}
.danger {
--v-list-item-color: var(--danger);
--v-list-item-icon-color: var(--danger);
}
.action-delete {
--v-button-background-color: var(--danger-25);
--v-button-color: var(--danger);
--v-button-background-color-hover: var(--danger-50);
--v-button-color-hover: var(--danger);
}
</style>

View File

@@ -19,10 +19,7 @@
<template v-if="bookmarks.length > 0">
<v-divider />
<v-list-item exact v-for="bookmark in bookmarks" :key="bookmark.id" :to="bookmark.to">
<v-list-item-icon><v-icon name="bookmark" /></v-list-item-icon>
<v-list-item-content>{{ bookmark.title }}</v-list-item-content>
</v-list-item>
<navigation-bookmark v-for="bookmark of bookmarks" :key="bookmark.id" :bookmark="bookmark" />
</template>
<div v-if="!customNavItems && !navItems.length && !bookmarks.length" class="empty">
@@ -33,17 +30,18 @@
{{ $t('no_collections_copy') }}
</template>
</div>
</v-list>
</template>
<script lang="ts">
import { defineComponent, computed } from '@vue/composition-api';
import useNavigation from '../composables/use-navigation';
import { usePresetsStore } from '@/stores/';
import { useUserStore } from '@/stores';
import { usePresetsStore, useUserStore } from '@/stores/';
import { orderBy } from 'lodash';
import NavigationBookmark from './navigation-bookmark.vue';
export default defineComponent({
components: { NavigationBookmark },
props: {
exact: {
type: Boolean,
@@ -57,16 +55,25 @@ export default defineComponent({
const isAdmin = computed(() => userStore.state.currentUser?.role.admin === true);
const bookmarks = computed(() => {
return presetsStore.state.collectionPresets
.filter((preset) => {
return preset.title !== null && preset.collection.startsWith('directus_') === false;
})
.map((preset) => {
return {
...preset,
to: `/collections/${preset.collection}?bookmark=${preset.id}`,
};
});
return orderBy(
presetsStore.state.collectionPresets
.filter((preset) => {
return preset.title !== null && preset.collection.startsWith('directus_') === false;
})
.map((preset) => {
let scope = 'global';
if (!!preset.role) scope = 'role';
if (!!preset.user) scope = 'user';
return {
...preset,
to: `/collections/${preset.collection}?bookmark=${preset.id}`,
scope,
};
}),
['title'],
['asc']
);
});
return { navItems, bookmarks, customNavItems, isAdmin };

View File

@@ -13,30 +13,45 @@
</template>
<template #title-outer:append>
<bookmark-add
v-if="!bookmark"
class="bookmark-add"
v-model="bookmarkDialogActive"
@save="createBookmark"
:saving="creatingBookmark"
>
<template #activator="{ on }">
<v-icon class="toggle" name="bookmark_outline" @click="on" />
</template>
</bookmark-add>
<div class="bookmark-controls">
<bookmark-add
v-if="!bookmark"
class="add"
v-model="bookmarkDialogActive"
@save="createBookmark"
:saving="creatingBookmark"
>
<template #activator="{ on }">
<v-icon class="toggle" name="bookmark_outline" @click="on" />
</template>
</bookmark-add>
<bookmark-edit
v-else
class="bookmark-edit"
v-model="bookmarkDialogActive"
:saving="editingBookmark"
:name="bookmarkName"
@save="editBookmark"
>
<template #activator="{ on }">
<v-icon class="toggle" name="bookmark" @click="on" />
<v-icon class="saved" name="bookmark" v-else-if="bookmarkSaved" />
<template v-else-if="bookmarkIsMine">
<v-icon class="save" @click="savePreset()" name="bookmark_save" v-tooltip.bottom="$t('update_bookmark')" />
</template>
</bookmark-edit>
<bookmark-add
v-else
class="add"
v-model="bookmarkDialogActive"
@save="createBookmark"
:saving="creatingBookmark"
>
<template #activator="{ on }">
<v-icon class="toggle" name="bookmark_outline" @click="on" />
</template>
</bookmark-add>
<v-icon
v-if="bookmark && !bookmarkSaving && bookmarkSaved === false"
name="settings_backup_restore"
@click="clearLocalSave"
class="clear"
v-tooltip.bottom="$t('reset_bookmark')"
/>
</div>
</template>
<template #actions:prepend>
@@ -277,6 +292,10 @@ export default defineComponent({
saveCurrentAsBookmark,
title: bookmarkName,
resetPreset,
bookmarkSaved,
bookmarkIsMine,
busy: bookmarkSaving,
clearLocalSave,
} = usePreset(collection, bookmarkID);
const {
@@ -345,6 +364,10 @@ export default defineComponent({
deleteError,
createAllowed,
resetPreset,
bookmarkSaved,
bookmarkIsMine,
bookmarkSaving,
clearLocalSave,
};
function useBreadcrumb() {
@@ -559,25 +582,50 @@ export default defineComponent({
--layout-offset-top: 64px;
}
.bookmark-add .toggle,
.bookmark-edit .toggle {
margin-left: 8px;
transition: color var(--fast) var(--transition);
}
.bookmark-add {
color: var(--foreground-subdued);
&:hover {
color: var(--foreground-normal);
}
}
.bookmark-edit {
color: var(--primary);
}
.header-icon {
--v-button-color-disabled: var(--foreground-normal);
}
.bookmark-controls {
.add,
.save,
.saved,
.clear {
display: inline-block;
margin-left: 8px;
}
.add,
.save,
.clear {
cursor: pointer;
color: var(--foreground-subdued);
transition: color var(--fast) var(--transition);
&:hover {
color: var(--foreground-normal);
}
}
.save {
color: var(--warning);
&:hover {
color: var(--warning-125);
}
}
.saved {
color: var(--primary);
}
.clear {
color: var(--foreground-subdued);
margin-left: 4px;
&:hover {
color: var(--warning);
}
}
}
</style>

View File

@@ -70,7 +70,7 @@
<v-card>
<v-card-title>{{ $t('rename_folder') }}</v-card-title>
<v-card-text>
<v-input v-model="renameValue" />
<v-input v-model="renameValue" autofocus @keyup.enter="renameSave" />
</v-card-text>
<v-card-actions>
<v-button secondary @click="renameActive = false">{{ $t('cancel') }}</v-button>

View File

@@ -98,6 +98,40 @@
<v-checkbox v-model="fieldData.schema.is_nullable" :label="$t('allow_null_label')" block />
</div>
<div class="field full">
<div class="label type-label">{{ $t('translation') }}</div>
<interface-repeater
v-model="fieldData.meta.translation"
:template="'{{ translation }} ({{ locale }})'"
:fields="[
{
field: 'locale',
type: 'string',
name: $t('language'),
meta: {
interface: 'system-language',
width: 'half',
},
schema: {
default_value: 'en-US'
},
},
{
field: 'translation',
type: 'string',
name: $t('translation'),
meta: {
interface: 'text-input',
width: 'half',
options: {
placeholder: 'Enter a translation...'
},
},
},
]"
/>
</div>
<!--
@todo add unique when the API supports it

View File

@@ -143,30 +143,30 @@ export default defineComponent({
label: 'sort',
icon: 'low_priority',
},
userCreated: {
enabled: false,
name: 'user_created',
label: 'created_by',
icon: 'account_circle',
},
userUpdated: {
enabled: false,
name: 'user_updated',
label: 'updated_by',
icon: 'account_circle',
},
dateCreated: {
enabled: false,
name: 'date_created',
label: 'created_on',
icon: 'access_time',
},
userCreated: {
enabled: false,
name: 'user_created',
label: 'created_by',
icon: 'account_circle',
},
dateUpdated: {
enabled: false,
name: 'date_updated',
label: 'updated_on',
icon: 'access_time',
},
userUpdated: {
enabled: false,
name: 'user_updated',
label: 'updated_by',
icon: 'account_circle',
},
});
const saving = ref(false);
@@ -332,7 +332,7 @@ export default defineComponent({
},
readonly: true,
hidden: true,
width: 'full',
width: 'half',
},
schema: {},
});
@@ -347,7 +347,7 @@ export default defineComponent({
interface: 'datetime',
readonly: true,
hidden: true,
width: 'full',
width: 'half',
},
schema: {},
});
@@ -366,7 +366,7 @@ export default defineComponent({
},
readonly: true,
hidden: true,
width: 'full',
width: 'half',
},
schema: {},
});
@@ -381,7 +381,7 @@ export default defineComponent({
interface: 'datetime',
readonly: true,
hidden: true,
width: 'full',
width: 'half',
},
schema: {},
});

View File

@@ -169,7 +169,7 @@ import CommentsDrawerDetail from '@/views/private/components/comments-drawer-det
import useItem from '@/composables/use-item';
import SaveOptions from '@/views/private/components/save-options';
import api from '@/api';
import { useFieldsStore } from '@/stores/';
import { useFieldsStore, useUserStore } from '@/stores/';
import useFormFields from '@/composables/use-form-fields';
import { Field } from '@/types';
import UserInfoDrawerDetail from '../components/user-info-drawer-detail.vue';
@@ -209,6 +209,7 @@ export default defineComponent({
},
setup(props) {
const fieldsStore = useFieldsStore();
const userStore = useUserStore();
const { primaryKey } = toRefs(props);
const { breadcrumb } = useBreadcrumb();
@@ -344,6 +345,7 @@ export default defineComponent({
async function saveAndQuit() {
await save();
await refreshCurrentUser();
router.push(`/users`);
}
@@ -361,6 +363,7 @@ export default defineComponent({
async function saveAndAddNew() {
await save();
await refreshCurrentUser();
router.push(`/users/+`);
}
@@ -374,6 +377,12 @@ export default defineComponent({
router.push(`/users`);
}
async function refreshCurrentUser() {
if (userStore.state.currentUser!.id === item.value.id) {
await userStore.hydrate();
}
}
function useUserPreview() {
const loading = ref(false);
const error = ref(null);