fix: misc responsive improvements, date clipping in sidebar (#165), express-fileupload schema validation (#180)
* fix: misc responsive improvements * fix: date clipping in sidebar * fix: revises popup * fix: admin _verified field not displaying proper field value * fix: properly typed express-fileupload config options
This commit is contained in:
@@ -58,10 +58,19 @@ const DateTime: React.FC<Props> = (props) => {
|
||||
|
||||
return (
|
||||
<div className={classes}>
|
||||
<div className={`${baseClass}__input-wrapper`}>
|
||||
<DatePicker {...dateTimePickerProps} />
|
||||
<div className={`${baseClass}__icon-wrap`}>
|
||||
<CalendarIcon />
|
||||
</div>
|
||||
<div className={`${baseClass}__input-wrapper`}>
|
||||
<DatePicker
|
||||
{...dateTimePickerProps}
|
||||
popperModifiers={{
|
||||
preventOverflow: {
|
||||
enabled: true,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -8,6 +8,21 @@ $cal-icon-width: 18px;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
&__icon-wrap {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
|
||||
.icon {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
right: base(.75);
|
||||
top: base(.625);
|
||||
width: $cal-icon-width;
|
||||
height: auto;
|
||||
@include color-svg($color-dark-gray);
|
||||
}
|
||||
}
|
||||
|
||||
&__appearance--timeOnly {
|
||||
.react-datepicker {
|
||||
width: 100%;
|
||||
@@ -35,21 +50,6 @@ $cal-icon-width: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
&__input-wrapper {
|
||||
position: relative;
|
||||
|
||||
.icon {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
right: base(.75);
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: $cal-icon-width;
|
||||
height: auto;
|
||||
@include color-svg($color-dark-gray);
|
||||
}
|
||||
}
|
||||
|
||||
.react-datepicker-wrapper {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ const Localizer: React.FC<Props> = () => {
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<Popup
|
||||
align="left"
|
||||
horizontalAlign="left"
|
||||
button={locale}
|
||||
render={({ close }) => (
|
||||
<div>
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
@include shadow-sm;
|
||||
}
|
||||
|
||||
&.popup--align-left {
|
||||
&.popup--h-align-left {
|
||||
.popup__content {
|
||||
left: - base(.5);
|
||||
|
||||
@@ -175,6 +175,7 @@
|
||||
top: unset;
|
||||
bottom: calc(100% - 1px);
|
||||
border-top-color: transparent !important;
|
||||
border-bottom-color: white;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@ const baseClass = 'popup';
|
||||
const Popup: React.FC<Props> = (props) => {
|
||||
const {
|
||||
render,
|
||||
align = 'center',
|
||||
size = 'small',
|
||||
color = 'light',
|
||||
button,
|
||||
@@ -88,7 +87,6 @@ const Popup: React.FC<Props> = (props) => {
|
||||
|
||||
const classes = [
|
||||
baseClass,
|
||||
`${baseClass}--align-${align}`,
|
||||
`${baseClass}--size-${size}`,
|
||||
`${baseClass}--color-${color}`,
|
||||
`${baseClass}--v-align-${verticalAlign}`,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
export type Props = {
|
||||
render?: (any) => void,
|
||||
children?: React.ReactNode,
|
||||
align?: 'left' | 'center' | 'right',
|
||||
horizontalAlign?: 'left' | 'center' | 'right',
|
||||
size?: 'small' | 'large' | 'wide',
|
||||
color?: 'light' | 'dark',
|
||||
|
||||
@@ -31,7 +31,7 @@ const ActionPanel: React.FC<Props> = (props) => {
|
||||
showOnHover
|
||||
size="wide"
|
||||
color="dark"
|
||||
horizontalAlign="center"
|
||||
horizontalAlign="right"
|
||||
buttonType="custom"
|
||||
button={(
|
||||
<Button
|
||||
@@ -56,7 +56,7 @@ const ActionPanel: React.FC<Props> = (props) => {
|
||||
<Popup
|
||||
buttonType="custom"
|
||||
size="large"
|
||||
horizontalAlign="center"
|
||||
horizontalAlign="right"
|
||||
button={(
|
||||
<Button
|
||||
className={`${baseClass}__add-row`}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
@import '../../../scss/styles';
|
||||
|
||||
.error-message {
|
||||
.field-error.tooltip {
|
||||
top: 0;
|
||||
bottom: auto;
|
||||
left: auto;
|
||||
right: base(.5);
|
||||
transform: none;
|
||||
|
||||
@@ -4,6 +4,8 @@ import { Props } from './types';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const baseClass = 'field-error';
|
||||
|
||||
const Error: React.FC<Props> = (props) => {
|
||||
const {
|
||||
showError = false,
|
||||
@@ -12,7 +14,7 @@ const Error: React.FC<Props> = (props) => {
|
||||
|
||||
if (showError) {
|
||||
return (
|
||||
<Tooltip className="error-message">
|
||||
<Tooltip className={baseClass}>
|
||||
{message}
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -165,12 +165,14 @@ const RenderArray = React.memo((props: RenderArrayProps) => {
|
||||
<div
|
||||
className={baseClass}
|
||||
>
|
||||
<header className={`${baseClass}__header`}>
|
||||
<h3>{label}</h3>
|
||||
<div className={`${baseClass}__error-wrap`}>
|
||||
<Error
|
||||
showError={showError}
|
||||
message={errorMessage}
|
||||
/>
|
||||
</div>
|
||||
<header className={`${baseClass}__header`}>
|
||||
<h3>{label}</h3>
|
||||
</header>
|
||||
<Droppable droppableId="array-drop">
|
||||
{(provided) => (
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
|
||||
.field-type.array {
|
||||
margin: base(2) 0;
|
||||
min-width: base(15);
|
||||
|
||||
&__error-wrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&__add-button-wrap {
|
||||
margin-left: base(0);
|
||||
@@ -33,6 +38,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include mid-break {
|
||||
min-width: calc(100vw - #{base(2)});
|
||||
}
|
||||
}
|
||||
|
||||
.field-type.group,
|
||||
|
||||
@@ -3,6 +3,7 @@ import React, {
|
||||
} from 'react';
|
||||
import { DragDropContext, Droppable } from 'react-beautiful-dnd';
|
||||
|
||||
import { classes } from 'http-status';
|
||||
import withCondition from '../../withCondition';
|
||||
import Button from '../../../elements/Button';
|
||||
import reducer from '../rowReducer';
|
||||
@@ -179,13 +180,14 @@ const RenderBlocks = React.memo((props: RenderBlockProps) => {
|
||||
<div
|
||||
className={baseClass}
|
||||
>
|
||||
<header className={`${baseClass}__header`}>
|
||||
<h3>{label}</h3>
|
||||
|
||||
<div className={`${baseClass}__error-wrap`}>
|
||||
<Error
|
||||
showError={showError}
|
||||
message={errorMessage}
|
||||
/>
|
||||
</div>
|
||||
<header className={`${baseClass}__header`}>
|
||||
<h3>{label}</h3>
|
||||
</header>
|
||||
|
||||
<Droppable
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
|
||||
.field-type.blocks {
|
||||
margin: base(2) 0;
|
||||
min-width: base(15);
|
||||
|
||||
&__error-wrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&__add-button-wrap {
|
||||
|
||||
@@ -24,6 +29,10 @@
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@include mid-break {
|
||||
min-width: calc(100vw - #{base(2)});
|
||||
}
|
||||
}
|
||||
|
||||
.field-type.group,
|
||||
|
||||
@@ -2,11 +2,16 @@
|
||||
|
||||
.checkbox {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
|
||||
.tooltip {
|
||||
right: auto;
|
||||
}
|
||||
|
||||
&__error-wrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
button {
|
||||
@extend %btn-reset;
|
||||
display: flex;
|
||||
|
||||
@@ -60,10 +60,12 @@ const Checkbox: React.FC<Props> = (props) => {
|
||||
width,
|
||||
}}
|
||||
>
|
||||
<Error
|
||||
showError={showError}
|
||||
message={errorMessage}
|
||||
/>
|
||||
<div className={`${baseClass}__error-wrap`}>
|
||||
<Error
|
||||
showError={showError}
|
||||
message={errorMessage}
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
type="checkbox"
|
||||
name={path}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
.field-type.code {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
|
||||
&.error {
|
||||
textarea {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
.field-type.confirm-password {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
|
||||
input {
|
||||
@include formInput;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
@import '../../../../scss/styles';
|
||||
|
||||
.date-time-field {
|
||||
margin-bottom: $baseline;
|
||||
|
||||
&--has-error {
|
||||
.react-datepicker__input-container input {
|
||||
background-color: lighten($color-red, 20%);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@import '../../../../scss/styles.scss';
|
||||
|
||||
.field-type.email {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
|
||||
input {
|
||||
@include formInput;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
.field-type.number {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
|
||||
input {
|
||||
@include formInput;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
.field-type.password {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
|
||||
input {
|
||||
@include formInput;
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
@import '../../../../scss/styles.scss';
|
||||
|
||||
.radio-group {
|
||||
margin-bottom: $baseline;
|
||||
|
||||
&__error-wrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&--layout-horizontal {
|
||||
ul {
|
||||
display: flex;
|
||||
|
||||
@@ -64,10 +64,12 @@ const RadioGroup: React.FC<Props> = (props) => {
|
||||
width,
|
||||
}}
|
||||
>
|
||||
<Error
|
||||
showError={showError}
|
||||
message={errorMessage}
|
||||
/>
|
||||
<div className={`${baseClass}__error-wrap`}>
|
||||
<Error
|
||||
showError={showError}
|
||||
message={errorMessage}
|
||||
/>
|
||||
</div>
|
||||
<Label
|
||||
htmlFor={path}
|
||||
label={label}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
.field-type.relationship {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
}
|
||||
|
||||
.relationship__error-loading {
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
&__wrap {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&__wrapper {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
> * {
|
||||
padding-left: base(.5);
|
||||
padding-right: base(.5);
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
@include mid-break {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
.field-type.select {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
}
|
||||
|
||||
.select--read-only {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
.field-type.text {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
|
||||
input {
|
||||
@include formInput;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
.field-type.textarea {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
|
||||
textarea {
|
||||
@include formInput();
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
.upload {
|
||||
position: relative;
|
||||
margin-bottom: $baseline;
|
||||
min-width: base(15);
|
||||
|
||||
&__wrap {
|
||||
display: flex;
|
||||
@@ -11,8 +13,12 @@
|
||||
.btn {
|
||||
margin: 0 $baseline base(.5) 0;
|
||||
}
|
||||
}
|
||||
|
||||
@include mid-break {
|
||||
@include mid-break {
|
||||
min-width: calc(100vw - #{base(2)});
|
||||
|
||||
&__wrap {
|
||||
display: block;
|
||||
padding: $baseline $baseline base(.5);
|
||||
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
@import '../../../scss/styles.scss';
|
||||
|
||||
.field-type {
|
||||
margin-bottom: $baseline;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
|
||||
.required {
|
||||
color: $color-red;
|
||||
margin-left: base(.25);
|
||||
margin-right: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.tooltip.error-message {
|
||||
left: auto;
|
||||
right: 0;
|
||||
transform: none;
|
||||
background-color: $color-red;
|
||||
bottom: calc(100% - #{base(1.125)});
|
||||
|
||||
span {
|
||||
border-top-color: $color-red;
|
||||
}
|
||||
}
|
||||
|
||||
@include small-break {
|
||||
margin-bottom: base(.5);
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,6 @@ import { useFormProcessing, useFormSubmitted, useFormModified, useForm } from '.
|
||||
import useDebounce from '../../../hooks/useDebounce';
|
||||
import { Options, FieldType } from './types';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const useFieldType = <T extends unknown>(options: Options): FieldType<T> => {
|
||||
const {
|
||||
path,
|
||||
|
||||
@@ -95,70 +95,74 @@ const DefaultAccount: React.FC<Props> = (props) => {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={`${baseClass}__sidebar`}>
|
||||
<ul className={`${baseClass}__collection-actions`}>
|
||||
{(permissions?.create?.permission) && (
|
||||
<React.Fragment>
|
||||
<li><Link to={`${admin}/collections/${slug}/create`}>Create New</Link></li>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</ul>
|
||||
<div className={`${baseClass}__document-actions${preview ? ` ${baseClass}__document-actions--with-preview` : ''}`}>
|
||||
<PreviewButton
|
||||
generatePreviewURL={preview}
|
||||
data={data}
|
||||
/>
|
||||
{hasSavePermission && (
|
||||
<FormSubmit>Save</FormSubmit>
|
||||
)}
|
||||
</div>
|
||||
<div className={`${baseClass}__api-url`}>
|
||||
<span className={`${baseClass}__label`}>
|
||||
API URL
|
||||
{' '}
|
||||
<CopyToClipboard value={apiURL} />
|
||||
</span>
|
||||
<a
|
||||
href={apiURL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{apiURL}
|
||||
</a>
|
||||
</div>
|
||||
<div className={`${baseClass}__sidebar-fields`}>
|
||||
<RenderFields
|
||||
operation="update"
|
||||
permissions={permissions.fields}
|
||||
readOnly={!hasSavePermission}
|
||||
filter={(field) => field?.admin?.position === 'sidebar'}
|
||||
fieldTypes={fieldTypes}
|
||||
fieldSchema={fields}
|
||||
/>
|
||||
</div>
|
||||
<ul className={`${baseClass}__meta`}>
|
||||
<li>
|
||||
<div className={`${baseClass}__label`}>ID</div>
|
||||
<div>{data?.id}</div>
|
||||
</li>
|
||||
{timestamps && (
|
||||
<React.Fragment>
|
||||
{data.updatedAt && (
|
||||
<div className={`${baseClass}__sidebar-wrap`}>
|
||||
<div className={`${baseClass}__sidebar`}>
|
||||
<div className={`${baseClass}__sidebar-sticky-wrap`}>
|
||||
<ul className={`${baseClass}__collection-actions`}>
|
||||
{(permissions?.create?.permission) && (
|
||||
<React.Fragment>
|
||||
<li><Link to={`${admin}/collections/${slug}/create`}>Create New</Link></li>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</ul>
|
||||
<div className={`${baseClass}__document-actions${preview ? ` ${baseClass}__document-actions--with-preview` : ''}`}>
|
||||
<PreviewButton
|
||||
generatePreviewURL={preview}
|
||||
data={data}
|
||||
/>
|
||||
{hasSavePermission && (
|
||||
<FormSubmit>Save</FormSubmit>
|
||||
)}
|
||||
</div>
|
||||
<div className={`${baseClass}__sidebar-fields`}>
|
||||
<RenderFields
|
||||
operation="update"
|
||||
permissions={permissions.fields}
|
||||
readOnly={!hasSavePermission}
|
||||
filter={(field) => field?.admin?.position === 'sidebar'}
|
||||
fieldTypes={fieldTypes}
|
||||
fieldSchema={fields}
|
||||
/>
|
||||
</div>
|
||||
<ul className={`${baseClass}__meta`}>
|
||||
<li className={`${baseClass}__api-url`}>
|
||||
<span className={`${baseClass}__label`}>
|
||||
API URL
|
||||
{' '}
|
||||
<CopyToClipboard value={apiURL} />
|
||||
</span>
|
||||
<a
|
||||
href={apiURL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{apiURL}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<div className={`${baseClass}__label`}>ID</div>
|
||||
<div>{data?.id}</div>
|
||||
</li>
|
||||
{timestamps && (
|
||||
<React.Fragment>
|
||||
{data.updatedAt && (
|
||||
<li>
|
||||
<div className={`${baseClass}__label`}>Last Modified</div>
|
||||
<div>{format(new Date(data.updatedAt), dateFormat)}</div>
|
||||
</li>
|
||||
)}
|
||||
{data.createdAt && (
|
||||
)}
|
||||
{data.createdAt && (
|
||||
<li>
|
||||
<div className={`${baseClass}__label`}>Created</div>
|
||||
<div>{format(new Date(data.createdAt), dateFormat)}</div>
|
||||
</li>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)}
|
||||
|
||||
</ul>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
|
||||
@@ -4,20 +4,14 @@
|
||||
width: 100%;
|
||||
|
||||
&__form {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
&__main,
|
||||
&__sidebar {
|
||||
width: auto;
|
||||
min-width: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&__main {
|
||||
flex-grow: 1;
|
||||
width: calc(100% - #{base(15)});
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
&__header {
|
||||
@@ -44,24 +38,34 @@
|
||||
padding: base(5) base(6) base(6);
|
||||
margin-bottom: base(1.5);
|
||||
flex-grow: 1;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
&__sidebar-wrap {
|
||||
position: fixed;
|
||||
width: base(15);
|
||||
height: 100%;
|
||||
top: 0;
|
||||
right: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
&__sidebar {
|
||||
padding-bottom: base(1);
|
||||
width: base(15);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
&__sidebar-sticky-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
height: 100vh;
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
&__collection-actions,
|
||||
&__document-actions,
|
||||
&__meta,
|
||||
&__sidebar-fields,
|
||||
&__api-url {
|
||||
&__sidebar-fields {
|
||||
padding-left: base(1.5);
|
||||
}
|
||||
|
||||
@@ -109,7 +113,7 @@
|
||||
}
|
||||
|
||||
&__meta {
|
||||
margin: auto 0 0;
|
||||
margin: auto 0 $baseline;
|
||||
padding-top: $baseline;
|
||||
list-style: none;
|
||||
|
||||
@@ -142,6 +146,17 @@
|
||||
}
|
||||
|
||||
@include mid-break {
|
||||
&__main {
|
||||
width: 100%;
|
||||
min-height: initial;
|
||||
}
|
||||
|
||||
&__sidebar-wrap {
|
||||
position: static;
|
||||
width: 100%;
|
||||
height: initial;
|
||||
}
|
||||
|
||||
&__form {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -66,8 +66,8 @@ const DefaultGlobalView: React.FC<Props> = (props) => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={`${baseClass}__sidebar`}>
|
||||
<div className={`${baseClass}__sidebar-sticky`}>
|
||||
<div className={`${baseClass}__sidebar-wrap`}>
|
||||
<div className={`${baseClass}__sidebar`}>
|
||||
<div className={`${baseClass}__sidebar-sticky-wrap`}>
|
||||
<div className={`${baseClass}__document-actions${preview ? ` ${baseClass}__document-actions--with-preview` : ''}`}>
|
||||
<PreviewButton
|
||||
@@ -78,22 +78,6 @@ const DefaultGlobalView: React.FC<Props> = (props) => {
|
||||
<FormSubmit>Save</FormSubmit>
|
||||
)}
|
||||
</div>
|
||||
{data && (
|
||||
<div className={`${baseClass}__api-url`}>
|
||||
<span className={`${baseClass}__label`}>
|
||||
API URL
|
||||
{' '}
|
||||
<CopyToClipboard value={apiURL} />
|
||||
</span>
|
||||
<a
|
||||
href={apiURL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{apiURL}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
<div className={`${baseClass}__sidebar-fields`}>
|
||||
<RenderFields
|
||||
operation="update"
|
||||
@@ -106,6 +90,22 @@ const DefaultGlobalView: React.FC<Props> = (props) => {
|
||||
</div>
|
||||
{data && (
|
||||
<ul className={`${baseClass}__meta`}>
|
||||
{data && (
|
||||
<li className={`${baseClass}__api-url`}>
|
||||
<span className={`${baseClass}__label`}>
|
||||
API URL
|
||||
{' '}
|
||||
<CopyToClipboard value={apiURL} />
|
||||
</span>
|
||||
<a
|
||||
href={apiURL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{apiURL}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
{data.updatedAt && (
|
||||
<li>
|
||||
<div className={`${baseClass}__label`}>Last Modified</div>
|
||||
|
||||
@@ -4,20 +4,14 @@
|
||||
width: 100%;
|
||||
|
||||
&__form {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
&__main,
|
||||
&__sidebar {
|
||||
width: auto;
|
||||
min-width: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&__main {
|
||||
width: calc(100% - #{base(15)});
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
&__header {
|
||||
@@ -44,34 +38,34 @@
|
||||
padding: base(5) base(6) base(6);
|
||||
margin-bottom: base(1.5);
|
||||
flex-grow: 1;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
&__sidebar-wrap {
|
||||
position: fixed;
|
||||
width: base(15);
|
||||
height: 100%;
|
||||
top: 0;
|
||||
right: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
&__sidebar {
|
||||
width: base(15);
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__sidebar-sticky {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
height: 100vh;
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
&__sidebar-sticky-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100%;
|
||||
padding-top: base(3);
|
||||
padding-bottom: base(1);
|
||||
}
|
||||
|
||||
&__collection-actions,
|
||||
&__document-actions,
|
||||
&__meta,
|
||||
&__sidebar-fields,
|
||||
&__api-url {
|
||||
&__sidebar-fields {
|
||||
padding-left: base(1.5);
|
||||
}
|
||||
|
||||
@@ -119,7 +113,7 @@
|
||||
}
|
||||
|
||||
&__meta {
|
||||
margin: auto 0 0;
|
||||
margin: auto 0 $baseline;
|
||||
padding-top: $baseline;
|
||||
list-style: none;
|
||||
|
||||
@@ -158,8 +152,15 @@
|
||||
}
|
||||
|
||||
@include mid-break {
|
||||
&__sidebar {
|
||||
width: unset;
|
||||
&__main {
|
||||
width: 100%;
|
||||
min-height: initial;
|
||||
}
|
||||
|
||||
&__sidebar-wrap {
|
||||
position: static;
|
||||
width: 100%;
|
||||
height: initial;
|
||||
}
|
||||
|
||||
&__form {
|
||||
@@ -181,8 +182,7 @@
|
||||
|
||||
&__document-actions,
|
||||
&__meta,
|
||||
&__sidebar-fields,
|
||||
&__api-url {
|
||||
&__sidebar-fields {
|
||||
padding-left: $baseline;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import CopyToClipboard from '../../../../elements/CopyToClipboard';
|
||||
import { text } from '../../../../../../fields/validations';
|
||||
import { useWatchForm } from '../../../../forms/Form/context';
|
||||
|
||||
import './index.scss';
|
||||
import GenerateConfirmation from '../../../../elements/GenerateConfirmation';
|
||||
|
||||
const path = 'apiKey';
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
}
|
||||
|
||||
.field-type.api-key {
|
||||
margin-bottom: $baseline;
|
||||
|
||||
input {
|
||||
@include formInput;
|
||||
}
|
||||
|
||||
@@ -80,63 +80,63 @@ const DefaultEditView: React.FC<Props> = (props) => {
|
||||
<LeaveWithoutSaving />
|
||||
<div className={`${baseClass}__edit`}>
|
||||
{isLoading && (
|
||||
<Loading />
|
||||
<Loading />
|
||||
)}
|
||||
{!isLoading && (
|
||||
<React.Fragment>
|
||||
<header className={`${baseClass}__header`}>
|
||||
<h1>
|
||||
<RenderTitle {...{ data, useAsTitle, fallback: '[Untitled]' }} />
|
||||
</h1>
|
||||
</header>
|
||||
{auth && (
|
||||
<Auth
|
||||
useAPIKey={auth.useAPIKey}
|
||||
requirePassword={!isEditing}
|
||||
verify={auth.verify}
|
||||
collection={collection}
|
||||
email={data?.email}
|
||||
operation={operation}
|
||||
/>
|
||||
)}
|
||||
{upload && (
|
||||
<Upload
|
||||
data={data}
|
||||
collection={collection}
|
||||
/>
|
||||
)}
|
||||
<RenderFields
|
||||
operation={operation}
|
||||
readOnly={!hasSavePermission}
|
||||
permissions={permissions.fields}
|
||||
filter={(field) => (!field?.admin?.position || (field?.admin?.position !== 'sidebar'))}
|
||||
fieldTypes={fieldTypes}
|
||||
fieldSchema={fields}
|
||||
/>
|
||||
</React.Fragment>
|
||||
<React.Fragment>
|
||||
<header className={`${baseClass}__header`}>
|
||||
<h1>
|
||||
<RenderTitle {...{ data, useAsTitle, fallback: '[Untitled]' }} />
|
||||
</h1>
|
||||
</header>
|
||||
{auth && (
|
||||
<Auth
|
||||
useAPIKey={auth.useAPIKey}
|
||||
requirePassword={!isEditing}
|
||||
verify={auth.verify}
|
||||
collection={collection}
|
||||
email={data?.email}
|
||||
operation={operation}
|
||||
/>
|
||||
)}
|
||||
{upload && (
|
||||
<Upload
|
||||
data={data}
|
||||
collection={collection}
|
||||
/>
|
||||
)}
|
||||
<RenderFields
|
||||
operation={operation}
|
||||
readOnly={!hasSavePermission}
|
||||
permissions={permissions.fields}
|
||||
filter={(field) => (!field?.admin?.position || (field?.admin?.position !== 'sidebar'))}
|
||||
fieldTypes={fieldTypes}
|
||||
fieldSchema={fields}
|
||||
/>
|
||||
</React.Fragment>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className={`${baseClass}__sidebar`}>
|
||||
<div className={`${baseClass}__sidebar-sticky`}>
|
||||
<div className={`${baseClass}__sidebar-wrap`}>
|
||||
<div className={`${baseClass}__sidebar`}>
|
||||
<div className={`${baseClass}__sidebar-sticky-wrap`}>
|
||||
{isEditing ? (
|
||||
<ul className={`${baseClass}__collection-actions`}>
|
||||
{(permissions?.create?.permission) && (
|
||||
<React.Fragment>
|
||||
<li><Link to={`${admin}/collections/${slug}/create`}>Create New</Link></li>
|
||||
{!disableDuplicate && (
|
||||
<li><DuplicateDocument slug={slug} /></li>
|
||||
)}
|
||||
</React.Fragment>
|
||||
<React.Fragment>
|
||||
<li><Link to={`${admin}/collections/${slug}/create`}>Create New</Link></li>
|
||||
{!disableDuplicate && (
|
||||
<li><DuplicateDocument slug={slug} /></li>
|
||||
)}
|
||||
</React.Fragment>
|
||||
)}
|
||||
{permissions?.delete?.permission && (
|
||||
<li>
|
||||
<DeleteDocument
|
||||
collection={collection}
|
||||
id={id}
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<DeleteDocument
|
||||
collection={collection}
|
||||
id={id}
|
||||
/>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
) : undefined}
|
||||
@@ -148,25 +148,9 @@ const DefaultEditView: React.FC<Props> = (props) => {
|
||||
/>
|
||||
)}
|
||||
{hasSavePermission && (
|
||||
<FormSubmit>Save</FormSubmit>
|
||||
<FormSubmit>Save</FormSubmit>
|
||||
)}
|
||||
</div>
|
||||
{isEditing && (
|
||||
<div className={`${baseClass}__api-url`}>
|
||||
<span className={`${baseClass}__label`}>
|
||||
API URL
|
||||
{' '}
|
||||
<CopyToClipboard value={apiURL} />
|
||||
</span>
|
||||
<a
|
||||
href={apiURL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{apiURL}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
{!isLoading && (
|
||||
<React.Fragment>
|
||||
<div className={`${baseClass}__sidebar-fields`}>
|
||||
@@ -181,6 +165,20 @@ const DefaultEditView: React.FC<Props> = (props) => {
|
||||
</div>
|
||||
{isEditing && (
|
||||
<ul className={`${baseClass}__meta`}>
|
||||
<li className={`${baseClass}__api-url`}>
|
||||
<span className={`${baseClass}__label`}>
|
||||
API URL
|
||||
{' '}
|
||||
<CopyToClipboard value={apiURL} />
|
||||
</span>
|
||||
<a
|
||||
href={apiURL}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{apiURL}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<div className={`${baseClass}__label`}>ID</div>
|
||||
<div>{id}</div>
|
||||
|
||||
@@ -4,20 +4,14 @@
|
||||
width: 100%;
|
||||
|
||||
&__form {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
&__main,
|
||||
&__sidebar {
|
||||
width: auto;
|
||||
min-width: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&__main {
|
||||
width: calc(100% - #{base(15)});
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
&__header {
|
||||
@@ -44,20 +38,23 @@
|
||||
background: white;
|
||||
padding: base(5) base(6) base(6);
|
||||
margin-bottom: base(1.5);
|
||||
overflow-x: scroll;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
&__sidebar {
|
||||
&__sidebar-wrap {
|
||||
position: fixed;
|
||||
width: base(15);
|
||||
flex-grow: 0;
|
||||
flex-shrink: 0;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
right: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
&__sidebar-sticky {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
height: 100vh;
|
||||
overflow: auto;
|
||||
&__sidebar {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
&__sidebar-sticky-wrap {
|
||||
@@ -69,8 +66,7 @@
|
||||
&__collection-actions,
|
||||
&__document-actions,
|
||||
&__meta,
|
||||
&__sidebar-fields,
|
||||
&__api-url {
|
||||
&__sidebar-fields {
|
||||
padding-left: base(1.5);
|
||||
}
|
||||
|
||||
@@ -79,6 +75,7 @@
|
||||
padding-right: $baseline;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: $z-nav;
|
||||
}
|
||||
|
||||
&__document-actions--with-preview {
|
||||
@@ -160,13 +157,15 @@
|
||||
}
|
||||
|
||||
@include mid-break {
|
||||
&__sidebar {
|
||||
width: unset;
|
||||
&__main {
|
||||
width: 100%;
|
||||
min-height: initial;
|
||||
}
|
||||
|
||||
&__sidebar-sticky {
|
||||
height: auto;
|
||||
overflow: hidden;
|
||||
&__sidebar-wrap {
|
||||
position: static;
|
||||
width: 100%;
|
||||
height: initial;
|
||||
}
|
||||
|
||||
&__form {
|
||||
@@ -176,7 +175,6 @@
|
||||
&__edit {
|
||||
padding: $baseline;
|
||||
margin-bottom: $baseline;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
&__document-actions {
|
||||
@@ -190,15 +188,10 @@
|
||||
|
||||
&__document-actions,
|
||||
&__meta,
|
||||
&__sidebar-fields,
|
||||
&__api-url {
|
||||
&__sidebar-fields {
|
||||
padding-left: $baseline;
|
||||
}
|
||||
|
||||
&__api-url {
|
||||
margin-bottom: base(.5);
|
||||
}
|
||||
|
||||
&__collection-actions {
|
||||
margin-top: base(.5);
|
||||
padding-left: $baseline;
|
||||
|
||||
@@ -83,13 +83,7 @@ export default joi.object({
|
||||
middleware: joi.array().items(joi.func()),
|
||||
}),
|
||||
local: joi.boolean(),
|
||||
upload: joi.object()
|
||||
.keys({
|
||||
limits: joi.object()
|
||||
.keys({
|
||||
fileSize: joi.number(),
|
||||
}),
|
||||
}),
|
||||
upload: joi.object(),
|
||||
rateLimit: joi.object()
|
||||
.keys({
|
||||
window: joi.number(),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Express } from 'express';
|
||||
import { DeepRequired } from 'ts-essentials';
|
||||
import { Transporter } from 'nodemailer';
|
||||
import { Options } from 'express-fileupload';
|
||||
import { Configuration } from 'webpack';
|
||||
import SMTPConnection from 'nodemailer/lib/smtp-connection';
|
||||
import GraphQL from 'graphql';
|
||||
@@ -125,11 +126,7 @@ export type PayloadConfig = {
|
||||
trustProxy?: boolean;
|
||||
skip?: (req: PayloadRequest) => boolean;
|
||||
};
|
||||
upload?: {
|
||||
limits?: {
|
||||
fileSize?: number;
|
||||
};
|
||||
};
|
||||
upload?: Options;
|
||||
localization?: {
|
||||
locales: string[]
|
||||
defaultLocale: string
|
||||
|
||||
@@ -25,7 +25,9 @@ export default [
|
||||
read: ({ req: { user } }) => Boolean(user),
|
||||
},
|
||||
admin: {
|
||||
disabled: true,
|
||||
components: {
|
||||
Field: () => null,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user