From 93b26cc383a1b909f389f80f2b40643d473fa345 Mon Sep 17 00:00:00 2001 From: shuai Date: Wed, 16 Nov 2022 12:06:33 +0800 Subject: [PATCH] fix: required tags and reserved tags --- ui/public/index.html | 1 - ui/src/assets/images/favicon.ico | Bin 16958 -> 0 bytes ui/src/common/interface.ts | 5 +- ui/src/components/SchemaForm/index.tsx | 21 +++++++- ui/src/components/Tag/index.tsx | 9 +++- ui/src/components/TagSelector/index.tsx | 32 ++++++++---- ui/src/index.scss | 14 +++++- ui/src/pages/Admin/Write/index.tsx | 64 ++++++++++++------------ ui/src/pages/Layout/index.tsx | 4 +- ui/src/pages/Tags/Info/index.tsx | 2 + ui/src/services/admin/settings.ts | 8 +++ ui/src/utils/request.ts | 2 +- 12 files changed, 114 insertions(+), 48 deletions(-) delete mode 100644 ui/src/assets/images/favicon.ico diff --git a/ui/public/index.html b/ui/public/index.html index 97b4bcb2..304d295f 100644 --- a/ui/public/index.html +++ b/ui/public/index.html @@ -2,7 +2,6 @@ - diff --git a/ui/src/assets/images/favicon.ico b/ui/src/assets/images/favicon.ico deleted file mode 100644 index 0013c7e1b42ec9209e21551fbe205602fbfca0ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16958 zcmeHO>5~*S6d#Hz{15!99}#av1Vmf`6$AxE1Vy1JP*J>4E;+2FKrBlw5Lpfp6%P;! z2X?w$LYUoUAgGkv7z*d4&Pl})CTyu4pJd3ni8+iacK zUsaWj{lC)IwZF}Fxy@$l$^v_2t7ZYu6{>2BjQ$f*Hx?GLP|HGyg$td40a$>kn;v$S z*M)@|7Sb$StORVp*hTCI{AOVf3zrH3tJ()FjXt^5s0L$zfyR8ln8&={dj)2$>-5SP zK2HY&6y5y|`Ij!DrUkE1)BL&A^5$z4cyBp{zt}>t-+rZZz;DPyFEicJLb1cYPUXj@14}L7%nZ&6RhHavK57S%FX)=_H!B;(- zGO?IURf^UAWhzU`RWHR$wh-1Iv5#}pezx9OArZgxo&l6@ZI!MrgFm?TBWZpz1sf-h z)y3}`HJmcZWFDJLB2Hx$Kl(ShA00_`J+4;kxW|poDkI|euU?sz*R*h+T91#jq|2$6k-^`P)1F$n zz=U&P-#nQho1NJKnN&*UnqoGa{w!e)=Rq7X&wcri94eU0Liz{hzDCJfA73L!2UD*)pluC<-qT}yI?@Z5pN{t`qB{)-QFTx(vJ@P)yK2Yby&DT)fE!QcbVZvisqKWt&Y&_Ku z8>EIh)MI@q2ItU$YQA&u9a(wqF%K$dNLdZE!w={K-^>@YWD#W+)46tnh+iycHQj{0 zL!x~1U8WlY?=Ca#gHq$Sv%N)-odZ+~MRt5a_FMGNLQ9F?(dR~re!II;psRAkemO{v z+k0w`g;L>n4(LPizy4I)th7Y@$RF%X`lh@-a6SA0B^`D(XxWHnb02GL;^grTgAAP? zW;R`D-NzK(zLipL?K7s>p`QuokY;$Sb z;J!K2HEnJ9eyrd#E2}l%;aBobdp@rn=tk`S;9Dt{;Dd4vyF0H9QQn5}j}QrGAiPcA ztf_|eL>@+I_;+#%XQi-pjq1TZDRv(OUz<@IJWp#a%7j8x&+HanrjXb*VyNc1uz$rn zQhZw$zmeN@(!X(zhp{9hw|mS8O}=qbemz~+4$$!siu`qqs z#Lnb7ADKAS*r3TXym_M~KD;gXPqYYjJ})B^x4iY578}!NE@qR<@aJWs%-5qwVdm43 z9S2xHBRhBKu_EYr-lj~9{XroFJ4=Q?FB9Ff)36!LCo>;f|H`EVn+Rv6&O2`-&vTOqGRqL|M`1kSj1f8*dIsUA3XJB!gI_K z**1;jf^Dle-yj?Ca*F?bg!Fu+kO_LBR4OD@gkDv*(a^lpF2~m-r;GZb(>=rW2{*s z;cmN_ZlOmzLy5d6A&c%rJ}jtq1mt)jK?m?+~=^1&A{9G}f=Y_3y+n zY5cGQDIV`|%(()OVRyN)U&Y=GXQKqXDbGCzV8=ILB^^{c zT~ld~fm!(;mc=!dCR{tnqkR8(?i@H1?a}o ztc{FO2NGZ76kR7`MLO2@;EJLfte{5# diff --git a/ui/src/common/interface.ts b/ui/src/common/interface.ts index 8f5a9d5d..822673d3 100644 --- a/ui/src/common/interface.ts +++ b/ui/src/common/interface.ts @@ -24,6 +24,8 @@ export interface ReportParams { export interface TagBase { display_name: string; slug_name: string; + recommend: boolean; + reserved: boolean; } export interface Tag extends TagBase { @@ -303,8 +305,9 @@ export interface AdminSettingsLegal { } export interface AdminSettingsWrite { - recommend_tags: string; + recommend_tags: string[]; required_tag: string; + reserved_tags: string[]; } /** diff --git a/ui/src/components/SchemaForm/index.tsx b/ui/src/components/SchemaForm/index.tsx index dfbcc167..833e07b6 100644 --- a/ui/src/components/SchemaForm/index.tsx +++ b/ui/src/components/SchemaForm/index.tsx @@ -90,7 +90,21 @@ const SchemaForm: FC = ({ const handleInputChange = (e: React.ChangeEvent) => { const { name, value } = e.target; - const data = { ...formData, [name]: { ...formData[name], value } }; + const data = { + ...formData, + [name]: { ...formData[name], value, isInvalid: false }, + }; + if (onChange instanceof Function) { + onChange(data); + } + }; + + const handleSwitchChange = (e: React.ChangeEvent) => { + const { name, checked } = e.target; + const data = { + ...formData, + [name]: { ...formData[name], value: checked, isInvalid: false }, + }; if (onChange instanceof Function) { onChange(data); } @@ -155,6 +169,7 @@ const SchemaForm: FC = ({ const errors = requiredValidator(); if (errors.length > 0) { formData = errors.reduce((acc, cur) => { + console.log('schema.properties[cur]', cur); acc[cur] = { ...formData[cur], isInvalid: true, @@ -262,17 +277,21 @@ const SchemaForm: FC = ({ } if (widget === 'switch') { + console.log(formData[key]?.value, 'switch====='); return ( {title} {formData[key]?.errorMsg} diff --git a/ui/src/components/Tag/index.tsx b/ui/src/components/Tag/index.tsx index 729c75b3..ac7910ff 100644 --- a/ui/src/components/Tag/index.tsx +++ b/ui/src/components/Tag/index.tsx @@ -14,7 +14,14 @@ const Index: FC = ({ className = '', href, data }) => { href = href || `/tags/${data.main_tag_slug_name || data.slug_name}`.toLowerCase(); return ( - + {data.slug_name} ); diff --git a/ui/src/components/TagSelector/index.tsx b/ui/src/components/TagSelector/index.tsx index 8747f65a..ab028dac 100644 --- a/ui/src/components/TagSelector/index.tsx +++ b/ui/src/components/TagSelector/index.tsx @@ -1,3 +1,4 @@ +/* eslint-disable no-nested-ternary */ import { FC, useState, useEffect } from 'react'; import { Dropdown, FormControl, Button, Form } from 'react-bootstrap'; import { useTranslation } from 'react-i18next'; @@ -95,17 +96,16 @@ const TagSelector: FC = ({ } }, [value]); - useEffect(() => { - if (!tag) { - setTags(null); - return; - } - - queryTags(tag).then((res) => { + const fetchTags = (str) => { + queryTags(str).then((res) => { const tagArray: Type.Tag[] = filterTags(res || []); setTags(tagArray); }); - }, [tag]); + }; + + useEffect(() => { + fetchTags(tag); + }, [visibleMenu]); const handleClick = (val: Type.Tag) => { const findIndex = initialValue.findIndex( @@ -143,7 +143,9 @@ const TagSelector: FC = ({ }; const handleSearch = async (e: React.ChangeEvent) => { - setTag(e.currentTarget.value.replace(';', '')); + const searchStr = e.currentTarget.value.replace(';', ''); + setTag(searchStr); + fetchTags(searchStr); }; const handleSelect = (eventKey) => { @@ -186,7 +188,9 @@ const TagSelector: FC = ({ 'm-1 text-nowrap d-flex align-items-center', index === repeatIndex && 'warning', )} - variant="outline-secondary" + variant={`outline-${ + item.reserved ? 'danger' : item.recommend ? 'dark' : 'secondary' + }`} size="sm"> {item.slug_name} handleRemove(item)}> @@ -220,6 +224,14 @@ const TagSelector: FC = ({ )} + {tags && tags.filter((v) => v.recommend)?.length > 0 && ( + + Required tag (at least one) + + )} {tags?.map((item, index) => { return ( diff --git a/ui/src/index.scss b/ui/src/index.scss index 69fcb80c..7cfa95b8 100644 --- a/ui/src/index.scss +++ b/ui/src/index.scss @@ -69,20 +69,32 @@ a { padding: 1px 0.5rem 2px; color: $blue-700; height: 24px; + border: 1px solid rgba($blue-100, 0.5); &:hover { background: rgba($blue-100, 1); } } .badge-tag-required { - background: rgba($gray-400, 0.5); + background: rgba($gray-200, 0.5); color: $gray-700; + border: 1px solid $gray-400; &:hover { color: $gray-700; background: rgba($gray-400, 1); } } +.badge-tag-reserved { + background: rgba($orange-100, 0.5); + color: $orange-700; + border: 1px solid $orange-400; + &:hover { + color: $orange-700; + background: rgba($orange-400, 1); + } +} + .divide-line { border-bottom: 1px solid rgba(33, 37, 41, 0.25); } diff --git a/ui/src/pages/Admin/Write/index.tsx b/ui/src/pages/Admin/Write/index.tsx index 3ee34469..12cf4e98 100644 --- a/ui/src/pages/Admin/Write/index.tsx +++ b/ui/src/pages/Admin/Write/index.tsx @@ -3,9 +3,11 @@ 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 { useToast } from '@/hooks'; +import { + getRequireAndReservedTag, + postRequireAndReservedTag, +} from '@/services'; import '../index.scss'; @@ -13,13 +15,11 @@ const Legal: FC = () => { const { t } = useTranslation('translation', { keyPrefix: 'admin.write', }); - // const Toast = useToast(); + 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', @@ -62,38 +62,40 @@ const Legal: FC = () => { evt.stopPropagation(); const reqParams: Type.AdminSettingsWrite = { - recommend_tags: formData.recommend_tags.value, + recommend_tags: formData.recommend_tags.value.trim().split('\n'), required_tag: formData.required_tag.value, + reserved_tags: formData.reserved_tags.value.trim().split('\n'), }; 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 }); - // }); + postRequireAndReservedTag(reqParams) + .then(() => { + Toast.onShow({ + msg: t('update', { keyPrefix: 'toast' }), + variant: 'success', + }); + }) + .catch((err) => { + if (err.isError && err.key) { + formData[err.key].isInvalid = true; + formData[err.key].errorMsg = err.value; + } + setFormData({ ...formData }); + }); + }; + + const initData = () => { + getRequireAndReservedTag().then((res) => { + formData.recommend_tags.value = res.recommend_tags.join('\n'); + formData.required_tag.value = res.required_tag; + formData.reserved_tags.value = res.reserved_tags.join('\n'); + setFormData({ ...formData }); + }); }; useEffect(() => { - if (!setting) { - return; - } - const formMeta = {}; - Object.keys(setting).forEach((k) => { - formMeta[k] = { ...formData[k], value: setting[k] }; - }); - setFormData({ ...formData, ...formMeta }); - }, [setting]); + initData(); + }, []); const handleOnChange = (data) => { setFormData(data); diff --git a/ui/src/pages/Layout/index.tsx b/ui/src/pages/Layout/index.tsx index 7539c2fa..94478af9 100644 --- a/ui/src/pages/Layout/index.tsx +++ b/ui/src/pages/Layout/index.tsx @@ -4,12 +4,13 @@ import { Helmet, HelmetProvider } from 'react-helmet-async'; import { SWRConfig } from 'swr'; -import { siteInfoStore, toastStore } from '@/stores'; +import { siteInfoStore, toastStore, brandingStore } from '@/stores'; import { Header, Footer, Toast } from '@/components'; const Layout: FC = () => { const { msg: toastMsg, variant, clear: toastClear } = toastStore(); const { siteInfo } = siteInfoStore.getState(); + const { favicon } = brandingStore((state) => state.branding); const closeToast = () => { toastClear(); }; @@ -17,6 +18,7 @@ const Layout: FC = () => { return ( + {siteInfo && } { slug_name: tagName || '', main_tag_slug_name: '', display_name: '', + recommend: false, + reserved: false, }} /> diff --git a/ui/src/services/admin/settings.ts b/ui/src/services/admin/settings.ts index b901a365..f0fe7399 100644 --- a/ui/src/services/admin/settings.ts +++ b/ui/src/services/admin/settings.ts @@ -96,3 +96,11 @@ export const getBrandSetting = () => { export const brandSetting = (params: Type.AdmingSettingBranding) => { return request.put('/answer/admin/api/siteinfo/branding', params); }; + +export const getRequireAndReservedTag = () => { + return request.get('/answer/admin/api/siteinfo/write'); +}; + +export const postRequireAndReservedTag = (params) => { + return request.put('/answer/admin/api/siteinfo/write', params); +}; diff --git a/ui/src/utils/request.ts b/ui/src/utils/request.ts index e553b803..7d880bbf 100644 --- a/ui/src/utils/request.ts +++ b/ui/src/utils/request.ts @@ -49,7 +49,7 @@ class Request { }, (error) => { const { status, data: respData, msg: respMsg } = error.response || {}; - const { data, msg = '' } = respData; + const { data = {}, msg = '' } = respData || {}; if (status === 400) { // show error message if (data instanceof Object && data.err_type) {