@@ -0,0 +1,57 @@
|
||||
import {FormTextField} from '@common/ui/forms/input-field/text-field/text-field';
|
||||
import {Trans} from '@common/i18n/trans';
|
||||
import React, {Fragment} from 'react';
|
||||
import {useFormContext} from 'react-hook-form';
|
||||
import {UpdateChannelPayload} from '@common/admin/channels/requests/use-update-channel';
|
||||
import {SlugEditor} from '@common/ui/slug-editor';
|
||||
import {useTrans} from '@common/i18n/use-trans';
|
||||
import {message} from '@common/i18n/message';
|
||||
import clsx from 'clsx';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
autoFocus?: boolean;
|
||||
}
|
||||
export function ChannelNameField({className, autoFocus}: Props) {
|
||||
return (
|
||||
<Fragment>
|
||||
<FormTextField
|
||||
name="name"
|
||||
label={<Trans message="Title" />}
|
||||
required
|
||||
autoFocus={autoFocus}
|
||||
className={clsx('mb-10', className)}
|
||||
/>
|
||||
<FormSlugField />
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
function FormSlugField() {
|
||||
const {watch, setValue} = useFormContext<UpdateChannelPayload>();
|
||||
const value = watch('slug');
|
||||
const name = watch('name');
|
||||
const disableSlugEditing = watch('config.lockSlug');
|
||||
const restriction = watch('config.restriction');
|
||||
const restrictionId = watch('config.restrictionModelId');
|
||||
const {trans} = useTrans();
|
||||
return (
|
||||
<SlugEditor
|
||||
hideButton={disableSlugEditing}
|
||||
placeholder={name}
|
||||
suffix={
|
||||
restriction && restrictionId === 'urlParam'
|
||||
? trans(message(':restriction_name', {values: {restriction}}))
|
||||
: undefined
|
||||
}
|
||||
className="text-sm"
|
||||
pattern="[A-Za-z0-9_-]+"
|
||||
minLength={3}
|
||||
maxLength={20}
|
||||
value={value}
|
||||
onChange={newSlug => {
|
||||
setValue('slug', newSlug);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import {FormSelect, Option} from '@common/ui/forms/select/select';
|
||||
import {Trans} from '@common/i18n/trans';
|
||||
import {ChannelContentConfig} from '@common/admin/channels/channel-editor/channel-content-config';
|
||||
|
||||
interface Props {
|
||||
config: ChannelContentConfig;
|
||||
className?: string;
|
||||
}
|
||||
export function ChannelPaginationTypeField({className}: Props) {
|
||||
return (
|
||||
<FormSelect
|
||||
className={className}
|
||||
selectionMode="single"
|
||||
name="config.paginationType"
|
||||
label={<Trans message="Pagination type" />}
|
||||
>
|
||||
<Option value="infiniteScroll">
|
||||
<Trans message="Infinite scroll" />
|
||||
</Option>
|
||||
<Option value="lengthAware">
|
||||
<Trans message="List of page buttons" />
|
||||
</Option>
|
||||
<Option value="simple">
|
||||
<Trans message="Next/previous page buttons only" />
|
||||
</Option>
|
||||
</FormSelect>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import {useFormContext} from 'react-hook-form';
|
||||
import {FormSelect, Option} from '@common/ui/forms/select/select';
|
||||
import {Trans} from '@common/i18n/trans';
|
||||
import {InfoDialogTrigger} from '@common/ui/overlays/dialog/info-dialog-trigger/info-dialog-trigger';
|
||||
import {Fragment, ReactNode} from 'react';
|
||||
import {UpdateChannelPayload} from '@common/admin/channels/requests/use-update-channel';
|
||||
import {ChannelContentConfig} from '@common/admin/channels/channel-editor/channel-content-config';
|
||||
import {FormTextField} from '@common/ui/forms/input-field/text-field/text-field';
|
||||
import clsx from 'clsx';
|
||||
import {ChannelsDocsLink} from '@common/admin/channels/channels-docs-link';
|
||||
|
||||
interface Props {
|
||||
children?: ReactNode;
|
||||
config: ChannelContentConfig;
|
||||
className?: string;
|
||||
}
|
||||
export function ContentAutoUpdateField({children, config, className}: Props) {
|
||||
const {watch, setValue} = useFormContext<UpdateChannelPayload>();
|
||||
const modelConfig = config.models[watch('config.contentModel')];
|
||||
const selectedMethodConfig =
|
||||
config.autoUpdateMethods[watch('config.autoUpdateMethod')!];
|
||||
|
||||
if (
|
||||
watch('config.contentType') !== 'autoUpdate' ||
|
||||
!modelConfig.autoUpdateMethods?.length
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={clsx('items-end gap-14 md:flex', className)}>
|
||||
<FormSelect
|
||||
required
|
||||
className="flex-auto"
|
||||
selectionMode="single"
|
||||
name="config.autoUpdateMethod"
|
||||
onSelectionChange={value => {
|
||||
if (config.autoUpdateMethods[value].provider) {
|
||||
setValue(
|
||||
'config.autoUpdateProvider',
|
||||
config.autoUpdateMethods[value].provider,
|
||||
);
|
||||
}
|
||||
}}
|
||||
label={
|
||||
<Fragment>
|
||||
<Trans message="Auto update method" />
|
||||
<InfoDialogTrigger
|
||||
body={
|
||||
<Fragment>
|
||||
<div className="mb-20">
|
||||
<Trans message="This option will automatically update channel content every 24 hours using the specified method." />
|
||||
</div>
|
||||
<ChannelsDocsLink hash="automatically-update-content-with-specified-method" />
|
||||
</Fragment>
|
||||
}
|
||||
/>
|
||||
</Fragment>
|
||||
}
|
||||
>
|
||||
{modelConfig.autoUpdateMethods.map(method => (
|
||||
<Option value={method} key={method}>
|
||||
<Trans {...config.autoUpdateMethods[method].label} />
|
||||
</Option>
|
||||
))}
|
||||
</FormSelect>
|
||||
{selectedMethodConfig?.value ? (
|
||||
<FormTextField
|
||||
name="config.autoUpdateValue"
|
||||
required
|
||||
className="flex-auto"
|
||||
label={<Trans {...selectedMethodConfig?.value.label} />}
|
||||
type={selectedMethodConfig?.value.inputType}
|
||||
/>
|
||||
) : null}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import {useFormContext} from 'react-hook-form';
|
||||
import {FormSelect, Option} from '@common/ui/forms/select/select';
|
||||
import {Trans} from '@common/i18n/trans';
|
||||
import {UpdateChannelPayload} from '@common/admin/channels/requests/use-update-channel';
|
||||
import {ReactNode} from 'react';
|
||||
import {ChannelContentConfig} from '@common/admin/channels/channel-editor/channel-content-config';
|
||||
import clsx from 'clsx';
|
||||
|
||||
interface Props {
|
||||
config: ChannelContentConfig;
|
||||
className?: string;
|
||||
}
|
||||
export function ContentLayoutFields({config, className}: Props) {
|
||||
return (
|
||||
<div className={clsx('items-end gap-14 md:flex', className)}>
|
||||
<LayoutField
|
||||
config={config}
|
||||
name="config.layout"
|
||||
label={<Trans message="Layout" />}
|
||||
/>
|
||||
<LayoutField
|
||||
config={config}
|
||||
name="config.nestedLayout"
|
||||
label={<Trans message="Layout when nested" />}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface LayoutFieldProps extends Props {
|
||||
name: string;
|
||||
label: ReactNode;
|
||||
}
|
||||
function LayoutField({config, name, label}: LayoutFieldProps) {
|
||||
const {watch} = useFormContext<UpdateChannelPayload>();
|
||||
const contentModel = watch('config.contentModel');
|
||||
const modelConfig = config.models[contentModel];
|
||||
|
||||
if (!modelConfig.layoutMethods?.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<FormSelect
|
||||
className="w-full flex-auto"
|
||||
selectionMode="single"
|
||||
name={name}
|
||||
label={label}
|
||||
>
|
||||
{modelConfig.layoutMethods.map(method => {
|
||||
const label = config.layoutMethods[method].label;
|
||||
return (
|
||||
<Option key={method} value={method}>
|
||||
<Trans {...label} />
|
||||
</Option>
|
||||
);
|
||||
})}
|
||||
</FormSelect>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import {useFormContext} from 'react-hook-form';
|
||||
import {FormSelect, Option} from '@common/ui/forms/select/select';
|
||||
import {Trans} from '@common/i18n/trans';
|
||||
import {UpdateChannelPayload} from '@common/admin/channels/requests/use-update-channel';
|
||||
import React from 'react';
|
||||
import {ChannelContentConfig} from '@common/admin/channels/channel-editor/channel-content-config';
|
||||
|
||||
interface Props {
|
||||
config: ChannelContentConfig;
|
||||
className?: string;
|
||||
exclude?: string[];
|
||||
}
|
||||
export function ContentModelField({config, className, exclude}: Props) {
|
||||
const {setValue, getValues} = useFormContext<UpdateChannelPayload>();
|
||||
return (
|
||||
<FormSelect
|
||||
className={className}
|
||||
selectionMode="single"
|
||||
name="config.contentModel"
|
||||
label={<Trans message="Type of content" />}
|
||||
onSelectionChange={newValue => {
|
||||
const modelConfig = config.models[newValue];
|
||||
if (
|
||||
getValues('config.contentType') === 'autoUpdate' &&
|
||||
!modelConfig.autoUpdateMethods?.length
|
||||
) {
|
||||
(setValue as any)('config.contentType', 'manual');
|
||||
}
|
||||
setValue('config.autoUpdateMethod', modelConfig.autoUpdateMethods?.[0]);
|
||||
setValue(
|
||||
'config.contentOrder',
|
||||
modelConfig.sortMethods[0] || 'channelables.order:asc',
|
||||
);
|
||||
setValue('config.layout', modelConfig.layoutMethods[0]);
|
||||
}}
|
||||
>
|
||||
{Object.entries(config.models)
|
||||
.filter(([model]) => !exclude?.includes(model))
|
||||
.map(([model, {label}]) => (
|
||||
<Option value={model} key={model}>
|
||||
<Trans {...label} />
|
||||
</Option>
|
||||
))}
|
||||
</FormSelect>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import {useFormContext} from 'react-hook-form';
|
||||
import {FormSelect, Option} from '@common/ui/forms/select/select';
|
||||
import {Trans} from '@common/i18n/trans';
|
||||
import {UpdateChannelPayload} from '@common/admin/channels/requests/use-update-channel';
|
||||
import {ChannelContentConfig} from '@common/admin/channels/channel-editor/channel-content-config';
|
||||
|
||||
interface Props {
|
||||
config: ChannelContentConfig;
|
||||
className?: string;
|
||||
}
|
||||
export function ContentOrderField({config, className}: Props) {
|
||||
const {watch} = useFormContext<UpdateChannelPayload>();
|
||||
const contentType = watch('config.contentType');
|
||||
const modelConfig = config.models[watch('config.contentModel')];
|
||||
const sortMethods = [...modelConfig.sortMethods, 'channelables.order:asc'];
|
||||
|
||||
return (
|
||||
<FormSelect
|
||||
className={className}
|
||||
selectionMode="single"
|
||||
name="config.contentOrder"
|
||||
label={<Trans message="How to order content" />}
|
||||
>
|
||||
{sortMethods.map(method => {
|
||||
const sortConfig = config.sortingMethods[method];
|
||||
if (
|
||||
!sortConfig.contentTypes ||
|
||||
sortConfig.contentTypes.includes(contentType)
|
||||
) {
|
||||
return (
|
||||
<Option value={method} key={method}>
|
||||
<Trans {...sortConfig.label} />
|
||||
</Option>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</FormSelect>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import {useFormContext} from 'react-hook-form';
|
||||
import {FormSelect, Option} from '@common/ui/forms/select/select';
|
||||
import {Trans} from '@common/i18n/trans';
|
||||
import {UpdateChannelPayload} from '@common/admin/channels/requests/use-update-channel';
|
||||
import {ChannelContentConfig} from '@common/admin/channels/channel-editor/channel-content-config';
|
||||
|
||||
interface Props {
|
||||
config: ChannelContentConfig;
|
||||
className?: string;
|
||||
}
|
||||
export function ContentTypeField({config, className}: Props) {
|
||||
const {setValue} = useFormContext<UpdateChannelPayload>();
|
||||
return (
|
||||
<FormSelect
|
||||
className={className}
|
||||
selectionMode="single"
|
||||
name="config.contentType"
|
||||
label={<Trans message="Content" />}
|
||||
onSelectionChange={newValue => {
|
||||
// if content type is "auto update" select first model that
|
||||
// can be auto updated, otherwise select first available model
|
||||
let model = Object.entries(config.models)[0];
|
||||
if (newValue === 'autoUpdate') {
|
||||
const newModel = Object.entries(config.models).find(
|
||||
([, modelConfig]) => modelConfig.autoUpdateMethods?.length,
|
||||
);
|
||||
if (newModel) {
|
||||
model = newModel;
|
||||
}
|
||||
}
|
||||
const [modelName, modelConfig] = model;
|
||||
|
||||
setValue('config.contentModel', modelName);
|
||||
setValue('config.restrictionModelId', undefined);
|
||||
setValue(
|
||||
'config.autoUpdateMethod',
|
||||
newValue === 'autoUpdate' ? modelConfig.autoUpdateMethods?.[0] : '',
|
||||
);
|
||||
setValue('config.contentOrder', modelConfig.sortMethods[0]);
|
||||
(setValue as any)('config.restriction', null);
|
||||
}}
|
||||
>
|
||||
<Option value="listAll">
|
||||
<Trans message="List all content of specified type" />
|
||||
</Option>
|
||||
<Option value="manual">
|
||||
<Trans message="Manage content manually" />
|
||||
</Option>
|
||||
<Option value="autoUpdate">
|
||||
<Trans message="Automatically update content with specified method" />
|
||||
</Option>
|
||||
</FormSelect>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user