Add files module (#361)

This commit is contained in:
Rijk van Zanten
2020-04-08 13:26:43 -04:00
committed by GitHub
parent a9bfa469d9
commit 442ad58ff6
9 changed files with 420 additions and 2 deletions

View File

@@ -122,7 +122,11 @@ export function useItems(collection: Ref<string>, options: Options) {
const { currentProjectKey } = projectsStore.state;
const response = await api.get(`/${currentProjectKey}/items/${collection.value}`, {
const endpoint = collection.value.startsWith('directus_')
? `/${currentProjectKey}/${collection.value.substring(9)}`
: `/${currentProjectKey}/items/${collection.value}`;
const response = await api.get(endpoint, {
params: {
limit: 0,
fields: primaryKeyField.value.field,

View File

@@ -0,0 +1,4 @@
import FilesNavigation from './navigation.vue';
export { FilesNavigation };
export default FilesNavigation;

View File

@@ -0,0 +1,3 @@
<template>
<div>files nav</div>
</template>

View File

@@ -0,0 +1,23 @@
import { defineModule } from '@/modules/define';
import FilesBrowse from './routes/browse/';
import FilesDetail from './routes/detail/';
export default defineModule(({ i18n }) => ({
id: 'files',
name: i18n.t('files'),
icon: 'folder',
routes: [
{
name: 'files-browse',
path: '/',
component: FilesBrowse,
props: true,
},
{
name: 'files-detail',
path: '/:primaryKey',
component: FilesDetail,
props: true,
},
],
}));

View File

@@ -0,0 +1,178 @@
<template>
<private-view :title="$t('files')">
<template #title-outer:prepend>
<v-button rounded disabled icon secondary>
<v-icon name="folder" />
</v-button>
</template>
<template #drawer><portal-target name="drawer" /></template>
<template #actions>
<v-dialog v-model="confirmDelete">
<template #activator="{ on }">
<v-button
rounded
icon
class="action-delete"
v-if="selection.length > 0"
@click="on"
>
<v-icon name="delete" />
</v-button>
</template>
<v-card>
<v-card-title>{{ $tc('batch_delete_confirm', selection.length) }}</v-card-title>
<v-card-actions>
<v-button @click="confirmDelete = false" secondary>
{{ $t('cancel') }}
</v-button>
<v-button @click="batchDelete" class="action-delete" :loading="deleting">
{{ $t('delete') }}
</v-button>
</v-card-actions>
</v-card>
</v-dialog>
<v-button rounded icon class="action-batch" v-if="selection.length > 1" :to="batchLink">
<v-icon name="edit" />
</v-button>
<v-button rounded icon :to="addNewLink">
<v-icon name="add" />
</v-button>
</template>
<template #navigation>
<files-navigation />
</template>
<layout-tabular
class="layout"
ref="layout"
collection="directus_files"
:selection.sync="selection"
:view-options.sync="viewOptions"
:view-query.sync="viewQuery"
:detail-route="'/{{project}}/files/{{primaryKey}}'"
/>
</private-view>
</template>
<script lang="ts">
import { defineComponent, computed, ref } from '@vue/composition-api';
import FilesNavigation from '../../components/navigation/';
import useProjectsStore from '@/stores/projects';
import { i18n } from '@/lang';
import api from '@/api';
import { LayoutComponent } from '@/layouts/types';
import useCollectionPreset from '@/compositions/use-collection-preset';
type Item = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[field: string]: any;
};
export default defineComponent({
name: 'files-browse',
components: { FilesNavigation },
props: {},
setup() {
const layout = ref<LayoutComponent>(null);
const projectsStore = useProjectsStore();
const selection = ref<Item[]>([]);
const { viewOptions, viewQuery } = useCollectionPreset(ref('directus_files'));
const { addNewLink, batchLink } = useLinks();
const { confirmDelete, deleting, batchDelete } = useBatchDelete();
const { breadcrumb } = useBreadcrumb();
return {
addNewLink,
batchLink,
selection,
breadcrumb,
confirmDelete,
batchDelete,
deleting,
layout,
viewOptions,
viewQuery,
};
function useBatchDelete() {
const confirmDelete = ref(false);
const deleting = ref(false);
return { confirmDelete, deleting, batchDelete };
async function batchDelete() {
const currentProjectKey = projectsStore.state.currentProjectKey;
deleting.value = true;
confirmDelete.value = false;
const batchPrimaryKeys = selection.value.map((item) => item.id).join();
await api.delete(`/${currentProjectKey}/files/${batchPrimaryKeys}`);
await layout.value?.refresh();
selection.value = [];
deleting.value = false;
confirmDelete.value = false;
}
}
function useLinks() {
const addNewLink = computed<string>(() => {
const currentProjectKey = projectsStore.state.currentProjectKey;
return `/${currentProjectKey}/files/+`;
});
const batchLink = computed<string>(() => {
const currentProjectKey = projectsStore.state.currentProjectKey;
const batchPrimaryKeys = selection.value.map((item) => item.id).join();
return `/${currentProjectKey}/files/${batchPrimaryKeys}`;
});
return { addNewLink, batchLink };
}
function useBreadcrumb() {
const breadcrumb = computed(() => {
const currentProjectKey = projectsStore.state.currentProjectKey;
return [
{
name: i18n.tc('collection', 2),
to: `/${currentProjectKey}/collections`,
},
];
});
return { breadcrumb };
}
},
});
</script>
<style lang="scss" scoped>
.action-delete {
--v-button-background-color: var(--danger);
--v-button-background-color-hover: var(--danger-dark);
}
.action-batch {
--v-button-background-color: var(--warning);
--v-button-background-color-hover: var(--warning-150);
}
.layout {
--layout-offset-top: 64px;
}
</style>

View File

@@ -0,0 +1,4 @@
import FilesBrowse from './browse.vue';
export { FilesBrowse };
export default FilesBrowse;

View File

@@ -0,0 +1,197 @@
<template>
<private-view :title="$t('editing', { collection: $t('files') })">
<template #title-outer:prepend>
<v-button rounded icon secondary exact :to="breadcrumb[0].to">
<v-icon name="arrow_back" />
</v-button>
</template>
<template #headline>
<v-breadcrumb :items="breadcrumb" />
</template>
<template #actions>
<v-dialog v-model="confirmDelete">
<template #activator="{ on }">
<v-button
rounded
icon
class="action-delete"
:disabled="item === null"
@click="on"
>
<v-icon name="delete" />
</v-button>
</template>
<v-card>
<v-card-title>{{ $t('delete_are_you_sure') }}</v-card-title>
<v-card-actions>
<v-button @click="confirmDelete = false" secondary>
{{ $t('cancel') }}
</v-button>
<v-button @click="deleteAndQuit" class="action-delete" :loading="deleting">
{{ $t('delete') }}
</v-button>
</v-card-actions>
</v-card>
</v-dialog>
<v-button
rounded
icon
:loading="saving"
:disabled="hasEdits === false"
@click="saveAndQuit"
>
<v-icon name="check" />
<template #append-outer>
<save-options
:disabled="hasEdits === false"
@save-and-stay="saveAndStay"
@save-and-add-new="saveAndAddNew"
@save-as-copy="saveAsCopyAndNavigate"
/>
</template>
</v-button>
</template>
<template #navigation>
<files-navigation />
</template>
<v-form
:loading="loading"
:initial-values="item"
collection="directus_files"
:batch-mode="isBatch"
v-model="edits"
/>
<template #drawer>
<activity-drawer-detail
v-if="isNew === false"
collection="directus_files"
:primary-key="primaryKey"
/>
</template>
</private-view>
</template>
<script lang="ts">
import { defineComponent, computed, toRefs, ref } from '@vue/composition-api';
import useProjectsStore from '@/stores/projects';
import FilesNavigation from '../../components/navigation/';
import { i18n } from '@/lang';
import router from '@/router';
import ActivityDrawerDetail from '@/views/private/components/activity-drawer-detail';
import useItem from '@/compositions/use-item';
import SaveOptions from '@/views/private/components/save-options';
type Values = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[field: string]: any;
};
export default defineComponent({
name: 'collections-detail',
components: { FilesNavigation, ActivityDrawerDetail, SaveOptions },
props: {
primaryKey: {
type: String,
required: true,
},
},
setup(props) {
const projectsStore = useProjectsStore();
const { currentProjectKey } = toRefs(projectsStore.state);
const { primaryKey } = toRefs(props);
const { breadcrumb } = useBreadcrumb();
const {
isNew,
edits,
item,
saving,
loading,
error,
save,
remove,
deleting,
saveAsCopy,
isBatch,
} = useItem(ref('directus_files'), primaryKey);
const hasEdits = computed<boolean>(() => Object.keys(edits.value).length > 0);
const confirmDelete = ref(false);
return {
item,
loading,
error,
isNew,
breadcrumb,
edits,
hasEdits,
saving,
saveAndQuit,
deleteAndQuit,
confirmDelete,
deleting,
saveAndStay,
saveAndAddNew,
saveAsCopyAndNavigate,
isBatch,
};
function useBreadcrumb() {
const breadcrumb = computed(() => [
{
name: i18n.t('files'),
to: `/${currentProjectKey.value}/files/`,
},
]);
return { breadcrumb };
}
async function saveAndQuit() {
await save();
router.push(`/${currentProjectKey.value}/files`);
}
async function saveAndStay() {
await save();
}
async function saveAndAddNew() {
await save();
router.push(`/${currentProjectKey.value}/files/+`);
}
async function saveAsCopyAndNavigate() {
const newPrimaryKey = await saveAsCopy();
router.push(`/${currentProjectKey.value}/files/${newPrimaryKey}`);
}
async function deleteAndQuit() {
await remove();
router.push(`/${currentProjectKey.value}/files`);
}
},
});
</script>
<style lang="scss" scoped>
.action-delete {
--v-button-background-color: var(--danger);
--v-button-background-color-hover: var(--danger-dark);
}
.v-form {
padding: var(--content-padding);
}
</style>

View File

@@ -0,0 +1,4 @@
import FilesDetail from './detail.vue';
export { FilesDetail };
export default FilesDetail;

View File

@@ -1,6 +1,7 @@
import CollectionsModule from './collections/';
import FilesModule from './files/';
import UsersModule from './users/';
import SettingsModule from './settings/';
export const modules = [CollectionsModule, UsersModule, SettingsModule];
export const modules = [CollectionsModule, UsersModule, FilesModule, SettingsModule];
export default modules;