mirror of https://gitee.com/answerdev/answer.git
fix: required tags and reserved tags
This commit is contained in:
parent
bc904d8d59
commit
93b26cc383
|
@ -2,7 +2,6 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<!-- <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />-->
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 17 KiB |
|
@ -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[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -90,7 +90,21 @@ const SchemaForm: FC<IProps> = ({
|
|||
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
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<HTMLInputElement>) => {
|
||||
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<IProps> = ({
|
|||
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<IProps> = ({
|
|||
}
|
||||
|
||||
if (widget === 'switch') {
|
||||
console.log(formData[key]?.value, 'switch=====');
|
||||
return (
|
||||
<Form.Group key={title} className="mb-3" controlId={key}>
|
||||
<Form.Label>{title}</Form.Label>
|
||||
<Form.Check
|
||||
required
|
||||
id={title}
|
||||
name={key}
|
||||
type="switch"
|
||||
label={title}
|
||||
checked={formData[key]?.value}
|
||||
feedback={formData[key]?.errorMsg}
|
||||
feedbackType="invalid"
|
||||
isInvalid={formData[key].isInvalid}
|
||||
onChange={handleSwitchChange}
|
||||
/>
|
||||
<Form.Control.Feedback type="invalid">
|
||||
{formData[key]?.errorMsg}
|
||||
|
|
|
@ -14,7 +14,14 @@ const Index: FC<IProps> = ({ className = '', href, data }) => {
|
|||
href =
|
||||
href || `/tags/${data.main_tag_slug_name || data.slug_name}`.toLowerCase();
|
||||
return (
|
||||
<a href={href} className={classNames('badge-tag rounded-1', className)}>
|
||||
<a
|
||||
href={href}
|
||||
className={classNames(
|
||||
'badge-tag rounded-1',
|
||||
data.reserved && 'badge-tag-reserved',
|
||||
data.recommend && 'badge-tag-required',
|
||||
className,
|
||||
)}>
|
||||
{data.slug_name}
|
||||
</a>
|
||||
);
|
||||
|
|
|
@ -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<IProps> = ({
|
|||
}
|
||||
}, [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<IProps> = ({
|
|||
};
|
||||
|
||||
const handleSearch = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
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<IProps> = ({
|
|||
'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}
|
||||
<span className="ms-1" onMouseUp={() => handleRemove(item)}>
|
||||
|
@ -220,6 +224,14 @@ const TagSelector: FC<IProps> = ({
|
|||
</Form>
|
||||
</Dropdown.Header>
|
||||
)}
|
||||
{tags && tags.filter((v) => v.recommend)?.length > 0 && (
|
||||
<Dropdown.Item
|
||||
disabled
|
||||
style={{ fontWeight: 500 }}
|
||||
className="text-secondary">
|
||||
Required tag (at least one)
|
||||
</Dropdown.Item>
|
||||
)}
|
||||
|
||||
{tags?.map((item, index) => {
|
||||
return (
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 (
|
||||
<HelmetProvider>
|
||||
<Helmet>
|
||||
<link rel="icon" href={favicon || '/favicon.ico'} />
|
||||
{siteInfo && <meta name="description" content={siteInfo.description} />}
|
||||
</Helmet>
|
||||
<SWRConfig
|
||||
|
|
|
@ -159,6 +159,8 @@ const TagIntroduction = () => {
|
|||
slug_name: tagName || '',
|
||||
main_tag_slug_name: '',
|
||||
display_name: '',
|
||||
recommend: false,
|
||||
reserved: false,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue