diff --git a/api/package-lock.json b/api/package-lock.json index 0211af9546..120203ccaf 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -1,6 +1,6 @@ { "name": "directus", - "version": "9.0.0-alpha.32", + "version": "9.0.0-alpha.33", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -3882,11 +3882,6 @@ } } }, - "knex-schema-inspector": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/knex-schema-inspector/-/knex-schema-inspector-0.0.9.tgz", - "integrity": "sha512-WN3m3dSxadXKtXYIyP3bRLHr786EjUXzbj6PweTc6CJKOOEWHA/FK8aOqNcYAFzjIT8opNR/iAgV8ZssKerDpA==" - }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", diff --git a/api/package.json b/api/package.json index f8f33d7efc..39126785da 100644 --- a/api/package.json +++ b/api/package.json @@ -1,6 +1,6 @@ { "name": "directus", - "version": "9.0.0-alpha.32", + "version": "9.0.0-alpha.33", "license": "GPL-3.0-only", "homepage": "https://github.com/directus/next#readme", "description": "Directus is a real-time API and App dashboard for managing SQL database content.", @@ -64,7 +64,7 @@ "example.env" ], "dependencies": { - "@directus/app": "^9.0.0-alpha.32", + "@directus/app": "^9.0.0-alpha.33", "@directus/format-title": "^3.2.0", "@slynova/flydrive": "^1.0.2", "@slynova/flydrive-gcs": "^1.0.2", diff --git a/api/src/database/seeds/system.yaml b/api/src/database/seeds/system.yaml index e0be01d795..d51e26ef0e 100644 --- a/api/src/database/seeds/system.yaml +++ b/api/src/database/seeds/system.yaml @@ -634,22 +634,24 @@ rows: special: json interface: repeater options: - template: '{{ locale }}' + template: '{{ translation }} ({{ locale }})' fields: - field: locale - name: Locale + name: Language type: string + schema: + default_value: en-US meta: - interface: language - options: - limit: true + interface: system-language width: half - field: translation name: Translation type: string meta: - interface: text-input + interface: system-language width: half + options: + placeholder: Enter a translation... locked: true sort: 8 width: full diff --git a/app/package.json b/app/package.json index eae45ec1ad..937771776f 100644 --- a/app/package.json +++ b/app/package.json @@ -1,6 +1,6 @@ { "name": "@directus/app", - "version": "9.0.0-alpha.32", + "version": "9.0.0-alpha.33", "private": false, "description": "Directus is an Open-Source Headless CMS & API for Managing Custom Databases", "author": "Rijk van Zanten ", diff --git a/app/src/api.ts b/app/src/api.ts index 6a814f85ff..45bf3402e3 100644 --- a/app/src/api.ts +++ b/app/src/api.ts @@ -64,7 +64,7 @@ export const onError = async (error: RequestError) => { try { newToken = await refresh(); } catch { - logout({ reason: LogoutReason.ERROR_SESSION_EXPIRED }); + logout({ reason: LogoutReason.SESSION_EXPIRED }); return Promise.reject(); } diff --git a/app/src/auth.ts b/app/src/auth.ts index 8bef238765..34c55f3654 100644 --- a/app/src/auth.ts +++ b/app/src/auth.ts @@ -49,13 +49,13 @@ export async function refresh({ navigate }: LogoutOptions = { navigate: true }) return accessToken; } catch (error) { - await logout({ navigate, reason: LogoutReason.ERROR_SESSION_EXPIRED }); + await logout({ navigate, reason: LogoutReason.SESSION_EXPIRED }); } } export enum LogoutReason { SIGN_OUT = 'SIGN_OUT', - ERROR_SESSION_EXPIRED = 'ERROR_SESSION_EXPIRED', + SESSION_EXPIRED = 'SESSION_EXPIRED', } export type LogoutOptions = { diff --git a/app/src/components/v-icon/custom-icons/bookmark_save.vue b/app/src/components/v-icon/custom-icons/bookmark_save.vue new file mode 100644 index 0000000000..21795b8a63 --- /dev/null +++ b/app/src/components/v-icon/custom-icons/bookmark_save.vue @@ -0,0 +1,21 @@ + + + diff --git a/app/src/components/v-icon/v-icon.vue b/app/src/components/v-icon/v-icon.vue index a2ec106598..5205c5114c 100644 --- a/app/src/components/v-icon/v-icon.vue +++ b/app/src/components/v-icon/v-icon.vue @@ -16,6 +16,7 @@ import { defineComponent, computed } from '@vue/composition-api'; import useSizeClass, { sizeProps } from '@/composables/size-class'; import CustomIconDirectus from './custom-icons/directus.vue'; +import CustomIconBookmarkSave from './custom-icons/bookmark_save.vue'; import CustomIconBox from './custom-icons/box.vue'; import CustomIconCommitNode from './custom-icons/commit_node.vue'; import CustomIconGrid1 from './custom-icons/grid_1.vue'; @@ -34,6 +35,7 @@ import CustomIconLogout from './custom-icons/logout.vue'; const customIcons: string[] = [ 'directus', + 'bookmark_save', 'box', 'commit_node', 'grid_1', @@ -54,6 +56,7 @@ const customIcons: string[] = [ export default defineComponent({ components: { CustomIconDirectus, + CustomIconBookmarkSave, CustomIconBox, CustomIconCommitNode, CustomIconGrid1, diff --git a/app/src/components/v-tabs/readme.md b/app/src/components/v-tabs/readme.md index a3505cac55..716c35b13e 100644 --- a/app/src/components/v-tabs/readme.md +++ b/app/src/components/v-tabs/readme.md @@ -13,7 +13,7 @@ device. Help - + I'm the content for Home! I'm the content for News! I'm the content for Help! diff --git a/app/src/composables/use-preset/use-preset.ts b/app/src/composables/use-preset/use-preset.ts index 3612230984..bef17c2505 100644 --- a/app/src/composables/use-preset/use-preset.ts +++ b/app/src/composables/use-preset/use-preset.ts @@ -9,26 +9,47 @@ export function usePreset(collection: Ref, bookmark: Ref const presetsStore = usePresetsStore(); const userStore = useUserStore(); + const busy = ref(false); + const { info: collectionInfo } = useCollection(collection); const bookmarkExists = computed(() => { if (!bookmark.value) return false; - return !!presetsStore.state.collectionPresets.find((preset) => preset.id === bookmark.value); }); const localPreset = ref>({}); initLocalPreset(); + const bookmarkSaved = computed(() => localPreset.value.$saved !== false); + const bookmarkIsMine = computed(() => localPreset.value.user === userStore.state.currentUser!.id); + const savePreset = async (preset?: Partial) => { + busy.value = true; const updatedValues = await presetsStore.savePreset(preset ? preset : localPreset.value); + initLocalPreset(); localPreset.value.id = updatedValues.id; + busy.value = false; return updatedValues; }; + const saveLocal = () => { + presetsStore.saveLocal(localPreset.value); + initLocalPreset(); + }; + + const clearLocalSave = async () => { + busy.value = true; + await presetsStore.clearLocalSave(localPreset.value); + initLocalPreset(); + busy.value = false; + }; + const autoSave = debounce(async () => { if (!bookmark || bookmark.value === null) { savePreset(); + } else { + saveLocal(); } }, 450); @@ -143,6 +164,10 @@ export function usePreset(collection: Ref, bookmark: Ref saveCurrentAsBookmark, title, resetPreset, + bookmarkSaved, + bookmarkIsMine, + busy, + clearLocalSave, }; async function resetPreset() { diff --git a/app/src/composables/use-sync/use-sync.ts b/app/src/composables/use-sync/use-sync.ts index 7fcdf999f0..21bfc436f0 100644 --- a/app/src/composables/use-sync/use-sync.ts +++ b/app/src/composables/use-sync/use-sync.ts @@ -1,15 +1,13 @@ import { computed, Ref } from '@vue/composition-api'; -import { clone } from 'lodash'; export default function useSync( props: T, key: K, - emit: (event: string, ...args: any[]) => void ): Ref> { return computed({ get() { - return clone(props[key]); + return props[key]; }, set(newVal) { emit(`update:${key}`, newVal); diff --git a/app/src/displays/badge/badge.vue b/app/src/displays/badge/badge.vue deleted file mode 100644 index d358c5a6ae..0000000000 --- a/app/src/displays/badge/badge.vue +++ /dev/null @@ -1,70 +0,0 @@ - - - - - diff --git a/app/src/displays/badge/index.ts b/app/src/displays/labels/index.ts similarity index 82% rename from app/src/displays/badge/index.ts rename to app/src/displays/labels/index.ts index 36f3804678..856885f833 100644 --- a/app/src/displays/badge/index.ts +++ b/app/src/displays/labels/index.ts @@ -1,12 +1,12 @@ import { defineDisplay } from '@/displays/define'; -import DisplayBadge from './badge.vue'; +import DisplayLabels from './labels.vue'; export default defineDisplay(({ i18n }) => ({ - id: 'badge', - name: i18n.t('badge'), - types: ['string'], + id: 'labels', + name: i18n.t('labels'), + types: ['string', 'json'], icon: 'flag', - handler: DisplayBadge, + handler: DisplayLabels, options: [ { field: 'defaultForeground', @@ -32,6 +32,18 @@ export default defineDisplay(({ i18n }) => ({ default_value: '#eceff1', }, }, + { + field: 'format', + name: i18n.t('format_text'), + type: 'boolean', + meta: { + width: 'half-left', + interface: 'toggle', + }, + schema: { + default_value: true, + }, + }, { field: 'choices', name: i18n.t('choices'), diff --git a/app/src/displays/labels/labels.vue b/app/src/displays/labels/labels.vue new file mode 100644 index 0000000000..fb73b79b1e --- /dev/null +++ b/app/src/displays/labels/labels.vue @@ -0,0 +1,99 @@ + + + + + diff --git a/app/src/displays/tags/index.ts b/app/src/displays/tags/index.ts deleted file mode 100644 index 1f3b6ffd03..0000000000 --- a/app/src/displays/tags/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { defineDisplay } from '@/displays/define'; -import DisplayTags from './tags.vue'; - -export default defineDisplay(({ i18n }) => ({ - id: 'tags', - name: i18n.t('tags'), - types: ['json'], - icon: 'label', - handler: DisplayTags, - options: [ - { - field: 'format', - name: i18n.t('format_text'), - type: 'boolean', - meta: { - width: 'half', - interface: 'toggle', - }, - schema: { - default_value: true, - }, - }, - ], -})); diff --git a/app/src/displays/tags/readme.md b/app/src/displays/tags/readme.md deleted file mode 100644 index 8d959d6588..0000000000 --- a/app/src/displays/tags/readme.md +++ /dev/null @@ -1,3 +0,0 @@ -# Tags - -Renders a CSV of strings as individual chips. diff --git a/app/src/displays/tags/tags.story.ts b/app/src/displays/tags/tags.story.ts deleted file mode 100644 index 4a8d02be65..0000000000 --- a/app/src/displays/tags/tags.story.ts +++ /dev/null @@ -1,24 +0,0 @@ -import withPadding from '../../../.storybook/decorators/with-padding'; -import { withKnobs, array } from '@storybook/addon-knobs'; -import readme from './readme.md'; -import { defineComponent } from '@vue/composition-api'; - -export default { - title: 'Displays / Tags', - decorators: [withPadding, withKnobs], - parameters: { - notes: readme, - }, -}; - -export const basic = () => - defineComponent({ - props: { - value: { - default: array('Value', ['vip', 'executive']), - }, - }, - template: ` - - `, - }); diff --git a/app/src/displays/tags/tags.test.ts b/app/src/displays/tags/tags.test.ts deleted file mode 100644 index 818533745c..0000000000 --- a/app/src/displays/tags/tags.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import DisplayTags from './tags.vue'; -import { createLocalVue, shallowMount } from '@vue/test-utils'; -import VueCompositionAPI from '@vue/composition-api'; -import VChip from '@/components/v-chip'; - -const localVue = createLocalVue(); -localVue.use(VueCompositionAPI); -localVue.component('v-chip', VChip); - -describe('Displays / Tags', () => { - it('Renders a chip for every value', () => { - const component = shallowMount(DisplayTags, { - localVue, - propsData: { - value: ['tag 1', 'tag 2', 'tag 3'], - }, - }); - - expect(component.findAll(VChip).length).toBe(3); - }); -}); diff --git a/app/src/displays/tags/tags.vue b/app/src/displays/tags/tags.vue deleted file mode 100644 index 65f1f1af5e..0000000000 --- a/app/src/displays/tags/tags.vue +++ /dev/null @@ -1,38 +0,0 @@ - - - - - diff --git a/app/src/interfaces/system-language/index.ts b/app/src/interfaces/system-language/index.ts new file mode 100644 index 0000000000..ff67cfdf90 --- /dev/null +++ b/app/src/interfaces/system-language/index.ts @@ -0,0 +1,12 @@ +import InterfaceSystemLanguage from './system-language.vue'; +import { defineInterface } from '@/interfaces/define'; + +export default defineInterface(({ i18n }) => ({ + id: 'system-language', + name: i18n.t('language'), + icon: 'translate', + component: InterfaceSystemLanguage, + system: true, + types: ['string'], + options: [], +})); diff --git a/app/src/interfaces/system-language/system-language.vue b/app/src/interfaces/system-language/system-language.vue new file mode 100644 index 0000000000..b2779412b6 --- /dev/null +++ b/app/src/interfaces/system-language/system-language.vue @@ -0,0 +1,29 @@ + + + diff --git a/app/src/lang/en-US/index.json b/app/src/lang/en-US/index.json index ba89a1ec99..c69988346a 100644 --- a/app/src/lang/en-US/index.json +++ b/app/src/lang/en-US/index.json @@ -19,6 +19,17 @@ "create_user": "Create User", "rename_folder": "Rename Folder", + "delete_folder": "Delete Folder", + "reset_bookmark": "Reset Bookmark", + "rename_bookmark": "Rename Bookmark", + "update_bookmark": "Update Bookmark", + "delete_bookmark": "Delete Bookmark", + "delete_bookmark_copy": "Are you sure you want to delete the \"{bookmark}\" bookmark? This action cannot be undone.", + + "logoutReason": { + "SIGN_OUT": "Signed out", + "SESSION_EXPIRED": "Session expired" + }, "public": "Public", "public_description": "Controls what API data is available without authenticating.", @@ -92,6 +103,8 @@ "color_dot": "Color Dot", "default_color": "Default Color", + "labels": "Labels", + "global": "Global", "admins_have_all_permissions": "Admins have all permissions", @@ -281,7 +294,6 @@ "submit": "Submit", "move_to_folder": "Move to Folder", - "delete_folder": "Delete Folder", "select_folder": "Select Folder", "move": "Move", @@ -1022,8 +1034,6 @@ "delete": "Delete", "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_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/app/src/layouts/tabular/tabular.vue b/app/src/layouts/tabular/tabular.vue index ecc1fa5f9c..97f38a1eb9 100644 --- a/app/src/layouts/tabular/tabular.vue +++ b/app/src/layouts/tabular/tabular.vue @@ -143,7 +143,7 @@ + + diff --git a/app/src/modules/collections/components/navigation.vue b/app/src/modules/collections/components/navigation.vue index ac985e0630..2043644485 100644 --- a/app/src/modules/collections/components/navigation.vue +++ b/app/src/modules/collections/components/navigation.vue @@ -19,10 +19,7 @@
@@ -33,17 +30,18 @@ {{ $t('no_collections_copy') }}
-