mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-30 19:52:20 +01:00
Store settings + update text editor options
This commit is contained in:
parent
27a3bdc308
commit
a5a45ee1ce
@ -12,11 +12,14 @@ import { MenuBar } from './menu-bar';
|
|||||||
import { WarningOctagon } from 'phosphor-react';
|
import { WarningOctagon } from 'phosphor-react';
|
||||||
|
|
||||||
interface FabTextEditorProps {
|
interface FabTextEditorProps {
|
||||||
paragraphTools?: boolean,
|
heading?: boolean,
|
||||||
content?: string,
|
bulletList?: boolean,
|
||||||
limit?: number,
|
blockquote?: boolean,
|
||||||
|
link?: boolean,
|
||||||
video?: boolean,
|
video?: boolean,
|
||||||
image?: boolean,
|
image?: boolean,
|
||||||
|
content?: string,
|
||||||
|
limit?: number,
|
||||||
onChange?: (content: string) => void,
|
onChange?: (content: string) => void,
|
||||||
placeholder?: string,
|
placeholder?: string,
|
||||||
error?: string,
|
error?: string,
|
||||||
@ -30,7 +33,7 @@ export interface FabTextEditorRef {
|
|||||||
/**
|
/**
|
||||||
* This component is a WYSIWYG text editor
|
* This component is a WYSIWYG text editor
|
||||||
*/
|
*/
|
||||||
export const FabTextEditor: React.ForwardRefRenderFunction<FabTextEditorRef, FabTextEditorProps> = ({ paragraphTools, content, limit = 400, video, image, onChange, placeholder, error, disabled = false }, ref: RefObject<FabTextEditorRef>) => {
|
export const FabTextEditor: React.ForwardRefRenderFunction<FabTextEditorRef, FabTextEditorProps> = ({ heading, bulletList, blockquote, content, limit = 400, video, image, link, onChange, placeholder, error, disabled = false }, ref: RefObject<FabTextEditorRef>) => {
|
||||||
const { t } = useTranslation('shared');
|
const { t } = useTranslation('shared');
|
||||||
const placeholderText = placeholder || t('app.shared.text_editor.fab_text_editor.text_placeholder');
|
const placeholderText = placeholder || t('app.shared.text_editor.fab_text_editor.text_placeholder');
|
||||||
// TODO: Add ctrl+click on link to visit
|
// TODO: Add ctrl+click on link to visit
|
||||||
@ -86,7 +89,7 @@ export const FabTextEditor: React.ForwardRefRenderFunction<FabTextEditorRef, Fab
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`fab-text-editor ${disabled && 'is-disabled'}`}>
|
<div className={`fab-text-editor ${disabled && 'is-disabled'}`}>
|
||||||
<MenuBar editor={editor} paragraphTools={paragraphTools} video={video} image={image} disabled={disabled} />
|
<MenuBar editor={editor} heading={heading} bulletList={bulletList} blockquote={blockquote} video={video} image={image} link={link} disabled={disabled} />
|
||||||
<EditorContent editor={editor} />
|
<EditorContent editor={editor} />
|
||||||
<div className="fab-text-editor-character-count">
|
<div className="fab-text-editor-character-count">
|
||||||
{editor?.storage.characterCount.characters()} / {limit}
|
{editor?.storage.characterCount.characters()} / {limit}
|
||||||
|
@ -6,7 +6,10 @@ import { TextAa, TextBolder, TextItalic, TextUnderline, LinkSimpleHorizontal, Li
|
|||||||
|
|
||||||
interface MenuBarProps {
|
interface MenuBarProps {
|
||||||
editor?: Editor,
|
editor?: Editor,
|
||||||
paragraphTools?: boolean,
|
heading?: boolean,
|
||||||
|
bulletList?: boolean,
|
||||||
|
blockquote?: boolean,
|
||||||
|
link?: boolean,
|
||||||
video?: boolean,
|
video?: boolean,
|
||||||
image?: boolean,
|
image?: boolean,
|
||||||
disabled?: boolean,
|
disabled?: boolean,
|
||||||
@ -15,7 +18,7 @@ interface MenuBarProps {
|
|||||||
/**
|
/**
|
||||||
* This component is the menu bar for the WYSIWYG text editor
|
* This component is the menu bar for the WYSIWYG text editor
|
||||||
*/
|
*/
|
||||||
export const MenuBar: React.FC<MenuBarProps> = ({ editor, paragraphTools, video, image, disabled = false }) => {
|
export const MenuBar: React.FC<MenuBarProps> = ({ editor, heading, bulletList, blockquote, link, video, image, disabled = false }) => {
|
||||||
const { t } = useTranslation('shared');
|
const { t } = useTranslation('shared');
|
||||||
|
|
||||||
const [submenu, setSubmenu] = useState('');
|
const [submenu, setSubmenu] = useState('');
|
||||||
@ -142,8 +145,7 @@ export const MenuBar: React.FC<MenuBarProps> = ({ editor, paragraphTools, video,
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={`fab-text-editor-menu ${disabled ? 'fab-text-editor-menu--disabled' : ''}`}>
|
<div className={`fab-text-editor-menu ${disabled ? 'fab-text-editor-menu--disabled' : ''}`}>
|
||||||
{ paragraphTools &&
|
{heading &&
|
||||||
(<>
|
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()}
|
onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()}
|
||||||
@ -152,6 +154,8 @@ export const MenuBar: React.FC<MenuBarProps> = ({ editor, paragraphTools, video,
|
|||||||
>
|
>
|
||||||
<TextAa size={24} />
|
<TextAa size={24} />
|
||||||
</button>
|
</button>
|
||||||
|
}
|
||||||
|
{bulletList &&
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
onClick={() => editor.chain().focus().toggleBulletList().run()}
|
onClick={() => editor.chain().focus().toggleBulletList().run()}
|
||||||
@ -160,6 +164,8 @@ export const MenuBar: React.FC<MenuBarProps> = ({ editor, paragraphTools, video,
|
|||||||
>
|
>
|
||||||
<ListBullets size={24} />
|
<ListBullets size={24} />
|
||||||
</button>
|
</button>
|
||||||
|
}
|
||||||
|
{blockquote &&
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
onClick={() => editor.chain().focus().toggleBlockquote().run()}
|
onClick={() => editor.chain().focus().toggleBlockquote().run()}
|
||||||
@ -168,9 +174,8 @@ export const MenuBar: React.FC<MenuBarProps> = ({ editor, paragraphTools, video,
|
|||||||
>
|
>
|
||||||
<Quotes size={24} />
|
<Quotes size={24} />
|
||||||
</button>
|
</button>
|
||||||
<span className='menu-divider'></span>
|
|
||||||
</>)
|
|
||||||
}
|
}
|
||||||
|
{ (heading || bulletList || blockquote) && <span className='menu-divider'></span> }
|
||||||
<button
|
<button
|
||||||
type='button'
|
type='button'
|
||||||
onClick={() => editor.chain().focus().toggleBold().run()}
|
onClick={() => editor.chain().focus().toggleBold().run()}
|
||||||
@ -195,14 +200,16 @@ export const MenuBar: React.FC<MenuBarProps> = ({ editor, paragraphTools, video,
|
|||||||
>
|
>
|
||||||
<TextUnderline size={24} />
|
<TextUnderline size={24} />
|
||||||
</button>
|
</button>
|
||||||
<button
|
{link &&
|
||||||
type='button'
|
<button
|
||||||
onClick={() => toggleSubmenu('link')}
|
type='button'
|
||||||
disabled={disabled}
|
onClick={() => toggleSubmenu('link')}
|
||||||
className={`ignore-onclickoutside ${editor.isActive('link') ? 'is-active' : ''}`}
|
disabled={disabled}
|
||||||
>
|
className={`ignore-onclickoutside ${editor.isActive('link') ? 'is-active' : ''}`}
|
||||||
<LinkSimpleHorizontal size={24} />
|
>
|
||||||
</button>
|
<LinkSimpleHorizontal size={24} />
|
||||||
|
</button>
|
||||||
|
}
|
||||||
{ (video || image) && <span className='menu-divider'></span> }
|
{ (video || image) && <span className='menu-divider'></span> }
|
||||||
{ video &&
|
{ video &&
|
||||||
(<>
|
(<>
|
||||||
|
@ -10,15 +10,18 @@ import { FieldPathValue, UnpackNestedValue } from 'react-hook-form/dist/types';
|
|||||||
interface FormRichTextProps<TFieldValues, TContext extends object> extends FormControlledComponent<TFieldValues, TContext>, AbstractFormItemProps<TFieldValues> {
|
interface FormRichTextProps<TFieldValues, TContext extends object> extends FormControlledComponent<TFieldValues, TContext>, AbstractFormItemProps<TFieldValues> {
|
||||||
valueDefault?: string,
|
valueDefault?: string,
|
||||||
limit?: number,
|
limit?: number,
|
||||||
paragraphTools?: boolean,
|
heading?: boolean,
|
||||||
|
bulletList?: boolean,
|
||||||
|
blockquote?: boolean,
|
||||||
|
link?: boolean,
|
||||||
video?: boolean,
|
video?: boolean,
|
||||||
image?: boolean,
|
image?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This component is a rich-text editor to use with react-hook-form.
|
* This component is a rich-text editor to use with react-hook-form.
|
||||||
*/
|
*/
|
||||||
export const FormRichText = <TFieldValues extends FieldValues, TContext extends object>({ id, label, tooltip, className, control, valueDefault, error, warning, rules, disabled = false, formState, limit, paragraphTools, video, image }: FormRichTextProps<TFieldValues, TContext>) => {
|
export const FormRichText = <TFieldValues extends FieldValues, TContext extends object>({ id, label, tooltip, className, control, valueDefault, error, warning, rules, disabled = false, formState, limit, heading, bulletList, blockquote, video, image, link }: FormRichTextProps<TFieldValues, TContext>) => {
|
||||||
const textEditorRef = React.useRef<FabTextEditorRef>();
|
const textEditorRef = React.useRef<FabTextEditorRef>();
|
||||||
const [isDisabled, setIsDisabled] = React.useState<boolean>(false);
|
const [isDisabled, setIsDisabled] = React.useState<boolean>(false);
|
||||||
|
|
||||||
@ -53,9 +56,12 @@ export const FormRichText = <TFieldValues extends FieldValues, TContext extends
|
|||||||
<FabTextEditor onChange={onChange}
|
<FabTextEditor onChange={onChange}
|
||||||
content={value}
|
content={value}
|
||||||
limit={limit}
|
limit={limit}
|
||||||
paragraphTools={paragraphTools}
|
heading={heading}
|
||||||
|
bulletList={bulletList}
|
||||||
|
blockquote={blockquote}
|
||||||
video={video}
|
video={video}
|
||||||
image={image}
|
image={image}
|
||||||
|
link={link}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
ref={textEditorRef} />
|
ref={textEditorRef} />
|
||||||
} />
|
} />
|
||||||
|
@ -362,7 +362,10 @@ export const ProductForm: React.FC<ProductFormProps> = ({ product, title, onSucc
|
|||||||
<HtmlTranslate trKey="app.admin.store.product_form.product_description_info" />
|
<HtmlTranslate trKey="app.admin.store.product_form.product_description_info" />
|
||||||
</FabAlert>
|
</FabAlert>
|
||||||
<FormRichText control={control}
|
<FormRichText control={control}
|
||||||
paragraphTools={true}
|
heading
|
||||||
|
bulletList
|
||||||
|
blockquote
|
||||||
|
link
|
||||||
limit={6000}
|
limit={6000}
|
||||||
id="description" />
|
id="description" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { react2angular } from 'react2angular';
|
||||||
|
import { Loader } from '../base/loader';
|
||||||
|
import { IApplication } from '../../models/application';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { HtmlTranslate } from '../base/html-translate';
|
||||||
|
import { useForm, SubmitHandler } from 'react-hook-form';
|
||||||
|
import { FabAlert } from '../base/fab-alert';
|
||||||
|
import { FormRichText } from '../form/form-rich-text';
|
||||||
|
import { FabButton } from '../base/fab-button';
|
||||||
|
|
||||||
|
declare const Application: IApplication;
|
||||||
|
|
||||||
|
interface StoreSettingsProps {
|
||||||
|
onError: (message: string) => void,
|
||||||
|
onSuccess: (message: string) => void
|
||||||
|
}
|
||||||
|
interface Settings {
|
||||||
|
withdrawal: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows store settings
|
||||||
|
*/
|
||||||
|
export const StoreSettings: React.FC<StoreSettingsProps> = (onError, onSuccess) => {
|
||||||
|
const { t } = useTranslation('admin');
|
||||||
|
|
||||||
|
const { control, handleSubmit } = useForm<Settings>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback triggered when the form is submitted: process with the product creation or update.
|
||||||
|
*/
|
||||||
|
const onSubmit: SubmitHandler<Settings> = (data) => {
|
||||||
|
console.log(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='store-settings'>
|
||||||
|
<header>
|
||||||
|
<h2>{t('app.admin.store_settings.title')}</h2>
|
||||||
|
</header>
|
||||||
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
<p>{t('app.admin.store_settings.withdrawal_instructions')}</p>
|
||||||
|
<FabAlert level="warning">
|
||||||
|
<HtmlTranslate trKey="app.admin.store_settings.withdrawal_info" />
|
||||||
|
</FabAlert>
|
||||||
|
<FormRichText control={control}
|
||||||
|
heading
|
||||||
|
bulletList
|
||||||
|
link
|
||||||
|
limit={400}
|
||||||
|
id="withdrawal" />
|
||||||
|
<FabButton type='submit' className='save-btn'>{t('app.admin.store_settings.save')}</FabButton>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const StoreSettingsWrapper: React.FC<StoreSettingsProps> = (props) => {
|
||||||
|
return (
|
||||||
|
<Loader>
|
||||||
|
<StoreSettings {...props} />
|
||||||
|
</Loader>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Application.Components.component('storeSettings', react2angular(StoreSettingsWrapper, ['onError', 'onSuccess']));
|
@ -101,6 +101,7 @@
|
|||||||
@import "modules/store/store-filters";
|
@import "modules/store/store-filters";
|
||||||
@import "modules/store/store-list-header";
|
@import "modules/store/store-list-header";
|
||||||
@import "modules/store/store-list";
|
@import "modules/store/store-list";
|
||||||
|
@import "modules/store/store-settings";
|
||||||
@import "modules/store/store";
|
@import "modules/store/store";
|
||||||
@import "modules/subscriptions/free-extend-modal";
|
@import "modules/subscriptions/free-extend-modal";
|
||||||
@import "modules/subscriptions/renew-modal";
|
@import "modules/subscriptions/renew-modal";
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
.store-settings {
|
||||||
|
max-width: 1600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding-bottom: 6rem;
|
||||||
|
@include grid-col(12);
|
||||||
|
gap: 3.2rem;
|
||||||
|
align-items: flex-start;
|
||||||
|
header {
|
||||||
|
@include header();
|
||||||
|
padding-bottom: 0;
|
||||||
|
grid-column: 2 / -2;
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
grid-column: 2 / 7;
|
||||||
|
p { @include title-base; }
|
||||||
|
.save-btn {
|
||||||
|
background-color: var(--main);
|
||||||
|
color: var(--gray-soft-lightest);
|
||||||
|
border: none;
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--main);
|
||||||
|
color: var(--gray-soft-lightest);
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1 +1 @@
|
|||||||
<h2>Settings page</h2>
|
<store-settings on-success="onSuccess" on-error="onError"/>
|
||||||
|
@ -2026,4 +2026,8 @@ en:
|
|||||||
gift_total: "Discount total"
|
gift_total: "Discount total"
|
||||||
coupon: "Coupon"
|
coupon: "Coupon"
|
||||||
cart_total: "Cart total"
|
cart_total: "Cart total"
|
||||||
|
store_settings:
|
||||||
|
title: 'Settings'
|
||||||
|
withdrawal_instructions: 'Product withdrawal instructions'
|
||||||
|
withdrawal_info: "This text is displayed on the checkout page to inform the client about the products withdrawal method"
|
||||||
|
save: "Save"
|
Loading…
x
Reference in New Issue
Block a user