Merge branch 'main' into feature-redis-cache

This commit is contained in:
Tanya Byrne
2020-08-19 16:48:14 +01:00
50 changed files with 879 additions and 160 deletions

View File

@@ -79,7 +79,9 @@ router.patch(
return res.json({ data: item || null });
}
throw new RouteNotFoundException(req.path);
const primaryKeys = await service.update(req.body);
const result = await service.readByKey(primaryKeys, req.sanitizedQuery);
return res.json({ data: result || null });
})
);
@@ -96,7 +98,6 @@ router.patch(
const primaryKey = req.params.pk.includes(',') ? req.params.pk.split(',') : req.params.pk;
const updatedPrimaryKey = await service.update(req.body, primaryKey as any);
const result = await service.readByKey(updatedPrimaryKey, req.sanitizedQuery);
res.json({ data: result || null });

View File

@@ -22,6 +22,9 @@ tables:
display_template:
type: string
length: 255
sort_field:
type: string
length: 64
directus_roles:
id:
@@ -84,6 +87,8 @@ tables:
type: text
tags:
type: json
avatar:
type: uuid
timezone:
type: string
length: 255
@@ -115,8 +120,6 @@ tables:
length: 255
last_login:
type: timestamp
avatar:
type: uuid
last_page:
type: string
length: 255
@@ -628,6 +631,8 @@ rows:
- collection: directus_roles
field: name
interface: text-input
options:
placeholder: The unique name for this role...
locked: true
sort: 1
width: half

View File

@@ -0,0 +1,16 @@
<template functional>
<svg
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill-rule="evenodd"
clip-rule="evenodd"
stroke-linejoin="round"
stroke-miterlimit="2"
>
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.2 14.15a1.8 1.8 0 01-.35-.12c-.1-.05-.14-.18-.13-.3.02-.41-.01-.78.03-1.2.18-1.85 1.38-1.26 2.46-1.57.58-.16 1.17-.47 1.42-1.06.07-.16.02-.35-.1-.48a13.66 13.66 0 00-2.27-1.98 14.25 14.25 0 00-9.85-2.31.2.2 0 00-.15.32A5.17 5.17 0 0011.93 7c.1.06.06.2-.06.18-.3-.06-.68-.18-1.06-.4a.43.43 0 00-.38-.05l-.44.18a.2.2 0 00-.05.34 5.32 5.32 0 006.14.42c.1-.06.25.07.22.18-.07.21-.15.52-.23.95-.48 2.37-1.87 2.18-3.58 1.59-3.36-1.19-5.3-.22-7-2.1-.19-.21-.5-.29-.7-.09a1.55 1.55 0 00.1 2.29c.15.12.36.07.5-.06.07-.06.13-.1.21-.14.1-.04.16.11.07.18-.4.33-.51.7-.76 1.48-.38 1.16-.22 2.36-1.98 2.67-.93.04-.91.66-1.25 1.58A5.2 5.2 0 01.3 18.27c-.4.41-.44 1.18.14 1.23.18 0 .36-.03.55-.1.99-.4 1.75-1.65 2.47-2.46.8-.9 2.72-.51 4.17-1.4.82-.48 1.3-1.1 1.08-2.07-.02-.12.12-.2.18-.09.1.2.18.41.23.63.05.22.25.39.47.4 1.36.1 3.02 1.3 4.63 1.88.32.11.56-.26.43-.57-.1-.24-.18-.48-.23-.7-.02-.13.16-.16.22-.05a3.5 3.5 0 002.88 1.88c.46.03.97-.02 1.5-.18.63-.18 1.22-.42 1.91-.3.52.1 1 .36 1.3.79.36.5 1.04.7 1.54.38.28-.19.29-.58.13-.87-1.14-2.08-3.61-2.25-4.7-2.52z"/>
</svg>
</template>
<script lang="ts">
export default {};
</script>

View File

@@ -15,6 +15,7 @@
import { defineComponent, computed } from '@vue/composition-api';
import useSizeClass, { sizeProps } from '@/composables/size-class';
import CustomIconDirectus from './custom-icons/directus.vue';
import CustomIconBox from './custom-icons/box.vue';
import CustomIconCommitNode from './custom-icons/commit_node.vue';
import CustomIconGrid1 from './custom-icons/grid_1.vue';
@@ -31,6 +32,7 @@ import CustomIconFlipVertical from './custom-icons/flip_vertical.vue';
import CustomIconFolderMove from './custom-icons/folder_move.vue';
const customIcons: string[] = [
'directus',
'box',
'commit_node',
'grid_1',
@@ -49,6 +51,7 @@ const customIcons: string[] = [
export default defineComponent({
components: {
CustomIconDirectus,
CustomIconBox,
CustomIconCommitNode,
CustomIconGrid1,

View File

@@ -120,10 +120,14 @@ body {
@keyframes indeterminate {
0% {
transform: scaleX(0);
}
10% {
transform: scaleX(0);
animation-timing-function: cubic-bezier(0.1, 0.6, 0.9, 0.5);
}
50% {
60% {
transform: scaleX(1) translateX(25%);
animation-timing-function: cubic-bezier(0.4, 0.1, 0.2, 0.9);
}

View File

@@ -33,7 +33,7 @@ export function useCollection(collectionKey: string | Ref<string>) {
});
const sortField = computed(() => {
return fields.value?.find((field) => field.meta?.special === 'sort') || null;
return info.value?.meta?.sort_field || null;
});
type Status = {

View File

@@ -8,6 +8,7 @@ import {
useSettingsStore,
useLatencyStore,
useRelationsStore,
usePermissionsStore,
} from '@/stores';
import { setLanguage, Language } from '@/lang';
@@ -30,6 +31,7 @@ export function useStores(
useSettingsStore,
useLatencyStore,
useRelationsStore,
usePermissionsStore,
]
) {
return stores.map((useStore) => useStore()) as GenericStore[];

View File

@@ -13,6 +13,7 @@ import InterfaceIcon from './icon';
import InterfaceImage from './image';
import InterfaceManyToMany from './many-to-many';
import InterfaceManyToOne from './many-to-one';
import InterfaceMarkdown from './markdown';
import InterfaceNotice from './notice';
import InterfaceNumeric from './numeric/';
import InterfaceOneToMany from './one-to-many';
@@ -45,6 +46,7 @@ export const interfaces = [
InterfaceImage,
InterfaceManyToMany,
InterfaceManyToOne,
InterfaceMarkdown,
InterfaceNotice,
InterfaceNumeric,
InterfaceOneToMany,

View File

@@ -0,0 +1,132 @@
# h1 Heading
## h2 Heading
### h3 Heading
#### h4 Heading
##### h5 Heading
###### h6 Heading
## Horizontal Rules
___
---
***
## Emphasis
**This is bold text**
__This is bold text__
*This is italic text*
_This is italic text_
~~Strikethrough~~
## Blockquotes
> Blockquotes can also be nested...
>> ...by using additional greater-than signs right next to each other...
> > > ...or with spaces between arrows.
## Lists
Unordered
+ Create a list by starting a line with `+`, `-`, or `*`
+ Sub-lists are made by indenting 2 spaces:
- Marker character change forces new list start:
* Ac tristique libero volutpat at
+ Facilisis in pretium nisl aliquet
- Nulla volutpat aliquam velit
+ Very easy!
Ordered
1. Lorem ipsum dolor sit amet
2. Consectetur adipiscing elit
3. Integer molestie lorem at massa
1. You can use sequential numbers...
1. ...or keep all the numbers as `1.`
Start numbering with offset:
57. foo
1. bar
## Code
Inline `code`
Indented code
// Some comments
line 1 of code
line 2 of code
line 3 of code
Block code "fences"
```
Sample text here...
```
Syntax highlighting
``` js
var foo = function (bar) {
return bar++;
};
console.log(foo(5));
```
## Tables
| Option | Description |
| ------ | ----------- |
| data | path to data files to supply the data that will be passed into templates. |
| engine | engine to be used for processing templates. Handlebars is the default. |
| ext | extension to be used for dest files. |
Right aligned columns
| Option | Description |
| ------:| -----------:|
| data | path to data files to supply the data that will be passed into templates. |
| engine | engine to be used for processing templates. Handlebars is the default. |
| ext | extension to be used for dest files. |
## Links
[link text](http://dev.nodeca.com)
[link with title](http://nodeca.github.io/pica/demo/ "title text!")
Autoconverted link https://github.com/nodeca/pica (enable linkify to see)
## Images
![Minion](https://octodex.github.com/images/minion.png)
![Stormtroopocat](https://octodex.github.com/images/stormtroopocat.jpg "The Stormtroopocat")
Like links, Images also have a footnote style syntax
![Alt text][id]
With a reference later in the document defining the URL location:
[id]: https://octodex.github.com/images/dojocat.jpg "The Dojocat"

View File

@@ -0,0 +1,30 @@
import InterfaceMarkdown from './markdown.vue';
import { defineInterface } from '@/interfaces/define';
export default defineInterface(({ i18n }) => ({
id: 'markdown',
name: i18n.t('markdown'),
icon: 'text_fields',
component: InterfaceMarkdown,
types: ['text'],
options: [
{
field: 'placeholder',
name: i18n.t('placeholder'),
type: 'string',
meta: {
width: 'half',
interface: 'text-input',
},
},
{
field: 'tabbed',
name: i18n.t('tabbed'),
type: 'boolean',
meta: {
width: 'half',
interface: 'toggle',
},
},
],
}));

View File

@@ -0,0 +1,51 @@
import { withKnobs, boolean, text, optionsKnob } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';
import Vue from 'vue';
import InterfaceMarkdown from './markdown.vue';
import markdown from './readme.md';
import withPadding from '../../../.storybook/decorators/with-padding';
import { defineComponent, ref } from '@vue/composition-api';
import RawValue from '../../../.storybook/raw-value.vue';
import i18n from '@/lang';
Vue.component('interface-markdown', InterfaceMarkdown);
export default {
title: 'Interfaces / Markdown',
decorators: [withKnobs, withPadding],
parameters: {
notes: markdown,
},
};
export const basic = () =>
defineComponent({
components: { RawValue },
i18n,
props: {
disabled: {
default: boolean('Disabled', false, 'Options'),
},
placeholder: {
default: text('Placeholder', 'Enter a value...', 'Options'),
},
tabbed: {
default: boolean('Tabbed', false, 'Options'),
},
},
setup() {
const value = ref('');
const onInput = action('input');
return { onInput, value };
},
template: `
<div>
<interface-markdown
v-model="value"
v-bind="{ placeholder, tabbed, disabled }"
@input="onInput"
/>
<raw-value>{{ value }}</raw-value>
</div>
`,
});

View File

@@ -0,0 +1,24 @@
import VueCompositionAPI from '@vue/composition-api';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import InterfaceMarkdown from './markdown.vue';
import VTextarea from '@/components/v-textarea';
const localVue = createLocalVue();
localVue.use(VueCompositionAPI);
localVue.component('v-textarea', VTextarea);
describe('Interfaces / Markdown', () => {
it('Renders a v-markdown', () => {
const component = shallowMount(InterfaceMarkdown, {
localVue,
propsData: {
placeholder: 'Enter value...',
},
listeners: {
input: () => {},
},
});
expect(component.find(VTextarea).exists()).toBe(true);
});
});

View File

@@ -0,0 +1,340 @@
<template>
<div class="interface-markdown" :class="{ tabbed }">
<div v-if="tabbed" class="toolbar">
<v-tabs v-model="currentTab">
<v-tab>
<v-icon name="code" left />
{{ $t('edit') }}
</v-tab>
<v-tab>
<v-icon name="visibility" left />
{{ $t('preview') }}
</v-tab>
</v-tabs>
</div>
<v-textarea
v-show="showEdit"
:placeholder="placeholder"
:value="value"
:disabled="disabled"
@input="$listeners.input"
/>
<div v-show="showPreview" class="preview-container">
<div class="preview" v-html="html"></div>
</div>
</div>
</template>
<script lang="ts">
import marked from 'marked';
import { defineComponent, computed, ref } from '@vue/composition-api';
export default defineComponent({
props: {
value: {
type: String,
default: null,
},
disabled: {
type: Boolean,
default: false,
},
placeholder: {
type: String,
default: null,
},
tabbed: {
type: Boolean,
default: true,
},
},
setup(props) {
const currentTab = ref([0]);
const html = computed(() => (props.value ? marked(props.value) : ''));
const showEdit = computed(() => !props.tabbed || currentTab.value[0] === 0);
const showPreview = computed(() => !props.tabbed || currentTab.value[0] !== 0);
return { html, currentTab, showEdit, showPreview };
},
});
</script>
<style lang="scss" scoped>
.interface-markdown {
--v-textarea-min-height: var(--input-height-tall);
--v-textarea-max-height: 400px;
display: flex;
flex-wrap: wrap;
.toolbar {
width: 100%;
border: var(--border-width) solid var(--border-normal);
}
.v-textarea {
height: unset;
border-radius: var(--border-radius) 0 0 var(--border-radius);
}
.preview-container {
min-height: var(--v-textarea-min-height);
max-height: var(--v-textarea-max-height);
padding: var(--input-padding);
overflow-y: auto;
border: var(--border-width) solid var(--border-normal);
border-radius: 0 var(--border-radius) var(--border-radius) 0;
}
.v-textarea,
.preview-container {
flex-basis: 100px;
flex-grow: 1;
}
&:not(.tabbed) .preview-container {
border-left: none;
}
&.tabbed .v-textarea {
border-top: none;
border-radius: 0 0 var(--border-radius) var(--border-radius);
}
&.tabbed .preview-container {
border-top: none;
border-radius: 0 0 var(--border-radius) var(--border-radius);
}
::v-deep {
.preview {
font-weight: 400;
font-size: 14px;
line-height: 1.6;
& > *:first-child {
margin-top: 0;
}
& > *:last-child {
margin-bottom: 0;
}
h1,
h2,
h3,
h4,
h5,
h6 {
position: relative;
margin: 20px 0 10px;
padding: 0;
font-weight: 600;
cursor: text;
}
pre {
padding: 6px 10px;
overflow: auto;
font-size: 13px;
line-height: 19px;
background-color: var(--background-page);
border: 1px solid var(--background-normal);
border-radius: var(--border-radius);
}
code,
tt {
margin: 0 2px;
padding: 0 5px;
white-space: nowrap;
background-color: var(--background-page);
border: 1px solid var(--background-normal);
border-radius: var(--border-radius);
}
pre code {
margin: 0;
padding: 0;
white-space: pre;
background: transparent;
border: none;
}
pre code,
pre tt {
background-color: transparent;
border: none;
}
h1 tt,
h1 code {
font-size: inherit;
}
h2 tt,
h2 code {
font-size: inherit;
}
h3 tt,
h3 code {
font-size: inherit;
}
h4 tt,
h4 code {
font-size: inherit;
}
h5 tt,
h5 code {
font-size: inherit;
}
h6 tt,
h6 code {
font-size: inherit;
}
h1 {
font-size: 28px;
}
h2 {
font-size: 24px;
}
h3 {
font-size: 18px;
}
h4 {
font-size: 16px;
}
h5 {
font-size: 14px;
}
h6 {
color: var(--foreground-normal);
font-size: 14px;
}
p,
blockquote,
ul,
ol,
dl,
li,
table,
pre {
margin: 15px 0;
}
& > h2:first-child {
margin-top: 0;
padding-top: 0;
}
& > h1:first-child {
margin-top: 0;
padding-top: 0;
}
& > h1:first-child + h2 {
margin-top: 0;
padding-top: 0;
}
& > h3:first-child,
& > h4:first-child,
& > h5:first-child,
& > h6:first-child {
margin-top: 0;
padding-top: 0;
}
a:first-child h1,
a:first-child h2,
a:first-child h3,
a:first-child h4,
a:first-child h5,
a:first-child h6 {
margin-top: 0;
padding-top: 0;
}
h1 p,
h2 p,
h3 p,
h4 p,
h5 p,
h6 p {
margin-top: 0;
}
li p.first {
display: inline-block;
}
ul,
ol {
padding-left: 30px;
li {
margin: 0;
}
}
ul :first-child,
ol :first-child {
margin-top: 0;
}
ul :last-child,
ol :last-child {
margin-bottom: 0;
}
blockquote {
padding: 0 15px;
color: var(--foreground-normal);
border-left: 4px solid var(--background-normal);
}
blockquote > :first-child {
margin-top: 0;
}
blockquote > :last-child {
margin-bottom: 0;
}
table {
padding: 0;
border-collapse: collapse;
border-spacing: 0;
}
table tr {
margin: 0;
padding: 0;
background-color: white;
border-top: 1px solid var(--background-normal);
}
table tr:nth-child(2n) {
background-color: var(--background-page);
}
table tr th {
margin: 0;
padding: 6px 13px;
font-weight: bold;
text-align: left;
border: 1px solid var(--background-normal);
}
table tr td {
margin: 0;
padding: 6px 13px;
text-align: left;
border: 1px solid var(--background-normal);
}
table tr th :first-child,
table tr td :first-child {
margin-top: 0;
}
table tr th :last-child,
table tr td :last-child {
margin-bottom: 0;
}
img {
max-width: 100%;
}
.highlight pre {
padding: 6px 10px;
overflow: auto;
font-size: 13px;
line-height: 19px;
background-color: var(--background-page);
border: 1px solid var(--background-normal);
border-radius: var(--border-radius);
}
hr {
margin: 20px auto;
border: none;
border-top: 1px solid var(--background-normal);
}
b,
strong {
font-weight: 600;
}
}
}
}
</style>

View File

@@ -0,0 +1,9 @@
# Markdown
## Options
| Option | Description | Default |
| ------------- | ------------------------------------------------------------------- | ------------ |
| `placeholder` | Text to show when no input is entered | `null` |
| `tabbed` | If the view should be tabbed | `false` |
| `disabled` | Disables the input | `false` |

View File

@@ -18,6 +18,9 @@
"nested_files_folders_will_be_moved": "Nested files and folders will be moved one level up.",
"markdown": "Markdown",
"tabbed": "Tabbed",
"revision_post_update": "Here is what this item looked like after the update...",
"changes_made": "These are the specific changes that were made...",
"no_relational_data": "Keep in mind that this does not include relational data.",
@@ -455,8 +458,8 @@
"presets": "Presets",
"unexpected_error": "An unexpected error occured",
"unexpected_error_copy": "Something went wrong.. Please try again later.",
"unexpected_error": "Unexpected Error",
"unexpected_error_copy": "An unexpected error has occured. Please try again later.",
"copy_details": "Copy Details",
"no_app_access": "No App Access",
@@ -760,19 +763,19 @@
"keep_editing": "Keep Editing",
"page_help_collections_overview": "**Collections Overview** — Lists of all collections you have access to.",
"page_help_collections_browse": "**Browse Items** — Lists all {collection} items you have access to. Customize layout, filters, and sorting to tailor your view, and even save bookmarks of these different configurations for quick access.<br><br><a href='https://docs.directus.io/guides/user-guide.html#items' target='_blank'>Learn More</a>",
"page_help_collections_browse": "**Browse Items** — Lists all {collection} items you have access to. Customize layout, filters, and sorting to tailor your view, and even save bookmarks of these different configurations for quick access.",
"page_help_collections_detail": "**Item Detail** — A form for viewing and managing this item. This sidebar also contains a full history of revisions, and embedded comments.",
"page_help_activity_browse": "**Browse Activity** — A comprehensive listing of all your user's system and content activity.",
"page_help_activity_detail": "**Activity Detail** — Shows accountability info, revision data, and the update message for this activity record.",
"page_help_files_browse": "**File Library** — Lists all file assets uploaded to this project. Customize layout, filters, and sorting to tailor your view, and even save bookmarks of these different configurations for quick access.",
"page_help_files_detail": "**File Detail** — A form for managing file metadata, editing the original asset, and updating access settings.",
"page_help_settings_project": "**Project Settings** — Your project's global configuration options.<br><br><a href='https://docs.directus.io/guides/admin-guide.html#global-settings' target='_blank'>Learn More</a>",
"page_help_settings_project": "**Project Settings** — Your project's global configuration options.",
"page_help_settings_datamodel_collections": "**Data Model: Collections** — Lists all collections available. This includes visible, hidden, and system collections, as well as unmanaged database tables that can be added.",
"page_help_settings_datamodel_fields": "**Data Model: Collection** — A form for managing this collection and its fields.",
"page_help_settings_roles_browse": "**Browse Roles** — Lists the Admin, Public and custom User Roles.",
"page_help_settings_roles_detail": "**Role Detail** — Manage a role's permissions and other settings.",
"page_help_settings_presets_browse": "**Browse Presets** — Lists all presets in the project, including: user, role, and global bookmarks, as well as default views.",
"page_help_settings_presets_detail": "**Preset Detail** — A form for managing bookmarks and default collection presets.<br><br>To create a default preset, choose a role... TK TK",
"page_help_settings_presets_detail": "**Preset Detail** — A form for managing bookmarks and default collection presets.",
"page_help_settings_webhooks_browse": "**Browse Webhooks** — Lists all webhooks within the project.",
"page_help_settings_webhooks_detail": "**Webhook Detail** — A form for creating and managing project webhooks.",
"page_help_users_browse": "**User Directory** — Lists all system users within this project.",
@@ -912,7 +915,7 @@
"connection": "Connection",
"contains": "Contains",
"continue": "Continue",
"continue_as": "<b>{name}</b> is already authenticated for this project. If you recognize this account, please press continue.",
"continue_as": "<b>{name}</b> is already authenticated. If you recognize this account, press continue.",
"creating_item": "Creating Item",
"creating_item_page_title": "Creating Item: {collection}",
"creating_role": "Creating Role",
@@ -964,6 +967,12 @@
"dialog_beginning": "Beginning of dialog window.",
"display_name": "Display Name",
"directus_version": "Directus Version",
"server_stack": "Server Stack",
"operating_system": "Operating System",
"installed_on": "Installed On",
"database_client": "Database Client",
"database_host": "Database Host",
"database_port": "Database Port",
"done": "Done",
"dont_manage": "Don't Manage",
"dont_manage_copy": "Privileges, preferences, and settings for this collection will be permanently removed from the system! Are you sure?",
@@ -1102,6 +1111,7 @@
"no_items_selected": "No items selected",
"no_related_entries": "Has no related entries",
"not_authenticated": "Not Authenticated",
"authenticated": "Authenticated",
"not_between": "Not between",
"not_contains": "Doesn't contain",
"not_empty": "Is not empty",

View File

@@ -78,7 +78,7 @@
:server-sort="itemCount === limit || totalPages > 1"
:item-key="primaryKeyField.field"
:show-manual-sort="_filters && _filters.length === 0 && sortField !== null"
:manual-sort-key="sortField && sortField.field"
:manual-sort-key="sortField"
selection-use-keys
@click:row="onRowClick"
@update:sort="onSortChange"
@@ -343,7 +343,7 @@ export default defineComponent({
_viewQuery.value?.fields ||
availableFields.value
.filter((field: Field) => {
return field.schema?.is_primary_key === false && field.meta.special !== 'sort';
return field.schema?.is_primary_key === false;
})
.slice(0, 4)
.map(({ field }) => field);

View File

@@ -34,13 +34,10 @@
<template #drawer>
<drawer-detail icon="info_outline" :title="$t('information')" close>
<div class="format-markdown" v-html="marked($t('page_help_activity_browse'))" />
<div class="page-description" v-html="marked($t('page_help_activity_browse'))" />
</drawer-detail>
<layout-drawer-detail @input="viewType = $event" :value="viewType" />
<portal-target name="drawer" />
<drawer-detail icon="help_outline" :title="$t('help_and_docs')">
<div class="format-markdown" v-html="marked($t('page_help_activity_browse'))" />
</drawer-detail>
</template>
</private-view>
</template>

View File

@@ -138,13 +138,8 @@
<template #drawer>
<drawer-detail icon="info_outline" :title="$t('information')" close>
Page Info Here...
</drawer-detail>
<layout-drawer-detail @input="viewType = $event" :value="viewType" />
<portal-target name="drawer" />
<drawer-detail icon="help_outline" :title="$t('help_and_docs')">
<div
class="format-markdown"
class="page-description"
v-html="
marked(
$t('page_help_collections_browse', {
@@ -154,6 +149,8 @@
"
/>
</drawer-detail>
<layout-drawer-detail @input="viewType = $event" :value="viewType" />
<portal-target name="drawer" />
</template>
</private-view>
</template>

View File

@@ -152,7 +152,7 @@
<template #drawer>
<drawer-detail icon="info_outline" :title="$t('information')" close>
<div class="format-markdown" v-html="marked($t('page_help_collections_detail'))" />
<div class="page-description" v-html="marked($t('page_help_collections_detail'))" />
</drawer-detail>
<revisions-drawer-detail
v-if="collectionInfo.meta.singleton === false && isBatch === false && isNew === false"
@@ -166,9 +166,6 @@
:collection="collection"
:primary-key="primaryKey"
/>
<drawer-detail icon="help_outline" :title="$t('help_and_docs')">
<div class="format-markdown" v-html="marked($t('page_help_collections_detail'))" />
</drawer-detail>
</template>
</private-view>
</template>

View File

@@ -30,10 +30,7 @@
<template #drawer>
<drawer-detail icon="info_outline" :title="$t('information')" close>
<div class="format-markdown" v-html="marked($t('page_help_collections_overview'))" />
</drawer-detail>
<drawer-detail icon="help_outline" :title="$t('help_and_docs')">
<div class="format-markdown" v-html="marked($t('page_help_collections_overview'))" />
<div class="page-description" v-html="marked($t('page_help_collections_overview'))" />
</drawer-detail>
</template>
</private-view>

View File

@@ -128,13 +128,10 @@
<template #drawer>
<drawer-detail icon="info_outline" :title="$t('information')" close>
<div class="format-markdown" v-html="marked($t('page_help_files_browse'))" />
<div class="page-description" v-html="marked($t('page_help_files_browse'))" />
</drawer-detail>
<layout-drawer-detail @input="viewType = $event" :value="viewType" />
<portal-target name="drawer" />
<drawer-detail icon="help_outline" :title="$t('help_and_docs')">
<div class="format-markdown" v-html="marked($t('page_help_files_browse'))" />
</drawer-detail>
</template>
</private-view>
</template>

View File

@@ -42,7 +42,7 @@
</div>
<div v-if="user">
<dt>{{ $t('uploaded_by') }}</dt>
<dt>{{ $t('owner') }}</dt>
<dd>
<user-popover :user="user.id">
<router-link :to="user.link">{{ user.name }}</router-link>
@@ -86,6 +86,11 @@
</div>
</template>
</dl>
<v-divider />
<div class="page-description" v-html="marked($t('page_help_files_detail'))" />
</drawer-detail>
</template>
@@ -94,6 +99,7 @@ import { defineComponent, computed, ref, watch } from '@vue/composition-api';
import readableMimeType from '@/utils/readable-mime-type';
import bytes from 'bytes';
import i18n from '@/lang';
import marked from 'marked';
import localizedFormat from '@/utils/localized-format';
import api from '@/api';
@@ -122,7 +128,7 @@ export default defineComponent({
const { user } = useUser();
const { folder } = useFolder();
return { readableMimeType, size, creationDate, user, folder };
return { readableMimeType, size, creationDate, user, folder, marked };
function useCreationDate() {
const creationDate = ref<string | null>(null);

View File

@@ -163,9 +163,6 @@
collection="directus_files"
:primary-key="primaryKey"
/>
<drawer-detail icon="help_outline" :title="$t('help_and_docs')">
<div class="format-markdown" v-html="marked($t('page_help_files_detail'))" />
</drawer-detail>
</template>
</private-view>
</template>
@@ -186,7 +183,6 @@ import FileLightbox from '@/views/private/components/file-lightbox';
import { useFieldsStore } from '@/stores/';
import { Field } from '@/types';
import FileInfoDrawerDetail from './components/file-info-drawer-detail.vue';
import marked from 'marked';
import useFormFields from '@/composables/use-form-fields';
import FolderPicker from '../../components/folder-picker';
import api from '@/api';
@@ -315,7 +311,6 @@ export default defineComponent({
previewActive,
revisionsDrawerDetail,
formFields,
marked,
confirmLeave,
leaveTo,
discardAndLeave,

View File

@@ -15,12 +15,21 @@
<v-list-item-title>{{ item.name }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-list-item href="https://github.com/directus/directus/releases" class="version">
<v-list-item-icon><v-icon name="directus" /></v-list-item-icon>
<v-list-item-content>
<v-list-item-title class="version">Directus {{ version }}</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
</template>
<script lang="ts">
import { defineComponent, toRefs } from '@vue/composition-api';
import { i18n } from '@/lang';
import { version } from '../../../../../package.json';
export default defineComponent({
setup() {
@@ -65,7 +74,30 @@ export default defineComponent({
},
];
return { navItems, externalItems };
return { version, navItems, externalItems };
},
});
</script>
<style lang="scss" scoped>
.version {
.v-icon {
color: var(--foreground-subdued);
transition: color var(--fast) var(--transition);
}
::v-deep .type-text {
color: var(--foreground-subdued);
transition: color var(--fast) var(--transition);
}
&:hover {
.v-icon {
color: var(--foreground-normal);
}
::v-deep .type-text {
color: var(--foreground-normal);
}
}
}
</style>

View File

@@ -77,12 +77,9 @@
<template #drawer>
<drawer-detail icon="info_outline" :title="$t('information')" close>
<div class="format-markdown" v-html="marked($t('page_help_settings_datamodel_collections'))" />
<div class="page-description" v-html="marked($t('page_help_settings_datamodel_collections'))" />
</drawer-detail>
<collections-filter v-model="activeTypes" />
<drawer-detail icon="help_outline" :title="$t('help_and_docs')">
<div class="format-markdown" v-html="marked($t('page_help_settings_datamodel_collections'))" />
</drawer-detail>
</template>
</private-view>
</template>

View File

@@ -75,10 +75,7 @@
<template #drawer>
<drawer-detail icon="info_outline" :title="$t('information')" close>
<div class="format-markdown" v-html="marked($t('page_help_settings_datamodel_fields'))" />
</drawer-detail>
<drawer-detail icon="help_outline" :title="$t('help_and_docs')">
<div class="format-markdown" v-html="marked($t('page_help_settings_datamodel_fields'))" />
<div class="page-description" v-html="marked($t('page_help_settings_datamodel_fields'))" />
</drawer-detail>
</template>
</private-view>

View File

@@ -64,15 +64,15 @@
<v-tab-item value="system">
<h2 class="type-title">{{ $t('creating_collection_system') }}</h2>
<div class="grid system">
<div class="field" v-for="field in systemFields" :key="field.id">
<div class="type-label">{{ $t(field.label) }}</div>
<v-input v-model="field.name" class="monospace">
<div class="field" v-for="(info, field) in systemFields" :key="field">
<div class="type-label">{{ $t(info.label) }}</div>
<v-input v-model="info.name" class="monospace">
<template #prepend>
<v-checkbox v-model="field.enabled" />
<v-checkbox v-model="info.enabled" />
</template>
<template #append>
<v-icon :name="field.icon" />
<v-icon :name="info.icon" />
</template>
</v-input>
</div>
@@ -108,7 +108,7 @@ import notify from '@/utils/notify';
import router from '@/router';
export default defineComponent({
setup(props) {
setup() {
const collectionsStore = useCollectionsStore();
const fieldsStore = useFieldsStore();
@@ -118,51 +118,51 @@ export default defineComponent({
const primaryKeyFieldName = ref('id');
const primaryKeyFieldType = ref<'auto_int' | 'uuid' | 'manual'>('auto_int');
const systemFields = reactive([
{
id: 'status',
const sortField = ref<string>();
const systemFields = reactive({
status: {
enabled: false,
name: 'status',
label: 'status',
icon: 'flag',
},
{
id: 'sort',
sort: {
enabled: false,
name: 'sort',
label: 'sort',
icon: 'low_priority',
},
/** @TODO re-enable these when the api supports the special types for created/modified by/on */
// {
// id: 'owner',
// enabled: false,
// name: 'created_by',
// label: 'created_by_owner',
// icon: 'account_circle',
// },
// {
// id: 'created_on',
// enabled: false,
// name: 'created_on',
// label: 'created_on',
// icon: 'access_time',
// },
// {
// id: 'modified_by',
// enabled: false,
// name: 'modified_by',
// label: 'modified_by',
// icon: 'account_circle',
// },
// {
// id: 'modified_on',
// enabled: false,
// name: 'modified_on',
// label: 'modified_on',
// icon: 'access_time',
// },
]);
});
/** @TODO re-enable these when the api supports the special types for created/modified by/on */
// {
// id: 'owner',
// enabled: false,
// name: 'created_by',
// label: 'created_by_owner',
// icon: 'account_circle',
// },
// {
// id: 'created_on',
// enabled: false,
// name: 'created_on',
// label: 'created_on',
// icon: 'access_time',
// },
// {
// id: 'modified_by',
// enabled: false,
// name: 'modified_by',
// label: 'modified_by',
// icon: 'account_circle',
// },
// {
// id: 'modified_on',
// enabled: false,
// name: 'modified_on',
// label: 'modified_on',
// icon: 'access_time',
const saving = ref(false);
const saveError = ref(null);
@@ -185,6 +185,7 @@ export default defineComponent({
await api.post(`/collections`, {
collection: collectionName.value,
fields: [getPrimaryKeyField(), ...getSystemFields()],
sort_field: sortField.value,
});
await collectionsStore.hydrate();
@@ -259,9 +260,10 @@ export default defineComponent({
function getSystemFields() {
const fields: DeepPartial<Field>[] = [];
if (systemFields[0].enabled === true) {
// Status
if (systemFields.status.enabled === true) {
fields.push({
field: systemFields[0].name,
field: systemFields.status.name,
type: 'string',
meta: {
width: 'full',
@@ -294,18 +296,19 @@ export default defineComponent({
});
}
if (systemFields[1].enabled === true) {
// Sort
if (systemFields.sort.enabled === true) {
fields.push({
field: systemFields[1].name,
field: systemFields.sort.name,
type: 'integer',
meta: {
interface: 'sort',
hidden: true,
width: 'full',
special: 'sort',
},
schema: {},
});
sortField.value = systemFields.sort.name;
}
// if (systemFields[2].enabled === true) {

View File

@@ -86,9 +86,6 @@
<template #drawer>
<presets-info-drawer-detail />
<drawer-detail icon="help_outline" :title="$t('help_and_docs')">
<div class="format-markdown" v-html="marked($t('page_help_settings_presets_browse'))" />
</drawer-detail>
</template>
</private-view>
</template>
@@ -105,7 +102,6 @@ import layouts from '@/layouts';
import { TranslateResult } from 'vue-i18n';
import router from '@/router';
import ValueNull from '@/views/private/components/value-null';
import marked from 'marked';
import PresetsInfoDrawerDetail from './components/presets-info-drawer-detail.vue';
type PresetRaw = {
@@ -152,7 +148,6 @@ export default defineComponent({
confirmDelete,
deleting,
deleteSelection,
marked,
};
function useLinks() {

View File

@@ -10,12 +10,18 @@
<dd>{{ presetsCount }}</dd>
</div>
</dl>
<v-divider />
<div class="page-description" v-html="marked($t('page_help_settings_presets_browse'))" />
</drawer-detail>
</template>
<script lang="ts">
import { defineComponent, ref } from '@vue/composition-api';
import api from '@/api';
import marked from 'marked';
export default defineComponent({
setup() {
@@ -26,7 +32,7 @@ export default defineComponent({
fetchCounts();
return { bookmarksCount, presetsCount };
return { bookmarksCount, presetsCount, marked };
async function fetchCounts() {
loading.value = true;
@@ -51,3 +57,9 @@ export default defineComponent({
},
});
</script>
<style lang="scss" scoped>
.v-divider {
margin: 20px 0;
}
</style>

View File

@@ -77,16 +77,12 @@
<template #drawer>
<drawer-detail icon="info_outline" :title="$t('information')" close>
<span class="subdued">{{ $t('no_additional_info') }}</span>
<div class="page-description" v-html="marked($t('page_help_settings_presets_detail'))" />
</drawer-detail>
<div class="layout-drawer">
<portal-target name="drawer" />
</div>
<drawer-detail icon="help_outline" :title="$t('help_and_docs')">
<div class="format-markdown" v-html="marked($t('page_help_settings_presets_detail'))" />
</drawer-detail>
</template>
</private-view>
</template>

View File

@@ -1,13 +1,47 @@
<template>
<drawer-detail icon="info_outline" :title="$t('information')" close>
<dl v-if="project">
<!-- @todo -->
<div>
<dt>{{ $t('directus_version') }}</dt>
<dd>{{ version }}</dd>
</div>
<div>
<dt>{{ $t('installed_on') }}</dt>
<dd>August 15, 2020</dd>
</div>
<div>
<dt>{{ $t('operating_system') }}</dt>
<dd>Ubuntu 8.0</dd>
</div>
<div>
<dt>{{ $t('server_stack') }}</dt>
<dd>Node.js 10.2</dd>
</div>
<div>
<dt>{{ $t('database_client') }}</dt>
<dd>MySQL 5.7</dd>
</div>
<div>
<dt>{{ $t('database_host') }}</dt>
<dd>Localhost</dd>
</div>
<div>
<dt>{{ $t('database_port') }}</dt>
<dd>3306</dd>
</div>
</dl>
<v-divider />
<div class="page-description" v-html="marked($t('page_help_settings_project'))" />
</drawer-detail>
</template>
<script lang="ts">
import { defineComponent } from '@vue/composition-api';
import i18n from '@/lang';
import marked from 'marked';
import { version } from '../../../../../../package.json';
import bytes from 'bytes';
@@ -18,7 +52,13 @@ import bytes from 'bytes';
export default defineComponent({
setup() {
return { project: {}, bytes };
return { version, project: {}, bytes, marked };
},
});
</script>
<style lang="scss" scoped>
.v-divider {
margin: 20px 0;
}
</style>

View File

@@ -23,9 +23,6 @@
<template #drawer>
<project-info-drawer-detail />
<drawer-detail icon="help_outline" :title="$t('help_and_docs')">
<div class="format-markdown" v-html="marked($t('page_help_settings_project'))" />
</drawer-detail>
</template>
</private-view>
</template>
@@ -35,7 +32,6 @@ import { defineComponent, ref, computed } from '@vue/composition-api';
import SettingsNavigation from '../../components/navigation/';
import useCollection from '@/composables/use-collection';
import { useSettingsStore } from '@/stores';
import marked from 'marked';
import ProjectInfoDrawerDetail from './components/project-info-drawer-detail.vue';
import { clone } from 'lodash';
@@ -54,7 +50,7 @@ export default defineComponent({
const saving = ref(false);
return { fields, initialValues, edits, noEdits, saving, save, marked };
return { fields, initialValues, edits, noEdits, saving, save };
async function save() {
if (edits.value === null) return;

View File

@@ -20,11 +20,7 @@
<template #drawer>
<drawer-detail icon="info_outline" :title="$t('information')" close>
<span class="subdued">{{ $t('no_additional_info') }}</span>
</drawer-detail>
<drawer-detail icon="help_outline" :title="$t('help_and_docs')">
<div class="format-markdown" v-html="marked($t('page_help_collections_overview'))" />
<div class="page-description" v-html="marked($t('page_help_settings_roles_browse'))" />
</drawer-detail>
</template>
@@ -159,11 +155,6 @@ export default defineComponent({
--v-button-background-color-disabled: var(--warning-25);
}
.subdued {
color: var(--foreground-subdued);
font-style: italic;
}
.roles {
padding: var(--content-padding);
padding-bottom: var(--content-padding-bottom);

View File

@@ -6,11 +6,16 @@
<dd>{{ role.id }}</dd>
</div>
</dl>
<v-divider />
<div class="page-description" v-html="marked($t('page_help_settings_roles_detail'))" />
</drawer-detail>
</template>
<script lang="ts">
import { defineComponent, PropType } from '@vue/composition-api';
import marked from 'marked';
export default defineComponent({
props: {
@@ -23,5 +28,14 @@ export default defineComponent({
default: null,
},
},
setup() {
return { marked };
},
});
</script>
<style lang="scss" scoped>
.v-divider {
margin: 20px 0;
}
</style>

View File

@@ -91,9 +91,6 @@
<template #drawer>
<role-info-drawer-detail :is-new="isNew" :role="item" />
<revisions-drawer-detail v-if="isNew === false" collection="directus_roles" :primary-key="primaryKey" />
<drawer-detail icon="help_outline" :title="$t('help_and_docs')">
<div class="format-markdown" v-html="marked($t('page_help_collections_overview'))" />
</drawer-detail>
</template>
</private-view>
</template>
@@ -106,7 +103,6 @@ import router from '@/router';
import RevisionsDrawerDetail from '@/views/private/components/revisions-drawer-detail';
import useItem from '@/composables/use-item';
import SaveOptions from '@/views/private/components/save-options';
import marked from 'marked';
import { useUserStore } from '@/stores/';
import RoleInfoDrawerDetail from './components/role-info-drawer-detail';
@@ -153,7 +149,6 @@ export default defineComponent({
saveAndAddNew,
saveAsCopyAndNavigate,
isBatch,
marked,
};
/**

View File

@@ -61,13 +61,10 @@
<template #drawer>
<drawer-detail icon="info_outline" :title="$t('information')" close>
<div class="format-markdown" v-html="marked($t('page_help_settings_webhooks_browse'))" />
<div class="page-description" v-html="marked($t('page_help_settings_webhooks_browse'))" />
</drawer-detail>
<layout-drawer-detail />
<portal-target name="drawer" />
<drawer-detail icon="help_outline" :title="$t('help_and_docs')">
<div class="format-markdown" v-html="marked($t('page_help_settings_webhooks_browse'))" />
</drawer-detail>
</template>
</private-view>
</template>

View File

@@ -59,12 +59,9 @@
<template #drawer>
<drawer-detail icon="info_outline" :title="$t('information')" close>
<div class="format-markdown" v-html="marked($t('page_help_settings_webhooks_detail'))" />
<div class="page-description" v-html="marked($t('page_help_settings_webhooks_detail'))" />
</drawer-detail>
<revisions-drawer-detail v-if="isNew === false" collection="directus_webhooks" :primary-key="primaryKey" />
<drawer-detail icon="help_outline" :title="$t('help_and_docs')">
<div class="format-markdown" v-html="marked($t('page_help_settings_webhooks_detail'))" />
</drawer-detail>
</template>
</private-view>
</template>

View File

@@ -93,13 +93,10 @@
<template #drawer>
<drawer-detail icon="info_outline" :title="$t('information')" close>
<div class="format-markdown" v-html="marked($t('page_help_users_browse'))" />
<div class="page-description" v-html="marked($t('page_help_users_browse'))" />
</drawer-detail>
<layout-drawer-detail @input="viewType = $event" :value="viewType" />
<portal-target name="drawer" />
<drawer-detail icon="help_outline" :title="$t('help_and_docs')">
<div class="format-markdown" v-html="marked($t('page_help_users_browse'))" />
</drawer-detail>
</template>
</private-view>
</template>

View File

@@ -29,14 +29,15 @@
</div>
</dl>
<template v-else>
--
</template>
<v-divider />
<div class="page-description" v-html="marked($t('page_help_users_detail'))" />
</drawer-detail>
</template>
<script lang="ts">
import { defineComponent } from '@vue/composition-api';
import marked from 'marked';
export default defineComponent({
props: {
@@ -49,5 +50,14 @@ export default defineComponent({
default: false,
},
},
setup(props) {
return { marked };
},
});
</script>
<style lang="scss" scoped>
.v-divider {
margin: 20px 0;
}
</style>

View File

@@ -121,9 +121,6 @@
collection="directus_users"
:primary-key="primaryKey"
/>
<drawer-detail icon="help_outline" :title="$t('help_and_docs')">
<div class="format-markdown" v-html="marked($t('page_help_users_detail'))" />
</drawer-detail>
</template>
</private-view>
</template>
@@ -138,7 +135,6 @@ import RevisionsDrawerDetail from '@/views/private/components/revisions-drawer-d
import CommentsDrawerDetail from '@/views/private/components/comments-drawer-detail';
import useItem from '@/composables/use-item';
import SaveOptions from '@/views/private/components/save-options';
import marked from 'marked';
import api from '@/api';
import { useFieldsStore } from '@/stores/';
import useFormFields from '@/composables/use-form-fields';
@@ -258,7 +254,6 @@ export default defineComponent({
saveAsCopyAndNavigate,
isBatch,
revisionsDrawerDetail,
marked,
previewLoading,
avatarSrc,
roleName,

View File

@@ -66,7 +66,7 @@ export default defineComponent({
::v-deep {
// In the translated string for continue as, there's a B element to emphasize the users name
b {
font-weight: 500;
font-weight: 600;
}
}

View File

@@ -6,7 +6,11 @@
<login-form v-else :sso-error="ssoErrorCode" />
<template #notice>
<template v-if="authenticated" #notice>
<v-icon name="lock_open" left />
{{ $t('authenticated') }}
</template>
<template v-else #notice>
<v-icon name="lock_outlined" left />
{{ $t('not_authenticated') }}
</template>

View File

@@ -3,6 +3,7 @@ export * from './collections';
export * from './fields';
export * from './latency';
export * from './notifications';
export * from './permissions';
export * from './presets';
export * from './relations';
export * from './requests';

View File

@@ -0,0 +1,18 @@
import { createStore } from 'pinia';
import api from '@/api';
export const usePermissionsStore = createStore({
id: 'permissionsStore',
state: () => ({
permissions: [],
}),
actions: {
async hydrate() {
const response = await api.get('/permissions/me');
this.state.permissions = response.data.data;
},
dehydrate() {
this.reset();
},
},
});

View File

@@ -111,7 +111,7 @@ strong {
dl > div {
display: flex;
margin-bottom: 12px;
margin-bottom: 8px;
}
dt,

View File

@@ -14,6 +14,7 @@ export interface CollectionRaw {
icon: string | null;
translation: Translation[] | null;
display_template: string | null;
sort_field: string | null;
} | null;
schema: Record<string, any>;
}

View File

@@ -151,7 +151,10 @@ body {
.content {
padding: 16px;
::v-deep {
.format-markdown {
.page-description {
color: var(--foreground-subdued);
margin-bottom: 8px;
a {
color: var(--primary);
}

View File

@@ -1,7 +1,16 @@
<template>
<div class="file-preview" v-if="type">
<div v-if="type === 'image'" class="image" :class="{ svg: isSVG, 'max-size': inModal === false }" @click="$emit('click')">
<img :src="src" :width="width" :height="height" :alt="title" />
<img
:src="src"
:width="width"
:height="height"
:style="{
'maxWidth': width ? width + 'px' : '100%',
'maxHeight': height ? height + 'px' : '100%'
}"
:alt="title"
/>
<v-icon v-if="inModal === false" name="fullscreen" />
</div>
@@ -101,6 +110,7 @@ audio {
max-height: inherit;
z-index: 1;
display: block;
margin: 0 auto;
}
.v-icon {

View File

@@ -119,11 +119,14 @@ export default defineComponent({
}
}
.fade-enter-active,
.fade-leave-active {
.fade-enter-active {
transition: opacity var(--slow) var(--transition);
}
.fade-leave-active {
transition: opacity var(--medium) var(--transition);
}
.fade-enter,
.fade-leave-to {
opacity: 0;

View File

@@ -108,7 +108,7 @@ export default defineComponent({
}
.title {
font-weight: 500;
font-weight: 600;
}
&::after {