fix: Optimize sidebar navigation style
This commit is contained in:
@@ -1515,6 +1515,7 @@ ui:
|
||||
display_below: Display below
|
||||
always_display: Always display
|
||||
or: or
|
||||
back_sites: Back to sites
|
||||
search:
|
||||
title: Search Results
|
||||
keywords: Keywords
|
||||
|
||||
@@ -1482,6 +1482,7 @@ ui:
|
||||
display_below: 展示当前
|
||||
always_display: 一直展示
|
||||
or: 或者
|
||||
back_sites: 返回网站
|
||||
search:
|
||||
title: 搜索结果
|
||||
keywords: 关键词
|
||||
|
||||
@@ -86,20 +86,25 @@ export const ADMIN_LIST_STATUS = {
|
||||
export const ADMIN_NAV_MENUS = [
|
||||
{
|
||||
name: 'dashboard',
|
||||
icon: 'speedometer',
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
name: 'contents',
|
||||
icon: 'file-earmark-text-fill',
|
||||
children: [{ name: 'questions' }, { name: 'answers' }],
|
||||
},
|
||||
{
|
||||
name: 'users',
|
||||
icon: 'people-fill',
|
||||
},
|
||||
{
|
||||
name: 'badges',
|
||||
icon: 'award-fill',
|
||||
},
|
||||
{
|
||||
name: 'customize',
|
||||
icon: 'palette-fill',
|
||||
children: [
|
||||
{
|
||||
name: 'themes',
|
||||
@@ -112,6 +117,7 @@ export const ADMIN_NAV_MENUS = [
|
||||
},
|
||||
{
|
||||
name: 'settings',
|
||||
icon: 'gear-fill',
|
||||
children: [
|
||||
{ name: 'general' },
|
||||
{ name: 'interface' },
|
||||
@@ -127,6 +133,7 @@ export const ADMIN_NAV_MENUS = [
|
||||
},
|
||||
{
|
||||
name: 'plugins',
|
||||
icon: 'plugin',
|
||||
children: [
|
||||
{
|
||||
name: 'installed_plugins',
|
||||
|
||||
@@ -17,20 +17,29 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
.page-main {
|
||||
max-width: 70%;
|
||||
.answer-container {
|
||||
width: 100%;
|
||||
min-height: calc(100vh - 95px - 62px);
|
||||
max-width: 1072px;
|
||||
}
|
||||
|
||||
.page-right-side {
|
||||
flex: none;
|
||||
width: 30%;
|
||||
width: 300px;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
// lg
|
||||
@media screen and (max-width: 1199.9px) {
|
||||
.answer-container {
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
.page-main {
|
||||
max-width: 100%;
|
||||
}
|
||||
.page-right-side {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,3 +24,21 @@
|
||||
.expanding .collapse-indicator {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
#answerAccordion {
|
||||
max-width: 208px;
|
||||
.nav-link {
|
||||
color: var(--an-side-nav-link);
|
||||
}
|
||||
.nav-link:focus-visible {
|
||||
box-shadow: none;
|
||||
}
|
||||
.nav-link:hover {
|
||||
color: var(--an-side-nav-link-hover-color);
|
||||
background-color: var(--bs-gray-100);
|
||||
}
|
||||
.nav-link.active {
|
||||
color: var(--an-side-nav-link-hover-color);
|
||||
background-color: var(--bs-gray-200);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,8 +51,10 @@ function MenuNode({
|
||||
}}
|
||||
className={classNames(
|
||||
'text-nowrap d-flex flex-nowrap align-items-center w-100',
|
||||
{ expanding, 'link-dark': activeKey !== menu.path },
|
||||
{ expanding, active: activeKey === menu.path },
|
||||
)}>
|
||||
{menu?.icon && <Icon name={menu.icon} className="me-2" />}
|
||||
|
||||
<span className="me-auto text-truncate">
|
||||
{menu.displayName ? menu.displayName : t(menu.name)}
|
||||
</span>
|
||||
@@ -73,8 +75,9 @@ function MenuNode({
|
||||
}}
|
||||
className={classNames(
|
||||
'text-nowrap d-flex flex-nowrap align-items-center w-100',
|
||||
{ expanding, 'link-dark': activeKey !== menu.path },
|
||||
{ expanding, active: activeKey === menu.path },
|
||||
)}>
|
||||
{menu?.icon && <Icon name={menu.icon} className="me-2" />}
|
||||
<span className="me-auto text-truncate">
|
||||
{menu.displayName ? menu.displayName : t(menu.name)}
|
||||
</span>
|
||||
@@ -88,7 +91,7 @@ function MenuNode({
|
||||
)}
|
||||
|
||||
{menu.children.length ? (
|
||||
<Accordion.Collapse eventKey={menu.path} className="ms-3">
|
||||
<Accordion.Collapse eventKey={menu.path} className="ms-4">
|
||||
<>
|
||||
{menu.children.map((leaf) => {
|
||||
return (
|
||||
@@ -169,7 +172,7 @@ const AccordionNav: FC<AccordionProps> = ({ menus = [], path = '/' }) => {
|
||||
setOpenKey(getOpenKey());
|
||||
}, [activeKey, menus]);
|
||||
return (
|
||||
<Accordion activeKey={openKey} flush>
|
||||
<Accordion activeKey={openKey} flush id="answerAccordion">
|
||||
<Nav variant="pills" className="flex-column" activeKey={activeKey}>
|
||||
{menus.map((li) => {
|
||||
return (
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#sideNav {
|
||||
max-width: 208px;
|
||||
.nav-link {
|
||||
color: var(--an-side-nav-link);
|
||||
}
|
||||
.nav-link:focus-visible {
|
||||
box-shadow: none;
|
||||
}
|
||||
.nav-link:hover {
|
||||
color: var(--an-side-nav-link-hover-color);
|
||||
background-color: var(--bs-gray-100);
|
||||
}
|
||||
.nav-link.active {
|
||||
color: var(--an-side-nav-link-hover-color);
|
||||
background-color: var(--bs-gray-200);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import { useEffect } from 'react';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
|
||||
import { AccordionNav, Icon } from '@/components';
|
||||
import { ADMIN_NAV_MENUS } from '@/common/constants';
|
||||
import { useQueryPlugins } from '@/services';
|
||||
import { interfaceStore } from '@/stores';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const AdminSideNav = () => {
|
||||
const { t } = useTranslation('translation', { keyPrefix: 'btns' });
|
||||
const interfaceLang = interfaceStore((_) => _.interface.language);
|
||||
const { data: configurablePlugins, mutate: updateConfigurablePlugins } =
|
||||
useQueryPlugins({
|
||||
status: 'active',
|
||||
have_config: true,
|
||||
});
|
||||
|
||||
const menus = cloneDeep(ADMIN_NAV_MENUS);
|
||||
if (configurablePlugins && configurablePlugins.length > 0) {
|
||||
menus.forEach((item) => {
|
||||
if (item.name === 'plugins' && item.children) {
|
||||
item.children = [
|
||||
...item.children,
|
||||
...configurablePlugins.map((plugin) => ({
|
||||
name: plugin.slug_name,
|
||||
displayName: plugin.name,
|
||||
})),
|
||||
];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const observePlugins = (evt) => {
|
||||
if (evt.data.msgType === 'refreshConfigurablePlugins') {
|
||||
updateConfigurablePlugins();
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
window.addEventListener('message', observePlugins);
|
||||
return () => {
|
||||
window.removeEventListener('message', observePlugins);
|
||||
};
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
updateConfigurablePlugins();
|
||||
}, [interfaceLang]);
|
||||
|
||||
return (
|
||||
<div id="adminSideNav">
|
||||
<NavLink to="/" className="pb-3 d-inline-block link-secondary">
|
||||
<Icon name="arrow-left" className="me-2" />
|
||||
<span>{t('back_sites')}</span>
|
||||
</NavLink>
|
||||
<AccordionNav menus={menus} path="/admin/" />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AdminSideNav;
|
||||
@@ -109,6 +109,7 @@ const handleCustomHeader = (content) => {
|
||||
};
|
||||
|
||||
const handleCustomFooter = (content) => {
|
||||
console.log('handleCustomFooter', content);
|
||||
const el = document.body;
|
||||
renderCustomArea(el, CUSTOM_MARK_FOOTER, 'beforeend', content);
|
||||
};
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Container } from 'react-bootstrap';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
|
||||
import dayjs from 'dayjs';
|
||||
@@ -33,32 +32,30 @@ const Index = () => {
|
||||
const cc = `${fullYear} ${siteName}`;
|
||||
|
||||
return (
|
||||
<footer className="py-3 bg-light">
|
||||
<Container>
|
||||
<p className="text-center mb-0 small">
|
||||
{/* Link to Terms of Service with right margin */}
|
||||
<Link to="/tos" className="me-3">
|
||||
{t('label', { keyPrefix: 'admin.legal.terms_of_service' })}
|
||||
</Link>
|
||||
<footer className="py-3 bg-light w-100">
|
||||
<p className="text-center mb-0 small">
|
||||
{/* Link to Terms of Service with right margin */}
|
||||
<Link to="/tos" className="me-3">
|
||||
{t('label', { keyPrefix: 'admin.legal.terms_of_service' })}
|
||||
</Link>
|
||||
|
||||
{/* Link to Privacy Policy with right margin for spacing */}
|
||||
<Link to="/privacy">
|
||||
{t('label', { keyPrefix: 'admin.legal.privacy_policy' })}
|
||||
</Link>
|
||||
</p>
|
||||
<p className="text-center mb-0 small">
|
||||
<Trans i18nKey="footer.build_on" values={{ cc }}>
|
||||
Powered by
|
||||
{/* eslint-disable-next-line react/jsx-no-target-blank */}
|
||||
<a href="https://answer.apache.org" target="_blank">
|
||||
Apache Answer
|
||||
</a>
|
||||
- the open-source software that powers Q&A communities.
|
||||
<br />
|
||||
Made with love. © 2022 Answer.
|
||||
</Trans>
|
||||
</p>
|
||||
</Container>
|
||||
{/* Link to Privacy Policy with right margin for spacing */}
|
||||
<Link to="/privacy">
|
||||
{t('label', { keyPrefix: 'admin.legal.privacy_policy' })}
|
||||
</Link>
|
||||
</p>
|
||||
<p className="text-center mb-0 small">
|
||||
<Trans i18nKey="footer.build_on" values={{ cc }}>
|
||||
Powered by
|
||||
{/* eslint-disable-next-line react/jsx-no-target-blank */}
|
||||
<a href="https://answer.apache.org" target="_blank">
|
||||
Apache Answer
|
||||
</a>
|
||||
- the open-source software that powers Q&A communities.
|
||||
<br />
|
||||
Made with love. © 2022 Answer.
|
||||
</Trans>
|
||||
</p>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
@import 'bootstrap/scss/functions';
|
||||
@import 'bootstrap/scss/variables';
|
||||
#header {
|
||||
z-index: 1041;
|
||||
transform: translate3d(0, 0, 0);
|
||||
--bs-navbar-padding-y: 0.75rem;
|
||||
background: var(--bs-primary);
|
||||
@@ -46,7 +47,7 @@
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.lg-none {
|
||||
.xl-none {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@@ -111,13 +112,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 991.9px) {
|
||||
@media (max-width: 1199.9px) {
|
||||
#header {
|
||||
.nav-grow {
|
||||
flex-grow: 1 !important;
|
||||
}
|
||||
|
||||
.lg-none {
|
||||
.xl-none {
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
|
||||
+154
-161
@@ -18,14 +18,7 @@
|
||||
*/
|
||||
|
||||
import { FC, memo, useState, useEffect } from 'react';
|
||||
import {
|
||||
Navbar,
|
||||
Container,
|
||||
Nav,
|
||||
Form,
|
||||
FormControl,
|
||||
Col,
|
||||
} from 'react-bootstrap';
|
||||
import { Navbar, Nav, Form, FormControl, Col } from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
useSearchParams,
|
||||
@@ -47,7 +40,7 @@ import {
|
||||
sideNavStore,
|
||||
} from '@/stores';
|
||||
import { logout, useQueryNotificationStatus } from '@/services';
|
||||
import { Icon } from '@/components';
|
||||
import { Icon, MobileSideNav } from '@/components';
|
||||
|
||||
import NavItems from './components/NavItems';
|
||||
|
||||
@@ -64,8 +57,9 @@ const Header: FC = () => {
|
||||
const siteInfo = siteInfoStore((state) => state.siteInfo);
|
||||
const brandingInfo = brandingStore((state) => state.branding);
|
||||
const loginSetting = loginSettingStore((state) => state.login);
|
||||
const { updateReview, updateVisible } = sideNavStore();
|
||||
const { updateReview } = sideNavStore();
|
||||
const { data: redDot } = useQueryNotificationStatus();
|
||||
const [showMobileSideNav, setShowMobileSideNav] = useState(false);
|
||||
/**
|
||||
* Automatically append `tag` information when creating a question
|
||||
*/
|
||||
@@ -108,18 +102,11 @@ const Header: FC = () => {
|
||||
}, [q]);
|
||||
|
||||
useEffect(() => {
|
||||
const collapse = document.querySelector('#navBarContent');
|
||||
if (collapse && collapse.classList.contains('show')) {
|
||||
const toggle = document.querySelector('#navBarToggle') as HTMLElement;
|
||||
if (toggle) {
|
||||
toggle?.click();
|
||||
}
|
||||
}
|
||||
|
||||
// clear search input when navigate to other page
|
||||
if (location.pathname !== '/search' && searchStr) {
|
||||
setSearch('');
|
||||
}
|
||||
setShowMobileSideNav(false);
|
||||
}, [location.pathname]);
|
||||
|
||||
let navbarStyle = 'theme-colored';
|
||||
@@ -128,159 +115,165 @@ const Header: FC = () => {
|
||||
navbarStyle = `theme-${theme_config[theme].navbar_style}`;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => {
|
||||
if (window.innerWidth >= 1199.9) {
|
||||
setShowMobileSideNav(false);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Navbar
|
||||
variant={navbarStyle === 'theme-colored' ? 'dark' : ''}
|
||||
expand="lg"
|
||||
className={classnames('sticky-top', navbarStyle)}
|
||||
id="header">
|
||||
<Container className="d-flex align-items-center">
|
||||
<Navbar.Toggle
|
||||
aria-controls="navBarContent"
|
||||
className="answer-navBar me-2"
|
||||
id="navBarToggle"
|
||||
onClick={() => {
|
||||
updateVisible();
|
||||
}}
|
||||
/>
|
||||
<>
|
||||
<Navbar
|
||||
variant={navbarStyle === 'theme-colored' ? 'dark' : ''}
|
||||
expand="xl"
|
||||
className={classnames('sticky-top', navbarStyle)}
|
||||
id="header">
|
||||
<div className="w-100 d-flex align-items-center px-3">
|
||||
<Navbar.Toggle
|
||||
className="answer-navBar me-2"
|
||||
onClick={() => {
|
||||
setShowMobileSideNav(!showMobileSideNav);
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="d-flex justify-content-between align-items-center nav-grow flex-nowrap">
|
||||
<Navbar.Brand to="/" as={Link} className="lh-1 me-0 me-sm-5 p-0">
|
||||
{brandingInfo.logo ? (
|
||||
<>
|
||||
<img
|
||||
className="d-none d-lg-block logo me-0"
|
||||
src={brandingInfo.logo}
|
||||
alt={siteInfo.name}
|
||||
/>
|
||||
<div className="d-flex justify-content-between align-items-center nav-grow flex-nowrap">
|
||||
<Navbar.Brand to="/" as={Link} className="lh-1 me-0 me-sm-5 p-0">
|
||||
{brandingInfo.logo ? (
|
||||
<>
|
||||
<img
|
||||
className="d-none d-xl-block logo me-0"
|
||||
src={brandingInfo.logo}
|
||||
alt={siteInfo.name}
|
||||
/>
|
||||
|
||||
<img
|
||||
className="lg-none logo me-0"
|
||||
src={brandingInfo.mobile_logo || brandingInfo.logo}
|
||||
alt={siteInfo.name}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<span>{siteInfo.name}</span>
|
||||
)}
|
||||
</Navbar.Brand>
|
||||
|
||||
{/* mobile nav */}
|
||||
<div className="d-flex lg-none align-items-center flex-lg-nowrap">
|
||||
{user?.username ? (
|
||||
<NavItems
|
||||
redDot={redDot}
|
||||
userInfo={user}
|
||||
logOut={(e) => handleLogout(e)}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<Link
|
||||
className={classnames('me-2 btn btn-link', {
|
||||
'link-light': navbarStyle === 'theme-colored',
|
||||
'link-primary': navbarStyle !== 'theme-colored',
|
||||
})}
|
||||
onClick={() => floppyNavigation.storageLoginRedirect()}
|
||||
to={userCenter.getLoginUrl()}>
|
||||
{t('btns.login')}
|
||||
</Link>
|
||||
{loginSetting.allow_new_registrations && (
|
||||
<Link
|
||||
className={classnames(
|
||||
'btn',
|
||||
navbarStyle === 'theme-colored'
|
||||
? 'btn-light'
|
||||
: 'btn-primary',
|
||||
)}
|
||||
to={userCenter.getSignUpUrl()}>
|
||||
{t('btns.signup')}
|
||||
</Link>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Navbar.Collapse id="navBarContent" className="me-auto">
|
||||
<hr className="hr lg-none mb-3" style={{ marginTop: '12px' }} />
|
||||
<Col lg={8} className="ps-0">
|
||||
<Form
|
||||
action="/search"
|
||||
className="w-100 maxw-400 position-relative"
|
||||
onSubmit={handleSearch}>
|
||||
<div className="search-wrap" onClick={handleSearch}>
|
||||
<Icon name="search" className="search-icon" />
|
||||
</div>
|
||||
<FormControl
|
||||
type="search"
|
||||
placeholder={t('header.search.placeholder')}
|
||||
className="placeholder-search"
|
||||
value={searchStr}
|
||||
name="q"
|
||||
onChange={(e) => handleInput(e.target.value)}
|
||||
/>
|
||||
</Form>
|
||||
</Col>
|
||||
|
||||
<Nav.Item className="lg-none mt-3 pb-1">
|
||||
<Link
|
||||
to={askUrl}
|
||||
className="text-capitalize text-nowrap btn btn-light">
|
||||
{t('btns.add_question')}
|
||||
</Link>
|
||||
</Nav.Item>
|
||||
{/* pc nav */}
|
||||
<Col
|
||||
lg={4}
|
||||
className="d-none d-lg-flex justify-content-start justify-content-sm-end">
|
||||
{user?.username ? (
|
||||
<Nav className="d-flex align-items-center flex-lg-nowrap">
|
||||
<Nav.Item className="me-3">
|
||||
<Link
|
||||
to={askUrl}
|
||||
className={classnames('text-capitalize text-nowrap btn', {
|
||||
'btn-light': navbarStyle !== 'theme-light',
|
||||
'btn-primary': navbarStyle === 'theme-light',
|
||||
})}>
|
||||
{t('btns.add_question')}
|
||||
</Link>
|
||||
</Nav.Item>
|
||||
<img
|
||||
className="xl-none logo me-0"
|
||||
src={brandingInfo.mobile_logo || brandingInfo.logo}
|
||||
alt={siteInfo.name}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<span>{siteInfo.name}</span>
|
||||
)}
|
||||
</Navbar.Brand>
|
||||
|
||||
{/* mobile nav */}
|
||||
<div className="d-flex xl-none align-items-center flex-lg-nowrap">
|
||||
{user?.username ? (
|
||||
<NavItems
|
||||
redDot={redDot}
|
||||
userInfo={user}
|
||||
logOut={handleLogout}
|
||||
logOut={(e) => handleLogout(e)}
|
||||
/>
|
||||
</Nav>
|
||||
) : (
|
||||
<>
|
||||
<Link
|
||||
className={classnames('me-2 btn btn-link', {
|
||||
'link-light': navbarStyle === 'theme-colored',
|
||||
'link-primary': navbarStyle !== 'theme-colored',
|
||||
})}
|
||||
onClick={() => floppyNavigation.storageLoginRedirect()}
|
||||
to={userCenter.getLoginUrl()}>
|
||||
{t('btns.login')}
|
||||
</Link>
|
||||
{loginSetting.allow_new_registrations && (
|
||||
) : (
|
||||
<>
|
||||
<Link
|
||||
className={classnames(
|
||||
'btn',
|
||||
navbarStyle === 'theme-colored'
|
||||
? 'btn-light'
|
||||
: 'btn-primary',
|
||||
)}
|
||||
to={userCenter.getSignUpUrl()}>
|
||||
{t('btns.signup')}
|
||||
className={classnames('me-2 btn btn-link', {
|
||||
'link-light': navbarStyle === 'theme-colored',
|
||||
'link-primary': navbarStyle !== 'theme-colored',
|
||||
})}
|
||||
onClick={() => floppyNavigation.storageLoginRedirect()}
|
||||
to={userCenter.getLoginUrl()}>
|
||||
{t('btns.login')}
|
||||
</Link>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Col>
|
||||
</Navbar.Collapse>
|
||||
</Container>
|
||||
</Navbar>
|
||||
{loginSetting.allow_new_registrations && (
|
||||
<Link
|
||||
className={classnames(
|
||||
'btn',
|
||||
navbarStyle === 'theme-colored'
|
||||
? 'btn-light'
|
||||
: 'btn-primary',
|
||||
)}
|
||||
to={userCenter.getSignUpUrl()}>
|
||||
{t('btns.signup')}
|
||||
</Link>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="d-none d-xl-flex flex-grow-1 me-auto">
|
||||
<Col lg={8} className="d-none d-xl-block ps-0">
|
||||
<Form
|
||||
action="/search"
|
||||
className="w-100 maxw-400 position-relative"
|
||||
onSubmit={handleSearch}>
|
||||
<div className="search-wrap" onClick={handleSearch}>
|
||||
<Icon name="search" className="search-icon" />
|
||||
</div>
|
||||
<FormControl
|
||||
type="search"
|
||||
placeholder="sddfsdf"
|
||||
className="placeholder-search"
|
||||
value={searchStr}
|
||||
name="q"
|
||||
onChange={(e) => handleInput(e.target.value)}
|
||||
/>
|
||||
</Form>
|
||||
</Col>
|
||||
|
||||
{/* pc nav */}
|
||||
<Col
|
||||
lg={4}
|
||||
className="d-none d-xl-flex justify-content-start justify-content-sm-end">
|
||||
{user?.username ? (
|
||||
<Nav className="d-flex align-items-center flex-lg-nowrap">
|
||||
<Nav.Item className="me-3">
|
||||
<Link
|
||||
to={askUrl}
|
||||
className={classnames('text-capitalize text-nowrap btn', {
|
||||
'btn-light': navbarStyle !== 'theme-light',
|
||||
'btn-primary': navbarStyle === 'theme-light',
|
||||
})}>
|
||||
{t('btns.add_question')}
|
||||
</Link>
|
||||
</Nav.Item>
|
||||
|
||||
<NavItems
|
||||
redDot={redDot}
|
||||
userInfo={user}
|
||||
logOut={handleLogout}
|
||||
/>
|
||||
</Nav>
|
||||
) : (
|
||||
<>
|
||||
<Link
|
||||
className={classnames('me-2 btn btn-link', {
|
||||
'link-light': navbarStyle === 'theme-colored',
|
||||
'link-primary': navbarStyle !== 'theme-colored',
|
||||
})}
|
||||
onClick={() => floppyNavigation.storageLoginRedirect()}
|
||||
to={userCenter.getLoginUrl()}>
|
||||
{t('btns.login')}
|
||||
</Link>
|
||||
{loginSetting.allow_new_registrations && (
|
||||
<Link
|
||||
className={classnames(
|
||||
'btn',
|
||||
navbarStyle === 'theme-colored'
|
||||
? 'btn-light'
|
||||
: 'btn-primary',
|
||||
)}
|
||||
to={userCenter.getSignUpUrl()}>
|
||||
{t('btns.signup')}
|
||||
</Link>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Col>
|
||||
</div>
|
||||
</div>
|
||||
</Navbar>
|
||||
<MobileSideNav show={showMobileSideNav} onHide={setShowMobileSideNav} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#mobileSideNav {
|
||||
top: 60px !important;
|
||||
width: 240px !important;
|
||||
height: calc(100vh - 60px) !important;
|
||||
overflow-y: auto;
|
||||
flex: none;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Offcanvas } from 'react-bootstrap';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
import { SideNav, AdminSideNav } from '@/components';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
const MobileSideNav = ({ show, onHide }) => {
|
||||
const { pathname } = useLocation();
|
||||
const isAdmin = pathname.includes('/admin');
|
||||
return (
|
||||
<Offcanvas
|
||||
show={show}
|
||||
onHide={() => {
|
||||
onHide(false);
|
||||
}}
|
||||
id="mobileSideNav"
|
||||
className="px-3 py-4">
|
||||
<Offcanvas.Body className="p-0">
|
||||
{isAdmin ? <AdminSideNav /> : <SideNav />}
|
||||
</Offcanvas.Body>
|
||||
</Offcanvas>
|
||||
);
|
||||
};
|
||||
|
||||
export default MobileSideNav;
|
||||
@@ -17,19 +17,17 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
#pcSideNav {
|
||||
width: 240px;
|
||||
top: 62px;
|
||||
box-sizing: border-box;
|
||||
height: calc(100vh - 62px);
|
||||
overflow-y: auto;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
#sideNav {
|
||||
.nav-wrap {
|
||||
position: sticky;
|
||||
width: auto;
|
||||
top: 62px;
|
||||
box-sizing: border-box;
|
||||
max-height: calc(100vh - 62px);
|
||||
overflow-y: auto;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.nav {
|
||||
max-width: calc(100% - 24px);
|
||||
}
|
||||
max-width: 208px;
|
||||
.nav-link {
|
||||
color: var(--an-side-nav-link);
|
||||
}
|
||||
@@ -44,26 +42,12 @@
|
||||
color: var(--an-side-nav-link-hover-color);
|
||||
background-color: var(--bs-gray-200);
|
||||
}
|
||||
.side-nav-right-line {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 12px;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background-color: var(--bs-border-color);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 991.9px) {
|
||||
#sideNav {
|
||||
.nav-wrap {
|
||||
max-height: fit-content;
|
||||
}
|
||||
.side-left-nav-wrap {
|
||||
.nav {
|
||||
max-width: 100%;
|
||||
}
|
||||
.side-nav-right-line {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,10 @@
|
||||
*/
|
||||
|
||||
import { FC } from 'react';
|
||||
import { Col, Nav } from 'react-bootstrap';
|
||||
import { Nav } from 'react-bootstrap';
|
||||
import { NavLink, useLocation } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import classnames from 'classnames';
|
||||
|
||||
import { loggedUserInfoStore, sideNavStore } from '@/stores';
|
||||
import { Icon } from '@/components';
|
||||
import './index.scss';
|
||||
@@ -32,75 +30,62 @@ const Index: FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const { pathname } = useLocation();
|
||||
const { user: userInfo } = loggedUserInfoStore();
|
||||
const { visible, can_revision, revision } = sideNavStore();
|
||||
const { can_revision, revision } = sideNavStore();
|
||||
|
||||
return (
|
||||
<Col
|
||||
xl={2}
|
||||
lg={3}
|
||||
md={12}
|
||||
className={classnames(
|
||||
'position-relative',
|
||||
visible ? '' : 'd-none d-lg-block',
|
||||
)}
|
||||
id="sideNav">
|
||||
<div className="nav-wrap pt-4">
|
||||
<Nav variant="pills" className="flex-column">
|
||||
<NavLink
|
||||
to="/questions"
|
||||
className={({ isActive }) =>
|
||||
isActive || pathname === '/' ? 'nav-link active' : 'nav-link'
|
||||
}>
|
||||
<Icon name="question-circle-fill" className="me-2" />
|
||||
<span>{t('header.nav.question')}</span>
|
||||
</NavLink>
|
||||
<Nav variant="pills" className="flex-column" id="sideNav">
|
||||
<NavLink
|
||||
to="/questions"
|
||||
className={({ isActive }) =>
|
||||
isActive || pathname === '/' ? 'nav-link active' : 'nav-link'
|
||||
}>
|
||||
<Icon name="question-circle-fill" className="me-2" />
|
||||
<span>{t('header.nav.question')}</span>
|
||||
</NavLink>
|
||||
|
||||
<NavLink
|
||||
to="/tags"
|
||||
className={() =>
|
||||
pathname === '/tags' ? 'nav-link active' : 'nav-link'
|
||||
}>
|
||||
<Icon name="tags-fill" className="me-2" />
|
||||
<span>{t('header.nav.tag')}</span>
|
||||
</NavLink>
|
||||
<NavLink
|
||||
to="/tags"
|
||||
className={() =>
|
||||
pathname === '/tags' ? 'nav-link active' : 'nav-link'
|
||||
}>
|
||||
<Icon name="tags-fill" className="me-2" />
|
||||
<span>{t('header.nav.tag')}</span>
|
||||
</NavLink>
|
||||
|
||||
<NavLink to="/users" className="nav-link">
|
||||
<Icon name="people-fill" className="me-2" />
|
||||
<span>{t('header.nav.user')}</span>
|
||||
</NavLink>
|
||||
<NavLink to="/users" className="nav-link">
|
||||
<Icon name="people-fill" className="me-2" />
|
||||
<span>{t('header.nav.user')}</span>
|
||||
</NavLink>
|
||||
|
||||
<NavLink to="/badges" className="nav-link">
|
||||
<Icon name="award-fill" className="me-2" />
|
||||
<span>{t('header.nav.badges')}</span>
|
||||
</NavLink>
|
||||
<NavLink to="/badges" className="nav-link">
|
||||
<Icon name="award-fill" className="me-2" />
|
||||
<span>{t('header.nav.badges')}</span>
|
||||
</NavLink>
|
||||
|
||||
{can_revision || userInfo?.role_id === 2 ? (
|
||||
<>
|
||||
<div className="py-2 px-3 mt-3 small fw-bold">
|
||||
{t('header.nav.moderation')}
|
||||
</div>
|
||||
{can_revision && (
|
||||
<NavLink to="/review" className="nav-link">
|
||||
<Icon name="shield-fill-check" className="me-2" />
|
||||
<span>{t('header.nav.review')}</span>
|
||||
<span className="float-end">
|
||||
{revision > 99 ? '99+' : revision > 0 ? revision : ''}
|
||||
</span>
|
||||
</NavLink>
|
||||
)}
|
||||
{can_revision || userInfo?.role_id === 2 ? (
|
||||
<>
|
||||
<div className="py-2 px-3 mt-3 small fw-bold">
|
||||
{t('header.nav.moderation')}
|
||||
</div>
|
||||
{can_revision && (
|
||||
<NavLink to="/review" className="nav-link">
|
||||
<Icon name="shield-fill-check" className="me-2" />
|
||||
<span>{t('header.nav.review')}</span>
|
||||
<span className="float-end">
|
||||
{revision > 99 ? '99+' : revision > 0 ? revision : ''}
|
||||
</span>
|
||||
</NavLink>
|
||||
)}
|
||||
|
||||
{userInfo?.role_id === 2 ? (
|
||||
<NavLink to="/admin" className="nav-link">
|
||||
<Icon name="gear-fill" className="me-2" />
|
||||
<span>{t('header.nav.admin')}</span>
|
||||
</NavLink>
|
||||
) : null}
|
||||
</>
|
||||
{userInfo?.role_id === 2 ? (
|
||||
<NavLink to="/admin" className="nav-link">
|
||||
<Icon name="gear-fill" className="me-2" />
|
||||
<span>{t('header.nav.admin')}</span>
|
||||
</NavLink>
|
||||
) : null}
|
||||
</Nav>
|
||||
</div>
|
||||
<div className="side-nav-right-line" />
|
||||
</Col>
|
||||
</>
|
||||
) : null}
|
||||
</Nav>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -62,6 +62,8 @@ import PluginRender from './PluginRender';
|
||||
import HighlightText from './HighlightText';
|
||||
import CardBadge from './CardBadge';
|
||||
import PinList from './PinList';
|
||||
import MobileSideNav from './MobileSideNav';
|
||||
import AdminSideNav from './AdminSideNav';
|
||||
|
||||
export {
|
||||
Avatar,
|
||||
@@ -111,5 +113,7 @@ export {
|
||||
HighlightText,
|
||||
CardBadge,
|
||||
PinList,
|
||||
MobileSideNav,
|
||||
AdminSideNav,
|
||||
};
|
||||
export type { EditorRef, JSONSchema, UISchema };
|
||||
|
||||
@@ -97,8 +97,8 @@ const Answers: FC = () => {
|
||||
return (
|
||||
<>
|
||||
<h3 className="mb-4">{t('page_title')}</h3>
|
||||
<div className="d-flex flex-wrap justify-content-between align-items-center mb-3">
|
||||
<Stack direction="horizontal" gap={3}>
|
||||
<div className="d-flex flex-wrap justify-content-between align-items-center">
|
||||
<Stack direction="horizontal" gap={3} className="mb-3">
|
||||
<QueryGroup
|
||||
data={answerFilterItems}
|
||||
currentSort={curFilter}
|
||||
@@ -122,7 +122,7 @@ const Answers: FC = () => {
|
||||
type="search"
|
||||
placeholder={t('filter.placeholder')}
|
||||
style={{ width: '12.25rem' }}
|
||||
className="mt-3 mt-sm-0"
|
||||
className="mb-3"
|
||||
/>
|
||||
</div>
|
||||
<Table responsive="md">
|
||||
|
||||
@@ -78,8 +78,8 @@ const Badges: FC = () => {
|
||||
return (
|
||||
<>
|
||||
<h3 className="mb-4">{t('title')}</h3>
|
||||
<div className="d-flex flex-wrap justify-content-between align-items-center mb-3">
|
||||
<Stack direction="horizontal" gap={3}>
|
||||
<div className="d-flex flex-wrap justify-content-between align-items-center">
|
||||
<Stack direction="horizontal" gap={3} className="mb-3">
|
||||
<QueryGroup
|
||||
data={BadgeFilterKeys}
|
||||
currentSort={curFilter}
|
||||
@@ -95,19 +95,19 @@ const Badges: FC = () => {
|
||||
onChange={handleFilter}
|
||||
placeholder={t('filter.placeholder')}
|
||||
style={{ width: '12.25rem' }}
|
||||
className="mt-3 mt-sm-0"
|
||||
className="mb-3"
|
||||
/>
|
||||
</div>
|
||||
<Table responsive="md">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{t('name')}</th>
|
||||
<th>{t('group')}</th>
|
||||
<th>{t('awards')}</th>
|
||||
|
||||
<th>{t('status')}</th>
|
||||
|
||||
<th className="text-end">{t('action')}</th>
|
||||
<th className="min-w-15">{t('name')}</th>
|
||||
<th style={{ width: '19%' }}>{t('group')}</th>
|
||||
<th style={{ width: '14%' }}>{t('awards')}</th>
|
||||
<th style={{ width: '12%' }}>{t('status')}</th>
|
||||
<th className="text-end" style={{ width: '9%' }}>
|
||||
{t('action')}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="align-middle">
|
||||
|
||||
@@ -95,8 +95,8 @@ const Questions: FC = () => {
|
||||
return (
|
||||
<>
|
||||
<h3 className="mb-4">{t('page_title')}</h3>
|
||||
<div className="d-flex flex-wrap justify-content-between align-items-center mb-3">
|
||||
<Stack direction="horizontal" gap={3}>
|
||||
<div className="d-flex flex-wrap justify-content-between align-items-center">
|
||||
<Stack direction="horizontal" gap={3} className="mb-3">
|
||||
<QueryGroup
|
||||
data={questionFilterItems}
|
||||
currentSort={curFilter}
|
||||
@@ -120,7 +120,7 @@ const Questions: FC = () => {
|
||||
placeholder={t('filter.placeholder')}
|
||||
onChange={handleFilter}
|
||||
style={{ width: '12.25rem' }}
|
||||
className="mt-3 mt-sm-0"
|
||||
className="mb-3"
|
||||
/>
|
||||
</div>
|
||||
<Table responsive="md">
|
||||
|
||||
@@ -190,8 +190,8 @@ const Users: FC = () => {
|
||||
return (
|
||||
<>
|
||||
<h3 className="mb-4">{t('title')}</h3>
|
||||
<div className="d-flex flex-wrap justify-content-between align-items-center mb-3">
|
||||
<Stack direction="horizontal" gap={3}>
|
||||
<div className="d-flex flex-wrap justify-content-between align-items-center">
|
||||
<Stack direction="horizontal" gap={3} className="mb-3">
|
||||
<QueryGroup
|
||||
data={UserFilterKeys}
|
||||
currentSort={curFilter}
|
||||
@@ -223,7 +223,7 @@ const Users: FC = () => {
|
||||
onChange={handleFilter}
|
||||
placeholder={t('filter.placeholder')}
|
||||
style={{ width: '12.25rem' }}
|
||||
className="mt-3 mt-sm-0"
|
||||
className="mb-3"
|
||||
/>
|
||||
</div>
|
||||
<Table responsive="md">
|
||||
|
||||
@@ -18,8 +18,9 @@
|
||||
*/
|
||||
|
||||
.admin-container {
|
||||
padding-top: 1.5rem;
|
||||
padding-bottom: 1.5rem;
|
||||
.page-right-side {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.min-w-15 {
|
||||
|
||||
@@ -17,19 +17,15 @@
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import { FC, useEffect } from 'react';
|
||||
import { Container, Row, Col } from 'react-bootstrap';
|
||||
import { FC } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Row, Col } from 'react-bootstrap';
|
||||
import { Outlet, useMatch } from 'react-router-dom';
|
||||
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
|
||||
import { usePageTags } from '@/hooks';
|
||||
import { AccordionNav } from '@/components';
|
||||
import { ADMIN_NAV_MENUS } from '@/common/constants';
|
||||
import { useQueryPlugins } from '@/services';
|
||||
import { interfaceStore } from '@/stores';
|
||||
import { AdminSideNav, Footer } from '@/components';
|
||||
|
||||
import '@/common/sideNavLayout.scss';
|
||||
import './index.scss';
|
||||
|
||||
const g10Paths = [
|
||||
@@ -46,66 +42,32 @@ const Index: FC = () => {
|
||||
const pathMatch = useMatch('/admin/:path');
|
||||
const curPath = pathMatch?.params.path || 'dashboard';
|
||||
|
||||
const interfaceLang = interfaceStore((_) => _.interface.language);
|
||||
const { data: configurablePlugins, mutate: updateConfigurablePlugins } =
|
||||
useQueryPlugins({
|
||||
status: 'active',
|
||||
have_config: true,
|
||||
});
|
||||
|
||||
const menus = cloneDeep(ADMIN_NAV_MENUS);
|
||||
if (configurablePlugins && configurablePlugins.length > 0) {
|
||||
menus.forEach((item) => {
|
||||
if (item.name === 'plugins' && item.children) {
|
||||
item.children = [
|
||||
...item.children,
|
||||
...configurablePlugins.map((plugin) => ({
|
||||
name: plugin.slug_name,
|
||||
displayName: plugin.name,
|
||||
})),
|
||||
];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const observePlugins = (evt) => {
|
||||
if (evt.data.msgType === 'refreshConfigurablePlugins') {
|
||||
updateConfigurablePlugins();
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
window.addEventListener('message', observePlugins);
|
||||
return () => {
|
||||
window.removeEventListener('message', observePlugins);
|
||||
};
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
updateConfigurablePlugins();
|
||||
}, [interfaceLang]);
|
||||
|
||||
usePageTags({
|
||||
title: t('admin'),
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<div className="bg-light py-2">
|
||||
<Container className="py-1">
|
||||
<h6 className="mb-0 fw-bold lh-base">
|
||||
{t('title', { keyPrefix: 'admin.admin_header' })}
|
||||
</h6>
|
||||
</Container>
|
||||
<div className="admin-container d-flex">
|
||||
<div
|
||||
className="position-sticky px-3 border-end pt-4 d-none d-xl-block"
|
||||
id="pcSideNav">
|
||||
<AdminSideNav />
|
||||
</div>
|
||||
<Container className="admin-container">
|
||||
<Row>
|
||||
<Col lg={2}>
|
||||
<AccordionNav menus={menus} path="/admin/" />
|
||||
</Col>
|
||||
<Col lg={g10Paths.find((v) => curPath === v) ? 10 : 6}>
|
||||
<Outlet />
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
</>
|
||||
<div className="flex-fill w-100">
|
||||
<div className="d-flex justify-content-center px-0 px-md-4">
|
||||
<div className="answer-container">
|
||||
<Row className="py-4">
|
||||
<Col className="page-main flex-auto">
|
||||
<Outlet />
|
||||
</Col>
|
||||
{g10Paths.find((v) => curPath === v) ? null : (
|
||||
<Col className="page-right-side" />
|
||||
)}
|
||||
</Row>
|
||||
</div>
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ import {
|
||||
} from '@/stores';
|
||||
import {
|
||||
Header,
|
||||
Footer,
|
||||
Toast,
|
||||
Customize,
|
||||
CustomizeTheme,
|
||||
@@ -210,7 +209,6 @@ const Layout: FC = () => {
|
||||
revalidateOnFocus: false,
|
||||
}}>
|
||||
<Header />
|
||||
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}
|
||||
<div className="position-relative page-wrap d-flex flex-column flex-fill">
|
||||
{httpStatusCode ? (
|
||||
<HttpErrorContent httpCode={httpStatusCode} />
|
||||
@@ -219,7 +217,6 @@ const Layout: FC = () => {
|
||||
)}
|
||||
</div>
|
||||
<Toast msg={toastMsg} variant={variant} onClose={closeToast} />
|
||||
<Footer />
|
||||
<Customize />
|
||||
<LoginToContinueModal visible={showLoginToContinueModal} />
|
||||
<BadgeModal
|
||||
|
||||
@@ -18,23 +18,29 @@
|
||||
*/
|
||||
|
||||
import { FC, memo } from 'react';
|
||||
import { Container, Row, Col } from 'react-bootstrap';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
import { SideNav } from '@/components';
|
||||
import { SideNav, Footer } from '@/components';
|
||||
|
||||
import '@/common/sideNavLayout.scss';
|
||||
|
||||
const Index: FC = () => {
|
||||
return (
|
||||
<Container className="d-flex flex-column flex-fill">
|
||||
<Row className="flex-fill">
|
||||
<div className="d-flex">
|
||||
<div
|
||||
className="position-sticky px-3 border-end pt-4 d-none d-xl-block"
|
||||
id="pcSideNav">
|
||||
<SideNav />
|
||||
<Col xl={10} lg={9} md={12}>
|
||||
<Outlet />
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
</div>
|
||||
<div className="flex-fill w-100">
|
||||
<div className="d-flex justify-content-center px-0 px-md-4">
|
||||
<div className="answer-container">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -25,22 +25,14 @@ type reviewData = {
|
||||
};
|
||||
|
||||
interface ErrorCodeType {
|
||||
visible: boolean;
|
||||
can_revision: boolean;
|
||||
revision: number;
|
||||
updateVisible: () => void;
|
||||
updateReview: (params: reviewData) => void;
|
||||
}
|
||||
|
||||
const Index = create<ErrorCodeType>((set) => ({
|
||||
visible: false,
|
||||
can_revision: false,
|
||||
revision: 0,
|
||||
updateVisible: () => {
|
||||
set((state) => {
|
||||
return { visible: !state.visible };
|
||||
});
|
||||
},
|
||||
updateReview: (params: reviewData) => {
|
||||
set(() => {
|
||||
return { ...params };
|
||||
|
||||
@@ -169,16 +169,16 @@
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="me-auto navbar-collapse collapse" id="navBarContent">
|
||||
<hr class="hr lg-none mb-2" style="margin-top: 12px" />
|
||||
<hr class="hr xl-none mb-2" style="margin-top: 12px" />
|
||||
<div class="ps-0 col-lg-8">
|
||||
<form action="/search" class="w-100 maxw-400">
|
||||
<input placeholder="Search" name="q" type="search" class="placeholder-search form-control" value="">
|
||||
</form>
|
||||
</div>
|
||||
<!-- <div class="lg-none mt-3 pb-1 nav-item">
|
||||
<!-- <div class="xl-none mt-3 pb-1 nav-item">
|
||||
<a class="text-capitalize text-nowrap btn btn-light" href="/questions/ask">{{translator $.language "ui.btns.add_question"}}</a>
|
||||
</div> -->
|
||||
<div class="d-none d-lg-flex justify-content-start justify-content-sm-end col-lg-4">
|
||||
<div class="d-none d-xl-flex justify-content-start justify-content-sm-end col-lg-4">
|
||||
<!-- <a role="button" tabindex="0" href="/users/login" class="me-2 link-light btn btn-link">
|
||||
{{translator $.language "ui.btns.login"}}
|
||||
</a>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
-->
|
||||
{{define "sidenav"}}
|
||||
<div id="sideNav" class="position-relative d-none d-lg-block col-xl-2 col-lg-3 col-md-12">
|
||||
<div id="sideNav" class="position-relative d-none d-xl-block col-xl-2 col-lg-3 col-md-12">
|
||||
<div class="nav-wrap pt-4">
|
||||
<div class="flex-column nav nav-pills">
|
||||
<a class="nav-link active" href="{{$.baseURL}}/questions">
|
||||
|
||||
Reference in New Issue
Block a user