mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-25 03:44:29 +03:00
Colocated AdminX types and helpers with API requests (#17629)
refs https://github.com/TryGhost/Product/issues/3349
This commit is contained in:
parent
116d11b6ab
commit
d8259fb4fe
@ -6,9 +6,8 @@ import RoutingProvider from './components/providers/RoutingProvider';
|
||||
import Settings from './components/Settings';
|
||||
import Sidebar from './components/Sidebar';
|
||||
import {GlobalDirtyStateProvider} from './hooks/useGlobalDirtyState';
|
||||
import {OfficialTheme} from './models/themes';
|
||||
import {OfficialTheme, ServicesProvider} from './components/providers/ServiceProvider';
|
||||
import {QueryClient, QueryClientProvider} from '@tanstack/react-query';
|
||||
import {ServicesProvider} from './components/providers/ServiceProvider';
|
||||
import {Toaster} from 'react-hot-toast';
|
||||
|
||||
interface AppProps {
|
||||
|
30
apps/admin-x-settings/src/api/config.ts
Normal file
30
apps/admin-x-settings/src/api/config.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import {createQuery} from '../utils/apiRequests';
|
||||
|
||||
export type JSONValue = string|number|boolean|null|Date|JSONObject|JSONArray;
|
||||
export interface JSONObject { [key: string]: JSONValue }
|
||||
export interface JSONArray extends Array<string|number|boolean|Date|JSONObject|JSONValue> {}
|
||||
|
||||
export type Config = {
|
||||
version: string;
|
||||
environment: string;
|
||||
editor: {
|
||||
url: string
|
||||
version: string
|
||||
};
|
||||
labs: Record<string, boolean>;
|
||||
stripeDirect: boolean;
|
||||
|
||||
// Config is relatively fluid, so we only type used properties above and still support arbitrary property access when needed
|
||||
[key: string]: JSONValue;
|
||||
};
|
||||
|
||||
export interface ConfigResponseType {
|
||||
config: Config;
|
||||
}
|
||||
|
||||
const dataType = 'ConfigResponseType';
|
||||
|
||||
export const useBrowseConfig = createQuery<ConfigResponseType>({
|
||||
dataType,
|
||||
path: '/config/'
|
||||
});
|
44
apps/admin-x-settings/src/api/customThemeSettings.ts
Normal file
44
apps/admin-x-settings/src/api/customThemeSettings.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import {Setting} from './settings';
|
||||
import {createMutation, createQuery} from '../utils/apiRequests';
|
||||
|
||||
type CustomThemeSettingData =
|
||||
{ type: 'text', value: string | null, default: string | null } |
|
||||
{ type: 'color', value: string, default: string } |
|
||||
{ type: 'image', value: string | null } |
|
||||
{ type: 'boolean', value: boolean, default: boolean } |
|
||||
{
|
||||
type: 'select',
|
||||
value: string
|
||||
default: string
|
||||
options: string[]
|
||||
};
|
||||
|
||||
export type CustomThemeSetting = CustomThemeSettingData & {
|
||||
id: string
|
||||
key: string
|
||||
description?: string
|
||||
// homepage and post are the only two groups we handle, but technically theme authors can put other things in package.json
|
||||
group?: 'homepage' | 'post' | string
|
||||
}
|
||||
|
||||
export interface CustomThemeSettingsResponseType {
|
||||
custom_theme_settings: CustomThemeSetting[];
|
||||
}
|
||||
|
||||
const dataType = 'CustomThemeSettingsResponseType';
|
||||
|
||||
export const useBrowseCustomThemeSettings = createQuery<CustomThemeSettingsResponseType>({
|
||||
dataType,
|
||||
path: '/custom_theme_settings/'
|
||||
});
|
||||
|
||||
export const useEditCustomThemeSettings = createMutation<CustomThemeSettingsResponseType, Setting[]>({
|
||||
method: 'PUT',
|
||||
path: () => '/custom_theme_settings/',
|
||||
body: settings => ({custom_theme_settings: settings}),
|
||||
|
||||
updateQueries: {
|
||||
dataType,
|
||||
update: newData => newData
|
||||
}
|
||||
});
|
@ -1,4 +1,4 @@
|
||||
import {createMutation} from '../apiRequests';
|
||||
import {createMutation} from '../utils/apiRequests';
|
||||
|
||||
export interface ImagesResponseType {
|
||||
images: {
|
@ -1,4 +1,4 @@
|
||||
import {Meta, createMutation, createQuery} from '../apiRequests';
|
||||
import {Meta, createMutation, createQuery} from '../utils/apiRequests';
|
||||
|
||||
export interface UserInvite {
|
||||
created_at: string;
|
@ -1,5 +1,12 @@
|
||||
import {Label} from '../../types/api';
|
||||
import {Meta, createQuery} from '../apiRequests';
|
||||
import {Meta, createQuery} from '../utils/apiRequests';
|
||||
|
||||
export type Label = {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface LabelsResponseType {
|
||||
meta?: Meta
|
@ -1,5 +1,8 @@
|
||||
import {Member} from '../../types/api';
|
||||
import {Meta, createQuery} from '../apiRequests';
|
||||
import {Meta, createQuery} from '../utils/apiRequests';
|
||||
|
||||
export type Member = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
export interface MembersResponseType {
|
||||
meta?: Meta
|
@ -1,5 +1,43 @@
|
||||
import {Meta, createMutation, createQuery} from '../apiRequests';
|
||||
import {Newsletter} from '../../types/api';
|
||||
import {Meta, createMutation, createQuery} from '../utils/apiRequests';
|
||||
|
||||
export type Newsletter = {
|
||||
id: string;
|
||||
uuid: string;
|
||||
name: string;
|
||||
description: string | null;
|
||||
feedback_enabled: boolean;
|
||||
slug: string;
|
||||
sender_name: string | null;
|
||||
sender_email: string | null;
|
||||
sender_reply_to: string;
|
||||
status: string;
|
||||
visibility: string;
|
||||
subscribe_on_signup: boolean;
|
||||
sort_order: number;
|
||||
header_image: string | null;
|
||||
show_header_icon: boolean;
|
||||
show_header_title: boolean;
|
||||
title_font_category: string;
|
||||
title_alignment: string;
|
||||
show_feature_image: boolean;
|
||||
body_font_category: string;
|
||||
footer_content: string | null;
|
||||
show_badge: boolean;
|
||||
show_header_name: boolean;
|
||||
show_post_title_section: boolean;
|
||||
show_comment_cta: boolean;
|
||||
show_subscription_details: boolean;
|
||||
show_latest_posts: boolean;
|
||||
background_color: string;
|
||||
border_color: string | null;
|
||||
title_color: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
count?: {
|
||||
posts?: number;
|
||||
active_members?: number;
|
||||
}
|
||||
}
|
||||
|
||||
export interface NewslettersResponseType {
|
||||
meta?: Meta
|
35
apps/admin-x-settings/src/api/offers.ts
Normal file
35
apps/admin-x-settings/src/api/offers.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import {Meta, createQuery} from '../utils/apiRequests';
|
||||
|
||||
export type Offer = {
|
||||
id: string;
|
||||
name: string;
|
||||
code: string;
|
||||
display_title: string;
|
||||
display_description: string;
|
||||
type: string;
|
||||
cadence: string;
|
||||
amount: number;
|
||||
duration: string;
|
||||
duration_in_months: number | null;
|
||||
currency_restriction: boolean;
|
||||
currency: string | null;
|
||||
status: string;
|
||||
redemption_count: number;
|
||||
tier: {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
}
|
||||
|
||||
export interface OffersResponseType {
|
||||
meta?: Meta
|
||||
offers: Offer[]
|
||||
}
|
||||
|
||||
const dataType = 'OffersResponseType';
|
||||
|
||||
export const useBrowseOffers = createQuery<OffersResponseType>({
|
||||
dataType,
|
||||
path: '/offers/',
|
||||
defaultSearchParams: {limit: 'all'}
|
||||
});
|
@ -1,5 +1,9 @@
|
||||
import {Meta, createQuery} from '../apiRequests';
|
||||
import {Post} from '../../types/api';
|
||||
import {Meta, createQuery} from '../utils/apiRequests';
|
||||
|
||||
export type Post = {
|
||||
id: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export interface PostsResponseType {
|
||||
meta?: Meta
|
24
apps/admin-x-settings/src/api/roles.ts
Normal file
24
apps/admin-x-settings/src/api/roles.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import {Meta, createQuery} from '../utils/apiRequests';
|
||||
|
||||
export type UserRoleType = 'Owner' | 'Administrator' | 'Editor' | 'Author' | 'Contributor';
|
||||
|
||||
export type UserRole = {
|
||||
id: string;
|
||||
name: UserRoleType;
|
||||
description: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export interface RolesResponseType {
|
||||
meta?: Meta;
|
||||
roles: UserRole[];
|
||||
}
|
||||
|
||||
const dataType = 'RolesResponseType';
|
||||
|
||||
export const useBrowseRoles = createQuery<RolesResponseType>({
|
||||
dataType,
|
||||
path: '/roles/',
|
||||
defaultSearchParams: {limit: 'all'}
|
||||
});
|
85
apps/admin-x-settings/src/api/settings.ts
Normal file
85
apps/admin-x-settings/src/api/settings.ts
Normal file
@ -0,0 +1,85 @@
|
||||
import {Config} from './config';
|
||||
import {Meta, createMutation, createQuery} from '../utils/apiRequests';
|
||||
|
||||
// Types
|
||||
|
||||
export type SettingValue = string | boolean | null;
|
||||
|
||||
export type Setting = {
|
||||
key: string;
|
||||
value: SettingValue;
|
||||
}
|
||||
|
||||
export type SettingsResponseMeta = Meta & { sent_email_verification?: boolean }
|
||||
|
||||
export interface SettingsResponseType {
|
||||
meta?: SettingsResponseMeta;
|
||||
settings: Setting[];
|
||||
}
|
||||
|
||||
// Requests
|
||||
|
||||
const dataType = 'SettingsResponseType';
|
||||
|
||||
export const useBrowseSettings = createQuery<SettingsResponseType>({
|
||||
dataType,
|
||||
path: '/settings/',
|
||||
defaultSearchParams: {
|
||||
group: 'site,theme,private,members,portal,newsletter,email,amp,labs,slack,unsplash,views,firstpromoter,editor,comments,analytics,announcement,pintura'
|
||||
}
|
||||
});
|
||||
|
||||
export const useEditSettings = createMutation<SettingsResponseType, Setting[]>({
|
||||
method: 'PUT',
|
||||
path: () => '/settings/',
|
||||
body: settings => ({settings: settings.map(({key, value}) => ({key, value}))}),
|
||||
updateQueries: {
|
||||
dataType,
|
||||
update: newData => ({
|
||||
...newData,
|
||||
settings: newData.settings
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
export const useDeleteStripeSettings = createMutation<unknown, null>({
|
||||
method: 'DELETE',
|
||||
path: () => '/settings/stripe/connect/',
|
||||
invalidateQueries: {dataType}
|
||||
});
|
||||
|
||||
// Helpers
|
||||
|
||||
export function humanizeSettingKey(key: string) {
|
||||
const allCaps = ['API', 'CTA', 'RSS'];
|
||||
|
||||
return key
|
||||
.replace(/^[a-z]/, char => char.toUpperCase())
|
||||
.replace(/_/g, ' ')
|
||||
.replace(new RegExp(`\\b(${allCaps.join('|')})\\b`, 'ig'), match => match.toUpperCase());
|
||||
}
|
||||
|
||||
export function getSettingValues<ValueType = SettingValue>(settings: Setting[] | null, keys: string[]): Array<ValueType | undefined> {
|
||||
return keys.map(key => settings?.find(setting => setting.key === key)?.value) as ValueType[];
|
||||
}
|
||||
|
||||
export function getSettingValue(settings: Setting[] | null | undefined, key: string): SettingValue {
|
||||
if (!settings) {
|
||||
return '';
|
||||
}
|
||||
const setting = settings.find(d => d.key === key);
|
||||
return setting?.value || null;
|
||||
}
|
||||
|
||||
export function checkStripeEnabled(settings: Setting[], config: Config) {
|
||||
const hasSetting = (key: string) => settings.some(setting => setting.key === key && setting.value);
|
||||
|
||||
const hasDirectKeys = hasSetting('stripe_secret_key') && hasSetting('stripe_publishable_key');
|
||||
const hasConnectKeys = hasSetting('stripe_connect_secret_key') && hasSetting('stripe_connect_publishable_key');
|
||||
|
||||
if (config.stripeDirect) {
|
||||
return hasDirectKeys;
|
||||
}
|
||||
|
||||
return hasConnectKeys || hasDirectKeys;
|
||||
}
|
49
apps/admin-x-settings/src/api/site.ts
Normal file
49
apps/admin-x-settings/src/api/site.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import {createQuery} from '../utils/apiRequests';
|
||||
|
||||
// Types
|
||||
|
||||
export type SiteData = {
|
||||
title: string;
|
||||
description: string;
|
||||
logo: string;
|
||||
icon: string;
|
||||
accent_color: string;
|
||||
url: string;
|
||||
locale: string;
|
||||
version: string;
|
||||
};
|
||||
|
||||
export interface SiteResponseType {
|
||||
site: SiteData;
|
||||
}
|
||||
|
||||
// Requests
|
||||
|
||||
const dataType = 'SiteResponseType';
|
||||
|
||||
export const useBrowseSite = createQuery<SiteResponseType>({
|
||||
dataType,
|
||||
path: '/site/'
|
||||
});
|
||||
|
||||
// Helpers
|
||||
|
||||
export function getHomepageUrl(siteData: SiteData): string {
|
||||
const url = new URL(siteData.url);
|
||||
const subdir = url.pathname.endsWith('/') ? url.pathname : `${url.pathname}/`;
|
||||
|
||||
return `${url.origin}${subdir}`;
|
||||
}
|
||||
|
||||
export function getEmailDomain(siteData: SiteData): string {
|
||||
const domain = new URL(siteData.url).hostname || '';
|
||||
if (domain.startsWith('www.')) {
|
||||
return domain.replace(/^(www)\.(?=[^/]*\..{2,5})/, '');
|
||||
}
|
||||
return domain;
|
||||
}
|
||||
|
||||
export function fullEmailAddress(value: 'noreply' | string, siteData: SiteData) {
|
||||
const emailDomain = getEmailDomain(siteData);
|
||||
return value === 'noreply' ? `noreply@${emailDomain}` : value;
|
||||
}
|
@ -1,5 +1,35 @@
|
||||
import {InstalledTheme, Theme} from '../../types/api';
|
||||
import {createMutation, createQuery} from '../apiRequests';
|
||||
import {createMutation, createQuery} from '../utils/apiRequests';
|
||||
|
||||
// Types
|
||||
|
||||
export type Theme = {
|
||||
active: boolean;
|
||||
name: string;
|
||||
package: {
|
||||
name?: string;
|
||||
description?: string;
|
||||
version?: string;
|
||||
};
|
||||
templates?: string[];
|
||||
}
|
||||
|
||||
export type InstalledTheme = Theme & {
|
||||
errors?: ThemeProblem<'error'>[];
|
||||
warnings?: ThemeProblem<'warning'>[];
|
||||
}
|
||||
|
||||
export type ThemeProblem<Level extends string = 'error' | 'warning'> = {
|
||||
code: string
|
||||
details: string
|
||||
failures: Array<{
|
||||
ref: string
|
||||
message?: string
|
||||
rule?: string
|
||||
}>
|
||||
fatal: boolean
|
||||
level: Level
|
||||
rule: string
|
||||
}
|
||||
|
||||
export interface ThemesResponseType {
|
||||
themes: Theme[];
|
||||
@ -9,6 +39,8 @@ export interface ThemesInstallResponseType {
|
||||
themes: InstalledTheme[];
|
||||
}
|
||||
|
||||
// Requests
|
||||
|
||||
const dataType = 'ThemesResponseType';
|
||||
|
||||
export const useBrowseThemes = createQuery<ThemesResponseType>({
|
||||
@ -85,3 +117,17 @@ export const useUploadTheme = createMutation<ThemesInstallResponseType, {file: F
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// Helpers
|
||||
|
||||
export function isActiveTheme(theme: Theme): boolean {
|
||||
return theme.active;
|
||||
}
|
||||
|
||||
export function isDefaultTheme(theme: Theme): boolean {
|
||||
return theme.name === 'casper';
|
||||
}
|
||||
|
||||
export function isDeletableTheme(theme: Theme): boolean {
|
||||
return !isDefaultTheme(theme) && !isActiveTheme(theme);
|
||||
}
|
@ -1,11 +1,32 @@
|
||||
import {Meta, createMutation, createQuery} from '../apiRequests';
|
||||
import {Tier} from '../../types/api';
|
||||
import {Meta, createMutation, createQuery} from '../utils/apiRequests';
|
||||
|
||||
// Types
|
||||
|
||||
export type Tier = {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string | null;
|
||||
slug: string;
|
||||
active: boolean,
|
||||
type: string;
|
||||
welcome_page_url: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
visibility: string;
|
||||
benefits: string[];
|
||||
currency?: string;
|
||||
monthly_price?: number;
|
||||
yearly_price?: number;
|
||||
trial_days: number;
|
||||
}
|
||||
|
||||
export interface TiersResponseType {
|
||||
meta?: Meta
|
||||
tiers: Tier[]
|
||||
}
|
||||
|
||||
// Requests
|
||||
|
||||
const dataType = 'TiersResponseType';
|
||||
|
||||
export const useBrowseTiers = createQuery<TiersResponseType>({
|
||||
@ -39,3 +60,23 @@ export const useEditTier = createMutation<TiersResponseType, Tier>({
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// Helpers
|
||||
|
||||
export function getPaidActiveTiers(tiers: Tier[]) {
|
||||
return tiers.filter((tier) => {
|
||||
return tier.type === 'paid' && tier.active;
|
||||
});
|
||||
}
|
||||
|
||||
export function getActiveTiers(tiers: Tier[]) {
|
||||
return tiers.filter((tier) => {
|
||||
return tier.active;
|
||||
});
|
||||
}
|
||||
|
||||
export function getArchivedTiers(tiers: Tier[]) {
|
||||
return tiers.filter((tier) => {
|
||||
return !tier.active;
|
||||
});
|
||||
}
|
@ -1,5 +1,37 @@
|
||||
import {Meta, createMutation, createQuery} from '../apiRequests';
|
||||
import {User} from '../../types/api';
|
||||
import {Meta, createMutation, createQuery} from '../utils/apiRequests';
|
||||
import {UserRole} from './roles';
|
||||
|
||||
// Types
|
||||
|
||||
export type User = {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
email: string;
|
||||
profile_image: string;
|
||||
cover_image: string|null;
|
||||
bio: string;
|
||||
website: string;
|
||||
location: string;
|
||||
facebook: string;
|
||||
twitter: string;
|
||||
accessibility: string|null;
|
||||
status: string;
|
||||
meta_title: string|null;
|
||||
meta_description: string|null;
|
||||
tour: string|null;
|
||||
last_seen: string|null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
comment_notifications: boolean;
|
||||
free_member_signup_notification: boolean;
|
||||
paid_subscription_canceled_notification: boolean;
|
||||
paid_subscription_started_notification: boolean;
|
||||
mention_notifications: boolean;
|
||||
milestone_notifications: boolean;
|
||||
roles: UserRole[];
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface UsersResponseType {
|
||||
meta?: Meta;
|
||||
@ -25,6 +57,8 @@ export interface DeleteUserResponse {
|
||||
}
|
||||
}
|
||||
|
||||
// Requests
|
||||
|
||||
const dataType = 'UsersResponseType';
|
||||
|
||||
const updateUsers = (newData: UsersResponseType, currentData: unknown) => ({
|
||||
@ -95,3 +129,13 @@ export const useMakeOwner = createMutation<UsersResponseType, string>({
|
||||
update: updateUsers
|
||||
}
|
||||
});
|
||||
|
||||
// Helpers
|
||||
|
||||
export function isOwnerUser(user: User) {
|
||||
return user.roles.some(role => role.name === 'Owner');
|
||||
}
|
||||
|
||||
export function isAdminUser(user: User) {
|
||||
return user.roles.some(role => role.name === 'Administrator');
|
||||
}
|
@ -4,7 +4,7 @@ import SettingNavItem from '../admin-x-ds/settings/SettingNavItem';
|
||||
import SettingNavSection from '../admin-x-ds/settings/SettingNavSection';
|
||||
import TextField from '../admin-x-ds/global/form/TextField';
|
||||
import useRouting from '../hooks/useRouting';
|
||||
import {getSettingValues} from '../utils/helpers';
|
||||
import {getSettingValues} from '../api/settings';
|
||||
import {useGlobalData} from './providers/GlobalDataProvider';
|
||||
import {useSearch} from './providers/ServiceProvider';
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
import {Config, Setting, SiteData, User} from '../../types/api';
|
||||
import {Config, useBrowseConfig} from '../../api/config';
|
||||
import {ReactNode, createContext, useContext} from 'react';
|
||||
import {useBrowseConfig} from '../../utils/api/config';
|
||||
import {useBrowseSettings} from '../../utils/api/settings';
|
||||
import {useBrowseSite} from '../../utils/api/site';
|
||||
import {useCurrentUser} from '../../utils/api/users';
|
||||
import {Setting, useBrowseSettings} from '../../api/settings';
|
||||
import {SiteData, useBrowseSite} from '../../api/site';
|
||||
import {User, useCurrentUser} from '../../api/users';
|
||||
|
||||
interface GlobalData {
|
||||
settings: Setting[]
|
||||
|
@ -1,6 +1,14 @@
|
||||
import React, {createContext, useContext} from 'react';
|
||||
import useSearchService, {SearchService} from '../../utils/search';
|
||||
import {OfficialTheme} from '../../models/themes';
|
||||
|
||||
export type OfficialTheme = {
|
||||
name: string;
|
||||
category: string;
|
||||
previewUrl: string;
|
||||
ref: string;
|
||||
image: string;
|
||||
url?: string;
|
||||
};
|
||||
|
||||
interface ServicesContextProps {
|
||||
ghostVersion: string
|
||||
|
@ -7,7 +7,7 @@ import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
|
||||
import TabView from '../../../admin-x-ds/global/TabView';
|
||||
import useSettingGroup from '../../../hooks/useSettingGroup';
|
||||
import {ReactCodeMirrorRef} from '@uiw/react-codemirror';
|
||||
import {getSettingValues} from '../../../utils/helpers';
|
||||
import {getSettingValues} from '../../../api/settings';
|
||||
import {html} from '@codemirror/lang-html';
|
||||
|
||||
const CodeInjection: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
||||
|
@ -5,10 +5,11 @@ import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
|
||||
import SettingGroupContent from '../../../admin-x-ds/settings/SettingGroupContent';
|
||||
import useSettingGroup from '../../../hooks/useSettingGroup';
|
||||
import {GroupBase, MultiValue} from 'react-select';
|
||||
import {getOptionLabel, getSettingValues} from '../../../utils/helpers';
|
||||
import {useBrowseLabels} from '../../../utils/api/labels';
|
||||
import {useBrowseOffers} from '../../../utils/api/offers';
|
||||
import {useBrowseTiers} from '../../../utils/api/tiers';
|
||||
import {getOptionLabel} from '../../../utils/helpers';
|
||||
import {getSettingValues} from '../../../api/settings';
|
||||
import {useBrowseLabels} from '../../../api/labels';
|
||||
import {useBrowseOffers} from '../../../api/offers';
|
||||
import {useBrowseTiers} from '../../../api/tiers';
|
||||
|
||||
type RefipientValueArgs = {
|
||||
defaultEmailRecipients: string;
|
||||
|
@ -4,7 +4,7 @@ import MailGun from './Mailgun';
|
||||
import Newsletters from './Newsletters';
|
||||
import React from 'react';
|
||||
import SettingSection from '../../../admin-x-ds/settings/SettingSection';
|
||||
import {getSettingValues} from '../../../utils/helpers';
|
||||
import {getSettingValues} from '../../../api/settings';
|
||||
import {useGlobalData} from '../../providers/GlobalDataProvider';
|
||||
|
||||
const searchKeywords = {
|
||||
|
@ -3,9 +3,7 @@ import React from 'react';
|
||||
import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
|
||||
import SettingGroupContent from '../../../admin-x-ds/settings/SettingGroupContent';
|
||||
import Toggle from '../../../admin-x-ds/global/form/Toggle';
|
||||
import {Setting} from '../../../types/api';
|
||||
import {getSettingValues} from '../../../utils/helpers';
|
||||
import {useEditSettings} from '../../../utils/api/settings';
|
||||
import {Setting, getSettingValues, useEditSettings} from '../../../api/settings';
|
||||
import {useGlobalData} from '../../providers/GlobalDataProvider';
|
||||
|
||||
const EnableNewsletters: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
||||
|
@ -6,7 +6,7 @@ import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
|
||||
import SettingGroupContent from '../../../admin-x-ds/settings/SettingGroupContent';
|
||||
import TextField from '../../../admin-x-ds/global/form/TextField';
|
||||
import useSettingGroup from '../../../hooks/useSettingGroup';
|
||||
import {getSettingValues} from '../../../utils/helpers';
|
||||
import {getSettingValues} from '../../../api/settings';
|
||||
|
||||
const MAILGUN_REGIONS = [
|
||||
{label: '🇺🇸 US', value: 'https://api.mailgun.net/v3'},
|
||||
|
@ -4,7 +4,7 @@ import React, {useState} from 'react';
|
||||
import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
|
||||
import TabView from '../../../admin-x-ds/global/TabView';
|
||||
import useRouting from '../../../hooks/useRouting';
|
||||
import {useBrowseNewsletters} from '../../../utils/api/newsletters';
|
||||
import {useBrowseNewsletters} from '../../../api/newsletters';
|
||||
|
||||
const Newsletters: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
||||
const {updateRoute} = useRouting();
|
||||
|
@ -10,8 +10,8 @@ import useForm from '../../../../hooks/useForm';
|
||||
import useRouting from '../../../../hooks/useRouting';
|
||||
import {showToast} from '../../../../admin-x-ds/global/Toast';
|
||||
import {toast} from 'react-hot-toast';
|
||||
import {useAddNewsletter} from '../../../../utils/api/newsletters';
|
||||
import {useBrowseMembers} from '../../../../utils/api/members';
|
||||
import {useAddNewsletter} from '../../../../api/newsletters';
|
||||
import {useBrowseMembers} from '../../../../api/members';
|
||||
|
||||
interface AddNewsletterModalProps {}
|
||||
|
||||
|
@ -18,13 +18,13 @@ import Toggle from '../../../../admin-x-ds/global/form/Toggle';
|
||||
import ToggleGroup from '../../../../admin-x-ds/global/form/ToggleGroup';
|
||||
import useForm from '../../../../hooks/useForm';
|
||||
import validator from 'validator';
|
||||
import {Newsletter} from '../../../../types/api';
|
||||
import {Newsletter, useEditNewsletter} from '../../../../api/newsletters';
|
||||
import {PreviewModalContent} from '../../../../admin-x-ds/global/modal/PreviewModal';
|
||||
import {fullEmailAddress, getSettingValues} from '../../../../utils/helpers';
|
||||
import {getImageUrl, useUploadImage} from '../../../../utils/api/images';
|
||||
import {fullEmailAddress} from '../../../../api/site';
|
||||
import {getImageUrl, useUploadImage} from '../../../../api/images';
|
||||
import {getSettingValues} from '../../../../api/settings';
|
||||
import {showToast} from '../../../../admin-x-ds/global/Toast';
|
||||
import {toast} from 'react-hot-toast';
|
||||
import {useEditNewsletter} from '../../../../utils/api/newsletters';
|
||||
import {useGlobalData} from '../../../providers/GlobalDataProvider';
|
||||
|
||||
interface NewsletterDetailModalProps {
|
||||
|
@ -6,8 +6,9 @@ import LatestPosts3 from '../../../../assets/images/latest-posts-3.png';
|
||||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import {ReactComponent as GhostOrb} from '../../../../admin-x-ds/assets/images/ghost-orb.svg';
|
||||
import {Newsletter} from '../../../../types/api';
|
||||
import {fullEmailAddress, getSettingValues} from '../../../../utils/helpers';
|
||||
import {Newsletter} from '../../../../api/newsletters';
|
||||
import {fullEmailAddress} from '../../../../api/site';
|
||||
import {getSettingValues} from '../../../../api/settings';
|
||||
import {useGlobalData} from '../../../providers/GlobalDataProvider';
|
||||
|
||||
const NewsletterPreview: React.FC<{newsletter: Newsletter}> = ({newsletter}) => {
|
||||
|
@ -7,8 +7,7 @@ import React from 'react';
|
||||
import Table from '../../../../admin-x-ds/global/Table';
|
||||
import TableCell from '../../../../admin-x-ds/global/TableCell';
|
||||
import TableRow from '../../../../admin-x-ds/global/TableRow';
|
||||
import {Newsletter} from '../../../../types/api';
|
||||
import {useEditNewsletter} from '../../../../utils/api/newsletters';
|
||||
import {Newsletter, useEditNewsletter} from '../../../../api/newsletters';
|
||||
|
||||
interface NewslettersListProps {
|
||||
newsletters: Newsletter[]
|
||||
|
@ -5,8 +5,8 @@ import SettingGroupContent from '../../../admin-x-ds/settings/SettingGroupConten
|
||||
import TextField from '../../../admin-x-ds/global/form/TextField';
|
||||
import useSettingGroup from '../../../hooks/useSettingGroup';
|
||||
import {ReactComponent as FacebookLogo} from '../../../admin-x-ds/assets/images/facebook-logo.svg';
|
||||
import {getImageUrl, useUploadImage} from '../../../utils/api/images';
|
||||
import {getSettingValues} from '../../../utils/helpers';
|
||||
import {getImageUrl, useUploadImage} from '../../../api/images';
|
||||
import {getSettingValues} from '../../../api/settings';
|
||||
|
||||
const Facebook: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
||||
const {
|
||||
|
@ -5,8 +5,8 @@ import TextField from '../../../admin-x-ds/global/form/TextField';
|
||||
import useRouting from '../../../hooks/useRouting';
|
||||
import validator from 'validator';
|
||||
import {showToast} from '../../../admin-x-ds/global/Toast';
|
||||
import {useAddInvite} from '../../../utils/api/invites';
|
||||
import {useBrowseRoles} from '../../../utils/api/roles';
|
||||
import {useAddInvite} from '../../../api/invites';
|
||||
import {useBrowseRoles} from '../../../api/roles';
|
||||
import {useEffect, useRef, useState} from 'react';
|
||||
|
||||
type RoleType = 'administrator' | 'editor' | 'author' | 'contributor';
|
||||
|
@ -6,7 +6,7 @@ import SettingGroupContent from '../../../admin-x-ds/settings/SettingGroupConten
|
||||
import TextField from '../../../admin-x-ds/global/form/TextField';
|
||||
import Toggle from '../../../admin-x-ds/global/form/Toggle';
|
||||
import useSettingGroup from '../../../hooks/useSettingGroup';
|
||||
import {getSettingValues} from '../../../utils/helpers';
|
||||
import {getSettingValues} from '../../../api/settings';
|
||||
|
||||
const LockSite: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
||||
const {
|
||||
|
@ -6,7 +6,7 @@ import TextField from '../../../admin-x-ds/global/form/TextField';
|
||||
import useSettingGroup from '../../../hooks/useSettingGroup';
|
||||
import {ReactComponent as GoogleLogo} from '../../../admin-x-ds/assets/images/google-logo.svg';
|
||||
import {ReactComponent as MagnifyingGlass} from '../../../admin-x-ds/assets/icons/magnifying-glass.svg';
|
||||
import {getSettingValues} from '../../../utils/helpers';
|
||||
import {getSettingValues} from '../../../api/settings';
|
||||
|
||||
interface SearchEnginePreviewProps {
|
||||
title: string;
|
||||
|
@ -3,7 +3,7 @@ import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
|
||||
import SettingGroupContent from '../../../admin-x-ds/settings/SettingGroupContent';
|
||||
import TextField from '../../../admin-x-ds/global/form/TextField';
|
||||
import useSettingGroup from '../../../hooks/useSettingGroup';
|
||||
import {getSettingValues} from '../../../utils/helpers';
|
||||
import {getSettingValues} from '../../../api/settings';
|
||||
|
||||
const PublicationLanguage: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
||||
const {
|
||||
|
@ -4,7 +4,7 @@ import SettingGroupContent from '../../../admin-x-ds/settings/SettingGroupConten
|
||||
import TextField from '../../../admin-x-ds/global/form/TextField';
|
||||
import useSettingGroup from '../../../hooks/useSettingGroup';
|
||||
import validator from 'validator';
|
||||
import {getSettingValues} from '../../../utils/helpers';
|
||||
import {getSettingValues} from '../../../api/settings';
|
||||
|
||||
function validateFacebookUrl(newUrl: string) {
|
||||
const errMessage = 'The URL must be in a format like https://www.facebook.com/yourPage';
|
||||
|
@ -4,7 +4,8 @@ import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
|
||||
import SettingGroupContent from '../../../admin-x-ds/settings/SettingGroupContent';
|
||||
import timezoneData from '@tryghost/timezone-data';
|
||||
import useSettingGroup from '../../../hooks/useSettingGroup';
|
||||
import {getLocalTime, getSettingValues} from '../../../utils/helpers';
|
||||
import {getLocalTime} from '../../../utils/helpers';
|
||||
import {getSettingValues} from '../../../api/settings';
|
||||
|
||||
interface TimezoneDataDropdownOption {
|
||||
name: string;
|
||||
|
@ -3,7 +3,7 @@ import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
|
||||
import SettingGroupContent from '../../../admin-x-ds/settings/SettingGroupContent';
|
||||
import TextField from '../../../admin-x-ds/global/form/TextField';
|
||||
import useSettingGroup from '../../../hooks/useSettingGroup';
|
||||
import {getSettingValues} from '../../../utils/helpers';
|
||||
import {getSettingValues} from '../../../api/settings';
|
||||
|
||||
const TitleAndDescription: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
||||
const {
|
||||
|
@ -5,8 +5,8 @@ import SettingGroupContent from '../../../admin-x-ds/settings/SettingGroupConten
|
||||
import TextField from '../../../admin-x-ds/global/form/TextField';
|
||||
import useSettingGroup from '../../../hooks/useSettingGroup';
|
||||
import {ReactComponent as TwitterLogo} from '../../../admin-x-ds/assets/images/twitter-logo.svg';
|
||||
import {getImageUrl, useUploadImage} from '../../../utils/api/images';
|
||||
import {getSettingValues} from '../../../utils/helpers';
|
||||
import {getImageUrl, useUploadImage} from '../../../api/images';
|
||||
import {getSettingValues} from '../../../api/settings';
|
||||
|
||||
const Twitter: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
||||
const {
|
||||
|
@ -14,13 +14,11 @@ import TextField from '../../../admin-x-ds/global/form/TextField';
|
||||
import Toggle from '../../../admin-x-ds/global/form/Toggle';
|
||||
import useStaffUsers from '../../../hooks/useStaffUsers';
|
||||
import validator from 'validator';
|
||||
import {User} from '../../../types/api';
|
||||
import {getImageUrl, useUploadImage} from '../../../utils/api/images';
|
||||
import {isAdminUser, isOwnerUser} from '../../../utils/helpers';
|
||||
import {User, isAdminUser, isOwnerUser, useDeleteUser, useEditUser, useMakeOwner, useUpdatePassword} from '../../../api/users';
|
||||
import {getImageUrl, useUploadImage} from '../../../api/images';
|
||||
import {showToast} from '../../../admin-x-ds/global/Toast';
|
||||
import {toast} from 'react-hot-toast';
|
||||
import {useBrowseRoles} from '../../../utils/api/roles';
|
||||
import {useDeleteUser, useEditUser, useMakeOwner, useUpdatePassword} from '../../../utils/api/users';
|
||||
import {useBrowseRoles} from '../../../api/roles';
|
||||
|
||||
interface CustomHeadingProps {
|
||||
children?: React.ReactNode;
|
||||
|
@ -10,8 +10,8 @@ import TabView from '../../../admin-x-ds/global/TabView';
|
||||
import UserDetailModal from './UserDetailModal';
|
||||
import useRouting from '../../../hooks/useRouting';
|
||||
import useStaffUsers from '../../../hooks/useStaffUsers';
|
||||
import {User} from '../../../types/api';
|
||||
import {UserInvite, useAddInvite, useDeleteInvite} from '../../../utils/api/invites';
|
||||
import {User} from '../../../api/users';
|
||||
import {UserInvite, useAddInvite, useDeleteInvite} from '../../../api/invites';
|
||||
import {generateAvatarColor, getInitials} from '../../../utils/helpers';
|
||||
import {showToast} from '../../../admin-x-ds/global/Toast';
|
||||
|
||||
|
@ -5,8 +5,9 @@ import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
|
||||
import SettingGroupContent from '../../../admin-x-ds/settings/SettingGroupContent';
|
||||
import useSettingGroup from '../../../hooks/useSettingGroup';
|
||||
import {GroupBase, MultiValue} from 'react-select';
|
||||
import {getOptionLabel, getSettingValues} from '../../../utils/helpers';
|
||||
import {useBrowseTiers} from '../../../utils/api/tiers';
|
||||
import {getOptionLabel} from '../../../utils/helpers';
|
||||
import {getSettingValues} from '../../../api/settings';
|
||||
import {useBrowseTiers} from '../../../api/tiers';
|
||||
|
||||
const MEMBERS_SIGNUP_ACCESS_OPTIONS = [
|
||||
{value: 'all', label: 'Anyone can sign up'},
|
||||
|
@ -4,7 +4,7 @@ import SettingGroup from '../../../admin-x-ds/settings/SettingGroup';
|
||||
import SettingGroupContent from '../../../admin-x-ds/settings/SettingGroupContent';
|
||||
import Toggle from '../../../admin-x-ds/global/form/Toggle';
|
||||
import useSettingGroup from '../../../hooks/useSettingGroup';
|
||||
import {getSettingValues} from '../../../utils/helpers';
|
||||
import {getSettingValues} from '../../../api/settings';
|
||||
|
||||
const Analytics: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
||||
const {
|
||||
|
@ -4,9 +4,8 @@ import StripeButton from '../../../admin-x-ds/settings/StripeButton';
|
||||
import TabView from '../../../admin-x-ds/global/TabView';
|
||||
import TiersList from './tiers/TiersList';
|
||||
import useRouting from '../../../hooks/useRouting';
|
||||
import {Tier} from '../../../types/api';
|
||||
import {checkStripeEnabled, getActiveTiers, getArchivedTiers} from '../../../utils/helpers';
|
||||
import {useBrowseTiers} from '../../../utils/api/tiers';
|
||||
import {Tier, getActiveTiers, getArchivedTiers, useBrowseTiers} from '../../../api/tiers';
|
||||
import {checkStripeEnabled} from '../../../api/settings';
|
||||
import {useGlobalData} from '../../providers/GlobalDataProvider';
|
||||
|
||||
const Tiers: React.FC<{ keywords: string[] }> = ({keywords}) => {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import Form from '../../../../admin-x-ds/global/form/Form';
|
||||
import React, {FocusEventHandler, useState} from 'react';
|
||||
import TextField from '../../../../admin-x-ds/global/form/TextField';
|
||||
import {Setting, SettingValue} from '../../../../types/api';
|
||||
import {fullEmailAddress, getEmailDomain, getSettingValues} from '../../../../utils/helpers';
|
||||
import {Setting, SettingValue, getSettingValues} from '../../../../api/settings';
|
||||
import {fullEmailAddress, getEmailDomain} from '../../../../api/site';
|
||||
import {useGlobalData} from '../../../providers/GlobalDataProvider';
|
||||
|
||||
const AccountPage: React.FC<{
|
||||
|
@ -1,21 +1,19 @@
|
||||
import Form from '../../../../admin-x-ds/global/form/Form';
|
||||
import Heading from '../../../../admin-x-ds/global/Heading';
|
||||
import Icon from '../../../../admin-x-ds/global/Icon';
|
||||
import ImageUpload from '../../../../admin-x-ds/global/form/ImageUpload';
|
||||
import React, {useState} from 'react';
|
||||
import Select from '../../../../admin-x-ds/global/form/Select';
|
||||
import TextField from '../../../../admin-x-ds/global/form/TextField';
|
||||
import Toggle from '../../../../admin-x-ds/global/form/Toggle';
|
||||
import {Setting, SettingValue} from '../../../../types/api';
|
||||
import {getSettingValues} from '../../../../utils/helpers';
|
||||
|
||||
import Heading from '../../../../admin-x-ds/global/Heading';
|
||||
import Icon from '../../../../admin-x-ds/global/Icon';
|
||||
import ImageUpload from '../../../../admin-x-ds/global/form/ImageUpload';
|
||||
import clsx from 'clsx';
|
||||
import {ReactComponent as PortalIcon1} from '../../../../assets/icons/portal-icon-1.svg';
|
||||
import {ReactComponent as PortalIcon2} from '../../../../assets/icons/portal-icon-2.svg';
|
||||
import {ReactComponent as PortalIcon3} from '../../../../assets/icons/portal-icon-3.svg';
|
||||
import {ReactComponent as PortalIcon4} from '../../../../assets/icons/portal-icon-4.svg';
|
||||
import {ReactComponent as PortalIcon5} from '../../../../assets/icons/portal-icon-5.svg';
|
||||
import {getImageUrl, useUploadImage} from '../../../../utils/api/images';
|
||||
import {Setting, SettingValue, getSettingValues} from '../../../../api/settings';
|
||||
import {getImageUrl, useUploadImage} from '../../../../api/images';
|
||||
|
||||
const defaultButtonIcons = [
|
||||
{
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React, {useEffect, useRef, useState} from 'react';
|
||||
import useSettingGroup from '../../../../hooks/useSettingGroup';
|
||||
import {Setting, SiteData, Tier} from '../../../../types/api';
|
||||
import {getSettingValue} from '../../../../utils/helpers';
|
||||
import {Setting, getSettingValue} from '../../../../api/settings';
|
||||
import {SiteData} from '../../../../api/site';
|
||||
import {Tier} from '../../../../api/tiers';
|
||||
|
||||
type PortalFrameProps = {
|
||||
settings: Setting[];
|
||||
|
@ -5,8 +5,8 @@ import ModalPage from '../../../../admin-x-ds/global/modal/ModalPage';
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import Select from '../../../../admin-x-ds/global/form/Select';
|
||||
import TextField from '../../../../admin-x-ds/global/form/TextField';
|
||||
import {getHomepageUrl, getPaidActiveTiers} from '../../../../utils/helpers';
|
||||
import {useBrowseTiers} from '../../../../utils/api/tiers';
|
||||
import {getHomepageUrl} from '../../../../api/site';
|
||||
import {getPaidActiveTiers, useBrowseTiers} from '../../../../api/tiers';
|
||||
import {useGlobalData} from '../../../providers/GlobalDataProvider';
|
||||
|
||||
interface PortalLinkPrefs {
|
||||
|
@ -9,10 +9,9 @@ import TabView, {Tab} from '../../../../admin-x-ds/global/TabView';
|
||||
import useForm, {Dirtyable} from '../../../../hooks/useForm';
|
||||
import useRouting from '../../../../hooks/useRouting';
|
||||
import {PreviewModalContent} from '../../../../admin-x-ds/global/modal/PreviewModal';
|
||||
import {Setting, SettingValue, Tier} from '../../../../types/api';
|
||||
import {fullEmailAddress, getPaidActiveTiers} from '../../../../utils/helpers';
|
||||
import {useBrowseTiers, useEditTier} from '../../../../utils/api/tiers';
|
||||
import {useEditSettings} from '../../../../utils/api/settings';
|
||||
import {Setting, SettingValue, useEditSettings} from '../../../../api/settings';
|
||||
import {Tier, getPaidActiveTiers, useBrowseTiers, useEditTier} from '../../../../api/tiers';
|
||||
import {fullEmailAddress} from '../../../../api/site';
|
||||
import {useGlobalData} from '../../../providers/GlobalDataProvider';
|
||||
|
||||
const Sidebar: React.FC<{
|
||||
|
@ -1,7 +1,8 @@
|
||||
import PortalFrame from './PortalFrame';
|
||||
import PortalLinks from './PortalLinks';
|
||||
import React from 'react';
|
||||
import {Setting, Tier} from '../../../../types/api';
|
||||
import {Setting} from '../../../../api/settings';
|
||||
import {Tier} from '../../../../api/tiers';
|
||||
|
||||
interface PortalPreviewProps {
|
||||
selectedTab: string;
|
||||
@ -39,4 +40,4 @@ const PortalPreview: React.FC<PortalPreviewProps> = ({
|
||||
return tabContents;
|
||||
};
|
||||
|
||||
export default PortalPreview;
|
||||
export default PortalPreview;
|
||||
|
@ -4,8 +4,8 @@ import HtmlField from '../../../../admin-x-ds/global/form/HtmlField';
|
||||
import React, {useEffect, useMemo} from 'react';
|
||||
import Toggle from '../../../../admin-x-ds/global/form/Toggle';
|
||||
import {CheckboxProps} from '../../../../admin-x-ds/global/form/Checkbox';
|
||||
import {Setting, SettingValue, Tier} from '../../../../types/api';
|
||||
import {checkStripeEnabled, getSettingValues} from '../../../../utils/helpers';
|
||||
import {Setting, SettingValue, checkStripeEnabled, getSettingValues} from '../../../../api/settings';
|
||||
import {Tier} from '../../../../api/tiers';
|
||||
import {useGlobalData} from '../../../providers/GlobalDataProvider';
|
||||
|
||||
const SignupOptions: React.FC<{
|
||||
|
@ -17,12 +17,12 @@ import useRouting from '../../../../hooks/useRouting';
|
||||
import useSettingGroup from '../../../../hooks/useSettingGroup';
|
||||
import {ApiError} from '../../../../utils/apiRequests';
|
||||
import {ReactComponent as StripeVerified} from '../../../../assets/images/stripe-verified.svg';
|
||||
import {checkStripeEnabled, getGhostPaths, getSettingValue, getSettingValues} from '../../../../utils/helpers';
|
||||
import {checkStripeEnabled, getSettingValue, getSettingValues, useDeleteStripeSettings, useEditSettings} from '../../../../api/settings';
|
||||
import {getGhostPaths} from '../../../../utils/helpers';
|
||||
import {showToast} from '../../../../admin-x-ds/global/Toast';
|
||||
import {toast} from 'react-hot-toast';
|
||||
import {useBrowseMembers} from '../../../../utils/api/members';
|
||||
import {useBrowseTiers, useEditTier} from '../../../../utils/api/tiers';
|
||||
import {useDeleteStripeSettings, useEditSettings} from '../../../../utils/api/settings';
|
||||
import {useBrowseMembers} from '../../../../api/members';
|
||||
import {useBrowseTiers, useEditTier} from '../../../../api/tiers';
|
||||
import {useGlobalData} from '../../../providers/GlobalDataProvider';
|
||||
|
||||
const RETRY_PRODUCT_SAVE_POLL_LENGTH = 1000;
|
||||
|
@ -14,12 +14,11 @@ import useForm from '../../../../hooks/useForm';
|
||||
import useRouting from '../../../../hooks/useRouting';
|
||||
import useSettingGroup from '../../../../hooks/useSettingGroup';
|
||||
import useSortableIndexedList from '../../../../hooks/useSortableIndexedList';
|
||||
import {Tier} from '../../../../types/api';
|
||||
import {Tier, useAddTier, useEditTier} from '../../../../api/tiers';
|
||||
import {currencies, currencyFromDecimal, currencyGroups, currencyToDecimal, getSymbol} from '../../../../utils/currency';
|
||||
import {getSettingValues} from '../../../../utils/helpers';
|
||||
import {getSettingValues} from '../../../../api/settings';
|
||||
import {showToast} from '../../../../admin-x-ds/global/Toast';
|
||||
import {toast} from 'react-hot-toast';
|
||||
import {useAddTier, useEditTier} from '../../../../utils/api/tiers';
|
||||
|
||||
interface TierDetailModalProps {
|
||||
tier?: Tier
|
||||
|
@ -3,8 +3,8 @@ import Heading from '../../../../admin-x-ds/global/Heading';
|
||||
import Icon from '../../../../admin-x-ds/global/Icon';
|
||||
import React, {useState} from 'react';
|
||||
import useSettingGroup from '../../../../hooks/useSettingGroup';
|
||||
import {Tier} from '../../../../types/api';
|
||||
import {getSettingValues} from '../../../../utils/helpers';
|
||||
import {Tier} from '../../../../api/tiers';
|
||||
import {getSettingValues} from '../../../../api/settings';
|
||||
import {getSymbol} from '../../../../utils/currency';
|
||||
import {numberWithCommas} from '../../../../utils/helpers';
|
||||
|
||||
|
@ -5,10 +5,9 @@ import NoValueLabel from '../../../../admin-x-ds/global/NoValueLabel';
|
||||
import React from 'react';
|
||||
import TierDetailModal from './TierDetailModal';
|
||||
import useRouting from '../../../../hooks/useRouting';
|
||||
import {Tier} from '../../../../types/api';
|
||||
import {Tier, useEditTier} from '../../../../api/tiers';
|
||||
import {currencyToDecimal, getSymbol} from '../../../../utils/currency';
|
||||
import {numberWithCommas} from '../../../../utils/helpers';
|
||||
import {useEditTier} from '../../../../utils/api/tiers';
|
||||
|
||||
interface TiersListProps {
|
||||
tab?: 'active-tiers' | 'archive-tiers' | 'free-tier';
|
||||
|
@ -10,12 +10,11 @@ import ThemePreview from './designAndBranding/ThemePreview';
|
||||
import ThemeSettings from './designAndBranding/ThemeSettings';
|
||||
import useForm from '../../../hooks/useForm';
|
||||
import useRouting from '../../../hooks/useRouting';
|
||||
import {CustomThemeSetting, Setting, SettingValue} from '../../../types/api';
|
||||
import {CustomThemeSetting, useBrowseCustomThemeSettings, useEditCustomThemeSettings} from '../../../api/customThemeSettings';
|
||||
import {PreviewModalContent} from '../../../admin-x-ds/global/modal/PreviewModal';
|
||||
import {getHomepageUrl, getSettingValues} from '../../../utils/helpers';
|
||||
import {useBrowseCustomThemeSettings, useEditCustomThemeSettings} from '../../../utils/api/customThemeSettings';
|
||||
import {useBrowsePosts} from '../../../utils/api/posts';
|
||||
import {useEditSettings} from '../../../utils/api/settings';
|
||||
import {Setting, SettingValue, getSettingValues, useEditSettings} from '../../../api/settings';
|
||||
import {getHomepageUrl} from '../../../api/site';
|
||||
import {useBrowsePosts} from '../../../api/posts';
|
||||
import {useGlobalData} from '../../providers/GlobalDataProvider';
|
||||
|
||||
const Sidebar: React.FC<{
|
||||
|
@ -5,7 +5,7 @@ import TabView from '../../../admin-x-ds/global/TabView';
|
||||
import useNavigationEditor, {NavigationItem} from '../../../hooks/site/useNavigationEditor';
|
||||
import useRouting from '../../../hooks/useRouting';
|
||||
import useSettingGroup from '../../../hooks/useSettingGroup';
|
||||
import {getSettingValues} from '../../../utils/helpers';
|
||||
import {getSettingValues} from '../../../api/settings';
|
||||
import {useState} from 'react';
|
||||
|
||||
const NavigationModal = NiceModal.create(() => {
|
||||
|
@ -12,9 +12,8 @@ import TabView from '../../../admin-x-ds/global/TabView';
|
||||
import ThemeInstalledModal from './theme/ThemeInstalledModal';
|
||||
import ThemePreview from './theme/ThemePreview';
|
||||
import useRouting from '../../../hooks/useRouting';
|
||||
import {OfficialTheme} from '../../../models/themes';
|
||||
import {Theme} from '../../../types/api';
|
||||
import {useBrowseThemes, useInstallTheme, useUploadTheme} from '../../../utils/api/themes';
|
||||
import {OfficialTheme} from '../../providers/ServiceProvider';
|
||||
import {Theme, useBrowseThemes, useInstallTheme, useUploadTheme} from '../../../api/themes';
|
||||
|
||||
interface ThemeToolbarProps {
|
||||
selectedTheme: OfficialTheme|null;
|
||||
|
@ -4,8 +4,8 @@ import ImageUpload from '../../../../admin-x-ds/global/form/ImageUpload';
|
||||
import React from 'react';
|
||||
import SettingGroupContent from '../../../../admin-x-ds/settings/SettingGroupContent';
|
||||
import TextField from '../../../../admin-x-ds/global/form/TextField';
|
||||
import {SettingValue} from '../../../../types/api';
|
||||
import {getImageUrl, useUploadImage} from '../../../../utils/api/images';
|
||||
import {SettingValue} from '../../../../api/settings';
|
||||
import {getImageUrl, useUploadImage} from '../../../../api/images';
|
||||
|
||||
export interface BrandSettingValues {
|
||||
description: string
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, {useEffect, useRef} from 'react';
|
||||
import {CustomThemeSetting} from '../../../../types/api';
|
||||
import {CustomThemeSetting} from '../../../../api/customThemeSettings';
|
||||
|
||||
type BrandSettings = {
|
||||
description: string;
|
||||
|
@ -6,9 +6,9 @@ import Select from '../../../../admin-x-ds/global/form/Select';
|
||||
import SettingGroupContent from '../../../../admin-x-ds/settings/SettingGroupContent';
|
||||
import TextField from '../../../../admin-x-ds/global/form/TextField';
|
||||
import Toggle from '../../../../admin-x-ds/global/form/Toggle';
|
||||
import {CustomThemeSetting} from '../../../../types/api';
|
||||
import {getImageUrl, useUploadImage} from '../../../../utils/api/images';
|
||||
import {humanizeSettingKey} from '../../../../utils/helpers';
|
||||
import {CustomThemeSetting} from '../../../../api/customThemeSettings';
|
||||
import {getImageUrl, useUploadImage} from '../../../../api/images';
|
||||
import {humanizeSettingKey} from '../../../../api/settings';
|
||||
|
||||
const ThemeSetting: React.FC<{
|
||||
setting: CustomThemeSetting,
|
||||
|
@ -6,10 +6,8 @@ import Menu from '../../../../admin-x-ds/global/Menu';
|
||||
import ModalPage from '../../../../admin-x-ds/global/modal/ModalPage';
|
||||
import NiceModal from '@ebay/nice-modal-react';
|
||||
import React from 'react';
|
||||
import {Theme} from '../../../../types/api';
|
||||
import {Theme, isActiveTheme, isDefaultTheme, isDeletableTheme, useActivateTheme, useDeleteTheme} from '../../../../api/themes';
|
||||
import {downloadFile, getGhostPaths} from '../../../../utils/helpers';
|
||||
import {isActiveTheme, isDefaultTheme, isDeletableTheme} from '../../../../models/themes';
|
||||
import {useActivateTheme, useDeleteTheme} from '../../../../utils/api/themes';
|
||||
|
||||
interface ThemeActionProps {
|
||||
theme: Theme;
|
||||
|
@ -1,9 +1,8 @@
|
||||
import Heading from '../../../../admin-x-ds/global/Heading';
|
||||
import ModalPage from '../../../../admin-x-ds/global/modal/ModalPage';
|
||||
import React from 'react';
|
||||
import {OfficialTheme} from '../../../../models/themes';
|
||||
import {OfficialTheme, useOfficialThemes} from '../../../providers/ServiceProvider';
|
||||
import {getGhostPaths} from '../../../../utils/helpers';
|
||||
import {useOfficialThemes} from '../../../providers/ServiceProvider';
|
||||
|
||||
const OfficialThemes: React.FC<{
|
||||
onSelectTheme?: (theme: OfficialTheme) => void;
|
||||
|
@ -5,9 +5,8 @@ import ListItem from '../../../../admin-x-ds/global/ListItem';
|
||||
import NiceModal from '@ebay/nice-modal-react';
|
||||
import React, {ReactNode, useState} from 'react';
|
||||
import {ConfirmationModalContent} from '../../../../admin-x-ds/global/modal/ConfirmationModal';
|
||||
import {InstalledTheme, ThemeProblem} from '../../../../types/api';
|
||||
import {InstalledTheme, ThemeProblem, useActivateTheme} from '../../../../api/themes';
|
||||
import {showToast} from '../../../../admin-x-ds/global/Toast';
|
||||
import {useActivateTheme} from '../../../../utils/api/themes';
|
||||
|
||||
const ThemeProblemView = ({problem}:{problem: ThemeProblem}) => {
|
||||
const [isExpanded, setExpanded] = useState(false);
|
||||
|
@ -7,8 +7,8 @@ import MobileChrome from '../../../../admin-x-ds/global/chrome/MobileChrome';
|
||||
import NiceModal from '@ebay/nice-modal-react';
|
||||
import PageHeader from '../../../../admin-x-ds/global/layout/PageHeader';
|
||||
import React, {useState} from 'react';
|
||||
import {OfficialTheme} from '../../../../models/themes';
|
||||
import {Theme} from '../../../../types/api';
|
||||
import {OfficialTheme} from '../../../providers/ServiceProvider';
|
||||
import {Theme} from '../../../../api/themes';
|
||||
|
||||
const ThemePreview: React.FC<{
|
||||
selectedTheme?: OfficialTheme;
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React, {useEffect, useRef, useState} from 'react';
|
||||
import useForm, {SaveState} from './useForm';
|
||||
import useGlobalDirtyState from './useGlobalDirtyState';
|
||||
import {Setting, SettingValue, SiteData} from '../types/api';
|
||||
import {useEditSettings} from '../utils/api/settings';
|
||||
import {Setting, SettingValue, useEditSettings} from '../api/settings';
|
||||
import {SiteData} from '../api/site';
|
||||
import {useGlobalData} from '../components/providers/GlobalDataProvider';
|
||||
|
||||
interface LocalSetting extends Setting {
|
||||
|
@ -1,7 +1,6 @@
|
||||
import {User} from '../types/api';
|
||||
import {UserInvite, useBrowseInvites} from '../utils/api/invites';
|
||||
import {useBrowseRoles} from '../utils/api/roles';
|
||||
import {useBrowseUsers} from '../utils/api/users';
|
||||
import {User, useBrowseUsers} from '../api/users';
|
||||
import {UserInvite, useBrowseInvites} from '../api/invites';
|
||||
import {useBrowseRoles} from '../api/roles';
|
||||
import {useGlobalData} from '../components/providers/GlobalDataProvider';
|
||||
|
||||
export type UsersHook = {
|
||||
|
@ -1,31 +0,0 @@
|
||||
export type Theme = {
|
||||
active: boolean;
|
||||
name: string;
|
||||
package: {
|
||||
name?: string;
|
||||
description?: string;
|
||||
version?: string;
|
||||
};
|
||||
templates?: string[];
|
||||
}
|
||||
|
||||
export type OfficialTheme = {
|
||||
name: string;
|
||||
category: string;
|
||||
previewUrl: string;
|
||||
ref: string;
|
||||
image: string;
|
||||
url?: string;
|
||||
};
|
||||
|
||||
export function isActiveTheme(theme: Theme): boolean {
|
||||
return theme.active;
|
||||
}
|
||||
|
||||
export function isDefaultTheme(theme: Theme): boolean {
|
||||
return theme.name === 'casper';
|
||||
}
|
||||
|
||||
export function isDeletableTheme(theme: Theme): boolean {
|
||||
return !isDefaultTheme(theme) && !isActiveTheme(theme);
|
||||
}
|
@ -1,219 +0,0 @@
|
||||
export type JSONValue = string|number|boolean|null|Date|JSONObject|JSONArray;
|
||||
export interface JSONObject { [key: string]: JSONValue }
|
||||
export interface JSONArray extends Array<string|number|boolean|Date|JSONObject|JSONValue> {}
|
||||
|
||||
export type SettingValue = string | boolean | null;
|
||||
|
||||
export type Setting = {
|
||||
key: string;
|
||||
value: SettingValue;
|
||||
}
|
||||
|
||||
export type Config = {
|
||||
version: string;
|
||||
environment: string;
|
||||
editor: {
|
||||
url: string
|
||||
version: string
|
||||
};
|
||||
labs: Record<string, boolean>;
|
||||
stripeDirect: boolean;
|
||||
|
||||
// Config is relatively fluid, so we only type used properties above and still support arbitrary property access when needed
|
||||
[key: string]: JSONValue;
|
||||
};
|
||||
|
||||
export type User = {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
email: string;
|
||||
profile_image: string;
|
||||
cover_image: string|null;
|
||||
bio: string;
|
||||
website: string;
|
||||
location: string;
|
||||
facebook: string;
|
||||
twitter: string;
|
||||
accessibility: string|null;
|
||||
status: string;
|
||||
meta_title: string|null;
|
||||
meta_description: string|null;
|
||||
tour: string|null;
|
||||
last_seen: string|null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
comment_notifications: boolean;
|
||||
free_member_signup_notification: boolean;
|
||||
paid_subscription_canceled_notification: boolean;
|
||||
paid_subscription_started_notification: boolean;
|
||||
mention_notifications: boolean;
|
||||
milestone_notifications: boolean;
|
||||
roles: UserRole[];
|
||||
url: string;
|
||||
}
|
||||
|
||||
export type UserRoleType = 'Owner' | 'Administrator' | 'Editor' | 'Author' | 'Contributor';
|
||||
|
||||
export type UserRole = {
|
||||
id: string;
|
||||
name: UserRoleType;
|
||||
description: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export type SiteData = {
|
||||
title: string;
|
||||
description: string;
|
||||
logo: string;
|
||||
icon: string;
|
||||
accent_color: string;
|
||||
url: string;
|
||||
locale: string;
|
||||
version: string;
|
||||
};
|
||||
|
||||
export type Post = {
|
||||
id: string;
|
||||
url: string;
|
||||
};
|
||||
|
||||
export type Member = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
export type Tier = {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string | null;
|
||||
slug: string;
|
||||
active: boolean,
|
||||
type: string;
|
||||
welcome_page_url: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
visibility: string;
|
||||
benefits: string[];
|
||||
currency?: string;
|
||||
monthly_price?: number;
|
||||
yearly_price?: number;
|
||||
trial_days: number;
|
||||
}
|
||||
|
||||
export type Label = {
|
||||
id: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export type Offer = {
|
||||
id: string;
|
||||
name: string;
|
||||
code: string;
|
||||
display_title: string;
|
||||
display_description: string;
|
||||
type: string;
|
||||
cadence: string;
|
||||
amount: number;
|
||||
duration: string;
|
||||
duration_in_months: number | null;
|
||||
currency_restriction: boolean;
|
||||
currency: string | null;
|
||||
status: string;
|
||||
redemption_count: number;
|
||||
tier: {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
}
|
||||
|
||||
type CustomThemeSettingData =
|
||||
{ type: 'text', value: string | null, default: string | null } |
|
||||
{ type: 'color', value: string, default: string } |
|
||||
{ type: 'image', value: string | null } |
|
||||
{ type: 'boolean', value: boolean, default: boolean } |
|
||||
{
|
||||
type: 'select',
|
||||
value: string
|
||||
default: string
|
||||
options: string[]
|
||||
};
|
||||
|
||||
export type CustomThemeSetting = CustomThemeSettingData & {
|
||||
id: string
|
||||
key: string
|
||||
description?: string
|
||||
// homepage and post are the only two groups we handle, but technically theme authors can put other things in package.json
|
||||
group?: 'homepage' | 'post' | string
|
||||
}
|
||||
|
||||
export type Theme = {
|
||||
active: boolean;
|
||||
name: string;
|
||||
package: {
|
||||
name?: string;
|
||||
description?: string;
|
||||
version?: string;
|
||||
};
|
||||
templates?: string[];
|
||||
}
|
||||
|
||||
export type InstalledTheme = Theme & {
|
||||
errors?: ThemeProblem<'error'>[];
|
||||
warnings?: ThemeProblem<'warning'>[];
|
||||
}
|
||||
|
||||
export type ThemeProblem<Level extends string = 'error' | 'warning'> = {
|
||||
code: string
|
||||
details: string
|
||||
failures: Array<{
|
||||
ref: string
|
||||
message?: string
|
||||
rule?: string
|
||||
}>
|
||||
fatal: boolean
|
||||
level: Level
|
||||
rule: string
|
||||
}
|
||||
|
||||
export type Newsletter = {
|
||||
id: string;
|
||||
uuid: string;
|
||||
name: string;
|
||||
description: string | null;
|
||||
feedback_enabled: boolean;
|
||||
slug: string;
|
||||
sender_name: string | null;
|
||||
sender_email: string | null;
|
||||
sender_reply_to: string;
|
||||
status: string;
|
||||
visibility: string;
|
||||
subscribe_on_signup: boolean;
|
||||
sort_order: number;
|
||||
header_image: string | null;
|
||||
show_header_icon: boolean;
|
||||
show_header_title: boolean;
|
||||
title_font_category: string;
|
||||
title_alignment: string;
|
||||
show_feature_image: boolean;
|
||||
body_font_category: string;
|
||||
footer_content: string | null;
|
||||
show_badge: boolean;
|
||||
show_header_name: boolean;
|
||||
show_post_title_section: boolean;
|
||||
show_comment_cta: boolean;
|
||||
show_subscription_details: boolean;
|
||||
show_latest_posts: boolean;
|
||||
background_color: string;
|
||||
border_color: string | null;
|
||||
title_color: string | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
count?: {
|
||||
posts?: number;
|
||||
active_members?: number;
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
import {Config} from '../../types/api';
|
||||
import {createQuery} from '../apiRequests';
|
||||
|
||||
export interface ConfigResponseType {
|
||||
config: Config;
|
||||
}
|
||||
|
||||
const dataType = 'ConfigResponseType';
|
||||
|
||||
export const useBrowseConfig = createQuery<ConfigResponseType>({
|
||||
dataType,
|
||||
path: '/config/'
|
||||
});
|
@ -1,24 +0,0 @@
|
||||
import {CustomThemeSetting, Setting} from '../../types/api';
|
||||
import {createMutation, createQuery} from '../apiRequests';
|
||||
|
||||
export interface CustomThemeSettingsResponseType {
|
||||
custom_theme_settings: CustomThemeSetting[];
|
||||
}
|
||||
|
||||
const dataType = 'CustomThemeSettingsResponseType';
|
||||
|
||||
export const useBrowseCustomThemeSettings = createQuery<CustomThemeSettingsResponseType>({
|
||||
dataType,
|
||||
path: '/custom_theme_settings/'
|
||||
});
|
||||
|
||||
export const useEditCustomThemeSettings = createMutation<CustomThemeSettingsResponseType, Setting[]>({
|
||||
method: 'PUT',
|
||||
path: () => '/custom_theme_settings/',
|
||||
body: settings => ({custom_theme_settings: settings}),
|
||||
|
||||
updateQueries: {
|
||||
dataType,
|
||||
update: newData => newData
|
||||
}
|
||||
});
|
@ -1,15 +0,0 @@
|
||||
import {Meta, createQuery} from '../apiRequests';
|
||||
import {Offer} from '../../types/api';
|
||||
|
||||
export interface OffersResponseType {
|
||||
meta?: Meta
|
||||
offers: Offer[]
|
||||
}
|
||||
|
||||
const dataType = 'OffersResponseType';
|
||||
|
||||
export const useBrowseOffers = createQuery<OffersResponseType>({
|
||||
dataType,
|
||||
path: '/offers/',
|
||||
defaultSearchParams: {limit: 'all'}
|
||||
});
|
@ -1,15 +0,0 @@
|
||||
import {Meta, createQuery} from '../apiRequests';
|
||||
import {UserRole} from '../../types/api';
|
||||
|
||||
export interface RolesResponseType {
|
||||
meta?: Meta;
|
||||
roles: UserRole[];
|
||||
}
|
||||
|
||||
const dataType = 'RolesResponseType';
|
||||
|
||||
export const useBrowseRoles = createQuery<RolesResponseType>({
|
||||
dataType,
|
||||
path: '/roles/',
|
||||
defaultSearchParams: {limit: 'all'}
|
||||
});
|
@ -1,38 +0,0 @@
|
||||
import {Meta, createMutation, createQuery} from '../apiRequests';
|
||||
import {Setting} from '../../types/api';
|
||||
|
||||
export type SettingsResponseMeta = Meta & { sent_email_verification?: boolean }
|
||||
|
||||
export interface SettingsResponseType {
|
||||
meta?: SettingsResponseMeta;
|
||||
settings: Setting[];
|
||||
}
|
||||
|
||||
const dataType = 'SettingsResponseType';
|
||||
|
||||
export const useBrowseSettings = createQuery<SettingsResponseType>({
|
||||
dataType,
|
||||
path: '/settings/',
|
||||
defaultSearchParams: {
|
||||
group: 'site,theme,private,members,portal,newsletter,email,amp,labs,slack,unsplash,views,firstpromoter,editor,comments,analytics,announcement,pintura'
|
||||
}
|
||||
});
|
||||
|
||||
export const useEditSettings = createMutation<SettingsResponseType, Setting[]>({
|
||||
method: 'PUT',
|
||||
path: () => '/settings/',
|
||||
body: settings => ({settings: settings.map(({key, value}) => ({key, value}))}),
|
||||
updateQueries: {
|
||||
dataType,
|
||||
update: newData => ({
|
||||
...newData,
|
||||
settings: newData.settings
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
export const useDeleteStripeSettings = createMutation<unknown, null>({
|
||||
method: 'DELETE',
|
||||
path: () => '/settings/stripe/connect/',
|
||||
invalidateQueries: {dataType}
|
||||
});
|
@ -1,13 +0,0 @@
|
||||
import {SiteData} from '../../types/api';
|
||||
import {createQuery} from '../apiRequests';
|
||||
|
||||
export interface SiteResponseType {
|
||||
site: SiteData;
|
||||
}
|
||||
|
||||
const dataType = 'SiteResponseType';
|
||||
|
||||
export const useBrowseSite = createQuery<SiteResponseType>({
|
||||
dataType,
|
||||
path: '/site/'
|
||||
});
|
@ -1,19 +1,9 @@
|
||||
import {Config, Setting, SettingValue, SiteData, Tier, User} from '../types/api';
|
||||
|
||||
export interface IGhostPaths {
|
||||
adminRoot: string;
|
||||
assetRoot: string;
|
||||
apiRoot: string;
|
||||
}
|
||||
|
||||
export function getSettingValue(settings: Setting[] | null | undefined, key: string): SettingValue {
|
||||
if (!settings) {
|
||||
return '';
|
||||
}
|
||||
const setting = settings.find(d => d.key === key);
|
||||
return setting?.value || null;
|
||||
}
|
||||
|
||||
export function getGhostPaths(): IGhostPaths {
|
||||
let path = window.location.pathname;
|
||||
let subdir = path.substr(0, path.search('/ghost/'));
|
||||
@ -59,27 +49,6 @@ export function generateAvatarColor(name: string) {
|
||||
return 'hsl(' + h + ', ' + s + '%, ' + l + '%)';
|
||||
}
|
||||
|
||||
export function humanizeSettingKey(key: string) {
|
||||
const allCaps = ['API', 'CTA', 'RSS'];
|
||||
|
||||
return key
|
||||
.replace(/^[a-z]/, char => char.toUpperCase())
|
||||
.replace(/_/g, ' ')
|
||||
.replace(new RegExp(`\\b(${allCaps.join('|')})\\b`, 'ig'), match => match.toUpperCase());
|
||||
}
|
||||
|
||||
export function getSettingValues<ValueType = SettingValue>(settings: Setting[] | null, keys: string[]): Array<ValueType | undefined> {
|
||||
return keys.map(key => settings?.find(setting => setting.key === key)?.value) as ValueType[];
|
||||
}
|
||||
|
||||
export function isOwnerUser(user: User) {
|
||||
return user.roles.some(role => role.name === 'Owner');
|
||||
}
|
||||
|
||||
export function isAdminUser(user: User) {
|
||||
return user.roles.some(role => role.name === 'Administrator');
|
||||
}
|
||||
|
||||
export function downloadFile(url: string) {
|
||||
let iframe = document.getElementById('iframeDownload');
|
||||
|
||||
@ -93,57 +62,6 @@ export function downloadFile(url: string) {
|
||||
iframe.setAttribute('src', url);
|
||||
}
|
||||
|
||||
export function getHomepageUrl(siteData: SiteData): string {
|
||||
const url = new URL(siteData.url);
|
||||
const subdir = url.pathname.endsWith('/') ? url.pathname : `${url.pathname}/`;
|
||||
|
||||
return `${url.origin}${subdir}`;
|
||||
}
|
||||
|
||||
export function getEmailDomain(siteData: SiteData): string {
|
||||
const domain = new URL(siteData.url).hostname || '';
|
||||
if (domain.startsWith('www.')) {
|
||||
return domain.replace(/^(www)\.(?=[^/]*\..{2,5})/, '');
|
||||
}
|
||||
return domain;
|
||||
}
|
||||
|
||||
export function fullEmailAddress(value: 'noreply' | string, siteData: SiteData) {
|
||||
const emailDomain = getEmailDomain(siteData);
|
||||
return value === 'noreply' ? `noreply@${emailDomain}` : value;
|
||||
}
|
||||
|
||||
export function checkStripeEnabled(settings: Setting[], config: Config) {
|
||||
const hasSetting = (key: string) => settings.some(setting => setting.key === key && setting.value);
|
||||
|
||||
const hasDirectKeys = hasSetting('stripe_secret_key') && hasSetting('stripe_publishable_key');
|
||||
const hasConnectKeys = hasSetting('stripe_connect_secret_key') && hasSetting('stripe_connect_publishable_key');
|
||||
|
||||
if (config.stripeDirect) {
|
||||
return hasDirectKeys;
|
||||
}
|
||||
|
||||
return hasConnectKeys || hasDirectKeys;
|
||||
}
|
||||
|
||||
export function getPaidActiveTiers(tiers: Tier[]) {
|
||||
return tiers.filter((tier) => {
|
||||
return tier.type === 'paid' && tier.active;
|
||||
});
|
||||
}
|
||||
|
||||
export function getActiveTiers(tiers: Tier[]) {
|
||||
return tiers.filter((tier) => {
|
||||
return tier.active;
|
||||
});
|
||||
}
|
||||
|
||||
export function getArchivedTiers(tiers: Tier[]) {
|
||||
return tiers.filter((tier) => {
|
||||
return !tier.active;
|
||||
});
|
||||
}
|
||||
|
||||
export function numberWithCommas(x: number) {
|
||||
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user