diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index 9ff1ea45..97fb1131 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -916,6 +916,7 @@ ui: interface: Interface smtp: SMTP branding: Branding + legal: Legal dashboard: title: Dashboard welcome: Welcome to Answer Admin! @@ -1108,10 +1109,20 @@ ui: smtp_authentication: label: SMTP Authentication msg: SMTP authentication cannot be empty. - 'yes': 'Yes' - 'no': 'No' + "yes": "Yes" + "no": "No" branding: page_title: Branding + legal: + page_title: Legal + terms_of_service: + label: Terms of Service + msg: Terms of service cannot be empty. + text: "You can add terms of service content here. If you already have a document hosted elsewhere, provide the full URL here." + privacy_policy: + label: Privacy Policy + msg: Privacy policy cannot be empty. + text: "You can add privacy policy content here. If you already have a document hosted elsewhere, provide the full URL here." form: empty: cannot be empty invalid: is invalid diff --git a/ui/src/common/constants.ts b/ui/src/common/constants.ts index 3ed5e37c..7bd2fc19 100644 --- a/ui/src/common/constants.ts +++ b/ui/src/common/constants.ts @@ -59,6 +59,8 @@ export const ADMIN_NAV_MENUS = [ { name: 'interface' }, { name: 'branding' }, { name: 'smtp' }, + { name: 'legal' }, + { name: 'write' }, ], }, ]; diff --git a/ui/src/common/interface.ts b/ui/src/common/interface.ts index 9ea391c7..23602e3c 100644 --- a/ui/src/common/interface.ts +++ b/ui/src/common/interface.ts @@ -289,6 +289,16 @@ export interface SiteSettings { interface: AdminSettingsInterface; } +export interface AdminSettingsLegal { + terms_of_service: string; + privacy_policy: string; +} + +export interface AdminSettingsWrite { + recommend_tags: string; + required_tags: string; +} + /** * @description interface for Activity */ diff --git a/ui/src/i18n/locales/en_US.yaml b/ui/src/i18n/locales/en_US.yaml index 293e066b..c0ccbbdd 100644 --- a/ui/src/i18n/locales/en_US.yaml +++ b/ui/src/i18n/locales/en_US.yaml @@ -916,6 +916,8 @@ ui: interface: Interface smtp: SMTP branding: Branding + legal: Legal + write: Write dashboard: title: Dashboard welcome: Welcome to Answer Admin! @@ -1108,22 +1110,29 @@ ui: smtp_authentication: label: SMTP Authentication msg: SMTP authentication cannot be empty. - 'yes': 'Yes' - 'no': 'No' + "yes": "Yes" + "no": "No" branding: page_title: Branding - logo: - label: Logo - text: The logo image at the top left of your site. Use a wide rectangular image with a height of 56 and an aspect ratio greater than 3:1. If left blank, the site title text will be shown. - mobile_logo: - label: Mobile Logo (optional) - text: The logo used on mobile version of your site. Use a wide rectangular image with a height of 56. If left blank, the image from the “logo” setting will be used. - square_icon: - label: Square Icon - text: Image used as the base for metadata icons. Should ideally be larger than 512x512. - favicon: - label: Favicon (optional) - text: A favicon for your site. To work correctly over a CDN it must be a png. Will be resized to 32x32. If left blank, “square icon” will be used. + legal: + page_title: Legal + terms_of_service: + label: Terms of Service + msg: Terms of service cannot be empty. + text: "You can add terms of service content here. If you already have a document hosted elsewhere, provide the full URL here." + privacy_policy: + label: Privacy Policy + msg: Privacy policy cannot be empty. + text: "You can add privacy policy content here. If you already have a document hosted elsewhere, provide the full URL here." + write: + page_title: Write + recommend_tags: + label: Recommend Tags + msg: Recommend tags cannot be empty. + text: "Please input tag slug above, one tag per line." + required_tags: + label: Required Tags + text: "Every new question must have at least one recommend tag" form: empty: cannot be empty invalid: is invalid diff --git a/ui/src/pages/Admin/Legal/index.tsx b/ui/src/pages/Admin/Legal/index.tsx new file mode 100644 index 00000000..1f494bbc --- /dev/null +++ b/ui/src/pages/Admin/Legal/index.tsx @@ -0,0 +1,108 @@ +import React, { FC, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { SchemaForm, JSONSchema, initFormData, UISchema } from '@/components'; +import type * as Type from '@/common/interface'; +// import { useToast } from '@/hooks'; +// import { siteInfoStore } from '@/stores'; +import { useGeneralSetting } from '@/services'; + +import '../index.scss'; + +const Legal: FC = () => { + const { t } = useTranslation('translation', { + keyPrefix: 'admin.legal', + }); + // const Toast = useToast(); + // const updateSiteInfo = siteInfoStore((state) => state.update); + + const { data: setting } = useGeneralSetting(); + const schema: JSONSchema = { + title: t('page_title'), + required: ['terms_of_service', 'privacy_policy'], + properties: { + terms_of_service: { + type: 'string', + title: t('terms_of_service.label'), + description: t('terms_of_service.text'), + }, + privacy_policy: { + type: 'string', + title: t('privacy_policy.label'), + description: t('privacy_policy.text'), + }, + }, + }; + const uiSchema: UISchema = { + terms_of_service: { + 'ui:widget': 'textarea', + 'ui:options': { + rows: 10, + }, + }, + privacy_policy: { + 'ui:widget': 'textarea', + 'ui:options': { + rows: 10, + }, + }, + }; + const [formData, setFormData] = useState(initFormData(schema)); + + const onSubmit = (evt) => { + evt.preventDefault(); + evt.stopPropagation(); + + const reqParams: Type.AdminSettingsLegal = { + terms_of_service: formData.terms_of_service.value, + privacy_policy: formData.privacy_policy.value, + }; + + console.log(reqParams); + // updateGeneralSetting(reqParams) + // .then(() => { + // Toast.onShow({ + // msg: t('update', { keyPrefix: 'toast' }), + // variant: 'success', + // }); + // updateSiteInfo(reqParams); + // }) + // .catch((err) => { + // if (err.isError && err.key) { + // formData[err.key].isInvalid = true; + // formData[err.key].errorMsg = err.value; + // } + // setFormData({ ...formData }); + // }); + }; + + useEffect(() => { + if (!setting) { + return; + } + const formMeta = {}; + Object.keys(setting).forEach((k) => { + formMeta[k] = { ...formData[k], value: setting[k] }; + }); + setFormData({ ...formData, ...formMeta }); + }, [setting]); + + const handleOnChange = (data) => { + setFormData(data); + }; + + return ( + <> +

{t('page_title')}

+ + + ); +}; + +export default Legal; diff --git a/ui/src/pages/Admin/Write/index.tsx b/ui/src/pages/Admin/Write/index.tsx new file mode 100644 index 00000000..436d1f61 --- /dev/null +++ b/ui/src/pages/Admin/Write/index.tsx @@ -0,0 +1,105 @@ +import React, { FC, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { SchemaForm, JSONSchema, initFormData, UISchema } from '@/components'; +import type * as Type from '@/common/interface'; +// import { useToast } from '@/hooks'; +// import { siteInfoStore } from '@/stores'; +import { useGeneralSetting } from '@/services'; + +import '../index.scss'; + +const Legal: FC = () => { + const { t } = useTranslation('translation', { + keyPrefix: 'admin.write', + }); + // const Toast = useToast(); + // const updateSiteInfo = siteInfoStore((state) => state.update); + + const { data: setting } = useGeneralSetting(); + const schema: JSONSchema = { + title: t('page_title'), + required: ['terms_of_service', 'privacy_policy'], + properties: { + recommend_tags: { + type: 'string', + title: t('recommend_tags.label'), + description: t('recommend_tags.text'), + }, + required_tags: { + type: 'boolean', + title: t('required_tags.label'), + description: t('required_tags.text'), + }, + }, + }; + const uiSchema: UISchema = { + recommend_tags: { + 'ui:widget': 'textarea', + 'ui:options': { + rows: 5, + }, + }, + required_tags: { + 'ui:widget': 'switch', + }, + }; + const [formData, setFormData] = useState(initFormData(schema)); + + const onSubmit = (evt) => { + evt.preventDefault(); + evt.stopPropagation(); + + const reqParams: Type.AdminSettingsWrite = { + recommend_tags: formData.recommend_tags.value, + required_tags: formData.required_tags.value, + }; + + console.log(reqParams); + // updateGeneralSetting(reqParams) + // .then(() => { + // Toast.onShow({ + // msg: t('update', { keyPrefix: 'toast' }), + // variant: 'success', + // }); + // updateSiteInfo(reqParams); + // }) + // .catch((err) => { + // if (err.isError && err.key) { + // formData[err.key].isInvalid = true; + // formData[err.key].errorMsg = err.value; + // } + // setFormData({ ...formData }); + // }); + }; + + useEffect(() => { + if (!setting) { + return; + } + const formMeta = {}; + Object.keys(setting).forEach((k) => { + formMeta[k] = { ...formData[k], value: setting[k] }; + }); + setFormData({ ...formData, ...formMeta }); + }, [setting]); + + const handleOnChange = (data) => { + setFormData(data); + }; + + return ( + <> +

{t('page_title')}

+ + + ); +}; + +export default Legal; diff --git a/ui/src/pages/Admin/index.tsx b/ui/src/pages/Admin/index.tsx index 8b22ec05..f8abfc74 100644 --- a/ui/src/pages/Admin/index.tsx +++ b/ui/src/pages/Admin/index.tsx @@ -8,7 +8,14 @@ import { ADMIN_NAV_MENUS } from '@/common/constants'; import './index.scss'; -const formPaths = ['general', 'smtp', 'interface', 'branding']; +const formPaths = [ + 'general', + 'smtp', + 'interface', + 'branding', + 'legal', + 'write', +]; const Dashboard: FC = () => { const { t } = useTranslation('translation', { keyPrefix: 'page_title' }); diff --git a/ui/src/router/routes.ts b/ui/src/router/routes.ts index 110c1d4d..32e8644c 100644 --- a/ui/src/router/routes.ts +++ b/ui/src/router/routes.ts @@ -255,6 +255,14 @@ const routes: RouteNode[] = [ path: 'branding', page: 'pages/Admin/Branding', }, + { + path: 'legal', + page: 'pages/Admin/Legal', + }, + { + path: 'write', + page: 'pages/Admin/Write', + }, ], }, {