mirror of https://gitee.com/answerdev/answer.git
Merge branch 'feat/ui-0.5.0' into test
This commit is contained in:
commit
5451625725
|
@ -551,7 +551,7 @@ ui:
|
|||
footer:
|
||||
build_on: >-
|
||||
Built on <1> Answer </1>- the open-source software that power Q&A
|
||||
communities<br />Made with love © 2022 Answer
|
||||
communities.<br />Made with love © {{cc}}.
|
||||
upload_img:
|
||||
name: Change
|
||||
loading: loading...
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export const DEFAULT_SITE_NAME = 'Answer';
|
||||
export const DEFAULT_LANG = 'en_US';
|
||||
export const CURRENT_LANG_STORAGE_KEY = '_a_lang_';
|
||||
export const LANG_RESOURCE_STORAGE_KEY = '_a_lang_r_';
|
||||
|
@ -9,29 +10,29 @@ export const CAPTCHA_CODE_STORAGE_KEY = '_a_captcha_';
|
|||
export const ADMIN_LIST_STATUS = {
|
||||
// normal;
|
||||
1: {
|
||||
variant: 'success',
|
||||
variant: 'text-bg-success',
|
||||
name: 'normal',
|
||||
},
|
||||
// closed;
|
||||
2: {
|
||||
variant: 'warning',
|
||||
variant: 'text-bg-warning',
|
||||
name: 'closed',
|
||||
},
|
||||
// deleted
|
||||
10: {
|
||||
variant: 'danger',
|
||||
variant: 'text-bg-danger',
|
||||
name: 'deleted',
|
||||
},
|
||||
normal: {
|
||||
variant: 'success',
|
||||
variant: 'text-bg-success',
|
||||
name: 'normal',
|
||||
},
|
||||
closed: {
|
||||
variant: 'warning',
|
||||
variant: 'text-bg-warning',
|
||||
name: 'closed',
|
||||
},
|
||||
deleted: {
|
||||
variant: 'danger',
|
||||
variant: 'text-bg-danger',
|
||||
name: 'deleted',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -287,9 +287,9 @@ export interface AdminSettingsSmtp {
|
|||
from_name: string;
|
||||
smtp_authentication: boolean;
|
||||
smtp_host: string;
|
||||
smtp_password: string;
|
||||
smtp_password?: string;
|
||||
smtp_port: number;
|
||||
smtp_username: string;
|
||||
smtp_username?: string;
|
||||
test_email_recipient?: string;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { FC } from 'react';
|
||||
import { Accordion, Badge, Button, Stack } from 'react-bootstrap';
|
||||
import { Accordion, Button, Stack } from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate, useMatch } from 'react-router-dom';
|
||||
|
||||
|
@ -33,9 +33,9 @@ function MenuNode({ menu, callback, activeKey, isLeaf = false }) {
|
|||
{!isLeaf ? <Icon name="chevron-right" className="me-1" /> : null}
|
||||
{t(menu.name)}
|
||||
{menu.badgeContent ? (
|
||||
<Badge bg="dark" className="ms-auto top-0">
|
||||
<span className="badge text-bg-dark ms-auto top-0">
|
||||
{menu.badgeContent}
|
||||
</Badge>
|
||||
</span>
|
||||
) : null}
|
||||
</Stack>
|
||||
</Button>
|
||||
|
|
|
@ -2,12 +2,20 @@ import React from 'react';
|
|||
import { Container } from 'react-bootstrap';
|
||||
import { Trans } from 'react-i18next';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
import { siteInfoStore } from '@/stores';
|
||||
import { DEFAULT_SITE_NAME } from '@/common/constants';
|
||||
|
||||
const Index = () => {
|
||||
const fullYear = dayjs().format('YYYY');
|
||||
const siteName = siteInfoStore.getState().siteInfo.name || DEFAULT_SITE_NAME;
|
||||
const cc = `${fullYear} ${siteName}`;
|
||||
return (
|
||||
<footer className="bg-light py-3">
|
||||
<Container>
|
||||
<p className="text-center mb-0 fs-14 text-secondary">
|
||||
<Trans i18nKey="footer.build_on">
|
||||
<Trans i18nKey="footer.build_on" values={{ cc }}>
|
||||
Built on
|
||||
{/* eslint-disable-next-line react/jsx-no-target-blank */}
|
||||
<a href="https://answer.dev/" target="_blank">
|
||||
|
@ -15,7 +23,7 @@ const Index = () => {
|
|||
</a>
|
||||
- the open-source software that powers Q&A communities.
|
||||
<br />
|
||||
Made with love. © 2022 Answer .
|
||||
Made with love. © 2022 Answer.
|
||||
</Trans>
|
||||
</p>
|
||||
</Container>
|
||||
|
|
|
@ -20,6 +20,7 @@ import {
|
|||
import { loggedUserInfoStore, siteInfoStore, brandingStore } from '@/stores';
|
||||
import { logout, useQueryNotificationStatus } from '@/services';
|
||||
import { RouteAlias } from '@/router/alias';
|
||||
import { DEFAULT_SITE_NAME } from '@/common/constants';
|
||||
|
||||
import NavItems from './components/NavItems';
|
||||
|
||||
|
@ -88,7 +89,7 @@ const Header: FC = () => {
|
|||
/>
|
||||
</>
|
||||
) : (
|
||||
<span>{siteInfo.name || 'Answer'}</span>
|
||||
<span>{siteInfo.name || DEFAULT_SITE_NAME}</span>
|
||||
)}
|
||||
</Navbar.Brand>
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@ import { FC } from 'react';
|
|||
import { Form, Button, Stack } from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import classnames from 'classnames';
|
||||
|
||||
import BrandUpload from '../BrandUpload';
|
||||
import TimeZonePicker from '../TimeZonePicker';
|
||||
import type * as Type from '@/common/interface';
|
||||
|
@ -52,7 +54,10 @@ export interface UISchema {
|
|||
| 'url'
|
||||
| 'week';
|
||||
empty?: string;
|
||||
validator?: (value) => Promise<string | true | void> | true | string;
|
||||
validator?: (
|
||||
value,
|
||||
formData?,
|
||||
) => Promise<string | true | void> | true | string;
|
||||
textRender?: () => React.ReactElement;
|
||||
imageType?: Type.UploadType;
|
||||
acceptType?: string;
|
||||
|
@ -153,7 +158,7 @@ const SchemaForm: FC<IProps> = ({
|
|||
const value = formData[key]?.value;
|
||||
promises.push({
|
||||
key,
|
||||
promise: validator(value),
|
||||
promise: validator(value, formData),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -263,7 +268,10 @@ const SchemaForm: FC<IProps> = ({
|
|||
uiSchema[key] || {};
|
||||
if (widget === 'select') {
|
||||
return (
|
||||
<Form.Group key={title} controlId={key} className="mb-3">
|
||||
<Form.Group
|
||||
key={title}
|
||||
controlId={key}
|
||||
className={classnames('mb-3', formData[key].hidden && 'd-none')}>
|
||||
<Form.Label>{title}</Form.Label>
|
||||
<Form.Select
|
||||
aria-label={description}
|
||||
|
@ -282,13 +290,18 @@ const SchemaForm: FC<IProps> = ({
|
|||
<Form.Control.Feedback type="invalid">
|
||||
{formData[key]?.errorMsg}
|
||||
</Form.Control.Feedback>
|
||||
<Form.Text>{description}</Form.Text>
|
||||
{description && (
|
||||
<Form.Text className="text-muted">{description}</Form.Text>
|
||||
)}
|
||||
</Form.Group>
|
||||
);
|
||||
}
|
||||
if (widget === 'checkbox' || widget === 'radio') {
|
||||
return (
|
||||
<Form.Group key={title} className="mb-3" controlId={key}>
|
||||
<Form.Group
|
||||
key={title}
|
||||
className={classnames('mb-3', formData[key].hidden && 'd-none')}
|
||||
controlId={key}>
|
||||
<Form.Label>{title}</Form.Label>
|
||||
<Stack direction="horizontal">
|
||||
{properties[key].enum?.map((item, index) => {
|
||||
|
@ -313,14 +326,19 @@ const SchemaForm: FC<IProps> = ({
|
|||
<Form.Control.Feedback type="invalid">
|
||||
{formData[key]?.errorMsg}
|
||||
</Form.Control.Feedback>
|
||||
<Form.Text>{description}</Form.Text>
|
||||
{description && (
|
||||
<Form.Text className="text-muted">{description}</Form.Text>
|
||||
)}
|
||||
</Form.Group>
|
||||
);
|
||||
}
|
||||
|
||||
if (widget === 'switch') {
|
||||
return (
|
||||
<Form.Group key={title} className="mb-3" controlId={key}>
|
||||
<Form.Group
|
||||
key={title}
|
||||
className={classnames('mb-3', formData[key].hidden && 'd-none')}
|
||||
controlId={key}>
|
||||
<Form.Label>{title}</Form.Label>
|
||||
<Form.Check
|
||||
required
|
||||
|
@ -337,13 +355,18 @@ const SchemaForm: FC<IProps> = ({
|
|||
<Form.Control.Feedback type="invalid">
|
||||
{formData[key]?.errorMsg}
|
||||
</Form.Control.Feedback>
|
||||
<Form.Text>{description}</Form.Text>
|
||||
{description && (
|
||||
<Form.Text className="text-muted">{description}</Form.Text>
|
||||
)}
|
||||
</Form.Group>
|
||||
);
|
||||
}
|
||||
if (widget === 'timezone') {
|
||||
return (
|
||||
<Form.Group key={title} className="mb-3" controlId={key}>
|
||||
<Form.Group
|
||||
key={title}
|
||||
className={classnames('mb-3', formData[key].hidden && 'd-none')}
|
||||
controlId={key}>
|
||||
<Form.Label>{title}</Form.Label>
|
||||
<TimeZonePicker
|
||||
value={formData[key]?.value}
|
||||
|
@ -358,14 +381,19 @@ const SchemaForm: FC<IProps> = ({
|
|||
<Form.Control.Feedback type="invalid">
|
||||
{formData[key]?.errorMsg}
|
||||
</Form.Control.Feedback>
|
||||
<Form.Text>{description}</Form.Text>
|
||||
{description && (
|
||||
<Form.Text className="text-muted">{description}</Form.Text>
|
||||
)}
|
||||
</Form.Group>
|
||||
);
|
||||
}
|
||||
|
||||
if (widget === 'upload') {
|
||||
return (
|
||||
<Form.Group key={title} className="mb-3" controlId={key}>
|
||||
<Form.Group
|
||||
key={title}
|
||||
className={classnames('mb-3', formData[key].hidden && 'd-none')}
|
||||
controlId={key}>
|
||||
<Form.Label>{title}</Form.Label>
|
||||
<BrandUpload
|
||||
type={options.imageType || 'avatar'}
|
||||
|
@ -381,14 +409,19 @@ const SchemaForm: FC<IProps> = ({
|
|||
<Form.Control.Feedback type="invalid">
|
||||
{formData[key]?.errorMsg}
|
||||
</Form.Control.Feedback>
|
||||
<Form.Text>{description}</Form.Text>
|
||||
{description && (
|
||||
<Form.Text className="text-muted">{description}</Form.Text>
|
||||
)}
|
||||
</Form.Group>
|
||||
);
|
||||
}
|
||||
|
||||
if (widget === 'textarea') {
|
||||
return (
|
||||
<Form.Group controlId={key} key={key} className="mb-3">
|
||||
<Form.Group
|
||||
controlId={key}
|
||||
key={key}
|
||||
className={classnames('mb-3', formData[key].hidden && 'd-none')}>
|
||||
<Form.Label>{title}</Form.Label>
|
||||
<Form.Control
|
||||
as="textarea"
|
||||
|
@ -404,12 +437,17 @@ const SchemaForm: FC<IProps> = ({
|
|||
{formData[key]?.errorMsg}
|
||||
</Form.Control.Feedback>
|
||||
|
||||
<Form.Text>{description}</Form.Text>
|
||||
{description && (
|
||||
<Form.Text className="text-muted">{description}</Form.Text>
|
||||
)}
|
||||
</Form.Group>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Form.Group controlId={key} key={key} className="mb-3">
|
||||
<Form.Group
|
||||
controlId={key}
|
||||
key={key}
|
||||
className={classnames('mb-3', formData[key].hidden && 'd-none')}>
|
||||
<Form.Label>{title}</Form.Label>
|
||||
<Form.Control
|
||||
name={key}
|
||||
|
@ -424,7 +462,9 @@ const SchemaForm: FC<IProps> = ({
|
|||
{formData[key]?.errorMsg}
|
||||
</Form.Control.Feedback>
|
||||
|
||||
<Form.Text>{description}</Form.Text>
|
||||
{description && (
|
||||
<Form.Text className="text-muted">{description}</Form.Text>
|
||||
)}
|
||||
</Form.Group>
|
||||
);
|
||||
})}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { FC } from 'react';
|
||||
import { Button, Form, Table, Stack, Badge } from 'react-bootstrap';
|
||||
import { Button, Form, Table, Stack } from 'react-bootstrap';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import {
|
||||
FormatTime,
|
||||
Icon,
|
||||
|
@ -159,9 +161,13 @@ const Answers: FC = () => {
|
|||
</Stack>
|
||||
</td>
|
||||
<td>
|
||||
<Badge bg={ADMIN_LIST_STATUS[curFilter]?.variant}>
|
||||
<span
|
||||
className={classNames(
|
||||
'badge',
|
||||
ADMIN_LIST_STATUS[curFilter]?.variant,
|
||||
)}>
|
||||
{t(ADMIN_LIST_STATUS[curFilter]?.name)}
|
||||
</Badge>
|
||||
</span>
|
||||
</td>
|
||||
{curFilter !== 'deleted' && (
|
||||
<td>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { FC } from 'react';
|
||||
import { Card, Row, Col, Badge } from 'react-bootstrap';
|
||||
import { Card, Row, Col } from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
|
@ -29,38 +29,31 @@ const HealthStatus: FC<IProps> = ({ data }) => {
|
|||
<span className="text-secondary me-1">{t('version')}</span>
|
||||
<strong>{version}</strong>
|
||||
{isLatest && (
|
||||
<Badge
|
||||
pill
|
||||
bg="success"
|
||||
className="ms-1"
|
||||
as="a"
|
||||
<a
|
||||
className="ms-1 badge rounded-pill text-bg-success"
|
||||
target="_blank"
|
||||
href="https://github.com/answerdev/answer/releases">
|
||||
href="https://github.com/answerdev/answer/releases"
|
||||
rel="noreferrer">
|
||||
{t('latest')}
|
||||
</Badge>
|
||||
</a>
|
||||
)}
|
||||
{!isLatest && hasNewerVersion && (
|
||||
<Badge
|
||||
pill
|
||||
bg="warning"
|
||||
text="dark"
|
||||
className="ms-1"
|
||||
as="a"
|
||||
<a
|
||||
className="ms-1 badge rounded-pill text-bg-warning"
|
||||
target="_blank"
|
||||
href="https://github.com/answerdev/answer/releases">
|
||||
href="https://github.com/answerdev/answer/releases"
|
||||
rel="noreferrer">
|
||||
{t('update_to')} {remote_version}
|
||||
</Badge>
|
||||
</a>
|
||||
)}
|
||||
{!isLatest && !remote_version && (
|
||||
<Badge
|
||||
pill
|
||||
bg="danger"
|
||||
className="ms-1"
|
||||
as="a"
|
||||
<a
|
||||
className="ms-1 badge rounded-pill text-bg-danger"
|
||||
target="_blank"
|
||||
href="https://github.com/answerdev/answer/releases">
|
||||
href="https://github.com/answerdev/answer/releases"
|
||||
rel="noreferrer">
|
||||
{t('check_failed')}
|
||||
</Badge>
|
||||
</a>
|
||||
)}
|
||||
</Col>
|
||||
<Col xs={6} className="mb-1">
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { FC } from 'react';
|
||||
import { Button, Form, Table, Stack, Badge } from 'react-bootstrap';
|
||||
import { Button, Form, Table, Stack } from 'react-bootstrap';
|
||||
import { Link, useSearchParams } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import {
|
||||
FormatTime,
|
||||
Icon,
|
||||
|
@ -167,9 +169,13 @@ const Questions: FC = () => {
|
|||
</Stack>
|
||||
</td>
|
||||
<td>
|
||||
<Badge bg={ADMIN_LIST_STATUS[curFilter]?.variant}>
|
||||
<span
|
||||
className={classNames(
|
||||
'badge',
|
||||
ADMIN_LIST_STATUS[curFilter]?.variant,
|
||||
)}>
|
||||
{t(ADMIN_LIST_STATUS[curFilter]?.name)}
|
||||
</Badge>
|
||||
</span>
|
||||
</td>
|
||||
{curFilter !== 'deleted' && (
|
||||
<td>
|
||||
|
|
|
@ -37,8 +37,8 @@ const Smtp: FC = () => {
|
|||
type: 'boolean',
|
||||
title: t('encryption.label'),
|
||||
description: t('encryption.text'),
|
||||
enum: ['SSL', ''],
|
||||
enumNames: ['SSL', ''],
|
||||
enum: ['SSL', 'None'],
|
||||
enumNames: ['SSL', 'None'],
|
||||
},
|
||||
smtp_port: {
|
||||
type: 'string',
|
||||
|
@ -54,12 +54,10 @@ const Smtp: FC = () => {
|
|||
smtp_username: {
|
||||
type: 'string',
|
||||
title: t('smtp_username.label'),
|
||||
description: t('smtp_username.text'),
|
||||
},
|
||||
smtp_password: {
|
||||
type: 'string',
|
||||
title: t('smtp_password.label'),
|
||||
description: t('smtp_password.text'),
|
||||
},
|
||||
test_email_recipient: {
|
||||
type: 'string',
|
||||
|
@ -70,15 +68,35 @@ const Smtp: FC = () => {
|
|||
};
|
||||
const uiSchema: UISchema = {
|
||||
encryption: {
|
||||
'ui:widget': 'radio',
|
||||
'ui:widget': 'select',
|
||||
},
|
||||
smtp_username: {
|
||||
'ui:options': {
|
||||
validator: (value: string, formData) => {
|
||||
if (formData.smtp_authentication.value) {
|
||||
if (!value) {
|
||||
return t('smtp_username.msg');
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
},
|
||||
smtp_password: {
|
||||
'ui:options': {
|
||||
type: 'password',
|
||||
validator: (value: string, formData) => {
|
||||
if (formData.smtp_authentication.value) {
|
||||
if (!value) {
|
||||
return t('smtp_password.msg');
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
},
|
||||
smtp_authentication: {
|
||||
'ui:widget': 'radio',
|
||||
'ui:widget': 'switch',
|
||||
},
|
||||
smtp_port: {
|
||||
'ui:options': {
|
||||
|
@ -116,8 +134,12 @@ const Smtp: FC = () => {
|
|||
encryption: formData.encryption.value,
|
||||
smtp_port: Number(formData.smtp_port.value),
|
||||
smtp_authentication: formData.smtp_authentication.value,
|
||||
smtp_username: formData.smtp_username.value,
|
||||
smtp_password: formData.smtp_password.value,
|
||||
...(formData.smtp_authentication.value
|
||||
? { smtp_username: formData.smtp_username.value }
|
||||
: {}),
|
||||
...(formData.smtp_authentication.value
|
||||
? { smtp_password: formData.smtp_password.value }
|
||||
: {}),
|
||||
test_email_recipient: formData.test_email_recipient.value,
|
||||
};
|
||||
|
||||
|
@ -151,6 +173,22 @@ const Smtp: FC = () => {
|
|||
setFormData(formState);
|
||||
}, [setting]);
|
||||
|
||||
useEffect(() => {
|
||||
if (formData.smtp_authentication.value) {
|
||||
setFormData({
|
||||
...formData,
|
||||
smtp_username: { ...formData.smtp_username, hidden: false },
|
||||
smtp_password: { ...formData.smtp_password, hidden: false },
|
||||
});
|
||||
} else {
|
||||
setFormData({
|
||||
...formData,
|
||||
smtp_username: { ...formData.smtp_username, hidden: true },
|
||||
smtp_password: { ...formData.smtp_password, hidden: true },
|
||||
});
|
||||
}
|
||||
}, [formData.smtp_authentication]);
|
||||
|
||||
const handleOnChange = (data) => {
|
||||
setFormData(data);
|
||||
};
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { FC } from 'react';
|
||||
import { Button, Form, Table, Badge } from 'react-bootstrap';
|
||||
import { Button, Form, Table } from 'react-bootstrap';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import {
|
||||
Pagination,
|
||||
FormatTime,
|
||||
|
@ -24,10 +26,10 @@ const UserFilterKeys: Type.UserFilterBy[] = [
|
|||
];
|
||||
|
||||
const bgMap = {
|
||||
normal: 'success',
|
||||
suspended: 'danger',
|
||||
deleted: 'danger',
|
||||
inactive: 'secondary',
|
||||
normal: 'text-bg-success',
|
||||
suspended: 'text-bg-danger',
|
||||
deleted: 'text-bg-danger',
|
||||
inactive: 'text-bg-secondary',
|
||||
};
|
||||
|
||||
const PAGE_SIZE = 10;
|
||||
|
@ -132,7 +134,9 @@ const Users: FC = () => {
|
|||
</td>
|
||||
)}
|
||||
<td>
|
||||
<Badge bg={bgMap[user.status]}>{t(user.status)}</Badge>
|
||||
<span className={classNames('badge', bgMap[user.status])}>
|
||||
{t(user.status)}
|
||||
</span>
|
||||
</td>
|
||||
{curFilter !== 'deleted' ? (
|
||||
<td>
|
||||
|
|
|
@ -1,13 +1,5 @@
|
|||
import { FC, useEffect, useState } from 'react';
|
||||
import {
|
||||
Container,
|
||||
Row,
|
||||
Col,
|
||||
Alert,
|
||||
Badge,
|
||||
Stack,
|
||||
Button,
|
||||
} from 'react-bootstrap';
|
||||
import { Container, Row, Col, Alert, Stack, Button } from 'react-bootstrap';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
|
@ -120,9 +112,7 @@ const Index: FC = () => {
|
|||
<Col lg={{ span: 7, offset: 1 }}>
|
||||
<Alert variant="secondary">
|
||||
<Stack className="align-items-start">
|
||||
<Badge bg="secondary" className="mb-2">
|
||||
{editBadge}
|
||||
</Badge>
|
||||
<span className="badge text-bg-secondary">{editBadge}</span>
|
||||
<Link to={itemLink} target="_blank">
|
||||
{itemTitle}
|
||||
</Link>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { memo, FC } from 'react';
|
||||
import { ListGroupItem, Badge } from 'react-bootstrap';
|
||||
import { ListGroupItem } from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Icon, Tag, FormatTime, BaseUserCard } from '@/components';
|
||||
|
@ -21,12 +21,11 @@ const Index: FC<Props> = ({ data }) => {
|
|||
return (
|
||||
<ListGroupItem className="py-3 px-0">
|
||||
<div className="mb-2 clearfix">
|
||||
<Badge
|
||||
bg="dark"
|
||||
className="me-2 float-start"
|
||||
<span
|
||||
className="float-start me-2 badge text-bg-dark"
|
||||
style={{ marginTop: '2px' }}>
|
||||
{data.object_type === 'question' ? 'Q' : 'A'}
|
||||
</Badge>
|
||||
</span>
|
||||
<a className="h5 mb-0 link-dark text-break" href={itemUrl}>
|
||||
{data.object.title}
|
||||
{data.object.status === 'closed'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { FC, memo } from 'react';
|
||||
import { Badge, OverlayTrigger, Tooltip } from 'react-bootstrap';
|
||||
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
|
@ -42,9 +42,7 @@ const Index: FC<Props> = ({ data }) => {
|
|||
<OverlayTrigger
|
||||
placement="top"
|
||||
overlay={<Tooltip>{t('mod_long')}</Tooltip>}>
|
||||
<Badge bg="light" className="text-body">
|
||||
{t('mod_short')}
|
||||
</Badge>
|
||||
<span className="badge text-bg-light">{t('mod_short')}</span>
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
)}
|
||||
|
|
Loading…
Reference in New Issue