mirror of
https://github.com/directus/directus.git
synced 2026-04-25 03:00:53 -04:00
Merge pull request #759 from directus/error-handling
Add error handling
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, PropType, ref } from '@vue/composition-api';
|
||||
import { isPlainObject } from 'lodash';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@@ -35,7 +36,9 @@ export default defineComponent({
|
||||
|
||||
async function copyError() {
|
||||
const error = props.error?.response?.data || props.error;
|
||||
await navigator.clipboard.writeText(JSON.stringify(error, null, 2));
|
||||
await navigator.clipboard.writeText(
|
||||
JSON.stringify(error, isPlainObject(error) ? null : Object.getOwnPropertyNames(error), 2)
|
||||
);
|
||||
copied.value = true;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -92,6 +92,7 @@ import uploadFile from '@/utils/upload-file';
|
||||
import DrawerCollection from '@/views/private/components/drawer-collection';
|
||||
import api from '@/api';
|
||||
import useItem from '@/composables/use-item';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
components: { DrawerCollection },
|
||||
@@ -118,16 +119,15 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const { uploading, progress, error, upload, onBrowseSelect, done, numberOfFiles } = useUpload();
|
||||
const { uploading, progress, upload, onBrowseSelect, done, numberOfFiles } = useUpload();
|
||||
const { onDragEnter, onDragLeave, onDrop, dragging } = useDragging();
|
||||
const { url, isValidURL, loading: urlLoading, error: urlError, importFromURL } = useURLImport();
|
||||
const { url, isValidURL, loading: urlLoading, importFromURL } = useURLImport();
|
||||
const { setSelection } = useSelection();
|
||||
const activeDialog = ref<'choose' | 'url' | null>(null);
|
||||
|
||||
return {
|
||||
uploading,
|
||||
progress,
|
||||
error,
|
||||
onDragEnter,
|
||||
onDragLeave,
|
||||
onDrop,
|
||||
@@ -148,14 +148,12 @@ export default defineComponent({
|
||||
const progress = ref(0);
|
||||
const numberOfFiles = ref(0);
|
||||
const done = ref(0);
|
||||
const error = ref(null);
|
||||
|
||||
return { uploading, progress, error, upload, onBrowseSelect, numberOfFiles, done };
|
||||
return { uploading, progress, upload, onBrowseSelect, numberOfFiles, done };
|
||||
|
||||
async function upload(files: FileList) {
|
||||
uploading.value = true;
|
||||
progress.value = 0;
|
||||
error.value = null;
|
||||
|
||||
try {
|
||||
numberOfFiles.value = files.length;
|
||||
@@ -185,8 +183,7 @@ export default defineComponent({
|
||||
uploadedFile && emit('input', uploadedFile);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
uploading.value = false;
|
||||
done.value = 0;
|
||||
@@ -255,7 +252,6 @@ export default defineComponent({
|
||||
function useURLImport() {
|
||||
const url = ref('');
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
|
||||
const isValidURL = computed(() => {
|
||||
try {
|
||||
@@ -266,7 +262,7 @@ export default defineComponent({
|
||||
}
|
||||
});
|
||||
|
||||
return { url, loading, error, isValidURL, importFromURL };
|
||||
return { url, loading, isValidURL, importFromURL };
|
||||
|
||||
async function importFromURL() {
|
||||
loading.value = true;
|
||||
@@ -280,7 +276,7 @@ export default defineComponent({
|
||||
activeDialog.value = null;
|
||||
url.value = '';
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import api from '@/api';
|
||||
import { Ref, ref, watch, computed } from '@vue/composition-api';
|
||||
import notify from '@/utils/notify';
|
||||
import i18n from '@/lang';
|
||||
import useCollection from '@/composables/use-collection';
|
||||
import { AxiosResponse } from 'axios';
|
||||
import { APIError } from '@/types';
|
||||
import { notify } from '@/utils/notify';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export function useItem(collection: Ref<string>, primaryKey: Ref<string | number | null>) {
|
||||
const { info: collectionInfo, primaryKeyField } = useCollection(collection);
|
||||
@@ -108,33 +109,15 @@ export function useItem(collection: Ref<string>, primaryKey: Ref<string | number
|
||||
edits.value = {};
|
||||
return response.data.data;
|
||||
} catch (err) {
|
||||
if (isNew.value) {
|
||||
notify({
|
||||
title: i18n.tc('item_create_failed', isBatch.value ? 2 : 1),
|
||||
type: 'error',
|
||||
});
|
||||
} else {
|
||||
notify({
|
||||
title: i18n.tc('item_update_failed', isBatch.value ? 2 : 1),
|
||||
text: i18n.tc('item_in', isBatch.value ? 2 : 1, {
|
||||
collection: collection.value,
|
||||
primaryKey: isBatch.value
|
||||
? (primaryKey.value as string).split(',').join(', ')
|
||||
: primaryKey.value,
|
||||
}),
|
||||
type: 'error',
|
||||
});
|
||||
}
|
||||
|
||||
if (err?.response?.data?.errors) {
|
||||
validationErrors.value = err.response.data.errors
|
||||
.filter((err: APIError) => err.extensions.code === 'FAILED_VALIDATION')
|
||||
.filter((err: APIError) => err?.extensions?.code === 'FAILED_VALIDATION')
|
||||
.map((err: APIError) => {
|
||||
return err.extensions;
|
||||
});
|
||||
} else {
|
||||
unexpectedError(err);
|
||||
}
|
||||
|
||||
throw err;
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
@@ -164,23 +147,14 @@ export function useItem(collection: Ref<string>, primaryKey: Ref<string | number
|
||||
|
||||
return primaryKeyField.value ? response.data.data[primaryKeyField.value.field] : null;
|
||||
} catch (err) {
|
||||
notify({
|
||||
title: i18n.t('item_create_failed'),
|
||||
text: i18n.tc('item_in', isBatch.value ? 2 : 1, {
|
||||
collection: collection.value,
|
||||
primaryKey: isBatch.value ? (primaryKey.value as string).split(',').join(', ') : primaryKey.value,
|
||||
}),
|
||||
type: 'error',
|
||||
});
|
||||
|
||||
if (err?.response?.data?.errors) {
|
||||
validationErrors.value = err.response.data.errors
|
||||
.filter((err: APIError) => err.extensions.code === 'FAILED_VALIDATION')
|
||||
.filter((err: APIError) => err?.extensions?.code === 'FAILED_VALIDATION')
|
||||
.map((err: APIError) => {
|
||||
return err.extensions;
|
||||
});
|
||||
} else {
|
||||
throw err;
|
||||
unexpectedError(err);
|
||||
}
|
||||
} finally {
|
||||
saving.value = false;
|
||||
@@ -222,16 +196,7 @@ export function useItem(collection: Ref<string>, primaryKey: Ref<string | number
|
||||
type: 'success',
|
||||
});
|
||||
} catch (err) {
|
||||
notify({
|
||||
title: i18n.tc('item_delete_failed', isBatch.value ? 2 : 1),
|
||||
text: i18n.tc('item_in', isBatch.value ? 2 : 1, {
|
||||
collection: collection.value,
|
||||
primaryKey: isBatch.value ? (primaryKey.value as string).split(',').join(', ') : primaryKey.value,
|
||||
}),
|
||||
type: 'error',
|
||||
});
|
||||
|
||||
throw err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
archiving.value = false;
|
||||
}
|
||||
@@ -250,16 +215,7 @@ export function useItem(collection: Ref<string>, primaryKey: Ref<string | number
|
||||
type: 'success',
|
||||
});
|
||||
} catch (err) {
|
||||
notify({
|
||||
title: i18n.tc('item_delete_failed', isBatch.value ? 2 : 1),
|
||||
text: i18n.tc('item_in', isBatch.value ? 2 : 1, {
|
||||
collection: collection.value,
|
||||
primaryKey: isBatch.value ? (primaryKey.value as string).split(',').join(', ') : primaryKey.value,
|
||||
}),
|
||||
type: 'error',
|
||||
});
|
||||
|
||||
throw err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
deleting.value = false;
|
||||
}
|
||||
|
||||
@@ -17,19 +17,23 @@ export async function registerDisplays() {
|
||||
try {
|
||||
const customResponse = await api.get('/extensions/displays');
|
||||
|
||||
if (customResponse.data.data && Array.isArray(customResponse.data.data) && customResponse.data.data.length > 0) {
|
||||
if (
|
||||
customResponse.data.data &&
|
||||
Array.isArray(customResponse.data.data) &&
|
||||
customResponse.data.data.length > 0
|
||||
) {
|
||||
for (const customKey of customResponse.data.data) {
|
||||
try {
|
||||
const module = await import(/* webpackIgnore: true */ `/extensions/displays/${customKey}/index.js`);
|
||||
modules.push(module.default);
|
||||
} catch (err) {
|
||||
console.error(`Couldn't load custom displays "${customKey}"`);
|
||||
console.error(err);
|
||||
console.warn(`Couldn't load custom displays "${customKey}"`);
|
||||
console.warn(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
console.error(`Couldn't load custom displays`);
|
||||
console.warn(`Couldn't load custom displays`);
|
||||
}
|
||||
|
||||
displays.value = modules;
|
||||
|
||||
@@ -116,6 +116,7 @@ import DrawerCollection from '@/views/private/components/drawer-collection';
|
||||
import api from '@/api';
|
||||
import readableMimeType from '@/utils/readable-mime-type';
|
||||
import getRootPath from '@/utils/get-root-path';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
type FileInfo = {
|
||||
id: number;
|
||||
@@ -137,7 +138,7 @@ export default defineComponent({
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const activeDialog = ref<'upload' | 'choose' | 'url' | null>(null);
|
||||
const { loading, error, file, fetchFile } = useFile();
|
||||
const { loading, file, fetchFile } = useFile();
|
||||
|
||||
watch(() => props.value, fetchFile, { immediate: true });
|
||||
|
||||
@@ -155,20 +156,18 @@ export default defineComponent({
|
||||
return assetURL.value + `?key=system-small-cover`;
|
||||
});
|
||||
|
||||
const { url, isValidURL, loading: urlLoading, error: urlError, importFromURL } = useURLImport();
|
||||
const { url, isValidURL, loading: urlLoading, importFromURL } = useURLImport();
|
||||
|
||||
return {
|
||||
activeDialog,
|
||||
setSelection,
|
||||
loading,
|
||||
error,
|
||||
file,
|
||||
fileExtension,
|
||||
imageThumbnail,
|
||||
onUpload,
|
||||
url,
|
||||
urlLoading,
|
||||
urlError,
|
||||
importFromURL,
|
||||
isValidURL,
|
||||
assetURL,
|
||||
@@ -176,16 +175,14 @@ export default defineComponent({
|
||||
|
||||
function useFile() {
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
const file = ref<FileInfo | null>(null);
|
||||
|
||||
return { loading, error, file, fetchFile };
|
||||
return { loading, file, fetchFile };
|
||||
|
||||
async function fetchFile() {
|
||||
if (props.value === null) {
|
||||
file.value = null;
|
||||
loading.value = false;
|
||||
error.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -200,7 +197,7 @@ export default defineComponent({
|
||||
|
||||
file.value = response.data.data;
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
@@ -224,7 +221,6 @@ export default defineComponent({
|
||||
function useURLImport() {
|
||||
const url = ref('');
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
|
||||
const isValidURL = computed(() => {
|
||||
try {
|
||||
@@ -235,7 +231,7 @@ export default defineComponent({
|
||||
}
|
||||
});
|
||||
|
||||
return { url, loading, error, isValidURL, importFromURL };
|
||||
return { url, loading, isValidURL, importFromURL };
|
||||
|
||||
async function importFromURL() {
|
||||
loading.value = true;
|
||||
@@ -251,7 +247,7 @@ export default defineComponent({
|
||||
url.value = '';
|
||||
emit('input', file.value?.id);
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
@@ -56,9 +56,9 @@ import formatFilesize from '@/utils/format-filesize';
|
||||
import i18n from '@/lang';
|
||||
import FileLightbox from '@/views/private/components/file-lightbox';
|
||||
import ImageEditor from '@/views/private/components/image-editor';
|
||||
|
||||
import { nanoid } from 'nanoid';
|
||||
import getRootPath from '@/utils/get-root-path';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
type Image = {
|
||||
id: string; // uuid
|
||||
@@ -84,7 +84,6 @@ export default defineComponent({
|
||||
setup(props, { emit }) {
|
||||
const loading = ref(false);
|
||||
const image = ref<Image | null>(null);
|
||||
const error = ref(null);
|
||||
const lightboxActive = ref(false);
|
||||
const editorActive = ref(false);
|
||||
|
||||
@@ -136,7 +135,6 @@ export default defineComponent({
|
||||
return {
|
||||
loading,
|
||||
image,
|
||||
error,
|
||||
src,
|
||||
meta,
|
||||
lightboxActive,
|
||||
@@ -159,7 +157,7 @@ export default defineComponent({
|
||||
|
||||
image.value = response.data.data;
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
@@ -179,7 +177,6 @@ export default defineComponent({
|
||||
|
||||
loading.value = false;
|
||||
image.value = null;
|
||||
error.value = null;
|
||||
lightboxActive.value = false;
|
||||
editorActive.value = false;
|
||||
}
|
||||
|
||||
@@ -109,6 +109,7 @@ import getFieldsFromTemplate from '@/utils/get-fields-from-template';
|
||||
import api from '@/api';
|
||||
import DrawerItem from '@/views/private/components/drawer-item';
|
||||
import DrawerCollection from '@/views/private/components/drawer-collection';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
/**
|
||||
* @NOTE
|
||||
@@ -192,7 +193,6 @@ export default defineComponent({
|
||||
function useCurrent() {
|
||||
const currentItem = ref<Record<string, any> | null>(null);
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
@@ -262,7 +262,7 @@ export default defineComponent({
|
||||
|
||||
currentItem.value = response.data.data;
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
@@ -274,7 +274,6 @@ export default defineComponent({
|
||||
|
||||
const items = ref<Record<string, any>[] | null>(null);
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
|
||||
watch(relatedCollection, () => {
|
||||
fetchTotalCount();
|
||||
@@ -308,7 +307,7 @@ export default defineComponent({
|
||||
|
||||
items.value = response.data.data;
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ import { Filter, Field } from '@/types';
|
||||
import { Header, Sort } from '@/components/v-table/types';
|
||||
import { isEqual, sortBy } from 'lodash';
|
||||
import { get } from 'lodash';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
components: { DrawerItem, DrawerCollection },
|
||||
@@ -114,7 +115,7 @@ export default defineComponent({
|
||||
const fieldsStore = useFieldsStore();
|
||||
|
||||
const { relation, relatedCollection, relatedPrimaryKeyField } = useRelation();
|
||||
const { tableHeaders, items, loading, error } = useTable();
|
||||
const { tableHeaders, items, loading } = useTable();
|
||||
const { currentlyEditing, editItem, editsAtStart, stageEdits, cancelEdit } = useEdits();
|
||||
const { stageSelection, selectModalActive, selectionFilters } = useSelection();
|
||||
const { sort, sortItems, sortedItems } = useSort();
|
||||
@@ -263,7 +264,6 @@ export default defineComponent({
|
||||
const tableHeaders = ref<Header[]>([]);
|
||||
const loading = ref(false);
|
||||
const items = ref<Record<string, any>[]>([]);
|
||||
const error = ref(null);
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
@@ -318,7 +318,7 @@ export default defineComponent({
|
||||
})
|
||||
.concat(...newItems);
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
@@ -360,7 +360,7 @@ export default defineComponent({
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
return { tableHeaders, items, loading, error };
|
||||
return { tableHeaders, items, loading };
|
||||
}
|
||||
|
||||
function useEdits() {
|
||||
|
||||
@@ -17,19 +17,25 @@ export async function registerInterfaces() {
|
||||
try {
|
||||
const customResponse = await api.get('/extensions/interfaces');
|
||||
|
||||
if (customResponse.data.data && Array.isArray(customResponse.data.data) && customResponse.data.data.length > 0) {
|
||||
if (
|
||||
customResponse.data.data &&
|
||||
Array.isArray(customResponse.data.data) &&
|
||||
customResponse.data.data.length > 0
|
||||
) {
|
||||
for (const customKey of customResponse.data.data) {
|
||||
try {
|
||||
const module = await import(/* webpackIgnore: true */ `/extensions/interfaces/${customKey}/index.js`);
|
||||
const module = await import(
|
||||
/* webpackIgnore: true */ `/extensions/interfaces/${customKey}/index.js`
|
||||
);
|
||||
modules.push(module.default);
|
||||
} catch (err) {
|
||||
console.error(`Couldn't load custom interface "${customKey}"`);
|
||||
console.error(err);
|
||||
console.warn(`Couldn't load custom interface "${customKey}"`);
|
||||
console.warn(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
console.error(`Couldn't load custom interfaces`);
|
||||
console.warn(`Couldn't load custom interfaces`);
|
||||
}
|
||||
|
||||
interfaces.value = modules;
|
||||
|
||||
@@ -36,6 +36,7 @@ import { Relation } from '@/types';
|
||||
import getFieldsFromTemplate from '@/utils/get-fields-from-template';
|
||||
import DrawerItem from '@/views/private/components/drawer-item/drawer-item.vue';
|
||||
import { useCollection } from '../../composables/use-collection';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
components: { DrawerItem },
|
||||
@@ -197,7 +198,7 @@ export default defineComponent({
|
||||
const response = await api.get(`/items/${languagesCollection.value}`, { params: { fields } });
|
||||
languages.value = response.data.data;
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@ import api from '@/api';
|
||||
import DrawerItem from '@/views/private/components/drawer-item';
|
||||
import DrawerCollection from '@/views/private/components/drawer-collection';
|
||||
import { userName } from '@/utils/user-name';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
components: { DrawerItem, DrawerCollection },
|
||||
@@ -148,7 +149,6 @@ export default defineComponent({
|
||||
function useCurrent() {
|
||||
const currentUser = ref<Record<string, any> | null>(null);
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
|
||||
watch(
|
||||
() => props.value,
|
||||
@@ -195,7 +195,7 @@ export default defineComponent({
|
||||
|
||||
currentUser.value = response.data.data;
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
@@ -207,7 +207,6 @@ export default defineComponent({
|
||||
|
||||
const users = ref<Record<string, any>[] | null>(null);
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
|
||||
fetchTotalCount();
|
||||
users.value = null;
|
||||
@@ -231,7 +230,7 @@ export default defineComponent({
|
||||
|
||||
users.value = response.data.data;
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
@@ -612,6 +612,7 @@
|
||||
"ITEM_LIMIT_REACHED": "Item limit reached.",
|
||||
"ITEM_NOT_FOUND": "Item not found.",
|
||||
"ROUTE_NOT_FOUND": "Not found.",
|
||||
"UNKNOWN": "Unexpected Error",
|
||||
"-1": "Couldn't Reach API"
|
||||
},
|
||||
|
||||
@@ -1017,10 +1018,10 @@
|
||||
"ip": "IP Address",
|
||||
"user_agent": "User Agent",
|
||||
"comment": "Comment"
|
||||
},
|
||||
"directus_collections": {
|
||||
"translations": "Collection Name Translations"
|
||||
},
|
||||
},
|
||||
"directus_collections": {
|
||||
"translations": "Collection Name Translations"
|
||||
},
|
||||
"directus_files": {
|
||||
"title": "Title",
|
||||
"tags": "Tags",
|
||||
@@ -1053,55 +1054,51 @@
|
||||
"avatar": "Avatar",
|
||||
"theme": "Theme",
|
||||
"tfa_secret": "Two-Factor Authentication"
|
||||
},
|
||||
"directus_settings": {
|
||||
"project_name": "Project Name",
|
||||
"project_url": "Project URL",
|
||||
"project_color": "Project Color",
|
||||
"project_logo": "Project Logo",
|
||||
"public_foreground": "Public Foreground",
|
||||
"public_background": "Public Background",
|
||||
"public_note": "Public Note",
|
||||
"auth_password_policy": "Auth Password Policy",
|
||||
"auth_idle_timeout": "Auth Idle Timeout",
|
||||
"auth_login_attempts": "Auth Login Attempts",
|
||||
"storage_asset_presets": "Storage Asset Presets",
|
||||
"storage_asset_transform": "Storage Asset Transform",
|
||||
"telemetry": "Telemetry"
|
||||
},
|
||||
"directus_fields": {
|
||||
"collection": "Collection Name",
|
||||
"icon": "Collection Icon",
|
||||
"note": "Note",
|
||||
"hidden": "Hidden",
|
||||
"singleton": "Singleton",
|
||||
"translation": "Field Name Translations",
|
||||
"display_template": "Template"
|
||||
},
|
||||
"directus_roles": {
|
||||
"name": "Role Name",
|
||||
"icon": "Role Icon",
|
||||
"description": "Description",
|
||||
"ip_access": "IP Access",
|
||||
"app_access": "App Access",
|
||||
"admin_access": "Admin Access",
|
||||
"enforce_tfa": "Require 2FA",
|
||||
"users": "Users in Role",
|
||||
"module_list": "Module Navigation",
|
||||
"collection_list": "Collection Navigation"
|
||||
}
|
||||
},
|
||||
"directus_settings": {
|
||||
"project_name": "Project Name",
|
||||
"project_url": "Project URL",
|
||||
"project_color": "Project Color",
|
||||
"project_logo": "Project Logo",
|
||||
"public_foreground": "Public Foreground",
|
||||
"public_background": "Public Background",
|
||||
"public_note": "Public Note",
|
||||
"auth_password_policy": "Auth Password Policy",
|
||||
"auth_idle_timeout": "Auth Idle Timeout",
|
||||
"auth_login_attempts": "Auth Login Attempts",
|
||||
"storage_asset_presets": "Storage Asset Presets",
|
||||
"storage_asset_transform": "Storage Asset Transform",
|
||||
"telemetry": "Telemetry"
|
||||
},
|
||||
"directus_fields": {
|
||||
"collection": "Collection Name",
|
||||
"icon": "Collection Icon",
|
||||
"note": "Note",
|
||||
"hidden": "Hidden",
|
||||
"singleton": "Singleton",
|
||||
"translation": "Field Name Translations",
|
||||
"display_template": "Template"
|
||||
},
|
||||
"directus_roles": {
|
||||
"name": "Role Name",
|
||||
"icon": "Role Icon",
|
||||
"description": "Description",
|
||||
"ip_access": "IP Access",
|
||||
"app_access": "App Access",
|
||||
"admin_access": "Admin Access",
|
||||
"enforce_tfa": "Require 2FA",
|
||||
"users": "Users in Role",
|
||||
"module_list": "Module Navigation",
|
||||
"collection_list": "Collection Navigation"
|
||||
}
|
||||
},
|
||||
|
||||
"no_fields_in_collection": "There are no fields in \"{collection}\" yet",
|
||||
|
||||
"do_nothing": "Do Nothing",
|
||||
"generate_and_save_uuid": "Generate and Save UUID",
|
||||
"save_current_user_id": "Save Current User ID",
|
||||
"save_current_user_role": "Save Current User Role",
|
||||
"save_current_datetime": "Save Current Date/Time",
|
||||
|
||||
"modules": {},
|
||||
|
||||
"coming_soon": "Coming Soon",
|
||||
"comment": "Comment",
|
||||
"config_error": "Missing Config",
|
||||
@@ -1150,7 +1147,6 @@
|
||||
"db_updated": "Database successfully updated",
|
||||
"default": "Default",
|
||||
"delete": "Delete",
|
||||
|
||||
"delete_are_you_sure": "This action is permanent and can not be undone. Are you sure you would like to proceed?",
|
||||
"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.",
|
||||
@@ -1232,12 +1228,12 @@
|
||||
"uuid": "A Universally Unique Identifier"
|
||||
},
|
||||
"js_types": {
|
||||
"object": "Object",
|
||||
"array": "Array",
|
||||
"string": "String",
|
||||
"number": "Number",
|
||||
"boolean": "Boolean",
|
||||
"undefined": "Undefined"
|
||||
"object": "Object",
|
||||
"array": "Array",
|
||||
"string": "String",
|
||||
"number": "Number",
|
||||
"boolean": "Boolean",
|
||||
"undefined": "Undefined"
|
||||
},
|
||||
"file": "File",
|
||||
"file_library": "File Library",
|
||||
@@ -1426,5 +1422,6 @@
|
||||
"wrapping_up": "Wrapping Up",
|
||||
"wrong_super_admin_password": "The super admin password you provided is incorrect.",
|
||||
"writable_fields_copy": "Select the fields that the user can edit",
|
||||
"yes": "Yes"
|
||||
"yes": "Yes",
|
||||
"report_error": "Report Error"
|
||||
}
|
||||
|
||||
@@ -16,19 +16,23 @@ export async function registerLayouts() {
|
||||
try {
|
||||
const customResponse = await api.get('/extensions/layouts');
|
||||
|
||||
if (customResponse.data.data && Array.isArray(customResponse.data.data) && customResponse.data.data.length > 0) {
|
||||
if (
|
||||
customResponse.data.data &&
|
||||
Array.isArray(customResponse.data.data) &&
|
||||
customResponse.data.data.length > 0
|
||||
) {
|
||||
for (const customKey of customResponse.data.data) {
|
||||
try {
|
||||
const module = await import(/* webpackIgnore: true */ `/extensions/layouts/${customKey}/index.js`);
|
||||
modules.push(module.default);
|
||||
} catch (err) {
|
||||
console.error(`Couldn't load custom layout "${customKey}"`);
|
||||
console.error(err);
|
||||
console.warn(`Couldn't load custom layout "${customKey}"`);
|
||||
console.warn(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
console.error(`Couldn't load custom layouts`);
|
||||
console.warn(`Couldn't load custom layouts`);
|
||||
}
|
||||
|
||||
layouts.value = modules;
|
||||
|
||||
@@ -48,7 +48,6 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, toRefs, ref, watch } from '@vue/composition-api';
|
||||
import { i18n } from '@/lang';
|
||||
import router from '@/router';
|
||||
import api from '@/api';
|
||||
import { userName } from '@/utils/user-name';
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
import { defineComponent, PropType, ref, computed } from '@vue/composition-api';
|
||||
import { Preset } from '@/types';
|
||||
import { useUserStore, usePresetsStore } from '@/stores';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@@ -106,8 +107,8 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
renameActive.value = false;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} catch (err) {
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
renameSaving.value = false;
|
||||
}
|
||||
@@ -127,8 +128,8 @@ export default defineComponent({
|
||||
try {
|
||||
await presetsStore.delete(props.bookmark.id);
|
||||
deleteActive.value = false;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} catch (err) {
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
deleteSaving.value = false;
|
||||
}
|
||||
|
||||
@@ -263,6 +263,7 @@ import router from '@/router';
|
||||
import marked from 'marked';
|
||||
import { usePermissionsStore, useUserStore } from '@/stores';
|
||||
import DrawerBatch from '@/views/private/components/drawer-batch';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
type Item = {
|
||||
[field: string]: any;
|
||||
@@ -513,8 +514,8 @@ export default defineComponent({
|
||||
router.push(`/collections/${newBookmark.collection}?bookmark=${newBookmark.id}`);
|
||||
|
||||
bookmarkDialogActive.value = false;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} catch (err) {
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
creatingBookmark.value = false;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import { defineComponent, ref } from '@vue/composition-api';
|
||||
import useFolders from '../composables/use-folders';
|
||||
import api from '@/api';
|
||||
import router from '@/router';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@@ -38,11 +39,10 @@ export default defineComponent({
|
||||
const dialogActive = ref(false);
|
||||
const saving = ref(false);
|
||||
const newFolderName = ref(null);
|
||||
const savingError = ref(null);
|
||||
|
||||
const { fetchFolders } = useFolders();
|
||||
|
||||
return { addFolder, dialogActive, newFolderName, saving, savingError };
|
||||
return { addFolder, dialogActive, newFolderName, saving };
|
||||
|
||||
async function addFolder() {
|
||||
saving.value = true;
|
||||
@@ -60,7 +60,7 @@ export default defineComponent({
|
||||
|
||||
router.push({ path: '/files', query: { folder: newFolder.data.data.id } });
|
||||
} catch (err) {
|
||||
savingError.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
import { defineComponent, ref, computed, PropType } from '@vue/composition-api';
|
||||
import api from '@/api';
|
||||
import FolderPickerListItem from './folder-picker-list-item.vue';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
type FolderRaw = {
|
||||
id: string;
|
||||
@@ -64,7 +65,6 @@ export default defineComponent({
|
||||
setup(props) {
|
||||
const loading = ref(false);
|
||||
const folders = ref<FolderRaw[]>([]);
|
||||
const error = ref<any>(null);
|
||||
const tree = computed<Folder[]>(() => {
|
||||
return folders.value
|
||||
.filter((folder) => folder.parent === null)
|
||||
@@ -125,7 +125,7 @@ export default defineComponent({
|
||||
|
||||
folders.value = response.data.data;
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
@@ -112,10 +112,10 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, ref, watch, computed } from '@vue/composition-api';
|
||||
import useFolders, { Folder } from '../composables/use-folders';
|
||||
import notify from '@/utils/notify';
|
||||
import api from '@/api';
|
||||
import FolderPicker from './folder-picker.vue';
|
||||
import router from '@/router';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'navigation-folder',
|
||||
@@ -169,8 +169,8 @@ export default defineComponent({
|
||||
await api.patch(`/folders/${props.folder.id}`, {
|
||||
name: renameValue.value,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} catch (err) {
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
renameSaving.value = false;
|
||||
await fetchFolders();
|
||||
@@ -193,8 +193,8 @@ export default defineComponent({
|
||||
await api.patch(`/folders/${props.folder.id}`, {
|
||||
parent: moveValue.value,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} catch (err) {
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
moveSaving.value = false;
|
||||
await fetchFolders();
|
||||
@@ -255,8 +255,8 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
deleteActive.value = false;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} catch (err) {
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
await fetchFolders();
|
||||
deleteSaving.value = false;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import api from '@/api';
|
||||
import i18n from '@/lang';
|
||||
import { ref, Ref } from '@vue/composition-api';
|
||||
import { TranslateResult } from 'vue-i18n';
|
||||
|
||||
|
||||
@@ -164,6 +164,7 @@ import { subDays } from 'date-fns';
|
||||
import useFolders from '../composables/use-folders';
|
||||
import useEventListener from '@/composables/use-event-listener';
|
||||
import uploadFiles from '@/utils/upload-files';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
type Item = {
|
||||
[field: string]: any;
|
||||
@@ -183,12 +184,12 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const notificationsStore = useNotificationsStore();
|
||||
const { folders } = useFolders();
|
||||
const layoutRef = ref<LayoutComponent | null>(null);
|
||||
const selection = ref<Item[]>([]);
|
||||
|
||||
const userStore = useUserStore();
|
||||
const notificationsStore = useNotificationsStore();
|
||||
|
||||
const { layout, layoutOptions, layoutQuery, filters, searchQuery } = usePreset(ref('directus_files'));
|
||||
const { batchLink } = useLinks();
|
||||
@@ -307,7 +308,7 @@ export default defineComponent({
|
||||
selection.value = [];
|
||||
await layoutRef.value?.refresh();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
deleting.value = false;
|
||||
}
|
||||
@@ -387,7 +388,7 @@ export default defineComponent({
|
||||
await Vue.nextTick();
|
||||
await refresh();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
moveToDialogActive.value = false;
|
||||
moving.value = false;
|
||||
|
||||
@@ -185,6 +185,7 @@ import SaveOptions from '@/views/private/components/save-options';
|
||||
import FilePreview from '@/views/private/components/file-preview';
|
||||
import ImageEditor from '@/views/private/components/image-editor';
|
||||
import { nanoid } from 'nanoid';
|
||||
import FileLightbox from '@/views/private/components/file-lightbox';
|
||||
import { useFieldsStore } from '@/stores/';
|
||||
import { Field } from '@/types';
|
||||
import FileInfoSidebarDetail from '../components/file-info-sidebar-detail.vue';
|
||||
@@ -196,6 +197,8 @@ import FilesNotFound from './not-found.vue';
|
||||
import useShortcut from '@/composables/use-shortcut';
|
||||
import ReplaceFile from '../components/replace-file.vue';
|
||||
import { usePermissions } from '@/composables/use-permissions';
|
||||
import { notify } from '@/utils/notify';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
type Values = {
|
||||
[field: string]: any;
|
||||
@@ -420,12 +423,28 @@ export default defineComponent({
|
||||
moving.value = true;
|
||||
|
||||
try {
|
||||
await api.patch(`/files/${props.primaryKey}`, {
|
||||
folder: selectedFolder.value,
|
||||
});
|
||||
const response = await api.patch(
|
||||
`/files/${props.primaryKey}`,
|
||||
{
|
||||
folder: selectedFolder.value,
|
||||
},
|
||||
{
|
||||
params: {
|
||||
fields: 'folder.name',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
await refresh();
|
||||
const folder = response.data.data.folder?.name || i18n.t('file_library');
|
||||
|
||||
notify({
|
||||
title: i18n.t('file_moved', { folder }),
|
||||
type: 'success',
|
||||
icon: 'folder_move',
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
moveToDialogActive.value = false;
|
||||
moving.value = false;
|
||||
|
||||
@@ -37,13 +37,13 @@ export async function loadModules() {
|
||||
});
|
||||
loadedModules.push(module.default);
|
||||
} catch (err) {
|
||||
console.error(`Couldn't load custom module "${customKey}"`);
|
||||
console.error(err);
|
||||
console.warn(`Couldn't load custom module "${customKey}"`);
|
||||
console.warn(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
console.error(`Couldn't load custom modules`);
|
||||
console.warn(`Couldn't load custom modules`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -121,10 +121,11 @@ import { useFieldsStore, useRelationsStore, useCollectionsStore } from '@/stores
|
||||
import { Field } from '@/types';
|
||||
import router from '@/router';
|
||||
import useCollection from '@/composables/use-collection';
|
||||
import notify from '@/utils/notify';
|
||||
import { getLocalTypeForField } from '../get-local-type';
|
||||
import { notify } from '@/utils/notify';
|
||||
|
||||
import { initLocalStore, state, clearLocalStore } from './store';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@@ -373,8 +374,8 @@ export default defineComponent({
|
||||
|
||||
router.push(`/settings/data-model/${props.collection}`);
|
||||
clearLocalStore();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} catch (err) {
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
|
||||
@@ -217,10 +217,11 @@ import { Field, Relation } from '@/types';
|
||||
import { useCollectionsStore, useFieldsStore, useRelationsStore } from '@/stores/';
|
||||
import { getInterfaces } from '@/interfaces';
|
||||
import router from '@/router';
|
||||
import notify from '@/utils/notify';
|
||||
import { i18n } from '@/lang';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { getLocalTypeForField } from '../../get-local-type';
|
||||
import { notify } from '@/utils/notify';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@@ -368,8 +369,8 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
duplicateActive.value = false;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} catch (err) {
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
duplicating.value = false;
|
||||
}
|
||||
|
||||
@@ -6,22 +6,6 @@
|
||||
persistent
|
||||
@cancel="$router.push('/settings/data-model')"
|
||||
>
|
||||
<v-dialog :active="saveError !== null" @toggle="saveError = null" @esc="saveError = null">
|
||||
<v-card class="selectable">
|
||||
<v-card-title>
|
||||
{{ saveError && saveError.message }}
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text>
|
||||
{{ saveError && saveError.response && saveError.response.data.errors[0].message }}
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions>
|
||||
<v-button @click="saveError = null">{{ $t('dismiss') }}</v-button>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<template #sidebar>
|
||||
<v-tabs vertical v-model="currentTab">
|
||||
<v-tab value="collection">{{ $t('collection_setup') }}</v-tab>
|
||||
@@ -138,8 +122,10 @@ import { defineComponent, ref, reactive } from '@vue/composition-api';
|
||||
import api from '@/api';
|
||||
import { Field, Relation } from '@/types';
|
||||
import { useFieldsStore, useCollectionsStore, useRelationsStore } from '@/stores/';
|
||||
import notify from '@/utils/notify';
|
||||
import { notify } from '@/utils/notify';
|
||||
import router from '@/router';
|
||||
import i18n from '@/lang';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
@@ -200,7 +186,6 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const saving = ref(false);
|
||||
const saveError = ref(null);
|
||||
|
||||
return {
|
||||
currentTab,
|
||||
@@ -209,7 +194,6 @@ export default defineComponent({
|
||||
primaryKeyFieldName,
|
||||
primaryKeyFieldType,
|
||||
collectionName,
|
||||
saveError,
|
||||
saving,
|
||||
singleton,
|
||||
};
|
||||
@@ -246,9 +230,8 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
router.push(`/settings/data-model/${collectionName.value}`);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
saveError.value = error;
|
||||
} catch (err) {
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
|
||||
@@ -96,7 +96,6 @@ import SettingsNavigation from '../../../components/navigation.vue';
|
||||
|
||||
import api from '@/api';
|
||||
import { Header } from '@/components/v-table/types';
|
||||
import i18n from '@/lang';
|
||||
import { useCollectionsStore } from '@/stores/';
|
||||
import { getLayouts } from '@/layouts';
|
||||
import { TranslateResult } from 'vue-i18n';
|
||||
@@ -104,6 +103,8 @@ import router from '@/router';
|
||||
import ValueNull from '@/views/private/components/value-null';
|
||||
import PresetsInfoSidebarDetail from './components/presets-info-sidebar-detail.vue';
|
||||
import { userName } from '@/utils/user-name';
|
||||
import i18n from '@/lang';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
type PresetRaw = {
|
||||
id: number;
|
||||
@@ -131,7 +132,7 @@ export default defineComponent({
|
||||
const selection = ref<Preset[]>([]);
|
||||
|
||||
const { addNewLink } = useLinks();
|
||||
const { loading, presets, error, getPresets } = usePresets();
|
||||
const { loading, presets, getPresets } = usePresets();
|
||||
const { headers } = useTable();
|
||||
const { confirmDelete, deleting, deleteSelection } = useDelete();
|
||||
|
||||
@@ -142,7 +143,6 @@ export default defineComponent({
|
||||
usePresets,
|
||||
loading,
|
||||
presets,
|
||||
error,
|
||||
getPresets,
|
||||
headers,
|
||||
selection,
|
||||
@@ -163,7 +163,6 @@ export default defineComponent({
|
||||
function usePresets() {
|
||||
const loading = ref(false);
|
||||
const presetsRaw = ref<PresetRaw[] | null>(null);
|
||||
const error = ref(null);
|
||||
|
||||
const presets = computed<Preset[]>(() => {
|
||||
return (presetsRaw.value || []).map((preset) => {
|
||||
@@ -190,7 +189,7 @@ export default defineComponent({
|
||||
});
|
||||
});
|
||||
|
||||
return { loading, presetsRaw, error, getPresets, presets };
|
||||
return { loading, presetsRaw, getPresets, presets };
|
||||
|
||||
async function getPresets() {
|
||||
loading.value = true;
|
||||
@@ -213,7 +212,7 @@ export default defineComponent({
|
||||
});
|
||||
presetsRaw.value = response.data.data;
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
import { defineComponent, ref } from '@vue/composition-api';
|
||||
import api from '@/api';
|
||||
import marked from 'marked';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const loading = ref(false);
|
||||
const error = ref<any>(null);
|
||||
const bookmarksCount = ref<number | null>(null);
|
||||
const presetsCount = ref<number | null>(null);
|
||||
|
||||
@@ -48,7 +48,7 @@ export default defineComponent({
|
||||
bookmarksCount.value = response.data.meta.filter_count as number;
|
||||
presetsCount.value = (response.data.meta.total_count as number) - bookmarksCount.value;
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
@@ -103,6 +103,7 @@ import { getLayouts } from '@/layouts';
|
||||
import router from '@/router';
|
||||
import marked from 'marked';
|
||||
import { userName } from '@/utils/user-name';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
type User = {
|
||||
id: number;
|
||||
@@ -145,7 +146,7 @@ export default defineComponent({
|
||||
|
||||
const { loading: usersLoading, users } = useUsers();
|
||||
const { loading: rolesLoading, roles } = useRoles();
|
||||
const { loading: presetLoading, error, preset } = usePreset();
|
||||
const { loading: presetLoading, preset } = usePreset();
|
||||
const { fields } = useForm();
|
||||
const { edits, hasEdits, initialValues, values, layoutQuery, layoutOptions } = useValues();
|
||||
const { save, saving } = useSave();
|
||||
@@ -156,7 +157,6 @@ export default defineComponent({
|
||||
return {
|
||||
backLink,
|
||||
loading,
|
||||
error,
|
||||
preset,
|
||||
edits,
|
||||
fields,
|
||||
@@ -213,7 +213,7 @@ export default defineComponent({
|
||||
|
||||
edits.value = {};
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
saving.value = false;
|
||||
router.push(`/settings/presets`);
|
||||
@@ -233,8 +233,8 @@ export default defineComponent({
|
||||
try {
|
||||
await api.delete(`/presets/${props.id}`);
|
||||
router.push(`/settings/presets`);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} catch (err) {
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
deleting.value = false;
|
||||
}
|
||||
@@ -321,12 +321,11 @@ export default defineComponent({
|
||||
|
||||
function usePreset() {
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
const preset = ref<Preset | null>(null);
|
||||
|
||||
fetchPreset();
|
||||
|
||||
return { loading, error, preset, fetchPreset };
|
||||
return { loading, preset, fetchPreset };
|
||||
|
||||
async function fetchPreset() {
|
||||
loading.value = true;
|
||||
@@ -336,7 +335,7 @@ export default defineComponent({
|
||||
|
||||
preset.value = response.data.data;
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
@@ -353,12 +352,11 @@ export default defineComponent({
|
||||
|
||||
function useUsers() {
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
const users = ref<User[] | null>(null);
|
||||
|
||||
fetchUsers();
|
||||
|
||||
return { loading, error, users };
|
||||
return { loading, users };
|
||||
|
||||
async function fetchUsers() {
|
||||
loading.value = true;
|
||||
@@ -375,7 +373,7 @@ export default defineComponent({
|
||||
id: user.id,
|
||||
}));
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
@@ -384,12 +382,11 @@ export default defineComponent({
|
||||
|
||||
function useRoles() {
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
const roles = ref<Role[] | null>(null);
|
||||
|
||||
fetchRoles();
|
||||
|
||||
return { loading, error, roles };
|
||||
return { loading, roles };
|
||||
|
||||
async function fetchRoles() {
|
||||
loading.value = true;
|
||||
@@ -403,7 +400,7 @@ export default defineComponent({
|
||||
|
||||
roles.value = response.data.data;
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import { defineComponent, ref } from '@vue/composition-api';
|
||||
import api from '@/api';
|
||||
import router from '@/router';
|
||||
import { permissions } from './app-required-permissions';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
@@ -46,19 +47,17 @@ export default defineComponent({
|
||||
const appAccess = ref(true);
|
||||
const adminAccess = ref(false);
|
||||
|
||||
const { saving, error, save } = useSave();
|
||||
const { saving, save } = useSave();
|
||||
|
||||
return { roleName, saving, error, save, appAccess, adminAccess };
|
||||
return { roleName, saving, save, appAccess, adminAccess };
|
||||
|
||||
function useSave() {
|
||||
const saving = ref(false);
|
||||
const error = ref<any>();
|
||||
|
||||
return { saving, error, save };
|
||||
return { saving, save };
|
||||
|
||||
async function save() {
|
||||
saving.value = true;
|
||||
error.value = null;
|
||||
|
||||
try {
|
||||
const roleResponse = await api.post('/roles', {
|
||||
@@ -79,7 +78,7 @@ export default defineComponent({
|
||||
|
||||
router.push(`/settings/roles/${roleResponse.data.data.id}`);
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ import marked from 'marked';
|
||||
import { Header as TableHeader } from '@/components/v-table/types';
|
||||
import ValueNull from '@/views/private/components/value-null';
|
||||
import router from '@/router';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
type Role = {
|
||||
id: number;
|
||||
@@ -81,7 +82,6 @@ export default defineComponent({
|
||||
setup() {
|
||||
const roles = ref<Role[]>([]);
|
||||
const loading = ref(false);
|
||||
const error = ref<any>(null);
|
||||
|
||||
const tableHeaders: TableHeader[] = [
|
||||
{
|
||||
@@ -120,7 +120,7 @@ export default defineComponent({
|
||||
return `/settings/roles/+`;
|
||||
});
|
||||
|
||||
return { marked, loading, roles, error, tableHeaders, addNewLink, navigateToRole };
|
||||
return { marked, loading, roles, tableHeaders, addNewLink, navigateToRole };
|
||||
|
||||
async function fetchRoles() {
|
||||
loading.value = true;
|
||||
@@ -146,7 +146,7 @@ export default defineComponent({
|
||||
}),
|
||||
];
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ import { defineComponent, PropType, computed, inject, ref } from '@vue/compositi
|
||||
import { Collection, Permission } from '@/types';
|
||||
import api from '@/api';
|
||||
import router from '@/router';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@@ -118,7 +119,7 @@ export default defineComponent({
|
||||
validation: null,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
await refresh?.();
|
||||
saving.value = false;
|
||||
@@ -132,7 +133,7 @@ export default defineComponent({
|
||||
fields: '*',
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
await refresh?.();
|
||||
saving.value = false;
|
||||
@@ -148,7 +149,7 @@ export default defineComponent({
|
||||
try {
|
||||
await api.delete(`/permissions/${props.permission.id}`);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
await refresh?.();
|
||||
saving.value = false;
|
||||
|
||||
@@ -68,6 +68,7 @@ import PermissionsOverviewRow from './permissions-overview-row.vue';
|
||||
import { Permission } from '@/types';
|
||||
import api from '@/api';
|
||||
import { permissions as appRequiredPermissions } from '../../app-required-permissions';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
components: { PermissionsOverviewHeader, PermissionsOverviewRow },
|
||||
@@ -99,7 +100,7 @@ export default defineComponent({
|
||||
|
||||
const systemVisible = ref(false);
|
||||
|
||||
const { permissions, loading, error, fetchPermissions, refreshPermission, refreshing } = usePermissions();
|
||||
const { permissions, loading, fetchPermissions, refreshPermission, refreshing } = usePermissions();
|
||||
|
||||
const { resetActive, resetSystemPermissions, resetting, resetError } = useReset();
|
||||
|
||||
@@ -113,7 +114,6 @@ export default defineComponent({
|
||||
systemCollections,
|
||||
permissions,
|
||||
loading,
|
||||
error,
|
||||
fetchPermissions,
|
||||
refreshPermission,
|
||||
refreshing,
|
||||
@@ -127,12 +127,10 @@ export default defineComponent({
|
||||
const permissions = ref<Permission[]>([]);
|
||||
const loading = ref(false);
|
||||
const refreshing = ref<number[]>([]);
|
||||
const error = ref();
|
||||
|
||||
return { permissions, loading, error, fetchPermissions, refreshPermission, refreshing };
|
||||
return { permissions, loading, fetchPermissions, refreshPermission, refreshing };
|
||||
|
||||
async function fetchPermissions() {
|
||||
error.value = null;
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
@@ -148,7 +146,7 @@ export default defineComponent({
|
||||
|
||||
permissions.value = response.data.data;
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
@@ -167,7 +165,7 @@ export default defineComponent({
|
||||
return permission;
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(`Couldn't refresh permissions ${id}`);
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
refreshing.value = refreshing.value.filter((inProgressID) => inProgressID !== id);
|
||||
}
|
||||
|
||||
@@ -66,8 +66,7 @@ export default function usePermissions(role: Ref<number>) {
|
||||
|
||||
await fetchPermissions();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
error.value = err;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,8 +82,7 @@ export default function usePermissions(role: Ref<number>) {
|
||||
|
||||
await fetchPermissions();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
error.value = err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,13 @@
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<v-button rounded icon @click="userInviteModalActive = true" v-tooltip.bottom="$t('invite_users')" class="invite-user">
|
||||
<v-button
|
||||
rounded
|
||||
icon
|
||||
@click="userInviteModalActive = true"
|
||||
v-tooltip.bottom="$t('invite_users')"
|
||||
class="invite-user"
|
||||
>
|
||||
<v-icon name="person_add" />
|
||||
</v-button>
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import { defineComponent, PropType, ref, inject } from '@vue/composition-api';
|
||||
import { Permission } from '@/types';
|
||||
import api from '@/api';
|
||||
import router from '@/router';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@@ -25,20 +26,18 @@ export default defineComponent({
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const loading = ref(false);
|
||||
const error = ref<any>();
|
||||
|
||||
return { save, loading };
|
||||
|
||||
async function save() {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
|
||||
try {
|
||||
await api.patch(`/permissions/${props.permission.id}`, props.permission);
|
||||
emit('refresh');
|
||||
router.push(`/settings/roles/${props.roleKey || 'public'}`);
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import { defineComponent, ref, reactive, computed, watch } from '@vue/compositio
|
||||
import api from '@/api';
|
||||
import { Permission, Role } from '@/types';
|
||||
import { useFieldsStore, useCollectionsStore } from '@/stores/';
|
||||
import notify from '@/utils/notify';
|
||||
import router from '@/router';
|
||||
import i18n from '@/lang';
|
||||
import Actions from './components/actions.vue';
|
||||
@@ -32,6 +31,7 @@ import Permissions from './components/permissions.vue';
|
||||
import Fields from './components/fields.vue';
|
||||
import Validation from './components/validation.vue';
|
||||
import Presets from './components/presets.vue';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
components: { Actions, Tabs, Permissions, Fields, Validation, Presets },
|
||||
@@ -50,7 +50,6 @@ export default defineComponent({
|
||||
|
||||
const permission = ref<Permission>();
|
||||
const role = ref<Role>();
|
||||
const error = ref<any>();
|
||||
const loading = ref(false);
|
||||
|
||||
const collectionName = computed(() => {
|
||||
@@ -127,7 +126,7 @@ export default defineComponent({
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
return { permission, role, error, loading, modalTitle, tabs, currentTab };
|
||||
return { permission, role, loading, modalTitle, tabs, currentTab };
|
||||
|
||||
async function load() {
|
||||
loading.value = true;
|
||||
@@ -141,10 +140,10 @@ export default defineComponent({
|
||||
const response = await api.get(`/permissions/${props.permissionKey}`);
|
||||
permission.value = response.data.data;
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
|
||||
if (err?.response?.status === 403) {
|
||||
router.push(`/settings/roles/${props.roleKey || 'public'}`);
|
||||
} else {
|
||||
unexpectedError(err);
|
||||
}
|
||||
} finally {
|
||||
loading.value = false;
|
||||
|
||||
@@ -181,6 +181,7 @@ import { isAllowed } from '@/utils/is-allowed';
|
||||
import useCollection from '@/composables/use-collection';
|
||||
import { userName } from '@/utils/user-name';
|
||||
import { usePermissions } from '@/composables/use-permissions';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
type Values = {
|
||||
[field: string]: any;
|
||||
@@ -308,7 +309,6 @@ export default defineComponent({
|
||||
title,
|
||||
item,
|
||||
loading,
|
||||
error,
|
||||
isNew,
|
||||
breadcrumb,
|
||||
edits,
|
||||
@@ -411,13 +411,12 @@ export default defineComponent({
|
||||
|
||||
function useUserPreview() {
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
const avatarSrc = ref<string | null>(null);
|
||||
const roleName = ref<string | null>(null);
|
||||
|
||||
watch(() => props.primaryKey, getUserPreviewData, { immediate: true });
|
||||
|
||||
return { loading, error, avatarSrc, roleName };
|
||||
return { loading, avatarSrc, roleName };
|
||||
|
||||
async function getUserPreviewData() {
|
||||
if (props.primaryKey === '+') return;
|
||||
@@ -436,7 +435,7 @@ export default defineComponent({
|
||||
: null;
|
||||
roleName.value = response.data.data.role.name;
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
@@ -18,21 +18,20 @@ import api from '@/api';
|
||||
import { hydrate } from '@/hydrate';
|
||||
import router from '@/router';
|
||||
import { userName } from '@/utils/user-name';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
const name = ref<string | null>(null);
|
||||
const lastPage = ref<string | null>(null);
|
||||
|
||||
fetchUser();
|
||||
|
||||
return { name, lastPage, loading, error, hydrateAndLogin };
|
||||
return { name, lastPage, loading, hydrateAndLogin };
|
||||
|
||||
async function fetchUser() {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
|
||||
try {
|
||||
const response = await api.get(`/users/me`, {
|
||||
@@ -44,7 +43,7 @@ export default defineComponent({
|
||||
name.value = userName(response.data.data);
|
||||
lastPage.value = response.data.data.last_page;
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
@@ -91,7 +91,6 @@ export default defineComponent({
|
||||
const lastPage = userStore.state.currentUser?.last_page;
|
||||
router.push(lastPage || '/collections');
|
||||
} catch (err) {
|
||||
/** @todo use new error code */
|
||||
if (
|
||||
err.response?.data?.errors?.[0]?.extensions?.code === 'INVALID_OTP' &&
|
||||
requiresTFA.value === false
|
||||
|
||||
@@ -14,12 +14,12 @@
|
||||
import { defineComponent, ref, onMounted } from '@vue/composition-api';
|
||||
import api from '@/api';
|
||||
import getRootPath from '@/utils/get-root-path';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const providers = ref([]);
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
|
||||
onMounted(() => fetchProviders());
|
||||
|
||||
@@ -27,7 +27,6 @@ export default defineComponent({
|
||||
|
||||
async function fetchProviders() {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
|
||||
try {
|
||||
const response = await api.get('/auth/oauth/');
|
||||
@@ -41,8 +40,7 @@ export default defineComponent({
|
||||
};
|
||||
});
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
console.error(err);
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@ 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';
|
||||
import { notify } from '@/utils/notify';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export const useCollectionsStore = createStore({
|
||||
id: 'collectionsStore',
|
||||
@@ -63,13 +64,8 @@ export const useCollectionsStore = createStore({
|
||||
type: 'success',
|
||||
title: i18n.t('update_collection_success'),
|
||||
});
|
||||
} catch (error) {
|
||||
notify({
|
||||
type: 'error',
|
||||
title: i18n.t('update_collection_failed'),
|
||||
text: collection,
|
||||
});
|
||||
throw error;
|
||||
} catch (err) {
|
||||
unexpectedError(err);
|
||||
}
|
||||
},
|
||||
async deleteCollection(collection: string) {
|
||||
@@ -80,13 +76,8 @@ export const useCollectionsStore = createStore({
|
||||
type: 'success',
|
||||
title: i18n.t('delete_collection_success'),
|
||||
});
|
||||
} catch (error) {
|
||||
notify({
|
||||
type: 'error',
|
||||
title: i18n.t('delete_collection_failed'),
|
||||
text: collection,
|
||||
});
|
||||
throw error;
|
||||
} catch (err) {
|
||||
unexpectedError(err);
|
||||
}
|
||||
},
|
||||
getCollection(collectionKey: string): Collection | null {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useRelationsStore } from '@/stores/';
|
||||
import { Relation, FieldRaw, Field } from '@/types';
|
||||
import { merge } from 'lodash';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
const fakeFilesField: Field = {
|
||||
collection: 'directus_files',
|
||||
@@ -120,10 +121,10 @@ export const useFieldsStore = createStore({
|
||||
});
|
||||
|
||||
return field;
|
||||
} catch (error) {
|
||||
} catch (err) {
|
||||
// reset the changes if the api sync failed
|
||||
this.state.fields = stateClone;
|
||||
throw error;
|
||||
unexpectedError(err);
|
||||
}
|
||||
},
|
||||
async updateField(collectionKey: string, fieldKey: string, updates: Record<string, Partial<Field>>) {
|
||||
@@ -150,10 +151,10 @@ export const useFieldsStore = createStore({
|
||||
|
||||
return field;
|
||||
});
|
||||
} catch (error) {
|
||||
} catch (err) {
|
||||
// reset the changes if the api sync failed
|
||||
this.state.fields = stateClone;
|
||||
throw error;
|
||||
unexpectedError(err);
|
||||
}
|
||||
},
|
||||
async updateFields(collectionKey: string, updates: Partial<Field>[]) {
|
||||
@@ -192,10 +193,10 @@ export const useFieldsStore = createStore({
|
||||
return field;
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
} catch (err) {
|
||||
// reset the changes if the api sync failed
|
||||
this.state.fields = stateClone;
|
||||
throw error;
|
||||
unexpectedError(err);
|
||||
}
|
||||
},
|
||||
async deleteField(collectionKey: string, fieldKey: string) {
|
||||
@@ -208,9 +209,9 @@ export const useFieldsStore = createStore({
|
||||
|
||||
try {
|
||||
await api.delete(`/fields/${collectionKey}/${fieldKey}`);
|
||||
} catch (error) {
|
||||
} catch (err) {
|
||||
this.state.fields = stateClone;
|
||||
throw error;
|
||||
unexpectedError(err);
|
||||
}
|
||||
},
|
||||
getPrimaryKeyFieldForCollection(collection: string) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import { reverse, sortBy } from 'lodash';
|
||||
export const useNotificationsStore = createStore({
|
||||
id: 'notificationsStore',
|
||||
state: () => ({
|
||||
dialogs: [] as Notification[],
|
||||
queue: [] as Notification[],
|
||||
previous: [] as Notification[],
|
||||
}),
|
||||
@@ -14,14 +15,27 @@ export const useNotificationsStore = createStore({
|
||||
const id = nanoid();
|
||||
const timestamp = Date.now();
|
||||
|
||||
this.state.queue = [
|
||||
...this.state.queue,
|
||||
{
|
||||
...notification,
|
||||
id,
|
||||
timestamp,
|
||||
},
|
||||
];
|
||||
if (notification.dialog === true) {
|
||||
notification.persist = true;
|
||||
|
||||
this.state.dialogs = [
|
||||
...this.state.dialogs,
|
||||
{
|
||||
...notification,
|
||||
id,
|
||||
timestamp,
|
||||
},
|
||||
];
|
||||
} else {
|
||||
this.state.queue = [
|
||||
...this.state.queue,
|
||||
{
|
||||
...notification,
|
||||
id,
|
||||
timestamp,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
if (notification.persist !== true) {
|
||||
setTimeout(() => {
|
||||
@@ -32,20 +46,27 @@ export const useNotificationsStore = createStore({
|
||||
return id;
|
||||
},
|
||||
hide(id: string) {
|
||||
const toBeHidden = this.state.queue.find((n) => n.id === id);
|
||||
const queues = [...this.state.queue, ...this.state.dialogs];
|
||||
const toBeHidden = queues.find((n) => n.id === id);
|
||||
if (!toBeHidden) return;
|
||||
|
||||
this.state.queue = this.state.queue.filter((n) => n.id !== id);
|
||||
if (toBeHidden.dialog === true) this.state.dialogs = this.state.dialogs.filter((n) => n.id !== id);
|
||||
else this.state.queue = this.state.queue.filter((n) => n.id !== id);
|
||||
|
||||
this.state.previous = [...this.state.previous, toBeHidden];
|
||||
},
|
||||
remove(id: string) {
|
||||
const toBeRemoved = this.state.queue.find((n) => n.id === id);
|
||||
const queues = [...this.state.queue, ...this.state.dialogs];
|
||||
|
||||
const toBeRemoved = queues.find((n) => n.id === id);
|
||||
if (!toBeRemoved) return;
|
||||
|
||||
this.state.queue = this.state.queue.filter((n) => n.id !== id);
|
||||
if (toBeRemoved.dialog === true) this.state.dialogs = this.state.dialogs.filter((n) => n.id !== id);
|
||||
else this.state.queue = this.state.queue.filter((n) => n.id !== id);
|
||||
},
|
||||
update(id: string, updates: Partial<Notification>) {
|
||||
this.state.queue = this.state.queue.map(updateIfNeeded);
|
||||
this.state.dialogs = this.state.dialogs.map(updateIfNeeded);
|
||||
this.state.previous = this.state.queue.map(updateIfNeeded);
|
||||
|
||||
function updateIfNeeded(notification: Notification) {
|
||||
@@ -61,7 +82,7 @@ export const useNotificationsStore = createStore({
|
||||
},
|
||||
getters: {
|
||||
lastFour(state) {
|
||||
const all = [...state.queue, ...state.previous];
|
||||
const all = [...state.queue, ...state.previous.filter((l) => l.dialog !== true)];
|
||||
const chronologicalAll = reverse(sortBy(all, ['timestamp']));
|
||||
const newestFour = chronologicalAll.slice(0, 4);
|
||||
return reverse(newestFour);
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { createStore } from 'pinia';
|
||||
import api from '@/api';
|
||||
import notify from '@/utils/notify';
|
||||
import { i18n } from '@/lang';
|
||||
import { merge } from 'lodash';
|
||||
import { notify } from '@/utils/notify';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export const useSettingsStore = createStore({
|
||||
id: 'settingsStore',
|
||||
@@ -11,10 +12,8 @@ export const useSettingsStore = createStore({
|
||||
}),
|
||||
actions: {
|
||||
async hydrate() {
|
||||
try {
|
||||
const response = await api.get(`/settings`);
|
||||
this.state.settings = response.data.data;
|
||||
} catch {}
|
||||
const response = await api.get(`/settings`);
|
||||
this.state.settings = response.data.data;
|
||||
},
|
||||
|
||||
async dehydrate() {
|
||||
@@ -36,14 +35,9 @@ export const useSettingsStore = createStore({
|
||||
title: i18n.t('settings_update_success'),
|
||||
type: 'success',
|
||||
});
|
||||
} catch (error) {
|
||||
} catch (err) {
|
||||
this.state.settings = settingsCopy;
|
||||
|
||||
notify({
|
||||
title: i18n.t('settings_update_failed'),
|
||||
text: Object.keys(updates).join(', '),
|
||||
type: 'error',
|
||||
});
|
||||
unexpectedError(err);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -10,6 +10,8 @@ export interface NotificationRaw {
|
||||
closeable?: boolean;
|
||||
progress?: number;
|
||||
loading?: boolean;
|
||||
dialog?: boolean;
|
||||
error?: Error;
|
||||
}
|
||||
|
||||
export interface Notification extends NotificationRaw {
|
||||
|
||||
9
app/src/utils/notify.ts
Normal file
9
app/src/utils/notify.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { useNotificationsStore } from '@/stores/';
|
||||
import { NotificationRaw } from '@/types';
|
||||
|
||||
let store: any;
|
||||
|
||||
export function notify(notification: NotificationRaw) {
|
||||
if (!store) store = useNotificationsStore();
|
||||
store.add(notification);
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
import notify from './notify';
|
||||
|
||||
export { notify };
|
||||
export default notify;
|
||||
@@ -1,23 +0,0 @@
|
||||
import Vue from 'vue';
|
||||
import VueCompositionAPI from '@vue/composition-api';
|
||||
import notify from './notify';
|
||||
import { useNotificationsStore } from '@/stores/';
|
||||
|
||||
describe('Utils / Notify', () => {
|
||||
beforeAll(() => {
|
||||
Vue.use(VueCompositionAPI);
|
||||
});
|
||||
|
||||
it('Calls notificationsStore.add with the passed notification', () => {
|
||||
const notificationsStore = useNotificationsStore();
|
||||
jest.spyOn(notificationsStore as any, 'add');
|
||||
|
||||
const notification = {
|
||||
title: 'test',
|
||||
};
|
||||
|
||||
notify(notification);
|
||||
|
||||
expect(notificationsStore.add).toHaveBeenCalledWith(notification);
|
||||
});
|
||||
});
|
||||
@@ -1,7 +0,0 @@
|
||||
import { useNotificationsStore } from '@/stores/';
|
||||
import { NotificationRaw } from '@/types';
|
||||
|
||||
export default function notify(notification: NotificationRaw) {
|
||||
const notificationsStore = useNotificationsStore();
|
||||
notificationsStore.add(notification);
|
||||
}
|
||||
22
app/src/utils/unexpected-error.ts
Normal file
22
app/src/utils/unexpected-error.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { useNotificationsStore } from '@/stores/';
|
||||
import { i18n } from '@/lang';
|
||||
import { RequestError } from '@/api';
|
||||
|
||||
let store: any;
|
||||
|
||||
export function unexpectedError(error: Error | RequestError) {
|
||||
if (!store) store = useNotificationsStore();
|
||||
|
||||
const code = (error as RequestError).response?.data?.errors?.[0]?.extensions?.code || 'UNKNOWN';
|
||||
const message = (error as RequestError).response?.data?.errors?.[0]?.message || error.message || undefined;
|
||||
|
||||
console.warn(error);
|
||||
|
||||
store.add({
|
||||
title: i18n.t(`errors.${code}`),
|
||||
text: message,
|
||||
type: 'error',
|
||||
dialog: true,
|
||||
error,
|
||||
});
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import api from '@/api';
|
||||
|
||||
import notify from '@/utils/notify';
|
||||
import i18n from '@/lang';
|
||||
import { notify } from '@/utils/notify';
|
||||
|
||||
import emitter, { Events } from '@/events';
|
||||
import { unexpectedError } from '../unexpected-error';
|
||||
|
||||
export default async function uploadFile(
|
||||
file: File,
|
||||
@@ -48,12 +48,8 @@ export default async function uploadFile(
|
||||
emitter.emit(Events.upload);
|
||||
|
||||
return response.data.data;
|
||||
} catch (error) {
|
||||
if (options?.notifications) {
|
||||
notify({
|
||||
title: i18n.t('upload_file_failed'),
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
unexpectedError(err);
|
||||
}
|
||||
|
||||
function onUploadProgress(progressEvent: { loaded: number; total: number }) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import uploadFile from '@/utils/upload-file';
|
||||
import notify from '@/utils/notify';
|
||||
import i18n from '@/lang';
|
||||
import { notify } from '@/utils/notify';
|
||||
import { unexpectedError } from '../unexpected-error';
|
||||
|
||||
export default async function uploadFiles(
|
||||
files: File[],
|
||||
@@ -34,14 +35,7 @@ export default async function uploadFiles(
|
||||
}
|
||||
|
||||
return uploadedFiles;
|
||||
} catch (error) {
|
||||
if (options?.notifications) {
|
||||
notify({
|
||||
title: i18n.t('upload_files_failed', { count: files.length }),
|
||||
type: 'error',
|
||||
});
|
||||
}
|
||||
|
||||
throw error;
|
||||
} catch (err) {
|
||||
unexpectedError(err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,11 +24,11 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, PropType } from '@vue/composition-api';
|
||||
|
||||
import notify from '@/utils/notify';
|
||||
import api from '@/api';
|
||||
import i18n from '@/lang';
|
||||
import useShortcut from '@/composables/use-shortcut';
|
||||
import { notify } from '@/utils/notify';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@@ -72,11 +72,8 @@ export default defineComponent({
|
||||
title: i18n.t('post_comment_success'),
|
||||
type: 'success',
|
||||
});
|
||||
} catch {
|
||||
notify({
|
||||
title: i18n.t('post_comment_failed'),
|
||||
type: 'error',
|
||||
});
|
||||
} catch (err) {
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ import { userName } from '@/utils/user-name';
|
||||
|
||||
import api from '@/api';
|
||||
import localizedFormat from '@/utils/localized-format';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@@ -114,8 +115,8 @@ export default defineComponent({
|
||||
await api.delete(`/activity/comment/${props.activity.id}`);
|
||||
await props.refresh();
|
||||
confirmDelete.value = false;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} catch (err) {
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
deleting.value = false;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ import marked from 'marked';
|
||||
import useShortcut from '@/composables/use-shortcut';
|
||||
|
||||
import api from '@/api';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
components: { CommentItemHeader },
|
||||
@@ -84,8 +85,8 @@ export default defineComponent({
|
||||
comment: edits.value,
|
||||
});
|
||||
await props.refresh();
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} catch (err) {
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
savingEdits.value = false;
|
||||
editing.value = false;
|
||||
|
||||
@@ -39,6 +39,7 @@ import useCollection from '@/composables/use-collection';
|
||||
import { useFieldsStore, useRelationsStore } from '@/stores';
|
||||
import i18n from '@/lang';
|
||||
import { Relation, Field } from '@/types';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
model: {
|
||||
@@ -84,7 +85,7 @@ export default defineComponent({
|
||||
junctionRelatedCollectionInfo,
|
||||
setJunctionEdits,
|
||||
} = useJunction();
|
||||
const { _edits, loading, error, item } = useItem();
|
||||
const { _edits, loading, item } = useItem();
|
||||
const { save, cancel } = useActions();
|
||||
|
||||
const { collection } = toRefs(props);
|
||||
@@ -115,7 +116,6 @@ export default defineComponent({
|
||||
_active,
|
||||
_edits,
|
||||
loading,
|
||||
error,
|
||||
item,
|
||||
save,
|
||||
cancel,
|
||||
@@ -162,7 +162,6 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
const item = ref<Record<string, any> | null>(null);
|
||||
|
||||
watch(
|
||||
@@ -173,7 +172,6 @@ export default defineComponent({
|
||||
if (props.relatedPrimaryKey !== '+') fetchRelatedItem();
|
||||
} else {
|
||||
loading.value = false;
|
||||
error.value = null;
|
||||
item.value = null;
|
||||
localEdits.value = {};
|
||||
}
|
||||
@@ -181,7 +179,7 @@ export default defineComponent({
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
return { _edits, loading, error, item, fetchItem };
|
||||
return { _edits, loading, item, fetchItem };
|
||||
|
||||
async function fetchItem() {
|
||||
loading.value = true;
|
||||
@@ -201,7 +199,7 @@ export default defineComponent({
|
||||
|
||||
item.value = response.data.data;
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
@@ -224,7 +222,7 @@ export default defineComponent({
|
||||
[junctionFieldInfo.value.field]: response.data.data,
|
||||
};
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import { nanoid } from 'nanoid';
|
||||
import FilePreview from '@/views/private/components/file-preview';
|
||||
|
||||
import getRootPath from '@/utils/get-root-path';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
type File = {
|
||||
type: string;
|
||||
@@ -69,7 +70,6 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const loading = ref(false);
|
||||
const error = ref(null);
|
||||
const file = ref<File | null>(null);
|
||||
const cacheBuster = ref(nanoid());
|
||||
|
||||
@@ -87,7 +87,7 @@ export default defineComponent({
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
return { _active, cacheBuster, loading, error, file, fileSrc };
|
||||
return { _active, cacheBuster, loading, file, fileSrc };
|
||||
|
||||
async function fetchFile() {
|
||||
cacheBuster.value = nanoid();
|
||||
@@ -103,7 +103,7 @@ export default defineComponent({
|
||||
|
||||
file.value = response.data.data;
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
@@ -114,6 +114,7 @@ import Vue from 'vue';
|
||||
import Cropper from 'cropperjs';
|
||||
import { nanoid } from 'nanoid';
|
||||
import throttle from 'lodash/throttle';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
type Image = {
|
||||
type: string;
|
||||
@@ -260,7 +261,7 @@ export default defineComponent({
|
||||
emit('refresh');
|
||||
_active.value = false;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
saving.value = false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
import NotificationDialogs from './notification-dialogs.vue';
|
||||
|
||||
export { NotificationDialogs };
|
||||
export default NotificationDialogs;
|
||||
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div class="notification-dialogs">
|
||||
<v-dialog :active="true" v-for="notification in notifications" :key="notification.id" persist>
|
||||
<v-card :class="[notification.type]">
|
||||
<v-card-title>{{ notification.title }}</v-card-title>
|
||||
<v-card-text v-if="notification.text">
|
||||
{{ notification.text }}
|
||||
|
||||
<v-error v-if="notification.error" :error="notification.error" />
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-button
|
||||
secondary
|
||||
v-if="notification.type === 'error' && admin && notification.code === 'UNKNOWN'"
|
||||
>
|
||||
<a target="_blank" :href="getGitHubIssueLink(notification.id, notification)">
|
||||
{{ $t('report_error') }}
|
||||
</a>
|
||||
</v-button>
|
||||
<v-button @click="done(notification.id)">{{ $t('dismiss') }}</v-button>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed, ref, watch } from '@vue/composition-api';
|
||||
import SidebarButton from '../sidebar-button';
|
||||
import NotificationItem from '../notification-item';
|
||||
import { useNotificationsStore, useUserStore } from '@/stores/';
|
||||
import router from '@/router';
|
||||
import { Notification } from '@/types';
|
||||
import { useProjectInfo } from '@/modules/settings/composables/use-project-info';
|
||||
|
||||
export default defineComponent({
|
||||
components: { SidebarButton, NotificationItem },
|
||||
setup(props) {
|
||||
const { parsedInfo } = useProjectInfo();
|
||||
const notificationsStore = useNotificationsStore();
|
||||
const userStore = useUserStore();
|
||||
|
||||
const notifications = computed(() => notificationsStore.state.dialogs);
|
||||
|
||||
return { notifications, admin: userStore.isAdmin, done, getGitHubIssueLink };
|
||||
|
||||
function getGitHubIssueLink(id: string, notification: Notification) {
|
||||
const debugInfo = `<!-- Please put a detailed explanation of the problem here. -->
|
||||
|
||||
---
|
||||
|
||||
### Project details
|
||||
Directus Version: ${parsedInfo.value?.directus.version}
|
||||
Environment: ${process.env.NODE_ENV}
|
||||
OS: ${parsedInfo.value?.os.type} ${parsedInfo.value?.os.version}
|
||||
Node: ${parsedInfo.value?.node.version}
|
||||
|
||||
### Error
|
||||
|
||||
Title: ${notification.title || 'none'}
|
||||
Message: ${notification.text || 'none'}
|
||||
|
||||
<details>
|
||||
<summary>Stack Trace</summary>
|
||||
<pre>
|
||||
${JSON.stringify(notification.error, Object.getOwnPropertyNames(notification.error), 2)}
|
||||
</pre>
|
||||
</details>
|
||||
`;
|
||||
|
||||
return `https://github.com/directus/next/issues/new?body=${encodeURIComponent(debugInfo)}`;
|
||||
}
|
||||
|
||||
function done(id: string) {
|
||||
notificationsStore.remove(id);
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.notification-dialogs {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.v-error {
|
||||
margin-top: 12px;
|
||||
}
|
||||
</style>
|
||||
@@ -48,6 +48,7 @@ import i18n from '@/lang';
|
||||
import formatLocalized from '@/utils/localized-format';
|
||||
import RevisionItem from './revision-item.vue';
|
||||
import RevisionsDrawer from './revisions-drawer.vue';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
components: { RevisionItem, RevisionsDrawer },
|
||||
@@ -62,10 +63,7 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
const { revisions, revisionsByDate, loading, error, refresh } = useRevisions(
|
||||
props.collection,
|
||||
props.primaryKey
|
||||
);
|
||||
const { revisions, revisionsByDate, loading, refresh } = useRevisions(props.collection, props.primaryKey);
|
||||
|
||||
const hasCreate = computed(() => {
|
||||
// We expect the very first revision record to be a creation
|
||||
@@ -83,7 +81,6 @@ export default defineComponent({
|
||||
revisions,
|
||||
revisionsByDate,
|
||||
loading,
|
||||
error,
|
||||
refresh,
|
||||
hasCreate,
|
||||
modalActive,
|
||||
@@ -100,15 +97,13 @@ export default defineComponent({
|
||||
function useRevisions(collection: string, primaryKey: number | string) {
|
||||
const revisions = ref<Revision[] | null>(null);
|
||||
const revisionsByDate = ref<RevisionsByDate[] | null>(null);
|
||||
const error = ref(null);
|
||||
const loading = ref(false);
|
||||
|
||||
getRevisions();
|
||||
|
||||
return { revisions, revisionsByDate, error, loading, refresh };
|
||||
return { revisions, revisionsByDate, loading, refresh };
|
||||
|
||||
async function getRevisions() {
|
||||
error.value = null;
|
||||
loading.value = true;
|
||||
|
||||
try {
|
||||
@@ -172,7 +167,7 @@ export default defineComponent({
|
||||
revisionsByDate.value = orderBy(revisionsGrouped, ['date'], ['desc']);
|
||||
revisions.value = orderBy(response.data.data, ['activity.timestamp'], ['desc']);
|
||||
} catch (err) {
|
||||
error.value = err;
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ import RevisionsDrawerPicker from './revisions-drawer-picker.vue';
|
||||
import RevisionsDrawerPreview from './revisions-drawer-preview.vue';
|
||||
import RevisionsDrawerUpdates from './revisions-drawer-updates.vue';
|
||||
import api from '@/api';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
components: { RevisionsDrawerPicker, RevisionsDrawerPreview, RevisionsDrawerUpdates },
|
||||
@@ -126,7 +127,7 @@ export default defineComponent({
|
||||
_active.value = false;
|
||||
emit('revert');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
reverting.value = false;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ import { defineComponent, computed, ref, PropType, watch } from '@vue/compositio
|
||||
import api from '@/api';
|
||||
import { useNotificationsStore } from '@/stores';
|
||||
import i18n from '@/lang';
|
||||
import { unexpectedError } from '@/utils/unexpected-error';
|
||||
|
||||
export default defineComponent({
|
||||
model: {
|
||||
@@ -79,12 +80,7 @@ export default defineComponent({
|
||||
);
|
||||
emit('toggle', false);
|
||||
} catch (err) {
|
||||
notifications.add({
|
||||
title: i18n.t('server_error'),
|
||||
text: err.message,
|
||||
persist: true,
|
||||
type: 'error',
|
||||
});
|
||||
unexpectedError(err);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
<v-overlay class="sidebar-overlay" :active="sidebarOpen" @click="sidebarOpen = false" />
|
||||
|
||||
<notifications-group v-if="notificationsPreviewActive === false" :dense="sidebarOpen === false" />
|
||||
<notification-dialogs />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -63,6 +64,7 @@ import ProjectInfo from './components/project-info';
|
||||
import SidebarButton from './components/sidebar-button/';
|
||||
import NotificationsGroup from './components/notifications-group/';
|
||||
import NotificationsPreview from './components/notifications-preview/';
|
||||
import NotificationDialogs from './components/notification-dialogs/';
|
||||
import { useUserStore, useAppStore } from '@/stores';
|
||||
import i18n from '@/lang';
|
||||
import emitter, { Events } from '@/events';
|
||||
@@ -76,6 +78,7 @@ export default defineComponent({
|
||||
SidebarButton,
|
||||
NotificationsGroup,
|
||||
NotificationsPreview,
|
||||
NotificationDialogs,
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
|
||||
@@ -133,8 +133,8 @@ export default defineComponent({
|
||||
transition: max-width var(--medium) var(--transition);
|
||||
|
||||
.content {
|
||||
max-width: 100%;
|
||||
width: 340px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
&.wide {
|
||||
@@ -161,8 +161,8 @@ export default defineComponent({
|
||||
background-size: cover;
|
||||
|
||||
.foreground {
|
||||
max-width: 400px;
|
||||
width: 80%;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.note {
|
||||
|
||||
Reference in New Issue
Block a user