diff --git a/src/components/v-list/v-list-item-icon.vue b/src/components/v-list/v-list-item-icon.vue
index 8aca952a6f..e226e37971 100644
--- a/src/components/v-list/v-list-item-icon.vue
+++ b/src/components/v-list/v-list-item-icon.vue
@@ -46,6 +46,7 @@ export default defineComponent({
}
}
}
+
&.dense {
#{$this} {
margin-top: 4px;
@@ -60,6 +61,10 @@ export default defineComponent({
}
}
}
+
+ &.dense:not(.nav) #{$this} {
+ color: var(--foreground-subdued);
+ }
}
}
}
diff --git a/src/components/v-progress/circular/v-progress-circular.vue b/src/components/v-progress/circular/v-progress-circular.vue
index 8b1253f671..5a350c8ccd 100644
--- a/src/components/v-progress/circular/v-progress-circular.vue
+++ b/src/components/v-progress/circular/v-progress-circular.vue
@@ -67,8 +67,10 @@ export default defineComponent({
}
&.small {
- --v-progress-circular-size: 16px;
+ --v-progress-circular-size: 20px;
--v-progress-circular-line-size: 3px;
+
+ margin: 2px;
}
&.large {
diff --git a/src/components/v-table/readme.md b/src/components/v-table/readme.md
index 76182e8055..d4a0eee634 100644
--- a/src/components/v-table/readme.md
+++ b/src/components/v-table/readme.md
@@ -137,10 +137,11 @@ export default defineComponent({
| `select` | Emitted when selected items change | `any[]` |
## Slots
-| Slot | Description |
-|------------------|----------------------------------|
-| `header.[value]` | Override individual header cells |
-| `item.[value]` | Override individual row cells |
+| Slot | Description |
+|------------------|----------------------------------------------------|
+| `header.[value]` | Override individual header cells |
+| `item.[value]` | Override individual row cells |
+| `item-append` | Append content at the end of each row in the table |
## CSS Variables
| Variable | Default |
diff --git a/src/components/v-table/table-row/table-row.vue b/src/components/v-table/table-row/table-row.vue
index 2159e0f221..717e2c6462 100644
--- a/src/components/v-table/table-row/table-row.vue
+++ b/src/components/v-table/table-row/table-row.vue
@@ -25,7 +25,7 @@
|
-
+ |
|
@@ -146,6 +146,7 @@ export default defineComponent({
.append {
display: flex;
align-items: center;
+ justify-content: flex-end;
}
}
diff --git a/src/lang/en-US/index.json b/src/lang/en-US/index.json
index 7343ce0733..ce616939bc 100644
--- a/src/lang/en-US/index.json
+++ b/src/lang/en-US/index.json
@@ -179,6 +179,21 @@
"all_users": "All Users",
+ "delete_collection": "Delete Collection",
+ "unmanage_collection": "Unmanage Collection",
+
+ "update_collection_success": "Updated Collection",
+ "update_collection_failed": "Couldn't Update Collection",
+ "delete_collection_success": "Deleted Collection",
+ "delete_collection_failed": "Couldn't Delete Collection",
+
+ "delete_collection_are_you_sure": "Are you sure you want to delete this collection? This will delete the collection and all items in it. This action is permanent.",
+
+ "collections_shown": "Collections Shown",
+ "visible_collections": "Visible Collections",
+ "hidden_collections": "Hidden Collections",
+ "unmanaged_collections": "Unmanaged Collections",
+ "system_collections": "System Collections",
"about_directus": "About Directus",
"activity_log": "Activity Log",
@@ -326,7 +341,6 @@
"delete_are_you_sure": "This action is permanent and can not be undone. Are you sure you would like to proceed?",
"delete_bookmark": "Delete Bookmark",
"delete_bookmark_body": "Are you sure you want to delete this bookmark? This action cannot be undone.",
- "delete_collection_are_you_sure": "Are you sure you want to delete this collection? This action can not be undone.",
"delete_confirmation": "Delete Confirmation",
"delete_field_are_you_sure": "Are you sure you want to delete the field \"{field}\"? This action can not be undone.",
"delete_role_are_you_sure": "Are you sure to delete the role \"{name}\"? This action cannot be undone.",
diff --git a/src/modules/settings/routes/data-model/collections/collections.vue b/src/modules/settings/routes/data-model/collections/collections.vue
index 9a7587c525..8d90cc90dd 100644
--- a/src/modules/settings/routes/data-model/collections/collections.vue
+++ b/src/modules/settings/routes/data-model/collections/collections.vue
@@ -16,28 +16,59 @@
-
-
-
-
+
+
+
-
-
- {{ item.name }}
-
-
+
+
+
+
+
-
-
- {{ item.note }}
-
-
-
+
+
+ {{ item.name }}
+
+
+
+
+
+ {{ item.note }}
+
+
+
+
+
+
+
+
@@ -53,11 +84,16 @@ import useCollectionsStore from '@/stores/collections';
import { Collection } from '@/stores/collections/types';
import useProjectsStore from '@/stores/projects';
import router from '@/router';
+import { sortBy } from 'lodash';
+import CollectionOptions from './components/collection-options';
+import CollectionsFilter from './components/collections-filter';
export default defineComponent({
- components: { SettingsNavigation, NewCollection },
+ components: { SettingsNavigation, NewCollection, CollectionOptions, CollectionsFilter },
setup() {
const addNewActive = ref(false);
+ const activeTypes = ref(['visible', 'hidden', 'unmanaged']);
+
const collectionsStore = useCollectionsStore();
const tableHeaders = ref([
@@ -67,27 +103,104 @@ export default defineComponent({
sortable: false,
},
{
- text: i18n.tc('collection', 0),
+ text: i18n.t('name'),
value: 'collection',
},
{
- text: i18n.t('note'),
+ text: i18n.t('description'),
value: 'note',
},
]);
- const items = computed(() => {
- return collectionsStore.state.collections.filter(
- ({ collection }) => collection.startsWith('directus_') === false
- );
- });
-
- return { addNewActive, tableHeaders, items, openCollection };
-
function openCollection({ collection }: Collection) {
const { currentProjectKey } = useProjectsStore().state;
router.push(`/${currentProjectKey}/settings/data-model/${collection}`);
}
+
+ const { items } = useItems();
+
+ return {
+ addNewActive,
+ tableHeaders,
+ items,
+ openCollection,
+ activeTypes,
+ };
+
+ function useItems() {
+ const visible = computed(() => {
+ return sortBy(
+ collectionsStore.state.collections.filter(
+ (collection) =>
+ collection.collection.startsWith('directus_') === false &&
+ collection.managed === true &&
+ collection.hidden === false
+ ),
+ 'collection'
+ );
+ });
+
+ const hidden = computed(() => {
+ return sortBy(
+ collectionsStore.state.collections
+ .filter(
+ (collection) =>
+ collection.collection.startsWith('directus_') === false &&
+ collection.managed === true &&
+ collection.hidden === true
+ )
+ .map((collection) => ({ ...collection, icon: 'visibility_off' })),
+ 'collection'
+ );
+ });
+
+ const system = computed(() => {
+ return sortBy(
+ collectionsStore.state.collections
+ .filter(
+ (collection) => collection.collection.startsWith('directus_') === true
+ )
+ .map((collection) => ({ ...collection, icon: 'settings' })),
+ 'collection'
+ );
+ });
+
+ const unmanaged = computed(() => {
+ return sortBy(
+ collectionsStore.state.collections
+ .filter(
+ (collection) => collection.collection.startsWith('directus_') === false
+ )
+ .filter((collection) => collection.managed === false)
+ .map((collection) => ({ ...collection, icon: 'block' })),
+ 'collection'
+ );
+ });
+
+ const items = computed(() => {
+ const items = [];
+
+ if (activeTypes.value.includes('visible')) {
+ items.push(visible.value);
+ }
+
+ if (activeTypes.value.includes('hidden')) {
+ items.push(hidden.value);
+ }
+
+ if (activeTypes.value.includes('unmanaged')) {
+ items.push(unmanaged.value);
+ }
+
+ if (activeTypes.value.includes('system')) {
+ items.push(system.value);
+ }
+
+ return items.flat();
+ });
+
+ return { items };
+ }
},
});
@@ -101,10 +214,24 @@ export default defineComponent({
color: var(--foreground-subdued);
}
-.v-table {
+.system {
+ color: var(--primary);
+}
+
+.unmanaged {
+ color: var(--warning);
+}
+
+.padding-box {
padding: var(--content-padding);
}
+.v-table {
+ --v-table-sticky-offset-top: 64px;
+
+ display: contents;
+}
+
.header-icon {
--v-button-color-disabled: var(--warning);
--v-button-background-color-disabled: var(--warning-alt);
diff --git a/src/modules/settings/routes/data-model/collections/components/collection-options/collection-options.vue b/src/modules/settings/routes/data-model/collections/components/collection-options/collection-options.vue
new file mode 100644
index 0000000000..3ee55921dd
--- /dev/null
+++ b/src/modules/settings/routes/data-model/collections/components/collection-options/collection-options.vue
@@ -0,0 +1,129 @@
+
+
+ {{ $t('manage') }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('unmanage_collection') }}
+
+
+
+
+
+
+
+
+
+ {{ $t('delete_collection') }}
+
+
+
+
+ {{ $t('delete_collection_are_you_sure') }}
+
+
+ {{ $t('cancel') }}
+
+
+ {{ $t('delete_collection') }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/settings/routes/data-model/collections/components/collection-options/index.ts b/src/modules/settings/routes/data-model/collections/components/collection-options/index.ts
new file mode 100644
index 0000000000..f0ccedb809
--- /dev/null
+++ b/src/modules/settings/routes/data-model/collections/components/collection-options/index.ts
@@ -0,0 +1,4 @@
+import CollectionOptions from './collection-options.vue';
+
+export { CollectionOptions };
+export default CollectionOptions;
diff --git a/src/modules/settings/routes/data-model/collections/components/collections-filter/collections-filter.vue b/src/modules/settings/routes/data-model/collections/components/collections-filter/collections-filter.vue
new file mode 100644
index 0000000000..c7952c556f
--- /dev/null
+++ b/src/modules/settings/routes/data-model/collections/components/collections-filter/collections-filter.vue
@@ -0,0 +1,40 @@
+
+
+ {{ $t('collections_shown') }}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/settings/routes/data-model/collections/components/collections-filter/index.ts b/src/modules/settings/routes/data-model/collections/components/collections-filter/index.ts
new file mode 100644
index 0000000000..913f2c0c42
--- /dev/null
+++ b/src/modules/settings/routes/data-model/collections/components/collections-filter/index.ts
@@ -0,0 +1,4 @@
+import CollectionsFilter from './collections-filter.vue';
+
+export { CollectionsFilter };
+export default CollectionsFilter;
diff --git a/src/stores/collections/collections.ts b/src/stores/collections/collections.ts
index 4d340a82b1..b17454210f 100644
--- a/src/stores/collections/collections.ts
+++ b/src/stores/collections/collections.ts
@@ -6,6 +6,7 @@ import i18n from '@/lang/';
import { notEmpty } from '@/utils/is-empty/';
import VueI18n from 'vue-i18n';
import formatTitle from '@directus/format-title';
+import notify from '@/utils/notify';
export const useCollectionsStore = createStore({
id: 'collectionsStore',
@@ -58,6 +59,46 @@ export const useCollectionsStore = createStore({
async dehydrate() {
this.reset();
},
+ async updateCollection(collection: string, updates: Partial) {
+ const { currentProjectKey } = useProjectsStore().state;
+
+ try {
+ await api.patch(`${currentProjectKey}/collections/${collection}`, updates);
+ await this.hydrate();
+ notify({
+ type: 'success',
+ title: i18n.t('update_collection_success'),
+ text: collection,
+ });
+ } catch (error) {
+ notify({
+ type: 'error',
+ title: i18n.t('update_collection_failed'),
+ text: collection,
+ });
+ throw error;
+ }
+ },
+ async deleteCollection(collection: string) {
+ const { currentProjectKey } = useProjectsStore().state;
+
+ try {
+ await api.delete(`${currentProjectKey}/collections/${collection}`);
+ await this.hydrate();
+ notify({
+ type: 'success',
+ title: i18n.t('delete_collection_success'),
+ text: collection,
+ });
+ } catch (error) {
+ notify({
+ type: 'error',
+ title: i18n.t('delete_collection_failed'),
+ text: collection,
+ });
+ throw error;
+ }
+ },
getCollection(collectionKey: string) {
return (
this.state.collections.find(