Temporarily fixed eslint rules being disabled in AdminX (#17565)
no issue
This commit is contained in:
parent
2b9e322b3e
commit
8000df963b
@ -17,6 +17,89 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
|
// ----------------------
|
||||||
|
// Rules COPIED from base config, remove these when the config is fixed
|
||||||
|
|
||||||
|
// Style Rules
|
||||||
|
// Require 4 spaces
|
||||||
|
indent: ['error', 4],
|
||||||
|
// Require single quotes for strings & properties (allows template literals)
|
||||||
|
quotes: ['error', 'single', {allowTemplateLiterals: true}],
|
||||||
|
'quote-props': ['error', 'as-needed'],
|
||||||
|
// Require semi colons, always at the end of a line
|
||||||
|
semi: ['error', 'always'],
|
||||||
|
'semi-style': ['error', 'last'],
|
||||||
|
// Don't allow dangling commas
|
||||||
|
'comma-dangle': ['error', 'never'],
|
||||||
|
// Always require curly braces, and position them at the end and beginning of lines
|
||||||
|
curly: 'error',
|
||||||
|
'brace-style': ['error', '1tbs'],
|
||||||
|
// Don't allow padding inside of blocks
|
||||||
|
'padded-blocks': ['error', 'never'],
|
||||||
|
// Require objects to be consistently formatted with newlines
|
||||||
|
'object-curly-newline': ['error', {consistent: true}],
|
||||||
|
// Don't allow more than 1 consecutive empty line or an empty 1st line
|
||||||
|
'no-multiple-empty-lines': ['error', {max: 1, maxBOF: 0}],
|
||||||
|
// Variables must be camelcase, but properties are not checked
|
||||||
|
camelcase: ['error', {properties: 'never'}],
|
||||||
|
// Allow newlines before dots, not after e.g. .then goes on a new line
|
||||||
|
'dot-location': ['error', 'property'],
|
||||||
|
// Prefer dot notation over array notation
|
||||||
|
'dot-notation': ['error'],
|
||||||
|
|
||||||
|
// Spacing rules
|
||||||
|
// Don't allow multiple spaces anywhere
|
||||||
|
'no-multi-spaces': 'error',
|
||||||
|
// Anonymous functions have a sape, named functions never do
|
||||||
|
'space-before-function-paren': ['error', {anonymous: 'always', named: 'never'}],
|
||||||
|
// Don't put spaces inside of objects or arrays
|
||||||
|
'object-curly-spacing': ['error', 'never'],
|
||||||
|
'array-bracket-spacing': ['error', 'never'],
|
||||||
|
// Allow a max of one space between colons and values
|
||||||
|
'key-spacing': ['error', {mode: 'strict'}],
|
||||||
|
// Require spaces before and after keywords like if, else, try, catch etc
|
||||||
|
'keyword-spacing': 'error',
|
||||||
|
// No spaces around semis
|
||||||
|
'semi-spacing': 'error',
|
||||||
|
// 1 space around arrows
|
||||||
|
'arrow-spacing': 'error',
|
||||||
|
// Don't allow spaces inside parenthesis
|
||||||
|
'space-in-parens': ['error', 'never'],
|
||||||
|
// Require single spaces either side of operators
|
||||||
|
'space-unary-ops': 'error',
|
||||||
|
'space-infix-ops': 'error',
|
||||||
|
|
||||||
|
// Best practice rules
|
||||||
|
// Require === / !==
|
||||||
|
eqeqeq: ['error', 'always'],
|
||||||
|
// Don't allow ++ and --
|
||||||
|
'no-plusplus': ['error', {allowForLoopAfterthoughts: true}],
|
||||||
|
// Don't allow eval
|
||||||
|
'no-eval': 'error',
|
||||||
|
// Throw errors for unnecessary usage of .call or .apply
|
||||||
|
'no-useless-call': 'error',
|
||||||
|
// Don't allow console.* calls
|
||||||
|
'no-console': 'error',
|
||||||
|
// Prevent [variable shadowing](https://en.wikipedia.org/wiki/Variable_shadowing)
|
||||||
|
'no-shadow': ['error'],
|
||||||
|
|
||||||
|
// Return rules
|
||||||
|
// Prevent missing return statements in array functions like map & reduce
|
||||||
|
'array-callback-return': 'error',
|
||||||
|
'no-constructor-return': 'error',
|
||||||
|
'no-promise-executor-return': 'error',
|
||||||
|
|
||||||
|
// Arrow function styles
|
||||||
|
// Do not enforce single lines when using arrow functions.
|
||||||
|
// https://eslint.org/docs/rules/arrow-body-style
|
||||||
|
'arrow-body-style': 'off',
|
||||||
|
'arrow-parens': ['error', 'as-needed', {requireForBlockBody: true}],
|
||||||
|
'implicit-arrow-linebreak': 'error',
|
||||||
|
'no-confusing-arrow': 'error',
|
||||||
|
|
||||||
|
// ----------------------
|
||||||
|
// Rules NOT COPIED from base config, keep these
|
||||||
|
|
||||||
// sort multiple import lines into alphabetical groups
|
// sort multiple import lines into alphabetical groups
|
||||||
'ghost/sort-imports-es6-autofix/sort-imports-es6': ['error', {
|
'ghost/sort-imports-es6-autofix/sort-imports-es6': ['error', {
|
||||||
memberSyntaxSortOrder: ['none', 'all', 'single', 'multiple']
|
memberSyntaxSortOrder: ['none', 'all', 'single', 'multiple']
|
||||||
|
@ -5,11 +5,11 @@ import NiceModal from '@ebay/nice-modal-react';
|
|||||||
import RoutingProvider from './components/providers/RoutingProvider';
|
import RoutingProvider from './components/providers/RoutingProvider';
|
||||||
import Settings from './components/Settings';
|
import Settings from './components/Settings';
|
||||||
import Sidebar from './components/Sidebar';
|
import Sidebar from './components/Sidebar';
|
||||||
import { GlobalDirtyStateProvider } from './hooks/useGlobalDirtyState';
|
import {GlobalDirtyStateProvider} from './hooks/useGlobalDirtyState';
|
||||||
import { OfficialTheme } from './models/themes';
|
import {OfficialTheme} from './models/themes';
|
||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
import {QueryClient, QueryClientProvider} from '@tanstack/react-query';
|
||||||
import { ServicesProvider } from './components/providers/ServiceProvider';
|
import {ServicesProvider} from './components/providers/ServiceProvider';
|
||||||
import { Toaster } from 'react-hot-toast';
|
import {Toaster} from 'react-hot-toast';
|
||||||
|
|
||||||
interface AppProps {
|
interface AppProps {
|
||||||
ghostVersion: string;
|
ghostVersion: string;
|
||||||
@ -26,9 +26,9 @@ function App({ghostVersion, officialThemes}: AppProps) {
|
|||||||
<RoutingProvider>
|
<RoutingProvider>
|
||||||
<GlobalDirtyStateProvider>
|
<GlobalDirtyStateProvider>
|
||||||
<div className="admin-x-settings h-[100vh] w-full overflow-y-auto" id="admin-x-root" style={{
|
<div className="admin-x-settings h-[100vh] w-full overflow-y-auto" id="admin-x-root" style={{
|
||||||
height: '100vh',
|
height: '100vh',
|
||||||
width: '100%'
|
width: '100%'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Toaster />
|
<Toaster />
|
||||||
<NiceModal.Provider>
|
<NiceModal.Provider>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import TaskList from './Tasklist';
|
import TaskList from './Tasklist';
|
||||||
import { ReactNode } from 'react';
|
import {ReactNode} from 'react';
|
||||||
|
|
||||||
import * as TaskStories from './Task.stories';
|
import * as TaskStories from './Task.stories';
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import Icon from './Icon';
|
import Icon from './Icon';
|
||||||
import React, { HTMLProps } from 'react';
|
import React, {HTMLProps} from 'react';
|
||||||
|
|
||||||
export type ButtonColor = 'clear' | 'grey' | 'black' | 'green' | 'red' | 'white' | 'outline';
|
export type ButtonColor = 'clear' | 'grey' | 'black' | 'green' | 'red' | 'white' | 'outline';
|
||||||
export type ButtonSize = 'sm' | 'md';
|
export type ButtonSize = 'sm' | 'md';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
interface UseDynamicSVGImportOptions {
|
interface UseDynamicSVGImportOptions {
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type {Meta, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
import * as ListItemStories from './ListItem.stories';
|
import * as ListItemStories from './ListItem.stories';
|
||||||
import List from './List';
|
import List from './List';
|
||||||
import ListItem from './ListItem';
|
import ListItem from './ListItem';
|
||||||
import { ReactNode } from 'react';
|
import {ReactNode} from 'react';
|
||||||
|
|
||||||
const meta = {
|
const meta = {
|
||||||
title: 'Global / List',
|
title: 'Global / List',
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { ReactNode } from 'react';
|
import React, {ReactNode} from 'react';
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type {Meta, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
import Avatar from './Avatar';
|
import Avatar from './Avatar';
|
||||||
import Button from './Button';
|
import Button from './Button';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ReactNode } from 'react';
|
import {ReactNode} from 'react';
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type {Meta, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
import Button from './Button';
|
import Button from './Button';
|
||||||
import Menu from './Menu';
|
import Menu from './Menu';
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { useArgs } from '@storybook/preview-api';
|
import {useArgs} from '@storybook/preview-api';
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type {Meta, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
import SortableList, { SortableListProps } from './SortableList';
|
import SortableList, {SortableListProps} from './SortableList';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { arrayMove } from '@dnd-kit/sortable';
|
import {arrayMove} from '@dnd-kit/sortable';
|
||||||
import { useState } from 'react';
|
import {useState} from 'react';
|
||||||
|
|
||||||
const Wrapper = (props: SortableListProps<{id: string}> & {updateArgs: (args: Partial<SortableListProps<{id: string}>>) => void}) => {
|
const Wrapper = (props: SortableListProps<{id: string}> & {updateArgs: (args: Partial<SortableListProps<{id: string}>>) => void}) => {
|
||||||
// Seems like Storybook recreates items on every render, so we need to keep our own state
|
// Seems like Storybook recreates items on every render, so we need to keep our own state
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ReactNode } from 'react';
|
import {ReactNode} from 'react';
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type {Meta, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
import StickyFooter from './StickyFooter';
|
import StickyFooter from './StickyFooter';
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ReactNode } from 'react';
|
import {ReactNode} from 'react';
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type {Meta, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
import Table from './Table';
|
import Table from './Table';
|
||||||
import TableCell from './TableCell';
|
import TableCell from './TableCell';
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { ReactNode } from 'react';
|
import {ReactNode} from 'react';
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type {Meta, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
import ToastContainer from './ToastContainer';
|
import ToastContainer from './ToastContainer';
|
||||||
import { Toaster } from 'react-hot-toast';
|
import {Toaster} from 'react-hot-toast';
|
||||||
|
|
||||||
const meta = {
|
const meta = {
|
||||||
title: 'Global / Toast',
|
title: 'Global / Toast',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import Icon from './Icon';
|
import Icon from './Icon';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { Toast as HotToast, ToastOptions, toast } from 'react-hot-toast';
|
import {Toast as HotToast, ToastOptions, toast} from 'react-hot-toast';
|
||||||
|
|
||||||
export type ToastType = 'neutral' | 'success' | 'error' | 'pageError';
|
export type ToastType = 'neutral' | 'success' | 'error' | 'pageError';
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ReactNode } from 'react';
|
import {ReactNode} from 'react';
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type {Meta, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
import DesktopChrome from './DesktopChrome';
|
import DesktopChrome from './DesktopChrome';
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ReactNode } from 'react';
|
import {ReactNode} from 'react';
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type {Meta, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
import MobileChrome from './MobileChrome';
|
import MobileChrome from './MobileChrome';
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ReactNode } from 'react';
|
import {ReactNode} from 'react';
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type {Meta, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
import Checkbox from './Checkbox';
|
import Checkbox from './Checkbox';
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { ReactNode, Suspense, useCallback, useMemo } from 'react';
|
import React, {ReactNode, Suspense, useCallback, useMemo} from 'react';
|
||||||
|
|
||||||
export interface HtmlEditorProps {
|
export interface HtmlEditorProps {
|
||||||
value?: string
|
value?: string
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import Heading from '../Heading';
|
import Heading from '../Heading';
|
||||||
import Hint from '../Hint';
|
import Hint from '../Hint';
|
||||||
import HtmlEditor, { HtmlEditorProps } from './HtmlEditor';
|
import HtmlEditor, {HtmlEditorProps} from './HtmlEditor';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ReactNode } from 'react';
|
import {ReactNode} from 'react';
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type {Meta, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
import ImageUpload from './ImageUpload';
|
import ImageUpload from './ImageUpload';
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { ReactNode } from 'react';
|
import {ReactNode} from 'react';
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type {Meta, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
import Radio, { RadioOption } from './Radio';
|
import Radio, {RadioOption} from './Radio';
|
||||||
|
|
||||||
const meta = {
|
const meta = {
|
||||||
title: 'Global / Form / Radio',
|
title: 'Global / Form / Radio',
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { ReactNode } from 'react';
|
import {ReactNode} from 'react';
|
||||||
import { useArgs } from '@storybook/preview-api';
|
import {useArgs} from '@storybook/preview-api';
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type {Meta, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
import Select, { SelectOption } from './Select';
|
import Select, {SelectOption} from './Select';
|
||||||
|
|
||||||
const meta = {
|
const meta = {
|
||||||
title: 'Global / Form / Select',
|
title: 'Global / Form / Select',
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useId } from 'react';
|
import React, {useId} from 'react';
|
||||||
|
|
||||||
import Heading from '../Heading';
|
import Heading from '../Heading';
|
||||||
import Hint from '../Hint';
|
import Hint from '../Hint';
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ReactNode } from 'react';
|
import {ReactNode} from 'react';
|
||||||
import { useArgs } from '@storybook/preview-api';
|
import {useArgs} from '@storybook/preview-api';
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type {Meta, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
import TextArea from './TextArea';
|
import TextArea from './TextArea';
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { ReactNode } from 'react';
|
import {ReactNode} from 'react';
|
||||||
import { useArgs } from '@storybook/preview-api';
|
import {useArgs} from '@storybook/preview-api';
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type {Meta, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
import TextField from './TextField';
|
import TextField from './TextField';
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ const TextField: React.FC<TextFieldProps> = ({
|
|||||||
return (
|
return (
|
||||||
<div className={`flex flex-col ${containerClassName}`}>
|
<div className={`flex flex-col ${containerClassName}`}>
|
||||||
{field}
|
{field}
|
||||||
{title && <Heading className={hideTitle ? 'sr-only' : 'order-1 !text-grey-700 peer-focus:!text-black'} htmlFor={id} useLabelTag={true}>{title}</Heading>}
|
{title && <Heading className={hideTitle ? 'sr-only' : 'order-1 !text-grey-700 peer-focus:!text-black'} htmlFor={id} useLabelTag={true}>{title}</Heading>}
|
||||||
{hint && <Hint className={'order-3' + hintClassName} color={error ? 'red' : ''}>{hint}</Hint>}
|
{hint && <Hint className={'order-3' + hintClassName} color={error ? 'red' : ''}>{hint}</Hint>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ReactNode } from 'react';
|
import {ReactNode} from 'react';
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type {Meta, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
import Toggle from './Toggle';
|
import Toggle from './Toggle';
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ReactNode } from 'react';
|
import {ReactNode} from 'react';
|
||||||
import type { Meta, StoryContext, StoryObj } from '@storybook/react';
|
import type {Meta, StoryContext, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
import ConfirmationModal from './ConfirmationModal';
|
import ConfirmationModal from './ConfirmationModal';
|
||||||
import ConfirmationModalContainer from './ConfirmationModalContainer';
|
import ConfirmationModalContainer from './ConfirmationModalContainer';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ReactNode } from 'react';
|
import {ReactNode} from 'react';
|
||||||
import type { Meta, StoryContext, StoryObj } from '@storybook/react';
|
import type {Meta, StoryContext, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import ModalContainer from './ModalContainer';
|
import ModalContainer from './ModalContainer';
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { ReactNode } from 'react';
|
import {ReactNode} from 'react';
|
||||||
import type { Meta, StoryContext, StoryObj } from '@storybook/react';
|
import type {Meta, StoryContext, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
import Heading from '../Heading';
|
import Heading from '../Heading';
|
||||||
import NiceModal from '@ebay/nice-modal-react';
|
import NiceModal from '@ebay/nice-modal-react';
|
||||||
import PreviewModal from './PreviewModal';
|
import PreviewModal from './PreviewModal';
|
||||||
import PreviewModalContainer from './PreviewModalContainer';
|
import PreviewModalContainer from './PreviewModalContainer';
|
||||||
import { Tab } from '../TabView';
|
import {Tab} from '../TabView';
|
||||||
|
|
||||||
const meta = {
|
const meta = {
|
||||||
title: 'Global / Modal / Preview Modal',
|
title: 'Global / Modal / Preview Modal',
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ReactNode } from 'react';
|
import {ReactNode} from 'react';
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type {Meta, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
import * as SettingGroupContentStories from './SettingGroupContent.stories';
|
import * as SettingGroupContentStories from './SettingGroupContent.stories';
|
||||||
import * as SettingGroupHeaderStories from './SettingGroupHeader.stories';
|
import * as SettingGroupHeaderStories from './SettingGroupHeader.stories';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { ReactNode } from 'react';
|
import {ReactNode} from 'react';
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type {Meta, StoryObj} from '@storybook/react';
|
||||||
|
|
||||||
import * as SettingGroupStories from './SettingGroup.stories';
|
import * as SettingGroupStories from './SettingGroup.stories';
|
||||||
import SettingGroup from './SettingGroup';
|
import SettingGroup from './SettingGroup';
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import Button, { ButtonProps } from '../global/Button';
|
import Button, {ButtonProps} from '../global/Button';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import ChangeThemeModal from "../settings/site/ThemeModal";
|
import ChangeThemeModal from '../settings/site/ThemeModal';
|
||||||
import DesignModal from "../settings/site/DesignModal";
|
import DesignModal from '../settings/site/DesignModal';
|
||||||
import InviteUserModal from "../settings/general/InviteUserModal";
|
import InviteUserModal from '../settings/general/InviteUserModal';
|
||||||
import NavigationModal from "../settings/site/NavigationModal";
|
import NavigationModal from '../settings/site/NavigationModal';
|
||||||
import NiceModal from "@ebay/nice-modal-react";
|
import NiceModal from '@ebay/nice-modal-react';
|
||||||
import PortalModal from "../settings/membership/portal/PortalModal";
|
import PortalModal from '../settings/membership/portal/PortalModal';
|
||||||
import React, { createContext, useCallback, useEffect, useState } from "react";
|
import React, {createContext, useCallback, useEffect, useState} from 'react';
|
||||||
import StripeConnectModal from "../settings/membership/stripe/StripeConnectModal";
|
import StripeConnectModal from '../settings/membership/stripe/StripeConnectModal';
|
||||||
import TierDetailModal from "../settings/membership/tiers/TierDetailModal";
|
import TierDetailModal from '../settings/membership/tiers/TierDetailModal';
|
||||||
|
|
||||||
type RoutingContextProps = {
|
type RoutingContextProps = {
|
||||||
route: string;
|
route: string;
|
||||||
@ -17,11 +17,11 @@ type RoutingContextProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const RouteContext = createContext<RoutingContextProps>({
|
export const RouteContext = createContext<RoutingContextProps>({
|
||||||
route: "",
|
route: '',
|
||||||
scrolledRoute: "",
|
scrolledRoute: '',
|
||||||
yScroll: 0,
|
yScroll: 0,
|
||||||
updateRoute: () => {},
|
updateRoute: () => {},
|
||||||
updateScrolled: () => {},
|
updateScrolled: () => {}
|
||||||
});
|
});
|
||||||
|
|
||||||
function getHashPath(urlPath: string | undefined) {
|
function getHashPath(urlPath: string | undefined) {
|
||||||
@ -41,7 +41,7 @@ function getHashPath(urlPath: string | undefined) {
|
|||||||
const scrollToSectionGroup = (pathName: string) => {
|
const scrollToSectionGroup = (pathName: string) => {
|
||||||
const element = document.getElementById(pathName);
|
const element = document.getElementById(pathName);
|
||||||
if (element) {
|
if (element) {
|
||||||
element.scrollIntoView({ behavior: "smooth" });
|
element.scrollIntoView({behavior: 'smooth'});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -56,19 +56,19 @@ const handleNavigation = (scroll: boolean = true) => {
|
|||||||
const pathName = getHashPath(hash);
|
const pathName = getHashPath(hash);
|
||||||
|
|
||||||
if (pathName) {
|
if (pathName) {
|
||||||
if (pathName === "design/edit/themes") {
|
if (pathName === 'design/edit/themes') {
|
||||||
NiceModal.show(ChangeThemeModal);
|
NiceModal.show(ChangeThemeModal);
|
||||||
} else if (pathName === "design/edit") {
|
} else if (pathName === 'design/edit') {
|
||||||
NiceModal.show(DesignModal);
|
NiceModal.show(DesignModal);
|
||||||
} else if (pathName === "navigation/edit") {
|
} else if (pathName === 'navigation/edit') {
|
||||||
NiceModal.show(NavigationModal);
|
NiceModal.show(NavigationModal);
|
||||||
} else if (pathName === "users/invite") {
|
} else if (pathName === 'users/invite') {
|
||||||
NiceModal.show(InviteUserModal);
|
NiceModal.show(InviteUserModal);
|
||||||
} else if (pathName === "portal/edit") {
|
} else if (pathName === 'portal/edit') {
|
||||||
NiceModal.show(PortalModal);
|
NiceModal.show(PortalModal);
|
||||||
} else if (pathName === "tiers/add") {
|
} else if (pathName === 'tiers/add') {
|
||||||
NiceModal.show(TierDetailModal);
|
NiceModal.show(TierDetailModal);
|
||||||
} else if (pathName === "stripe-connect") {
|
} else if (pathName === 'stripe-connect') {
|
||||||
NiceModal.show(StripeConnectModal);
|
NiceModal.show(StripeConnectModal);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,17 +78,17 @@ const handleNavigation = (scroll: boolean = true) => {
|
|||||||
|
|
||||||
return pathName;
|
return pathName;
|
||||||
}
|
}
|
||||||
return "";
|
return '';
|
||||||
};
|
};
|
||||||
|
|
||||||
type RouteProviderProps = {
|
type RouteProviderProps = {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
const RoutingProvider: React.FC<RouteProviderProps> = ({ children }) => {
|
const RoutingProvider: React.FC<RouteProviderProps> = ({children}) => {
|
||||||
const [route, setRoute] = useState<string>("");
|
const [route, setRoute] = useState<string>('');
|
||||||
const [yScroll, setYScroll] = useState(0);
|
const [yScroll, setYScroll] = useState(0);
|
||||||
const [scrolledRoute, setScrolledRoute] = useState<string>("");
|
const [scrolledRoute, setScrolledRoute] = useState<string>('');
|
||||||
|
|
||||||
const updateRoute = useCallback(
|
const updateRoute = useCallback(
|
||||||
(newPath: string) => {
|
(newPath: string) => {
|
||||||
@ -116,21 +116,21 @@ const RoutingProvider: React.FC<RouteProviderProps> = ({ children }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
const element = document.getElementById("admin-x-root");
|
const element = document.getElementById('admin-x-root');
|
||||||
const scrollPosition = element!.scrollTop;
|
const scrollPosition = element!.scrollTop;
|
||||||
setYScroll(scrollPosition);
|
setYScroll(scrollPosition);
|
||||||
};
|
};
|
||||||
|
|
||||||
const element = document.getElementById("admin-x-root");
|
const element = document.getElementById('admin-x-root');
|
||||||
const matchedRoute = handleNavigation();
|
const matchedRoute = handleNavigation();
|
||||||
setRoute(matchedRoute);
|
setRoute(matchedRoute);
|
||||||
element!.addEventListener("scroll", handleScroll);
|
element!.addEventListener('scroll', handleScroll);
|
||||||
|
|
||||||
window.addEventListener("hashchange", handleHashChange);
|
window.addEventListener('hashchange', handleHashChange);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
element!.removeEventListener("scroll", handleScroll);
|
element!.removeEventListener('scroll', handleScroll);
|
||||||
window.removeEventListener("hashchange", handleHashChange);
|
window.removeEventListener('hashchange', handleHashChange);
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -141,7 +141,7 @@ const RoutingProvider: React.FC<RouteProviderProps> = ({ children }) => {
|
|||||||
scrolledRoute,
|
scrolledRoute,
|
||||||
yScroll,
|
yScroll,
|
||||||
updateRoute,
|
updateRoute,
|
||||||
updateScrolled,
|
updateScrolled
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import Button from '../../../admin-x-ds/global/Button';
|
import Button from '../../../admin-x-ds/global/Button';
|
||||||
import NewslettersList from './newsletters/NewslettersList';
|
import NewslettersList from './newsletters/NewslettersList';
|
||||||
import React, { useState } from 'react';
|
import React, {useState} from 'react';
|
||||||
import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
|
import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
|
||||||
import TabView from '../../../admin-x-ds/global/TabView';
|
import TabView from '../../../admin-x-ds/global/TabView';
|
||||||
import useRouting from '../../../hooks/useRouting';
|
import useRouting from '../../../hooks/useRouting';
|
||||||
import { useBrowseNewsletters } from '../../../utils/api/newsletters';
|
import {useBrowseNewsletters} from '../../../utils/api/newsletters';
|
||||||
|
|
||||||
const Newsletters: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
const Newsletters: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
||||||
const {updateRoute} = useRouting();
|
const {updateRoute} = useRouting();
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import Form from '../../../../admin-x-ds/global/form/Form';
|
import Form from '../../../../admin-x-ds/global/form/Form';
|
||||||
import NiceModal, { useModal } from '@ebay/nice-modal-react';
|
import NiceModal, {useModal} from '@ebay/nice-modal-react';
|
||||||
|
|
||||||
import ButtonGroup from '../../../../admin-x-ds/global/ButtonGroup';
|
import ButtonGroup from '../../../../admin-x-ds/global/ButtonGroup';
|
||||||
import Heading from '../../../../admin-x-ds/global/Heading';
|
import Heading from '../../../../admin-x-ds/global/Heading';
|
||||||
@ -7,20 +7,20 @@ import Hint from '../../../../admin-x-ds/global/Hint';
|
|||||||
import Icon from '../../../../admin-x-ds/global/Icon';
|
import Icon from '../../../../admin-x-ds/global/Icon';
|
||||||
import ImageUpload from '../../../../admin-x-ds/global/form/ImageUpload';
|
import ImageUpload from '../../../../admin-x-ds/global/form/ImageUpload';
|
||||||
import NewsletterPreview from './NewsletterPreview';
|
import NewsletterPreview from './NewsletterPreview';
|
||||||
import React, { useState } from 'react';
|
import React, {useState} from 'react';
|
||||||
import Select, { SelectOption } from '../../../../admin-x-ds/global/form/Select';
|
import Select, {SelectOption} from '../../../../admin-x-ds/global/form/Select';
|
||||||
import StickyFooter from '../../../../admin-x-ds/global/StickyFooter';
|
import StickyFooter from '../../../../admin-x-ds/global/StickyFooter';
|
||||||
import TabView, { Tab } from '../../../../admin-x-ds/global/TabView';
|
import TabView, {Tab} from '../../../../admin-x-ds/global/TabView';
|
||||||
import TextArea from '../../../../admin-x-ds/global/form/TextArea';
|
import TextArea from '../../../../admin-x-ds/global/form/TextArea';
|
||||||
import TextField from '../../../../admin-x-ds/global/form/TextField';
|
import TextField from '../../../../admin-x-ds/global/form/TextField';
|
||||||
import Toggle from '../../../../admin-x-ds/global/form/Toggle';
|
import Toggle from '../../../../admin-x-ds/global/form/Toggle';
|
||||||
import useForm from '../../../../hooks/useForm';
|
import useForm from '../../../../hooks/useForm';
|
||||||
import useSettings from '../../../../hooks/useSettings';
|
import useSettings from '../../../../hooks/useSettings';
|
||||||
import { Newsletter } from '../../../../types/api';
|
import {Newsletter} from '../../../../types/api';
|
||||||
import { PreviewModalContent } from '../../../../admin-x-ds/global/modal/PreviewModal';
|
import {PreviewModalContent} from '../../../../admin-x-ds/global/modal/PreviewModal';
|
||||||
import { fullEmailAddress, getSettingValues } from '../../../../utils/helpers';
|
import {fullEmailAddress, getSettingValues} from '../../../../utils/helpers';
|
||||||
import { getImageUrl, useUploadImage } from '../../../../utils/api/images';
|
import {getImageUrl, useUploadImage} from '../../../../utils/api/images';
|
||||||
import { useEditNewsletter } from '../../../../utils/api/newsletters';
|
import {useEditNewsletter} from '../../../../utils/api/newsletters';
|
||||||
|
|
||||||
interface NewsletterDetailModalProps {
|
interface NewsletterDetailModalProps {
|
||||||
newsletter: Newsletter
|
newsletter: Newsletter
|
||||||
@ -248,7 +248,7 @@ const NewsletterDetailModal: React.FC<NewsletterDetailModalProps> = ({newsletter
|
|||||||
await editNewsletter(formState);
|
await editNewsletter(formState);
|
||||||
modal.remove();
|
modal.remove();
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
const updateNewsletter = (fields: Partial<Newsletter>) => {
|
const updateNewsletter = (fields: Partial<Newsletter>) => {
|
||||||
updateForm(state => ({...state, ...fields}));
|
updateForm(state => ({...state, ...fields}));
|
||||||
|
@ -6,10 +6,10 @@ import LatestPosts3 from '../../../../assets/images/latest-posts-3.png';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import useSettings from '../../../../hooks/useSettings';
|
import useSettings from '../../../../hooks/useSettings';
|
||||||
import { ReactComponent as GhostOrb } from '../../../../admin-x-ds/assets/images/ghost-orb.svg';
|
import {ReactComponent as GhostOrb} from '../../../../admin-x-ds/assets/images/ghost-orb.svg';
|
||||||
import { Newsletter } from '../../../../types/api';
|
import {Newsletter} from '../../../../types/api';
|
||||||
import { fullEmailAddress, getSettingValues } from '../../../../utils/helpers';
|
import {fullEmailAddress, getSettingValues} from '../../../../utils/helpers';
|
||||||
import { useGlobalData } from '../../../providers/DataProvider';
|
import {useGlobalData} from '../../../providers/DataProvider';
|
||||||
|
|
||||||
const NewsletterPreview: React.FC<{newsletter: Newsletter}> = ({newsletter}) => {
|
const NewsletterPreview: React.FC<{newsletter: Newsletter}> = ({newsletter}) => {
|
||||||
const {currentUser} = useGlobalData();
|
const {currentUser} = useGlobalData();
|
||||||
@ -35,7 +35,7 @@ const NewsletterPreview: React.FC<{newsletter: Newsletter}> = ({newsletter}) =>
|
|||||||
const currentYear = new Date().getFullYear();
|
const currentYear = new Date().getFullYear();
|
||||||
|
|
||||||
const showCommentCta = newsletter.show_comment_cta && commentsEnabled !== 'off';
|
const showCommentCta = newsletter.show_comment_cta && commentsEnabled !== 'off';
|
||||||
const showFeedback = newsletter.feedback_enabled && config.labs.audienceFeedback
|
const showFeedback = newsletter.feedback_enabled && config.labs.audienceFeedback;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex grow flex-col">
|
<div className="relative flex grow flex-col">
|
||||||
|
@ -5,7 +5,7 @@ import React from 'react';
|
|||||||
import Table from '../../../../admin-x-ds/global/Table';
|
import Table from '../../../../admin-x-ds/global/Table';
|
||||||
import TableCell from '../../../../admin-x-ds/global/TableCell';
|
import TableCell from '../../../../admin-x-ds/global/TableCell';
|
||||||
import TableRow from '../../../../admin-x-ds/global/TableRow';
|
import TableRow from '../../../../admin-x-ds/global/TableRow';
|
||||||
import { Newsletter } from '../../../../types/api';
|
import {Newsletter} from '../../../../types/api';
|
||||||
|
|
||||||
interface NewslettersListProps {
|
interface NewslettersListProps {
|
||||||
tab?: string;
|
tab?: string;
|
||||||
|
@ -4,10 +4,10 @@ import Radio from '../../../admin-x-ds/global/form/Radio';
|
|||||||
import TextField from '../../../admin-x-ds/global/form/TextField';
|
import TextField from '../../../admin-x-ds/global/form/TextField';
|
||||||
import useRouting from '../../../hooks/useRouting';
|
import useRouting from '../../../hooks/useRouting';
|
||||||
import validator from 'validator';
|
import validator from 'validator';
|
||||||
import { showToast } from '../../../admin-x-ds/global/Toast';
|
import {showToast} from '../../../admin-x-ds/global/Toast';
|
||||||
import { useAddInvite } from '../../../utils/api/invites';
|
import {useAddInvite} from '../../../utils/api/invites';
|
||||||
import { useBrowseRoles } from '../../../utils/api/roles';
|
import {useBrowseRoles} from '../../../utils/api/roles';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import {useEffect, useRef, useState} from 'react';
|
||||||
|
|
||||||
type RoleType = 'administrator' | 'editor' | 'author' | 'contributor';
|
type RoleType = 'administrator' | 'editor' | 'author' | 'contributor';
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import React, { useRef, useState } from 'react';
|
import React, {useRef, useState} from 'react';
|
||||||
import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
|
import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
|
||||||
import SettingGroupContent from '../../../admin-x-ds/settings/SettingGroupContent';
|
import SettingGroupContent from '../../../admin-x-ds/settings/SettingGroupContent';
|
||||||
import TextField from '../../../admin-x-ds/global/form/TextField';
|
import TextField from '../../../admin-x-ds/global/form/TextField';
|
||||||
import useSettingGroup from '../../../hooks/useSettingGroup';
|
import useSettingGroup from '../../../hooks/useSettingGroup';
|
||||||
import validator from 'validator';
|
import validator from 'validator';
|
||||||
import { getSettingValues } from '../../../utils/helpers';
|
import {getSettingValues} from '../../../utils/helpers';
|
||||||
|
|
||||||
function validateFacebookUrl(newUrl: string) {
|
function validateFacebookUrl(newUrl: string) {
|
||||||
const errMessage = 'The URL must be in a format like https://www.facebook.com/yourPage';
|
const errMessage = 'The URL must be in a format like https://www.facebook.com/yourPage';
|
||||||
|
@ -4,9 +4,9 @@ import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
|
|||||||
import SettingGroupContent from '../../../admin-x-ds/settings/SettingGroupContent';
|
import SettingGroupContent from '../../../admin-x-ds/settings/SettingGroupContent';
|
||||||
import TextField from '../../../admin-x-ds/global/form/TextField';
|
import TextField from '../../../admin-x-ds/global/form/TextField';
|
||||||
import useSettingGroup from '../../../hooks/useSettingGroup';
|
import useSettingGroup from '../../../hooks/useSettingGroup';
|
||||||
import { ReactComponent as TwitterLogo } from '../../../admin-x-ds/assets/images/twitter-logo.svg';
|
import {ReactComponent as TwitterLogo} from '../../../admin-x-ds/assets/images/twitter-logo.svg';
|
||||||
import { getImageUrl, useUploadImage } from '../../../utils/api/images';
|
import {getImageUrl, useUploadImage} from '../../../utils/api/images';
|
||||||
import { getSettingValues } from '../../../utils/helpers';
|
import {getSettingValues} from '../../../utils/helpers';
|
||||||
|
|
||||||
const Twitter: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
const Twitter: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
||||||
const {
|
const {
|
||||||
|
@ -3,24 +3,24 @@ import ConfirmationModal from '../../../admin-x-ds/global/modal/ConfirmationModa
|
|||||||
import Heading from '../../../admin-x-ds/global/Heading';
|
import Heading from '../../../admin-x-ds/global/Heading';
|
||||||
import Icon from '../../../admin-x-ds/global/Icon';
|
import Icon from '../../../admin-x-ds/global/Icon';
|
||||||
import ImageUpload from '../../../admin-x-ds/global/form/ImageUpload';
|
import ImageUpload from '../../../admin-x-ds/global/form/ImageUpload';
|
||||||
import Menu, { MenuItem } from '../../../admin-x-ds/global/Menu';
|
import Menu, {MenuItem} from '../../../admin-x-ds/global/Menu';
|
||||||
import Modal from '../../../admin-x-ds/global/modal/Modal';
|
import Modal from '../../../admin-x-ds/global/modal/Modal';
|
||||||
import NiceModal, { useModal } from '@ebay/nice-modal-react';
|
import NiceModal, {useModal} from '@ebay/nice-modal-react';
|
||||||
import Radio from '../../../admin-x-ds/global/form/Radio';
|
import Radio from '../../../admin-x-ds/global/form/Radio';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, {useEffect, useRef, useState} from 'react';
|
||||||
import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
|
import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
|
||||||
import SettingGroupContent from '../../../admin-x-ds/settings/SettingGroupContent';
|
import SettingGroupContent from '../../../admin-x-ds/settings/SettingGroupContent';
|
||||||
import TextField from '../../../admin-x-ds/global/form/TextField';
|
import TextField from '../../../admin-x-ds/global/form/TextField';
|
||||||
import Toggle from '../../../admin-x-ds/global/form/Toggle';
|
import Toggle from '../../../admin-x-ds/global/form/Toggle';
|
||||||
import useStaffUsers from '../../../hooks/useStaffUsers';
|
import useStaffUsers from '../../../hooks/useStaffUsers';
|
||||||
import validator from 'validator';
|
import validator from 'validator';
|
||||||
import { User } from '../../../types/api';
|
import {User} from '../../../types/api';
|
||||||
import { getImageUrl, useUploadImage } from '../../../utils/api/images';
|
import {getImageUrl, useUploadImage} from '../../../utils/api/images';
|
||||||
import { isAdminUser, isOwnerUser } from '../../../utils/helpers';
|
import {isAdminUser, isOwnerUser} from '../../../utils/helpers';
|
||||||
import { showToast } from '../../../admin-x-ds/global/Toast';
|
import {showToast} from '../../../admin-x-ds/global/Toast';
|
||||||
import { toast } from 'react-hot-toast';
|
import {toast} from 'react-hot-toast';
|
||||||
import { useBrowseRoles } from '../../../utils/api/roles';
|
import {useBrowseRoles} from '../../../utils/api/roles';
|
||||||
import { useDeleteUser, useEditUser, useMakeOwner, useUpdatePassword } from '../../../utils/api/users';
|
import {useDeleteUser, useEditUser, useMakeOwner, useUpdatePassword} from '../../../utils/api/users';
|
||||||
|
|
||||||
interface CustomHeadingProps {
|
interface CustomHeadingProps {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
@ -104,7 +104,7 @@ const BasicInputs: React.FC<UserDetailProps> = ({errors, validators, user, setUs
|
|||||||
<SettingGroupContent>
|
<SettingGroupContent>
|
||||||
<TextField
|
<TextField
|
||||||
error={!!errors?.name}
|
error={!!errors?.name}
|
||||||
hint={errors?.name || "Use real name so people can recognize you"}
|
hint={errors?.name || 'Use real name so people can recognize you'}
|
||||||
title="Full name"
|
title="Full name"
|
||||||
value={user.name}
|
value={user.name}
|
||||||
onBlur={(e) => {
|
onBlur={(e) => {
|
||||||
@ -633,7 +633,7 @@ const UserDetailModal:React.FC<UserDetailModalProps> = ({user}) => {
|
|||||||
if (error) {
|
if (error) {
|
||||||
showToast({
|
showToast({
|
||||||
type: 'pageError',
|
type: 'pageError',
|
||||||
message: "Can't save user! One or more fields have errors, please doublecheck you filled all mandatory fields"
|
message: 'Can\'t save user! One or more fields have errors, please doublecheck you filled all mandatory fields'
|
||||||
});
|
});
|
||||||
setSaveState('');
|
setSaveState('');
|
||||||
return;
|
return;
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import Button from '../../../admin-x-ds/global/Button';
|
import Button from '../../../admin-x-ds/global/Button';
|
||||||
import React, { useState } from 'react';
|
import React, {useState} from 'react';
|
||||||
import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
|
import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
|
||||||
import StripeButton from '../../../admin-x-ds/settings/StripeButton';
|
import StripeButton from '../../../admin-x-ds/settings/StripeButton';
|
||||||
import TabView from '../../../admin-x-ds/global/TabView';
|
import TabView from '../../../admin-x-ds/global/TabView';
|
||||||
import TiersList from './tiers/TiersList';
|
import TiersList from './tiers/TiersList';
|
||||||
import useRouting from '../../../hooks/useRouting';
|
import useRouting from '../../../hooks/useRouting';
|
||||||
import { Tier } from '../../../types/api';
|
import {Tier} from '../../../types/api';
|
||||||
import { checkStripeEnabled, getActiveTiers, getArchivedTiers } from '../../../utils/helpers';
|
import {checkStripeEnabled, getActiveTiers, getArchivedTiers} from '../../../utils/helpers';
|
||||||
import { useGlobalData } from '../../providers/DataProvider';
|
import {useGlobalData} from '../../providers/DataProvider';
|
||||||
|
|
||||||
const Tiers: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
const Tiers: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
||||||
const [selectedTab, setSelectedTab] = useState('active-tiers');
|
const [selectedTab, setSelectedTab] = useState('active-tiers');
|
||||||
@ -44,11 +44,11 @@ const Tiers: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
let content
|
let content;
|
||||||
if (checkStripeEnabled(settings, config)) {
|
if (checkStripeEnabled(settings, config)) {
|
||||||
content = <TabView selectedTab={selectedTab} tabs={tabs} onTabChange={setSelectedTab} />
|
content = <TabView selectedTab={selectedTab} tabs={tabs} onTabChange={setSelectedTab} />;
|
||||||
} else {
|
} else {
|
||||||
content = <TiersList tab='free-tier' tiers={activeTiers.filter(tier => tier.type === 'free')} />
|
content = <TiersList tab='free-tier' tiers={activeTiers.filter(tier => tier.type === 'free')} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, {useEffect, useRef, useState} from 'react';
|
||||||
import useSettingGroup from '../../../../hooks/useSettingGroup';
|
import useSettingGroup from '../../../../hooks/useSettingGroup';
|
||||||
import { Setting, SiteData, Tier } from '../../../../types/api';
|
import {Setting, SiteData, Tier} from '../../../../types/api';
|
||||||
import { getSettingValue } from '../../../../utils/helpers';
|
import {getSettingValue} from '../../../../utils/helpers';
|
||||||
|
|
||||||
type PortalFrameProps = {
|
type PortalFrameProps = {
|
||||||
settings: Setting[];
|
settings: Setting[];
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import CheckboxGroup from '../../../../admin-x-ds/global/form/CheckboxGroup';
|
import CheckboxGroup from '../../../../admin-x-ds/global/form/CheckboxGroup';
|
||||||
import Form from '../../../../admin-x-ds/global/form/Form';
|
import Form from '../../../../admin-x-ds/global/form/Form';
|
||||||
import HtmlField from '../../../../admin-x-ds/global/form/HtmlField';
|
import HtmlField from '../../../../admin-x-ds/global/form/HtmlField';
|
||||||
import React, { useEffect, useMemo } from 'react';
|
import React, {useEffect, useMemo} from 'react';
|
||||||
import Toggle from '../../../../admin-x-ds/global/form/Toggle';
|
import Toggle from '../../../../admin-x-ds/global/form/Toggle';
|
||||||
import { CheckboxProps } from '../../../../admin-x-ds/global/form/Checkbox';
|
import {CheckboxProps} from '../../../../admin-x-ds/global/form/Checkbox';
|
||||||
import { Setting, SettingValue, Tier } from '../../../../types/api';
|
import {Setting, SettingValue, Tier} from '../../../../types/api';
|
||||||
import { checkStripeEnabled, getSettingValues } from '../../../../utils/helpers';
|
import {checkStripeEnabled, getSettingValues} from '../../../../utils/helpers';
|
||||||
import { useGlobalData } from '../../../providers/DataProvider';
|
import {useGlobalData} from '../../../providers/DataProvider';
|
||||||
|
|
||||||
const SignupOptions: React.FC<{
|
const SignupOptions: React.FC<{
|
||||||
localSettings: Setting[]
|
localSettings: Setting[]
|
||||||
|
@ -6,8 +6,8 @@ import GhostLogo from '../../../../assets/images/orb-squircle.png';
|
|||||||
import GhostLogoPink from '../../../../assets/images/orb-pink.png';
|
import GhostLogoPink from '../../../../assets/images/orb-pink.png';
|
||||||
import Heading from '../../../../admin-x-ds/global/Heading';
|
import Heading from '../../../../admin-x-ds/global/Heading';
|
||||||
import Modal from '../../../../admin-x-ds/global/modal/Modal';
|
import Modal from '../../../../admin-x-ds/global/modal/Modal';
|
||||||
import NiceModal, { useModal } from '@ebay/nice-modal-react';
|
import NiceModal, {useModal} from '@ebay/nice-modal-react';
|
||||||
import React, { useState } from 'react';
|
import React, {useState} from 'react';
|
||||||
import StripeButton from '../../../../admin-x-ds/settings/StripeButton';
|
import StripeButton from '../../../../admin-x-ds/settings/StripeButton';
|
||||||
import StripeLogo from '../../../../assets/images/stripe-emblem.svg';
|
import StripeLogo from '../../../../assets/images/stripe-emblem.svg';
|
||||||
import TextArea from '../../../../admin-x-ds/global/form/TextArea';
|
import TextArea from '../../../../admin-x-ds/global/form/TextArea';
|
||||||
@ -16,15 +16,15 @@ import Toggle from '../../../../admin-x-ds/global/form/Toggle';
|
|||||||
import useRouting from '../../../../hooks/useRouting';
|
import useRouting from '../../../../hooks/useRouting';
|
||||||
import useSettingGroup from '../../../../hooks/useSettingGroup';
|
import useSettingGroup from '../../../../hooks/useSettingGroup';
|
||||||
import useSettings from '../../../../hooks/useSettings';
|
import useSettings from '../../../../hooks/useSettings';
|
||||||
import { ApiError } from '../../../../utils/apiRequests';
|
import {ApiError} from '../../../../utils/apiRequests';
|
||||||
import { ReactComponent as StripeVerified } from '../../../../assets/images/stripe-verified.svg';
|
import {ReactComponent as StripeVerified} from '../../../../assets/images/stripe-verified.svg';
|
||||||
import { checkStripeEnabled, getGhostPaths, getSettingValue, getSettingValues } from '../../../../utils/helpers';
|
import {checkStripeEnabled, getGhostPaths, getSettingValue, getSettingValues} from '../../../../utils/helpers';
|
||||||
import { showToast } from '../../../../admin-x-ds/global/Toast';
|
import {showToast} from '../../../../admin-x-ds/global/Toast';
|
||||||
import { toast } from 'react-hot-toast';
|
import {toast} from 'react-hot-toast';
|
||||||
import { useBrowseMembers } from '../../../../utils/api/members';
|
import {useBrowseMembers} from '../../../../utils/api/members';
|
||||||
import { useBrowseTiers, useEditTier } from '../../../../utils/api/tiers';
|
import {useBrowseTiers, useEditTier} from '../../../../utils/api/tiers';
|
||||||
import { useDeleteStripeSettings, useEditSettings } from '../../../../utils/api/settings';
|
import {useDeleteStripeSettings, useEditSettings} from '../../../../utils/api/settings';
|
||||||
import { useGlobalData } from '../../../providers/DataProvider';
|
import {useGlobalData} from '../../../providers/DataProvider';
|
||||||
|
|
||||||
const RETRY_PRODUCT_SAVE_POLL_LENGTH = 1000;
|
const RETRY_PRODUCT_SAVE_POLL_LENGTH = 1000;
|
||||||
const RETRY_PRODUCT_SAVE_MAX_POLL = 15 * RETRY_PRODUCT_SAVE_POLL_LENGTH;
|
const RETRY_PRODUCT_SAVE_MAX_POLL = 15 * RETRY_PRODUCT_SAVE_POLL_LENGTH;
|
||||||
@ -64,7 +64,7 @@ const Connect: React.FC = () => {
|
|||||||
|
|
||||||
const saveTier = async () => {
|
const saveTier = async () => {
|
||||||
const {data} = await fetchActiveTiers();
|
const {data} = await fetchActiveTiers();
|
||||||
const tier = data?.tiers[0]
|
const tier = data?.tiers[0];
|
||||||
|
|
||||||
if (tier) {
|
if (tier) {
|
||||||
tier.monthly_price = 500;
|
tier.monthly_price = 500;
|
||||||
@ -75,7 +75,7 @@ const Connect: React.FC = () => {
|
|||||||
/** To allow Stripe config to be ready in backend, we poll the save tier request */
|
/** To allow Stripe config to be ready in backend, we poll the save tier request */
|
||||||
while (pollTimeout < RETRY_PRODUCT_SAVE_MAX_POLL) {
|
while (pollTimeout < RETRY_PRODUCT_SAVE_MAX_POLL) {
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
setTimeout(resolve, RETRY_PRODUCT_SAVE_POLL_LENGTH)
|
setTimeout(resolve, RETRY_PRODUCT_SAVE_POLL_LENGTH);
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -118,7 +118,7 @@ const Connect: React.FC = () => {
|
|||||||
} else {
|
} else {
|
||||||
setError('Please enter a secure key');
|
setError('Please enter a secure key');
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const {apiRoot} = getGhostPaths();
|
const {apiRoot} = getGhostPaths();
|
||||||
const stripeConnectUrl = `${apiRoot}/members/stripe_connect?mode=${testMode ? 'test' : 'live'}`;
|
const stripeConnectUrl = `${apiRoot}/members/stripe_connect?mode=${testMode ? 'test' : 'live'}`;
|
||||||
@ -152,7 +152,7 @@ const Connected: React.FC<{onClose?: () => void}> = ({onClose}) => {
|
|||||||
const [stripeConnectAccountName, stripeConnectLivemode] = getSettingValues(settings, ['stripe_connect_account_name', 'stripe_connect_livemode']);
|
const [stripeConnectAccountName, stripeConnectLivemode] = getSettingValues(settings, ['stripe_connect_account_name', 'stripe_connect_livemode']);
|
||||||
|
|
||||||
const {refetch: fetchMembers, isFetching: isFetchingMembers} = useBrowseMembers({
|
const {refetch: fetchMembers, isFetching: isFetchingMembers} = useBrowseMembers({
|
||||||
searchParams: { filter: 'status:paid', limit: '0' },
|
searchParams: {filter: 'status:paid', limit: '0'},
|
||||||
enabled: false
|
enabled: false
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -160,11 +160,11 @@ const Connected: React.FC<{onClose?: () => void}> = ({onClose}) => {
|
|||||||
|
|
||||||
const openDisconnectStripeModal = async () => {
|
const openDisconnectStripeModal = async () => {
|
||||||
const {data} = await fetchMembers();
|
const {data} = await fetchMembers();
|
||||||
const hasActiveStripeSubscriptions = Boolean(data?.meta?.pagination.total)
|
const hasActiveStripeSubscriptions = Boolean(data?.meta?.pagination.total);
|
||||||
|
|
||||||
// const hasActiveStripeSubscriptions = false; //...
|
// const hasActiveStripeSubscriptions = false; //...
|
||||||
// this.ghostPaths.url.api('/members/') + '?filter=status:paid&limit=0';
|
// this.ghostPaths.url.api('/members/') + '?filter=status:paid&limit=0';
|
||||||
NiceModal.show(ConfirmationModal, {
|
NiceModal.show(ConfirmationModal, {
|
||||||
title: 'Are you sure you want to disconnect?',
|
title: 'Are you sure you want to disconnect?',
|
||||||
prompt: <>
|
prompt: <>
|
||||||
{hasActiveStripeSubscriptions && <p className="text-red">
|
{hasActiveStripeSubscriptions && <p className="text-red">
|
||||||
@ -233,20 +233,20 @@ const Direct: React.FC<{onClose: () => void}> = ({onClose}) => {
|
|||||||
showToast({
|
showToast({
|
||||||
type: 'pageError',
|
type: 'pageError',
|
||||||
message: 'Failed to save settings. Please check you copied both keys correctly.'
|
message: 'Failed to save settings. Please check you copied both keys correctly.'
|
||||||
})
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Heading level={3}>Connect Stripe</Heading>
|
<Heading level={3}>Connect Stripe</Heading>
|
||||||
<Form marginBottom={false} marginTop>
|
<Form marginBottom={false} marginTop>
|
||||||
<TextField title='Publishable key' value={publishableKey?.toString()} onChange={(e) => updateSetting('stripe_publishable_key', e.target.value)} />
|
<TextField title='Publishable key' value={publishableKey?.toString()} onChange={e => updateSetting('stripe_publishable_key', e.target.value)} />
|
||||||
<TextField title='Secure key' value={secretKey?.toString()} onChange={(e) => updateSetting('stripe_secret_key', e.target.value)} />
|
<TextField title='Secure key' value={secretKey?.toString()} onChange={e => updateSetting('stripe_secret_key', e.target.value)} />
|
||||||
<Button className='mt-5' color='green' disabled={saveState === 'saving'} label='Save Stripe settings' onClick={onSubmit} />
|
<Button className='mt-5' color='green' disabled={saveState === 'saving'} label='Save Stripe settings' onClick={onSubmit} />
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
@ -256,7 +256,7 @@ const Direct: React.FC<{onClose: () => void}> = ({onClose}) => {
|
|||||||
const StripeConnectModal: React.FC = () => {
|
const StripeConnectModal: React.FC = () => {
|
||||||
const {config} = useGlobalData();
|
const {config} = useGlobalData();
|
||||||
const {settings} = useSettings();
|
const {settings} = useSettings();
|
||||||
const stripeConnectAccountId = getSettingValue(settings, 'stripe_connect_account_id')
|
const stripeConnectAccountId = getSettingValue(settings, 'stripe_connect_account_id');
|
||||||
const {updateRoute} = useRouting();
|
const {updateRoute} = useRouting();
|
||||||
const [step, setStep] = useState<'start' | 'connect'>('start');
|
const [step, setStep] = useState<'start' | 'connect'>('start');
|
||||||
const mainModal = useModal();
|
const mainModal = useModal();
|
||||||
|
@ -3,8 +3,8 @@ import Form from '../../../../admin-x-ds/global/form/Form';
|
|||||||
import Heading from '../../../../admin-x-ds/global/Heading';
|
import Heading from '../../../../admin-x-ds/global/Heading';
|
||||||
import Icon from '../../../../admin-x-ds/global/Icon';
|
import Icon from '../../../../admin-x-ds/global/Icon';
|
||||||
import Modal from '../../../../admin-x-ds/global/modal/Modal';
|
import Modal from '../../../../admin-x-ds/global/modal/Modal';
|
||||||
import NiceModal, { useModal } from '@ebay/nice-modal-react';
|
import NiceModal, {useModal} from '@ebay/nice-modal-react';
|
||||||
import React, { useState } from 'react';
|
import React, {useState} from 'react';
|
||||||
import Select from '../../../../admin-x-ds/global/form/Select';
|
import Select from '../../../../admin-x-ds/global/form/Select';
|
||||||
import SortableList from '../../../../admin-x-ds/global/SortableList';
|
import SortableList from '../../../../admin-x-ds/global/SortableList';
|
||||||
import TextField from '../../../../admin-x-ds/global/form/TextField';
|
import TextField from '../../../../admin-x-ds/global/form/TextField';
|
||||||
@ -14,12 +14,12 @@ import useForm from '../../../../hooks/useForm';
|
|||||||
import useRouting from '../../../../hooks/useRouting';
|
import useRouting from '../../../../hooks/useRouting';
|
||||||
import useSettingGroup from '../../../../hooks/useSettingGroup';
|
import useSettingGroup from '../../../../hooks/useSettingGroup';
|
||||||
import useSortableIndexedList from '../../../../hooks/useSortableIndexedList';
|
import useSortableIndexedList from '../../../../hooks/useSortableIndexedList';
|
||||||
import { Tier } from '../../../../types/api';
|
import {Tier} from '../../../../types/api';
|
||||||
import { currencies, currencyFromDecimal, currencyGroups, currencyToDecimal, getSymbol } from '../../../../utils/currency';
|
import {currencies, currencyFromDecimal, currencyGroups, currencyToDecimal, getSymbol} from '../../../../utils/currency';
|
||||||
import { getSettingValues } from '../../../../utils/helpers';
|
import {getSettingValues} from '../../../../utils/helpers';
|
||||||
import { showToast } from '../../../../admin-x-ds/global/Toast';
|
import {showToast} from '../../../../admin-x-ds/global/Toast';
|
||||||
import { toast } from 'react-hot-toast';
|
import {toast} from 'react-hot-toast';
|
||||||
import { useAddTier, useEditTier } from '../../../../utils/api/tiers';
|
import {useAddTier, useEditTier} from '../../../../utils/api/tiers';
|
||||||
|
|
||||||
interface TierDetailModalProps {
|
interface TierDetailModalProps {
|
||||||
tier?: Tier
|
tier?: Tier
|
||||||
@ -123,7 +123,7 @@ const TierDetailModal: React.FC<TierDetailModalProps> = ({tier}) => {
|
|||||||
if (Object.values(validators).filter(validator => validator()).length) {
|
if (Object.values(validators).filter(validator => validator()).length) {
|
||||||
showToast({
|
showToast({
|
||||||
type: 'pageError',
|
type: 'pageError',
|
||||||
message: "Can't save tier! One or more fields have errors, please doublecheck you filled all mandatory fields"
|
message: 'Can\'t save tier! One or more fields have errors, please doublecheck you filled all mandatory fields'
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,10 @@ import NoValueLabel from '../../../../admin-x-ds/global/NoValueLabel';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import TierDetailModal from './TierDetailModal';
|
import TierDetailModal from './TierDetailModal';
|
||||||
import useRouting from '../../../../hooks/useRouting';
|
import useRouting from '../../../../hooks/useRouting';
|
||||||
import { Tier } from '../../../../types/api';
|
import {Tier} from '../../../../types/api';
|
||||||
import { currencyToDecimal, getSymbol } from '../../../../utils/currency';
|
import {currencyToDecimal, getSymbol} from '../../../../utils/currency';
|
||||||
import { numberWithCommas } from '../../../../utils/helpers';
|
import {numberWithCommas} from '../../../../utils/helpers';
|
||||||
import { useEditTier } from '../../../../utils/api/tiers';
|
import {useEditTier} from '../../../../utils/api/tiers';
|
||||||
|
|
||||||
interface TiersListProps {
|
interface TiersListProps {
|
||||||
tab?: 'active-tiers' | 'archive-tiers' | 'free-tier';
|
tab?: 'active-tiers' | 'archive-tiers' | 'free-tier';
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import BrandSettings, { BrandSettingValues } from './designAndBranding/BrandSettings';
|
import BrandSettings, {BrandSettingValues} from './designAndBranding/BrandSettings';
|
||||||
// import Button from '../../../admin-x-ds/global/Button';
|
// import Button from '../../../admin-x-ds/global/Button';
|
||||||
// import ChangeThemeModal from './ThemeModal';
|
// import ChangeThemeModal from './ThemeModal';
|
||||||
import Icon from '../../../admin-x-ds/global/Icon';
|
import Icon from '../../../admin-x-ds/global/Icon';
|
||||||
import NiceModal, { NiceModalHandler, useModal } from '@ebay/nice-modal-react';
|
import NiceModal, {NiceModalHandler, useModal} from '@ebay/nice-modal-react';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import StickyFooter from '../../../admin-x-ds/global/StickyFooter';
|
import StickyFooter from '../../../admin-x-ds/global/StickyFooter';
|
||||||
import TabView, { Tab } from '../../../admin-x-ds/global/TabView';
|
import TabView, {Tab} from '../../../admin-x-ds/global/TabView';
|
||||||
import ThemePreview from './designAndBranding/ThemePreview';
|
import ThemePreview from './designAndBranding/ThemePreview';
|
||||||
import ThemeSettings from './designAndBranding/ThemeSettings';
|
import ThemeSettings from './designAndBranding/ThemeSettings';
|
||||||
import useForm from '../../../hooks/useForm';
|
import useForm from '../../../hooks/useForm';
|
||||||
import useRouting from '../../../hooks/useRouting';
|
import useRouting from '../../../hooks/useRouting';
|
||||||
import useSettings from '../../../hooks/useSettings';
|
import useSettings from '../../../hooks/useSettings';
|
||||||
import { CustomThemeSetting, Setting, SettingValue } from '../../../types/api';
|
import {CustomThemeSetting, Setting, SettingValue} from '../../../types/api';
|
||||||
import { PreviewModalContent } from '../../../admin-x-ds/global/modal/PreviewModal';
|
import {PreviewModalContent} from '../../../admin-x-ds/global/modal/PreviewModal';
|
||||||
import { getHomepageUrl, getSettingValues } from '../../../utils/helpers';
|
import {getHomepageUrl, getSettingValues} from '../../../utils/helpers';
|
||||||
import { useBrowseCustomThemeSettings, useEditCustomThemeSettings } from '../../../utils/api/customThemeSettings';
|
import {useBrowseCustomThemeSettings, useEditCustomThemeSettings} from '../../../utils/api/customThemeSettings';
|
||||||
import { useBrowsePosts } from '../../../utils/api/posts';
|
import {useBrowsePosts} from '../../../utils/api/posts';
|
||||||
|
|
||||||
const Sidebar: React.FC<{
|
const Sidebar: React.FC<{
|
||||||
brandSettings: BrandSettingValues
|
brandSettings: BrandSettingValues
|
||||||
|
@ -4,17 +4,17 @@ import Button from '../../../admin-x-ds/global/Button';
|
|||||||
import ConfirmationModal from '../../../admin-x-ds/global/modal/ConfirmationModal';
|
import ConfirmationModal from '../../../admin-x-ds/global/modal/ConfirmationModal';
|
||||||
import FileUpload from '../../../admin-x-ds/global/form/FileUpload';
|
import FileUpload from '../../../admin-x-ds/global/form/FileUpload';
|
||||||
import Modal from '../../../admin-x-ds/global/modal/Modal';
|
import Modal from '../../../admin-x-ds/global/modal/Modal';
|
||||||
import NiceModal, { NiceModalHandler, useModal } from '@ebay/nice-modal-react';
|
import NiceModal, {NiceModalHandler, useModal} from '@ebay/nice-modal-react';
|
||||||
import OfficialThemes from './theme/OfficialThemes';
|
import OfficialThemes from './theme/OfficialThemes';
|
||||||
import PageHeader from '../../../admin-x-ds/global/layout/PageHeader';
|
import PageHeader from '../../../admin-x-ds/global/layout/PageHeader';
|
||||||
import React, { useState } from 'react';
|
import React, {useState} from 'react';
|
||||||
import TabView from '../../../admin-x-ds/global/TabView';
|
import TabView from '../../../admin-x-ds/global/TabView';
|
||||||
import ThemeInstalledModal from './theme/ThemeInstalledModal';
|
import ThemeInstalledModal from './theme/ThemeInstalledModal';
|
||||||
import ThemePreview from './theme/ThemePreview';
|
import ThemePreview from './theme/ThemePreview';
|
||||||
import useRouting from '../../../hooks/useRouting';
|
import useRouting from '../../../hooks/useRouting';
|
||||||
import { OfficialTheme } from '../../../models/themes';
|
import {OfficialTheme} from '../../../models/themes';
|
||||||
import { Theme } from '../../../types/api';
|
import {Theme} from '../../../types/api';
|
||||||
import { useBrowseThemes, useInstallTheme, useUploadTheme } from '../../../utils/api/themes';
|
import {useBrowseThemes, useInstallTheme, useUploadTheme} from '../../../utils/api/themes';
|
||||||
|
|
||||||
interface ThemeToolbarProps {
|
interface ThemeToolbarProps {
|
||||||
selectedTheme: OfficialTheme|null;
|
selectedTheme: OfficialTheme|null;
|
||||||
|
@ -6,9 +6,9 @@ import Select from '../../../../admin-x-ds/global/form/Select';
|
|||||||
import SettingGroupContent from '../../../../admin-x-ds/settings/SettingGroupContent';
|
import SettingGroupContent from '../../../../admin-x-ds/settings/SettingGroupContent';
|
||||||
import TextField from '../../../../admin-x-ds/global/form/TextField';
|
import TextField from '../../../../admin-x-ds/global/form/TextField';
|
||||||
import Toggle from '../../../../admin-x-ds/global/form/Toggle';
|
import Toggle from '../../../../admin-x-ds/global/form/Toggle';
|
||||||
import { CustomThemeSetting } from '../../../../types/api';
|
import {CustomThemeSetting} from '../../../../types/api';
|
||||||
import { getImageUrl, useUploadImage } from '../../../../utils/api/images';
|
import {getImageUrl, useUploadImage} from '../../../../utils/api/images';
|
||||||
import { humanizeSettingKey } from '../../../../utils/helpers';
|
import {humanizeSettingKey} from '../../../../utils/helpers';
|
||||||
|
|
||||||
const ThemeSetting: React.FC<{
|
const ThemeSetting: React.FC<{
|
||||||
setting: CustomThemeSetting,
|
setting: CustomThemeSetting,
|
||||||
@ -78,7 +78,7 @@ const ThemeSetting: React.FC<{
|
|||||||
const ThemeSettings: React.FC<{ settings: CustomThemeSetting[], updateSetting: (setting: CustomThemeSetting) => void }> = ({settings, updateSetting}) => {
|
const ThemeSettings: React.FC<{ settings: CustomThemeSetting[], updateSetting: (setting: CustomThemeSetting) => void }> = ({settings, updateSetting}) => {
|
||||||
return (
|
return (
|
||||||
<SettingGroupContent className='mt-7'>
|
<SettingGroupContent className='mt-7'>
|
||||||
{settings.map(setting => <ThemeSetting key={setting.key} setSetting={(value) => updateSetting({...setting, value} as CustomThemeSetting)} setting={setting} />)}
|
{settings.map(setting => <ThemeSetting key={setting.key} setSetting={value => updateSetting({...setting, value} as CustomThemeSetting)} setting={setting} />)}
|
||||||
</SettingGroupContent>
|
</SettingGroupContent>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useEffect, useState } from 'react';
|
import {useCallback, useEffect, useState} from 'react';
|
||||||
|
|
||||||
export type Dirtyable<Data> = Data & {
|
export type Dirtyable<Data> = Data & {
|
||||||
dirty?: boolean;
|
dirty?: boolean;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Setting } from '../types/api';
|
import {Setting} from '../types/api';
|
||||||
import { useCallback, useMemo } from 'react';
|
import {useCallback, useMemo} from 'react';
|
||||||
import { useEditSettings } from '../utils/api/settings';
|
import {useEditSettings} from '../utils/api/settings';
|
||||||
import { useGlobalData } from '../components/providers/DataProvider';
|
import {useGlobalData} from '../components/providers/DataProvider';
|
||||||
|
|
||||||
function serialiseSettingsData(settings: Setting[]): Setting[] {
|
function serialiseSettingsData(settings: Setting[]): Setting[] {
|
||||||
return settings.map((setting) => {
|
return settings.map((setting) => {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { CustomThemeSetting, Setting } from '../../types/api';
|
import {CustomThemeSetting, Setting} from '../../types/api';
|
||||||
import { createMutation, createQuery } from '../apiRequests';
|
import {createMutation, createQuery} from '../apiRequests';
|
||||||
|
|
||||||
export interface CustomThemeSettingsResponseType {
|
export interface CustomThemeSettingsResponseType {
|
||||||
custom_theme_settings: CustomThemeSetting[];
|
custom_theme_settings: CustomThemeSetting[];
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Meta, createMutation, createQuery } from '../apiRequests';
|
import {Meta, createMutation, createQuery} from '../apiRequests';
|
||||||
|
|
||||||
export interface UserInvite {
|
export interface UserInvite {
|
||||||
created_at: string;
|
created_at: string;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Member } from '../../types/api';
|
import {Member} from '../../types/api';
|
||||||
import { Meta, createQuery } from '../apiRequests';
|
import {Meta, createQuery} from '../apiRequests';
|
||||||
|
|
||||||
export interface MembersResponseType {
|
export interface MembersResponseType {
|
||||||
meta?: Meta
|
meta?: Meta
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Meta, createMutation, createQuery } from '../apiRequests';
|
import {Meta, createMutation, createQuery} from '../apiRequests';
|
||||||
import { Newsletter } from '../../types/api';
|
import {Newsletter} from '../../types/api';
|
||||||
|
|
||||||
export interface NewslettersResponseType {
|
export interface NewslettersResponseType {
|
||||||
meta?: Meta
|
meta?: Meta
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Meta, createMutation, createQuery } from '../apiRequests';
|
import {Meta, createMutation, createQuery} from '../apiRequests';
|
||||||
import { Setting } from '../../types/api';
|
import {Setting} from '../../types/api';
|
||||||
|
|
||||||
export type SettingsResponseMeta = Meta & { sent_email_verification?: boolean }
|
export type SettingsResponseMeta = Meta & { sent_email_verification?: boolean }
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { InstalledTheme, Theme } from '../../types/api';
|
import {InstalledTheme, Theme} from '../../types/api';
|
||||||
import { createMutation, createQuery } from '../apiRequests';
|
import {createMutation, createQuery} from '../apiRequests';
|
||||||
|
|
||||||
export interface ThemesResponseType {
|
export interface ThemesResponseType {
|
||||||
themes: Theme[];
|
themes: Theme[];
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Meta, createMutation, createQuery } from '../apiRequests';
|
import {Meta, createMutation, createQuery} from '../apiRequests';
|
||||||
import { Tier } from '../../types/api';
|
import {Tier} from '../../types/api';
|
||||||
|
|
||||||
export interface TiersResponseType {
|
export interface TiersResponseType {
|
||||||
meta?: Meta
|
meta?: Meta
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Meta, createMutation, createQuery } from '../apiRequests';
|
import {Meta, createMutation, createQuery} from '../apiRequests';
|
||||||
import { User } from '../../types/api';
|
import {User} from '../../types/api';
|
||||||
|
|
||||||
export interface UsersResponseType {
|
export interface UsersResponseType {
|
||||||
meta?: Meta;
|
meta?: Meta;
|
||||||
@ -44,7 +44,7 @@ export const useBrowseUsers = createQuery<UsersResponseType>({
|
|||||||
export const useCurrentUser = createQuery<User>({
|
export const useCurrentUser = createQuery<User>({
|
||||||
dataType,
|
dataType,
|
||||||
path: '/users/me/',
|
path: '/users/me/',
|
||||||
returnData: (originalData) => (originalData as UsersResponseType).users?.[0]
|
returnData: originalData => (originalData as UsersResponseType).users?.[0]
|
||||||
});
|
});
|
||||||
|
|
||||||
export const useEditUser = createMutation<UsersResponseType, User>({
|
export const useEditUser = createMutation<UsersResponseType, User>({
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { QueryClient, UseQueryOptions, useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
import {QueryClient, UseQueryOptions, useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
|
||||||
import { getGhostPaths } from './helpers';
|
import {getGhostPaths} from './helpers';
|
||||||
import { useServices } from '../components/providers/ServiceProvider';
|
import {useServices} from '../components/providers/ServiceProvider';
|
||||||
|
|
||||||
export interface Meta {
|
export interface Meta {
|
||||||
pagination: {
|
pagination: {
|
||||||
@ -68,7 +68,7 @@ export const useFetchApi = () => {
|
|||||||
|
|
||||||
if (response.status > 299) {
|
if (response.status > 299) {
|
||||||
const data = response.headers.get('content-type')?.includes('application/json') ? await response.json() : undefined;
|
const data = response.headers.get('content-type')?.includes('application/json') ? await response.json() : undefined;
|
||||||
throw new ApiError(response, data)
|
throw new ApiError(response, data);
|
||||||
} else if (response.status === 204) {
|
} else if (response.status === 204) {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
@ -139,11 +139,11 @@ const mutate = <ResponseData, Payload>({fetchApi, path, payload, searchParams, o
|
|||||||
const url = apiUrl(path, searchParams || defaultSearchParams);
|
const url = apiUrl(path, searchParams || defaultSearchParams);
|
||||||
const generatedBody = payload && body?.(payload);
|
const generatedBody = payload && body?.(payload);
|
||||||
|
|
||||||
let requestBody: string | FormData | undefined = undefined
|
let requestBody: string | FormData | undefined = undefined;
|
||||||
if (generatedBody instanceof FormData) {
|
if (generatedBody instanceof FormData) {
|
||||||
requestBody = generatedBody
|
requestBody = generatedBody;
|
||||||
} else if (generatedBody) {
|
} else if (generatedBody) {
|
||||||
requestBody = JSON.stringify(generatedBody)
|
requestBody = JSON.stringify(generatedBody);
|
||||||
}
|
}
|
||||||
|
|
||||||
return fetchApi(url, {
|
return fetchApi(url, {
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { expect, test } from '@playwright/test';
|
import {expect, test} from '@playwright/test';
|
||||||
import { mockApi, responseFixtures, updatedSettingsResponse } from '../../utils/e2e';
|
import {mockApi, responseFixtures, updatedSettingsResponse} from '../../utils/e2e';
|
||||||
|
|
||||||
const settingsWithStripe = updatedSettingsResponse([
|
const settingsWithStripe = updatedSettingsResponse([
|
||||||
{ key: 'stripe_connect_publishable_key', value: 'pk_test_123' },
|
{key: 'stripe_connect_publishable_key', value: 'pk_test_123'},
|
||||||
{ key: 'stripe_connect_secret_key', value: 'sk_test_123' },
|
{key: 'stripe_connect_secret_key', value: 'sk_test_123'},
|
||||||
{ key: 'stripe_connect_display_name', value: 'Dummy' },
|
{key: 'stripe_connect_display_name', value: 'Dummy'},
|
||||||
{ key: 'stripe_connect_account_id', value: 'acct_123' }
|
{key: 'stripe_connect_account_id', value: 'acct_123'}
|
||||||
])
|
]);
|
||||||
|
|
||||||
test.describe('Tier settings', async () => {
|
test.describe('Tier settings', async () => {
|
||||||
test('Supports creating a new tier', async ({page}) => {
|
test('Supports creating a new tier', async ({page}) => {
|
||||||
@ -47,13 +47,13 @@ test.describe('Tier settings', async () => {
|
|||||||
visibility: 'public',
|
visibility: 'public',
|
||||||
created_at: new Date().toISOString(),
|
created_at: new Date().toISOString(),
|
||||||
updated_at: new Date().toISOString()
|
updated_at: new Date().toISOString()
|
||||||
}
|
};
|
||||||
|
|
||||||
const lastApiRequests = await mockApi({page, responses: {
|
const lastApiRequests = await mockApi({page, responses: {
|
||||||
tiers: {
|
tiers: {
|
||||||
add: { tiers: [newTier] },
|
add: {tiers: [newTier]},
|
||||||
// This request will be reloaded after the new tier is added
|
// This request will be reloaded after the new tier is added
|
||||||
browse: { tiers: [...responseFixtures.tiers.tiers, newTier] }
|
browse: {tiers: [...responseFixtures.tiers.tiers, newTier]}
|
||||||
}
|
}
|
||||||
}});
|
}});
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { expect, test } from '@playwright/test';
|
import {expect, test} from '@playwright/test';
|
||||||
import { mockApi, responseFixtures } from '../../utils/e2e';
|
import {mockApi, responseFixtures} from '../../utils/e2e';
|
||||||
|
|
||||||
test.describe('Theme settings', async () => {
|
test.describe('Theme settings', async () => {
|
||||||
test('Browsing and installing default themes', async ({page}) => {
|
test('Browsing and installing default themes', async ({page}) => {
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import { ConfigResponseType } from '../../src/utils/api/config'
|
import {ConfigResponseType} from '../../src/utils/api/config';
|
||||||
import { CustomThemeSettingsResponseType } from '../../src/utils/api/customThemeSettings'
|
import {CustomThemeSettingsResponseType} from '../../src/utils/api/customThemeSettings';
|
||||||
import { ImagesResponseType } from '../../src/utils/api/images'
|
import {ImagesResponseType} from '../../src/utils/api/images';
|
||||||
import { InvitesResponseType } from '../../src/utils/api/invites'
|
import {InvitesResponseType} from '../../src/utils/api/invites';
|
||||||
import { LabelsResponseType } from '../../src/utils/api/labels'
|
import {LabelsResponseType} from '../../src/utils/api/labels';
|
||||||
import { OffersResponseType } from '../../src/utils/api/offers'
|
import {OffersResponseType} from '../../src/utils/api/offers';
|
||||||
import { Page, Request } from '@playwright/test'
|
import {Page, Request} from '@playwright/test';
|
||||||
import { PostsResponseType } from '../../src/utils/api/posts'
|
import {PostsResponseType} from '../../src/utils/api/posts';
|
||||||
import { RolesResponseType } from '../../src/utils/api/roles'
|
import {RolesResponseType} from '../../src/utils/api/roles';
|
||||||
import { SettingsResponseType } from '../../src/utils/api/settings'
|
import {SettingsResponseType} from '../../src/utils/api/settings';
|
||||||
import { SiteResponseType } from '../../src/utils/api/site'
|
import {SiteResponseType} from '../../src/utils/api/site';
|
||||||
import { ThemesResponseType } from '../../src/utils/api/themes'
|
import {ThemesResponseType} from '../../src/utils/api/themes';
|
||||||
import { TiersResponseType } from '../../src/utils/api/tiers'
|
import {TiersResponseType} from '../../src/utils/api/tiers';
|
||||||
import { UsersResponseType } from '../../src/utils/api/users'
|
import {UsersResponseType} from '../../src/utils/api/users';
|
||||||
import { readFileSync } from 'fs'
|
import {readFileSync} from 'fs';
|
||||||
|
|
||||||
export const responseFixtures = {
|
export const responseFixtures = {
|
||||||
settings: JSON.parse(readFileSync(`${__dirname}/responses/settings.json`).toString()) as SettingsResponseType,
|
settings: JSON.parse(readFileSync(`${__dirname}/responses/settings.json`).toString()) as SettingsResponseType,
|
||||||
|
Loading…
Reference in New Issue
Block a user