diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index f09bdb40..7fff9ebe 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -934,7 +934,7 @@ ui: smtp: SMTP branding: Branding legal: Legal - labels: Labels + write: Write dashboard: title: Dashboard welcome: Welcome to Answer Admin! @@ -1135,32 +1135,21 @@ ui: 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." - labels: - title: Labels - new_label: New Label - name: Name - color: color - description: Description - action: Action - form: - title: Create New Label - back: Back - display_name: - label: Display Name - url_slug: - label: URL Slug - text: Must use the character set “a-z”, “0-9”, “+ # - .” - description: - label: Description (optional) - color: - label: Color - + write: + page_title: Write + recommend_tags: + label: Recommend Tags + text: "Please input tag slug above, one tag per line." + required_tag: + label: Required Tag + text: "Every new question must have at least one recommend tag" + reserved_tags: + label: Reserved Tags + text: "Reserved tags can only be added to a post by moderator." form: empty: cannot be empty invalid: is invalid diff --git a/ui/src/common/constants.ts b/ui/src/common/constants.ts index def1e7c9..7bd2fc19 100644 --- a/ui/src/common/constants.ts +++ b/ui/src/common/constants.ts @@ -43,7 +43,7 @@ export const ADMIN_NAV_MENUS = [ }, { name: 'contents', - child: [{ name: 'questions' }, { name: 'answers' }, { name: 'labels' }], + child: [{ name: 'questions' }, { name: 'answers' }], }, { name: 'users', diff --git a/ui/src/common/interface.ts b/ui/src/common/interface.ts index 23602e3c..37f56a32 100644 --- a/ui/src/common/interface.ts +++ b/ui/src/common/interface.ts @@ -126,7 +126,8 @@ export interface UserInfoRes extends UserInfoBase { [prop: string]: any; } -export interface AvatarUploadReq { +export type UploadType = 'post' | 'avatar' | 'branding'; +export interface UploadReq { file: FormData; } @@ -296,7 +297,7 @@ export interface AdminSettingsLegal { export interface AdminSettingsWrite { recommend_tags: string; - required_tags: string; + required_tag: string; } /** @@ -359,3 +360,10 @@ export interface AdminDashboard { }; }; } + +export interface BrandReqParams { + logo: string; + square_icon: string; + mobile_logo?: string; + favicon?: string; +} diff --git a/ui/src/components/BrandUpload/index.tsx b/ui/src/components/BrandUpload/index.tsx index d1be627a..99f8dcf6 100644 --- a/ui/src/components/BrandUpload/index.tsx +++ b/ui/src/components/BrandUpload/index.tsx @@ -2,22 +2,17 @@ import { FC } from 'react'; import { ButtonGroup, Button } from 'react-bootstrap'; import { Icon, UploadImg } from '@/components'; -import { uploadAvatar } from '@/services'; +import { UploadType } from '@/common/interface'; interface Props { - type: 'logo' | 'avatar' | 'mobile_logo' | 'square_icon' | 'favicon'; + type: UploadType; value: string; onChange: (value: string) => void; } -const Index: FC = ({ type = 'logo', value, onChange }) => { - const onUpload = (file: any) => { - return new Promise((resolve) => { - uploadAvatar(file).then((res) => { - onChange(res); - resolve(true); - }); - }); +const Index: FC = ({ type = 'post', value, onChange }) => { + const onUpload = (imgPath: string) => { + onChange(imgPath); }; const onRemove = () => { @@ -29,7 +24,7 @@ const Index: FC = ({ type = 'logo', value, onChange }) => { - + diff --git a/ui/src/components/Editor/ToolBars/image.tsx b/ui/src/components/Editor/ToolBars/image.tsx index bfa4c530..2eb1c5e8 100644 --- a/ui/src/components/Editor/ToolBars/image.tsx +++ b/ui/src/components/Editor/ToolBars/image.tsx @@ -61,7 +61,7 @@ const Image: FC = ({ editor }) => { files: FileList, ): Promise<{ url: string; name: string }[]> => { const promises = Array.from(files).map(async (file) => { - const url = await uploadImage(file); + const url = await uploadImage({ file, type: 'post' }); return { name: file.name, @@ -209,7 +209,7 @@ const Image: FC = ({ editor }) => { return; } - uploadImage(e.target.files[0]).then((url) => { + uploadImage({ file: e.target.files[0], type: 'post' }).then((url) => { setLink({ ...link, value: url }); }); }; diff --git a/ui/src/components/FollowingTags/index.tsx b/ui/src/components/FollowingTags/index.tsx index 9e3156b0..a9fa3a0c 100644 --- a/ui/src/components/FollowingTags/index.tsx +++ b/ui/src/components/FollowingTags/index.tsx @@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'; import { NavLink } from 'react-router-dom'; import { TagSelector, Tag } from '@/components'; -import { tryLoggedAndActicevated } from '@/utils/guard'; +import { tryLoggedAndActivated } from '@/utils/guard'; import { useFollowingTags, followTags } from '@/services'; const Index: FC = () => { @@ -32,7 +32,7 @@ const Index: FC = () => { }); }; - if (!tryLoggedAndActicevated().ok) { + if (!tryLoggedAndActivated().ok) { return null; } @@ -73,11 +73,7 @@ const Index: FC = () => { <> {followingTags.map((item) => { const slugName = item?.slug_name; - return ( - - {slugName} - - ); + return ; })} ) : ( diff --git a/ui/src/components/QuestionList/index.tsx b/ui/src/components/QuestionList/index.tsx index a45cd401..44ae2fa4 100644 --- a/ui/src/components/QuestionList/index.tsx +++ b/ui/src/components/QuestionList/index.tsx @@ -158,14 +158,7 @@ const QuestionList: FC = ({ source }) => { {Array.isArray(li.tags) ? li.tags.map((tag) => { return ( - - {tag.slug_name} - + ); }) : null} diff --git a/ui/src/components/SchemaForm/index.tsx b/ui/src/components/SchemaForm/index.tsx index b3d30e84..6967c5a6 100644 --- a/ui/src/components/SchemaForm/index.tsx +++ b/ui/src/components/SchemaForm/index.tsx @@ -54,7 +54,7 @@ export interface UISchema { invalid?: string; validator?: (value) => boolean; textRender?: () => React.ReactElement; - imageType?: 'avatar' | 'logo' | 'mobile_logo' | 'square_icon' | 'favicon'; + imageType?: Type.UploadType; }; }; } @@ -225,6 +225,9 @@ const SchemaForm: FC = ({ ); })} + + {formData[key]?.errorMsg} + {description} ); @@ -243,6 +246,9 @@ const SchemaForm: FC = ({ feedbackType="invalid" isInvalid={formData[key].isInvalid} /> + + {formData[key]?.errorMsg} + {description} ); @@ -255,6 +261,14 @@ const SchemaForm: FC = ({ value={formData[key]?.value} onChange={handleInputChange} /> + + + {formData[key]?.errorMsg} + {description} ); @@ -269,6 +283,14 @@ const SchemaForm: FC = ({ value={formData[key]?.value} onChange={(value) => handleUploadChange(key, value)} /> + + + {formData[key]?.errorMsg} + {description} ); diff --git a/ui/src/components/Tag/index.tsx b/ui/src/components/Tag/index.tsx index ddab7d64..729c75b3 100644 --- a/ui/src/components/Tag/index.tsx +++ b/ui/src/components/Tag/index.tsx @@ -2,17 +2,20 @@ import React, { memo, FC } from 'react'; import classNames from 'classnames'; +import { Tag } from '@/common/interface'; + interface IProps { + data: Tag; + href?: string; className?: string; - children?: React.ReactNode; - href: string; } -const Index: FC = ({ className = '', children, href }) => { - href = href.toLowerCase(); +const Index: FC = ({ className = '', href, data }) => { + href = + href || `/tags/${data.main_tag_slug_name || data.slug_name}`.toLowerCase(); return ( - {children} + {data.slug_name} ); }; diff --git a/ui/src/components/UploadImg/index.tsx b/ui/src/components/UploadImg/index.tsx index 90cca3e4..3ac56b2a 100644 --- a/ui/src/components/UploadImg/index.tsx +++ b/ui/src/components/UploadImg/index.tsx @@ -1,14 +1,22 @@ import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { uploadImage } from '@/services'; +import * as Type from '@/common/interface'; + interface IProps { - type: string; + type: Type.UploadType; className?: string; children?: React.ReactNode; - upload: (data: FormData) => Promise; + uploadCallback: (img: string) => void; } -const Index: React.FC = ({ type, upload, children, className }) => { +const Index: React.FC = ({ + type, + uploadCallback, + children, + className, +}) => { const { t } = useTranslation(); const [status, setStatus] = useState(false); @@ -26,13 +34,13 @@ const Index: React.FC = ({ type, upload, children, className }) => { // return; // } setStatus(true); - const data = new FormData(); - - data.append('file', e.target.files[0]); - // do - upload(data).finally(() => { - setStatus(false); - }); + uploadImage({ file: e.target.files[0], type }) + .then((res) => { + uploadCallback(res); + }) + .finally(() => { + setStatus(false); + }); } }; @@ -44,7 +52,6 @@ const Index: React.FC = ({ type, upload, children, className }) => { className="d-none" accept="image/jpeg,image/jpg,image/png,image/webp" onChange={onChange} - id={type} /> ); diff --git a/ui/src/index.scss b/ui/src/index.scss index ea12ff75..69fcb80c 100644 --- a/ui/src/index.scss +++ b/ui/src/index.scss @@ -74,6 +74,15 @@ a { } } +.badge-tag-required { + background: rgba($gray-400, 0.5); + color: $gray-700; + &:hover { + color: $gray-700; + background: rgba($gray-400, 1); + } +} + .divide-line { border-bottom: 1px solid rgba(33, 37, 41, 0.25); } diff --git a/ui/src/pages/Admin/Branding/index.tsx b/ui/src/pages/Admin/Branding/index.tsx index 490122bb..bb3e857f 100644 --- a/ui/src/pages/Admin/Branding/index.tsx +++ b/ui/src/pages/Admin/Branding/index.tsx @@ -1,17 +1,23 @@ -import { FC, memo, useState } from 'react'; +import { FC, memo, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { JSONSchema, SchemaForm, UISchema } from '@/components'; import { FormDataType } from '@/common/interface'; +import { brandSetting, getBrandSetting } from '@/services'; +import { interfaceStore } from '@/stores'; +import { useToast } from '@/hooks'; +const uploadType = 'branding'; const Index: FC = () => { const { t } = useTranslation('translation', { keyPrefix: 'admin.branding', }); + const { interface: storeInterface, updateLogo } = interfaceStore(); + const Toast = useToast(); const [formData, setFormData] = useState({ logo: { - value: '', + value: storeInterface.logo, isInvalid: false, errorMsg: '', }, @@ -32,24 +38,6 @@ const Index: FC = () => { }, }); - // const onChange = (fieldName, fieldValue) => { - // if (!formData[fieldName]) { - // return; - // } - // const fieldData: FormDataType = { - // [fieldName]: { - // value: fieldValue, - // isInvalid: false, - // errorMsg: '', - // }, - // }; - // setFormData({ ...formData, ...fieldData }); - // }; - - // const [img, setImg] = useState( - // 'https://image-static.segmentfault.com/405/057/4050570037-636c7b0609a49', - // ); - const schema: JSONSchema = { title: t('page_title'), properties: { @@ -80,25 +68,25 @@ const Index: FC = () => { logo: { 'ui:widget': 'upload', 'ui:options': { - imageType: 'logo', + imageType: uploadType, }, }, mobile_logo: { 'ui:widget': 'upload', 'ui:options': { - imageType: 'mobile_logo', + imageType: uploadType, }, }, square_icon: { 'ui:widget': 'upload', 'ui:options': { - imageType: 'square_icon', + imageType: uploadType, }, }, favicon: { 'ui:widget': 'upload', 'ui:options': { - imageType: 'favicon', + imageType: uploadType, }, }, }; @@ -108,9 +96,46 @@ const Index: FC = () => { }; const onSubmit = () => { - // undo + const params = { + logo: formData.logo.value, + mobile_logo: formData.mobile_logo.value, + square_icon: formData.square_icon.value, + favicon: formData.favicon.value, + }; + brandSetting(params) + .then((res) => { + console.log(res); + updateLogo(formData.logo.value); + Toast.onShow({ + msg: t('update', { keyPrefix: 'toast' }), + variant: 'success', + }); + }) + .catch((err) => { + console.log(err); + if (err.key) { + formData[err.key].isInvalid = true; + formData[err.key].errorMsg = err.value; + setFormData({ ...formData }); + } + }); }; + const getBrandData = async () => { + const res = await getBrandSetting(); + if (res) { + formData.logo.value = res.logo; + formData.mobile_logo.value = res.mobile_logo; + formData.square_icon.value = res.square_icon; + formData.favicon.value = res.favicon; + setFormData({ ...formData }); + } + }; + + useEffect(() => { + getBrandData(); + }, []); + return (

{t('page_title')}

diff --git a/ui/src/pages/Admin/Labels/Form/index.tsx b/ui/src/pages/Admin/Labels/Form/index.tsx deleted file mode 100644 index 8ea510d9..00000000 --- a/ui/src/pages/Admin/Labels/Form/index.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import React, { FC, useEffect, useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { Button } from 'react-bootstrap'; - -import { SchemaForm, JSONSchema, initFormData, UISchema } from '@/components'; -import type * as Type from '@/common/interface'; -import { useToast } from '@/hooks'; -import { siteInfoStore } from '@/stores'; -import { useGeneralSetting, updateGeneralSetting } from '@/services'; - -interface IProps { - onClose: () => void; -} -const LabelForm: FC = ({ onClose }) => { - const { t } = useTranslation('translation', { - keyPrefix: 'admin.labels.form', - }); - const Toast = useToast(); - const updateSiteInfo = siteInfoStore((state) => state.update); - - const { data: setting } = useGeneralSetting(); - const schema: JSONSchema = { - title: t('title'), - required: ['name', 'site_url', 'contact_email'], - properties: { - display_name: { - type: 'string', - title: t('display_name.label'), - }, - url_slug: { - type: 'string', - title: t('url_slug.label'), - description: t('url_slug.text'), - }, - description: { - type: 'string', - title: t('description.label'), - description: t('description.text'), - }, - color: { - type: 'string', - title: t('color.label'), - }, - }, - }; - const uiSchema: UISchema = { - color: { - 'ui:options': { - type: 'color', - }, - }, - }; - const [formData, setFormData] = useState(initFormData(schema)); - - const onSubmit = (evt) => { - evt.preventDefault(); - evt.stopPropagation(); - - const reqParams: Type.AdminSettingsGeneral = { - name: formData.name.value, - description: formData.description.value, - short_description: formData.short_description.value, - site_url: formData.site_url.value, - contact_email: formData.contact_email.value, - }; - - updateGeneralSetting(reqParams) - .then(() => { - Toast.onShow({ - msg: t('update', { keyPrefix: 'toast' }), - variant: 'success', - }); - onClose(); - 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('title')}

- - - ); -}; - -export default LabelForm; diff --git a/ui/src/pages/Admin/Labels/index.tsx b/ui/src/pages/Admin/Labels/index.tsx deleted file mode 100644 index c588ae84..00000000 --- a/ui/src/pages/Admin/Labels/index.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import { FC, useState } from 'react'; -import { Button, Table } from 'react-bootstrap'; -import { useSearchParams } from 'react-router-dom'; -import { useTranslation } from 'react-i18next'; - -import { Pagination, BaseUserCard, Empty } from '@/components'; -import * as Type from '@/common/interface'; -import { useChangeModal } from '@/hooks'; -import { useQueryUsers } from '@/services'; - -import CreateForm from './Form'; - -import '../index.scss'; - -const UserFilterKeys: Type.UserFilterBy[] = [ - 'all', - 'inactive', - 'suspended', - 'deleted', -]; - -const PAGE_SIZE = 10; -const Users: FC = () => { - const { t } = useTranslation('translation', { keyPrefix: 'admin.labels' }); - - const [urlSearchParams] = useSearchParams(); - const curFilter = urlSearchParams.get('filter') || UserFilterKeys[0]; - const curPage = Number(urlSearchParams.get('page') || '1'); - const curQuery = urlSearchParams.get('query') || ''; - - const [isCreate, setCreateState] = useState(true); - const { - data, - isLoading, - mutate: refreshUsers, - } = useQueryUsers({ - page: curPage, - page_size: PAGE_SIZE, - query: curQuery, - ...(curFilter === 'all' ? {} : { status: curFilter }), - }); - const changeModal = useChangeModal({ - callback: refreshUsers, - }); - - const handleClick = ({ user_id, status }) => { - changeModal.onShow({ - id: user_id, - type: status, - }); - }; - - if (isCreate) { - return setCreateState(false)} />; - } - - return ( - <> -

{t('title')}

-
- -
- - - - - - - - {curFilter !== 'deleted' ? ( - - ) : null} - - - - {data?.list.map((user) => { - return ( - - - - - - {curFilter !== 'deleted' ? ( - - ) : null} - - ); - })} - -
{t('name')}{t('color')}{t('description')}{t('action')}
- - {user.rank}{user.e_mail} - {user.status !== 'deleted' && ( - - )} -
- {Number(data?.count) <= 0 && !isLoading && } -
- -
- - ); -}; - -export default Users; diff --git a/ui/src/pages/Admin/Write/index.tsx b/ui/src/pages/Admin/Write/index.tsx index 436d1f61..3ee34469 100644 --- a/ui/src/pages/Admin/Write/index.tsx +++ b/ui/src/pages/Admin/Write/index.tsx @@ -26,10 +26,15 @@ const Legal: FC = () => { title: t('recommend_tags.label'), description: t('recommend_tags.text'), }, - required_tags: { + required_tag: { type: 'boolean', - title: t('required_tags.label'), - description: t('required_tags.text'), + title: t('required_tag.label'), + description: t('required_tag.text'), + }, + reserved_tags: { + type: 'string', + title: t('reserved_tags.label'), + description: t('reserved_tags.text'), }, }, }; @@ -40,9 +45,15 @@ const Legal: FC = () => { rows: 5, }, }, - required_tags: { + required_tag: { 'ui:widget': 'switch', }, + reserved_tags: { + 'ui:widget': 'textarea', + 'ui:options': { + rows: 5, + }, + }, }; const [formData, setFormData] = useState(initFormData(schema)); @@ -52,7 +63,7 @@ const Legal: FC = () => { const reqParams: Type.AdminSettingsWrite = { recommend_tags: formData.recommend_tags.value, - required_tags: formData.required_tags.value, + required_tag: formData.required_tag.value, }; console.log(reqParams); diff --git a/ui/src/pages/Questions/Detail/components/Question/index.tsx b/ui/src/pages/Questions/Detail/components/Question/index.tsx index 5795ca42..685c0486 100644 --- a/ui/src/pages/Questions/Detail/components/Question/index.tsx +++ b/ui/src/pages/Questions/Detail/components/Question/index.tsx @@ -93,14 +93,7 @@ const Index: FC = ({ data, initPage, hasAnswer }) => {
{data?.tags?.map((item: any) => { - return ( - - {item.slug_name} - - ); + return ; })}
= ({ data }) => { )} {data.object?.tags?.map((item) => { - return ( - - {item.slug_name} - - ); + return ; })} ); diff --git a/ui/src/pages/Tags/Info/index.tsx b/ui/src/pages/Tags/Info/index.tsx index 3b792adb..308265c3 100644 --- a/ui/src/pages/Tags/Info/index.tsx +++ b/ui/src/pages/Tags/Info/index.tsx @@ -152,9 +152,15 @@ const TagIntroduction = () => { <>
{t('synonyms.text')}{' '} - - {tagName} - +
{ - {item.slug_name} - + data={item} + /> ); }) ) : ( diff --git a/ui/src/pages/Tags/index.tsx b/ui/src/pages/Tags/index.tsx index a01e3e56..bbd55a99 100644 --- a/ui/src/pages/Tags/index.tsx +++ b/ui/src/pages/Tags/index.tsx @@ -77,9 +77,8 @@ const Tags = () => { className="mb-4"> - - {tag.slug_name} - + +

{tag.original_text}

diff --git a/ui/src/pages/Users/Personal/components/Answers/index.tsx b/ui/src/pages/Users/Personal/components/Answers/index.tsx index a7d8c48a..4d7b6f1d 100644 --- a/ui/src/pages/Users/Personal/components/Answers/index.tsx +++ b/ui/src/pages/Users/Personal/components/Answers/index.tsx @@ -46,14 +46,7 @@ const Index: FC = ({ visible, data }) => {
{item.question_info?.tags?.map((tag) => { - return ( - - {tag.slug_name} - - ); + return ; })}
diff --git a/ui/src/pages/Users/Personal/components/DefaultList/index.tsx b/ui/src/pages/Users/Personal/components/DefaultList/index.tsx index e249067f..faa4ddfe 100644 --- a/ui/src/pages/Users/Personal/components/DefaultList/index.tsx +++ b/ui/src/pages/Users/Personal/components/DefaultList/index.tsx @@ -73,14 +73,7 @@ const Index: FC = ({ visible, tabName, data }) => {
{item.tags?.map((tag) => { - return ( - - {tag.slug_name} - - ); + return ; })}
diff --git a/ui/src/pages/Users/Settings/Profile/index.tsx b/ui/src/pages/Users/Settings/Profile/index.tsx index f62a747d..0b874997 100644 --- a/ui/src/pages/Users/Settings/Profile/index.tsx +++ b/ui/src/pages/Users/Settings/Profile/index.tsx @@ -9,7 +9,7 @@ import type { FormDataType } from '@/common/interface'; import { UploadImg, Avatar } from '@/components'; import { loggedUserInfoStore } from '@/stores'; import { useToast } from '@/hooks'; -import { modifyUserInfo, uploadAvatar, getLoggedUserInfo } from '@/services'; +import { modifyUserInfo, getLoggedUserInfo } from '@/services'; const Index: React.FC = () => { const { t } = useTranslation('translation', { @@ -60,21 +60,16 @@ const Index: React.FC = () => { setFormData({ ...formData, ...params }); }; - const avatarUpload = (file: any) => { - return new Promise((resolve) => { - uploadAvatar(file).then((res) => { - setFormData({ - ...formData, - avatar: { - ...formData.avatar, - type: 'custom', - custom: res, - isInvalid: false, - errorMsg: '', - }, - }); - resolve(true); - }); + const avatarUpload = (path: string) => { + setFormData({ + ...formData, + avatar: { + ...formData.avatar, + type: 'custom', + custom: path, + isInvalid: false, + errorMsg: '', + }, }); }; @@ -366,7 +361,7 @@ const Index: React.FC = () => {
diff --git a/ui/src/router/routes.ts b/ui/src/router/routes.ts index 4bb3fd0f..32e8644c 100644 --- a/ui/src/router/routes.ts +++ b/ui/src/router/routes.ts @@ -263,10 +263,6 @@ const routes: RouteNode[] = [ path: 'write', page: 'pages/Admin/Write', }, - { - path: 'labels', - page: 'pages/Admin/Labels', - }, ], }, { diff --git a/ui/src/services/admin/settings.ts b/ui/src/services/admin/settings.ts index 6bae3576..3e627284 100644 --- a/ui/src/services/admin/settings.ts +++ b/ui/src/services/admin/settings.ts @@ -88,3 +88,11 @@ export const getAdminLanguageOptions = () => { const apiUrl = `/answer/admin/api/language/options`; return request.get(apiUrl); }; + +export const getBrandSetting = () => { + return request.get('/answer/admin/api/siteinfo/branding'); +}; + +export const brandSetting = (params: Type.BrandReqParams) => { + return request.put('/answer/admin/api/siteinfo/branding', params); +}; diff --git a/ui/src/services/client/notification.ts b/ui/src/services/client/notification.ts index 6d34022b..c4e535c8 100644 --- a/ui/src/services/client/notification.ts +++ b/ui/src/services/client/notification.ts @@ -3,7 +3,7 @@ import qs from 'qs'; import request from '@/utils/request'; import type * as Type from '@/common/interface'; -import { tryLoggedAndActicevated } from '@/utils/guard'; +import { tryLoggedAndActivated } from '@/utils/guard'; export const useQueryNotifications = (params) => { const apiUrl = `/answer/api/v1/notification/page?${qs.stringify(params, { @@ -33,7 +33,7 @@ export const useQueryNotificationStatus = () => { const apiUrl = '/answer/api/v1/notification/status'; return useSWR<{ inbox: number; achievement: number }>( - tryLoggedAndActicevated().ok ? apiUrl : null, + tryLoggedAndActivated().ok ? apiUrl : null, request.instance.get, { refreshInterval: 3000, diff --git a/ui/src/services/client/tag.ts b/ui/src/services/client/tag.ts index b1ec3a9a..10e3c76a 100644 --- a/ui/src/services/client/tag.ts +++ b/ui/src/services/client/tag.ts @@ -2,7 +2,7 @@ import useSWR from 'swr'; import request from '@/utils/request'; import type * as Type from '@/common/interface'; -import { tryLoggedAndActicevated } from '@/utils/guard'; +import { tryLoggedAndActivated } from '@/utils/guard'; export const deleteTag = (id) => { return request.delete('/answer/api/v1/tag', { @@ -24,7 +24,7 @@ export const saveSynonymsTags = (params) => { export const useFollowingTags = () => { let apiUrl = ''; - if (tryLoggedAndActicevated().ok) { + if (tryLoggedAndActivated().ok) { apiUrl = '/answer/api/v1/tags/following'; } const { data, error, mutate } = useSWR(apiUrl, request.instance.get); diff --git a/ui/src/services/common.ts b/ui/src/services/common.ts index 1ddc7f18..8a541f03 100644 --- a/ui/src/services/common.ts +++ b/ui/src/services/common.ts @@ -4,12 +4,13 @@ import useSWR from 'swr'; import request from '@/utils/request'; import type * as Type from '@/common/interface'; -export const uploadImage = (file) => { +export const uploadImage = (params: { file: File; type: Type.UploadType }) => { const form = new FormData(); - - form.append('file', file); - return request.post('/answer/api/v1/user/post/file', form); + form.append('source', String(params.type)); + form.append('file', params.file); + return request.post('/answer/api/v1/file', form); }; + export const useQueryQuestionByTitle = (title) => { return useSWR>( title ? `/answer/api/v1/question/similar?title=${title}` : '', @@ -127,10 +128,6 @@ export const modifyUserInfo = (params: Type.ModifyUserReq) => { return request.put('/answer/api/v1/user/info', params); }; -export const uploadAvatar = (params: Type.AvatarUploadReq) => { - return request.post('/answer/api/v1/user/avatar/upload', params); -}; - export const resetPassword = (params: Type.PasswordResetReq) => { return request.post('/answer/api/v1/user/password/reset', params); }; diff --git a/ui/src/stores/interface.ts b/ui/src/stores/interface.ts index e92e0f8a..a5933509 100644 --- a/ui/src/stores/interface.ts +++ b/ui/src/stores/interface.ts @@ -6,6 +6,7 @@ import { DEFAULT_LANG } from '@/common/constants'; interface InterfaceType { interface: AdminSettingsInterface; update: (params: AdminSettingsInterface) => void; + updateLogo: (logo: string) => void; } const interfaceSetting = create((set) => ({ @@ -21,6 +22,15 @@ const interfaceSetting = create((set) => ({ interface: params, }; }), + updateLogo: (logo) => + set((state) => { + return { + interface: { + ...state.interface, + logo, + }, + }; + }), })); export default interfaceSetting; diff --git a/ui/src/utils/guard.ts b/ui/src/utils/guard.ts index 4fb21496..5761af01 100644 --- a/ui/src/utils/guard.ts +++ b/ui/src/utils/guard.ts @@ -182,9 +182,10 @@ export const tryNormalLogged = (canNavigate: boolean = false) => { return false; }; -export const tryLoggedAndActicevated = () => { +export const tryLoggedAndActivated = () => { const gr: TGuardResult = { ok: true }; const us = deriveLoginState(); + if (!us.isLogged || !us.isActivated) { gr.ok = false; } diff --git a/ui/src/utils/request.ts b/ui/src/utils/request.ts index 072578b4..e553b803 100644 --- a/ui/src/utils/request.ts +++ b/ui/src/utils/request.ts @@ -48,10 +48,7 @@ class Request { return data; }, (error) => { - if (error.isAxiosError) { - return Promise.reject(false); - } - const { status, data: respData, msg: respMsg } = error.response; + const { status, data: respData, msg: respMsg } = error.response || {}; const { data, msg = '' } = respData; if (status === 400) { // show error message