group posts

This commit is contained in:
Виталий Лавшонок
2025-11-16 00:03:07 +03:00
parent 56b6f9b339
commit b949837e13
9 changed files with 937 additions and 643 deletions

1434
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -18,13 +18,14 @@
"clsx": "^2.1.1",
"framer-motion": "^11.9.0",
"highlight.js": "^11.11.1",
"monaco-editor": "^0.54.0",
"monaco-editor": "^0.53.0",
"postcss": "^8.4.47",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-markdown": "^10.1.0",
"react-redux": "^9.2.0",
"react-router-dom": "^7.9.4",
"react-toastify": "^11.0.5",
"rehype-highlight": "^7.0.2",
"rehype-raw": "^7.0.0",
"rehype-sanitize": "^6.0.0",
@@ -45,6 +46,6 @@
"globals": "^15.9.0",
"typescript": "^5.5.3",
"typescript-eslint": "^8.0.1",
"vite": "^5.4.1"
"vite": "^7.2.2"
}
}

View File

@@ -33,13 +33,13 @@ export const SearchInput: React.FC<searchInputProps> = ({
return (
<label
className={cn(
'relative bg-liquid-lighter w-[200px] h-[40px] rounded-full px-[16px] pl-[50px] py-[8px] cursor-text',
'relative bg-liquid-lighter w-[200px] h-[40px] flex rounded-full px-[16px] pl-[50px] cursor-text',
className,
)}
>
<input
className={cn(
'placeholder:text-liquid-light h-[28px] w-[200px] bg-transparent outline-none text-liquid-white ',
'placeholder:text-liquid-light h-[28px] w-[200px] bg-transparent outline-none text-liquid-white my-[6px]',
)}
value={value}
name={name}

View File

@@ -0,0 +1,34 @@
import { toast } from 'react-toastify';
export const toastSuccess = (mes: string, autoClose: number = 3000) => {
toast.success(mes, {
position: 'top-right',
autoClose: autoClose,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
});
};
export const toastWarning = (mes: string, autoClose: number = 3000) => {
toast.warning(mes, {
position: 'top-right',
autoClose: autoClose,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
});
};
export const toastError = (mes: string, autoClose: number = 3000) => {
toast.error(mes, {
position: 'top-right',
autoClose: autoClose,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
});
};

View File

@@ -6,11 +6,13 @@ import './styles/palette/theme-light.css';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { store } from './redux/store';
import { ToastContainer } from 'react-toastify';
createRoot(document.getElementById('root')!).render(
<BrowserRouter>
<Provider store={store}>
<App />
<ToastContainer />
</Provider>
</BrowserRouter>,
);

View File

@@ -19,6 +19,11 @@ import { MissionsRightPanel } from '../views/home/rightpanel/Missions';
import { ArticlesRightPanel } from '../views/home/rightpanel/Articles';
import { GroupRightPanel } from '../views/home/rightpanel/Group';
import GroupInvite from '../views/home/groupinviter/GroupInvite';
import {
toastError,
toastSuccess,
toastWarning,
} from '../lib/toastNotification';
const Home = () => {
const name = useAppSelector((state) => state.auth.username);
@@ -75,6 +80,30 @@ const Home = () => {
>
выйти
</PrimaryButton>
<div className="flex mt-[20px] gap-[20px]">
<PrimaryButton
color="success"
text="Toast"
onClick={() => {
toastSuccess('Success');
}}
/>
<PrimaryButton
color="warning"
text="Toast"
onClick={() => {
toastWarning('Warning');
}}
/>
<PrimaryButton
color="error"
text="Toast"
onClick={() => {
toastError('Error');
}}
/>
</div>
</>
}
/>

View File

@@ -3,6 +3,7 @@
@import 'tailwindcss/utilities';
@import './latex-container.css';
@import './toast.css';
* {
-webkit-tap-highlight-color: transparent; /* Отключаем выделение синим при тапе на телефоне*/

32
src/styles/toast.css Normal file
View File

@@ -0,0 +1,32 @@
.Toastify__progress-bar--success {
background: #10be59 !important;
}
.Toastify__toast--success .Toastify__toast-icon svg path {
fill: #10be59 !important;
}
.Toastify__progress-bar--error {
background: #f13e5f !important;
}
.Toastify__toast--error .Toastify__toast-icon svg path {
fill: #f13e5f !important;
}
.Toastify__progress-bar--success {
background: #10be59 !important;
}
.Toastify__toast--success .Toastify__toast-icon svg path {
fill: #10be59 !important;
}
.Toastify__toast {
background: #292929 !important;
color: var(--color-liquid-white);
}
.Toastify__toast > button > svg {
fill: var(--color-liquid-white);
}

View File

@@ -1,9 +1,11 @@
import { FC, useEffect } from 'react';
import { FC, useEffect, useState } from 'react';
import { useAppSelector, useAppDispatch } from '../../../../redux/hooks';
import { fetchGroupPosts } from '../../../../redux/slices/groupfeed';
import { SearchInput } from '../../../../components/input/SearchInput';
import { setMenuActiveGroupPage } from '../../../../redux/slices/store';
import { fetchGroupById } from '../../../../redux/slices/groups';
import { SecondaryButton } from '../../../../components/button/SecondaryButton';
interface PostsProps {
groupId: number;
@@ -12,28 +14,59 @@ interface PostsProps {
export const Posts: FC<PostsProps> = ({ groupId }) => {
const dispatch = useAppDispatch();
const [isAdmin, setIsAdmin] = useState<boolean>(false);
const { pages, status } = useAppSelector(
(state) => state.groupfeed.fetchPosts,
);
const { id: userId } = useAppSelector((state) => state.auth);
const { group, status: statusGroup } = useAppSelector(
(state) => state.groups.fetchGroupById,
);
// Загружаем только первую страницу
useEffect(() => {
dispatch(fetchGroupPosts({ groupId, page: 0, pageSize: 20 }));
dispatch(fetchGroupById(groupId));
}, [groupId]);
useEffect(() => {
dispatch(setMenuActiveGroupPage('home'));
}, []);
useEffect(() => {
if (!group) return;
const isUserAdmin =
group.members?.some(
(m) =>
Number(m.userId) === Number(userId) &&
m.role.includes('Administrator'),
) || false;
setIsAdmin(isUserAdmin);
}, [group, userId]);
const page0 = pages[0];
return (
<div className="h-full overflow-y-scroll thin-dark-scrollbar">
<div className="h-[40px] mb-[20px]">
<div className="h-[40px] mb-[20px] relative">
<SearchInput
className="w-[216px]"
onChange={(v) => {}}
placeholder="Поиск сообщений"
/>
{isAdmin && (
<div className=" h-[40px] w-[200px] absolute top-0 right-0 flex items-center">
<SecondaryButton
onClick={() => {
// setModalActive(true);
}}
text="Добавить задачу"
/>
</div>
)}
</div>
{status === 'loading' && <div>Загрузка...</div>}