group posts
This commit is contained in:
@@ -27,7 +27,7 @@ const DateRangeInput: React.FC<DateRangeInputProps> = ({
|
|||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
value={startValue}
|
value={startValue}
|
||||||
onChange={(e) => onChange('startsAt', e.target.value)}
|
onChange={(e) => onChange('startsAt', e.target.value)}
|
||||||
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
|
className="mt-1 block w-full rounded-md border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -38,7 +38,7 @@ const DateRangeInput: React.FC<DateRangeInputProps> = ({
|
|||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
value={endValue}
|
value={endValue}
|
||||||
onChange={(e) => onChange('endsAt', e.target.value)}
|
onChange={(e) => onChange('endsAt', e.target.value)}
|
||||||
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
|
className="mt-1 block w-full rounded-md border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -215,7 +215,17 @@ export const deletePost = createAsyncThunk(
|
|||||||
const postsSlice = createSlice({
|
const postsSlice = createSlice({
|
||||||
name: 'posts',
|
name: 'posts',
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {},
|
reducers: {
|
||||||
|
setGroupFeedStatus: (
|
||||||
|
state,
|
||||||
|
action: PayloadAction<{ key: keyof PostsState; status: Status }>,
|
||||||
|
) => {
|
||||||
|
const { key, status } = action.payload;
|
||||||
|
if (state[key]) {
|
||||||
|
(state[key] as any).status = status;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
extraReducers: (builder) => {
|
extraReducers: (builder) => {
|
||||||
// fetchGroupPosts
|
// fetchGroupPosts
|
||||||
builder.addCase(fetchGroupPosts.pending, (state) => {
|
builder.addCase(fetchGroupPosts.pending, (state) => {
|
||||||
@@ -333,4 +343,5 @@ const postsSlice = createSlice({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const { setGroupFeedStatus } = postsSlice.actions;
|
||||||
export const groupFeedReducer = postsSlice.reducer;
|
export const groupFeedReducer = postsSlice.reducer;
|
||||||
|
|||||||
@@ -4,18 +4,7 @@ import 'highlight.js/styles/github-dark.css';
|
|||||||
|
|
||||||
import MarkdownPreview from './MarckDownPreview';
|
import MarkdownPreview from './MarckDownPreview';
|
||||||
|
|
||||||
interface MarkdownEditorProps {
|
export const MarkDownPattern = `# 🌙 Добро пожаловать в Markdown-редактор
|
||||||
defaultValue?: string;
|
|
||||||
onChange: (value: string) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const MarkdownEditor: FC<MarkdownEditorProps> = ({
|
|
||||||
defaultValue,
|
|
||||||
onChange,
|
|
||||||
}) => {
|
|
||||||
const [markdown, setMarkdown] = useState<string>(
|
|
||||||
defaultValue ||
|
|
||||||
`# 🌙 Добро пожаловать в Markdown-редактор
|
|
||||||
|
|
||||||
Добро пожаловать в **Markdown-редактор**!
|
Добро пожаловать в **Markdown-редактор**!
|
||||||
Здесь ты можешь писать в формате Markdown и видеть результат **в реальном времени** 👇
|
Здесь ты можешь писать в формате Markdown и видеть результат **в реальном времени** 👇
|
||||||
@@ -209,13 +198,29 @@ print(greet("Мир"))
|
|||||||
|
|
||||||
**🖤 Конец демонстрации. Спасибо, что используешь Markdown-редактор!**
|
**🖤 Конец демонстрации. Спасибо, что используешь Markdown-редактор!**
|
||||||
|
|
||||||
`,
|
`;
|
||||||
|
|
||||||
|
interface MarkdownEditorProps {
|
||||||
|
defaultValue?: string;
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MarkdownEditor: FC<MarkdownEditorProps> = ({
|
||||||
|
defaultValue,
|
||||||
|
onChange,
|
||||||
|
}) => {
|
||||||
|
const [markdown, setMarkdown] = useState<string>(
|
||||||
|
defaultValue || MarkDownPattern,
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onChange(markdown);
|
onChange(markdown);
|
||||||
}, [markdown]);
|
}, [markdown]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setMarkdown(defaultValue || MarkDownPattern);
|
||||||
|
}, [defaultValue]);
|
||||||
|
|
||||||
// Обработчик вставки
|
// Обработчик вставки
|
||||||
const handlePaste = async (
|
const handlePaste = async (
|
||||||
e: React.ClipboardEvent<HTMLTextAreaElement>,
|
e: React.ClipboardEvent<HTMLTextAreaElement>,
|
||||||
|
|||||||
@@ -30,12 +30,7 @@ const MarkdownPreview: FC<MarkdownPreviewProps> = ({
|
|||||||
className = '',
|
className = '',
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={cn('flex-1 bg-[#161b22] rounded-lg p-6', className)}>
|
||||||
className={cn(
|
|
||||||
'flex-1 bg-[#161b22] rounded-lg shadow-lg p-6',
|
|
||||||
className,
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className="prose prose-invert max-w-none h-full overflow-auto pr-4 medium-scrollbar">
|
<div className="prose prose-invert max-w-none h-full overflow-auto pr-4 medium-scrollbar">
|
||||||
<ReactMarkdown
|
<ReactMarkdown
|
||||||
remarkPlugins={[remarkGfm]}
|
remarkPlugins={[remarkGfm]}
|
||||||
|
|||||||
72
src/views/home/group/posts/ModalCreate.tsx
Normal file
72
src/views/home/group/posts/ModalCreate.tsx
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import { FC, useEffect, useState } from 'react';
|
||||||
|
import { Modal } from '../../../../components/modal/Modal';
|
||||||
|
import { PrimaryButton } from '../../../../components/button/PrimaryButton';
|
||||||
|
import { SecondaryButton } from '../../../../components/button/SecondaryButton';
|
||||||
|
import { Input } from '../../../../components/input/Input';
|
||||||
|
import { useAppDispatch, useAppSelector } from '../../../../redux/hooks';
|
||||||
|
import { createGroup } from '../../../../redux/slices/groups';
|
||||||
|
import MarkdownEditor from '../../../articleeditor/Editor';
|
||||||
|
import {
|
||||||
|
createPost,
|
||||||
|
setGroupFeedStatus,
|
||||||
|
} from '../../../../redux/slices/groupfeed';
|
||||||
|
|
||||||
|
interface ModalCreateProps {
|
||||||
|
groupId: number;
|
||||||
|
active: boolean;
|
||||||
|
setActive: (value: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ModalCreate: FC<ModalCreateProps> = ({ active, setActive, groupId }) => {
|
||||||
|
// const [name, setName] = useState<string>('');
|
||||||
|
const [content, setContent] = useState<string>('');
|
||||||
|
const status = useAppSelector((state) => state.groupfeed.createPost.status);
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (status == 'successful') {
|
||||||
|
setActive(false);
|
||||||
|
dispatch(setGroupFeedStatus({ key: 'createPost', status: 'idle' }));
|
||||||
|
}
|
||||||
|
}, [status]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
className="bg-liquid-background h-[calc(100vh-30%)] border-liquid-lighter border-[2px] p-[25px] rounded-[20px] text-liquid-white overflow-hidden"
|
||||||
|
onOpenChange={setActive}
|
||||||
|
open={active}
|
||||||
|
backdrop="blur"
|
||||||
|
>
|
||||||
|
<div className="max-w-[1400px] h-full overflow-hidden">
|
||||||
|
<div className="font-bold text-[30px]">Создать пост</div>
|
||||||
|
|
||||||
|
<div className="h-[calc(100%-45px-60px)]">
|
||||||
|
<MarkdownEditor
|
||||||
|
onChange={(v) => {
|
||||||
|
setContent(v);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row w-full items-center justify-end mt-[20px] gap-[20px]">
|
||||||
|
<PrimaryButton
|
||||||
|
onClick={() => {
|
||||||
|
dispatch(
|
||||||
|
createPost({ name: '', content, groupId }),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
text={status == 'idle' ? 'Опубликовать' : 'Загрузка...'}
|
||||||
|
disabled={status == 'loading'}
|
||||||
|
/>
|
||||||
|
<SecondaryButton
|
||||||
|
onClick={() => {
|
||||||
|
setActive(false);
|
||||||
|
}}
|
||||||
|
text="Отмена"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ModalCreate;
|
||||||
140
src/views/home/group/posts/ModalUpdate.tsx
Normal file
140
src/views/home/group/posts/ModalUpdate.tsx
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
import { FC, useEffect, useState } from 'react';
|
||||||
|
import { Modal } from '../../../../components/modal/Modal';
|
||||||
|
import { PrimaryButton } from '../../../../components/button/PrimaryButton';
|
||||||
|
import { SecondaryButton } from '../../../../components/button/SecondaryButton';
|
||||||
|
import { Input } from '../../../../components/input/Input';
|
||||||
|
import { useAppDispatch, useAppSelector } from '../../../../redux/hooks';
|
||||||
|
import { createGroup } from '../../../../redux/slices/groups';
|
||||||
|
import MarkdownEditor, { MarkDownPattern } from '../../../articleeditor/Editor';
|
||||||
|
import {
|
||||||
|
createPost,
|
||||||
|
deletePost,
|
||||||
|
fetchPostById,
|
||||||
|
setGroupFeedStatus,
|
||||||
|
updatePost,
|
||||||
|
} from '../../../../redux/slices/groupfeed';
|
||||||
|
import { ReverseButton } from '../../../../components/button/ReverseButton';
|
||||||
|
import { cn } from '../../../../lib/cn';
|
||||||
|
|
||||||
|
interface ModalUpdateProps {
|
||||||
|
groupId: number;
|
||||||
|
postId: number;
|
||||||
|
active: boolean;
|
||||||
|
setActive: (value: boolean) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ModalUpdate: FC<ModalUpdateProps> = ({
|
||||||
|
active,
|
||||||
|
setActive,
|
||||||
|
groupId,
|
||||||
|
postId,
|
||||||
|
}) => {
|
||||||
|
// const [name, setName] = useState<string>('');
|
||||||
|
const [content, setContent] = useState<string>('');
|
||||||
|
const status = useAppSelector((state) => state.groupfeed.updatePost.status);
|
||||||
|
const statusDelete = useAppSelector(
|
||||||
|
(state) => state.groupfeed.deletePost.status,
|
||||||
|
);
|
||||||
|
const { post, status: statusPost } = useAppSelector(
|
||||||
|
(state) => state.groupfeed.fetchPostById,
|
||||||
|
);
|
||||||
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (status == 'successful') {
|
||||||
|
setActive(false);
|
||||||
|
dispatch(setGroupFeedStatus({ key: 'updatePost', status: 'idle' }));
|
||||||
|
}
|
||||||
|
}, [status]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (statusDelete == 'successful') {
|
||||||
|
setActive(false);
|
||||||
|
dispatch(setGroupFeedStatus({ key: 'deletePost', status: 'idle' }));
|
||||||
|
}
|
||||||
|
}, [statusDelete]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(fetchPostById({ groupId, postId }));
|
||||||
|
}, [postId]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
className="bg-liquid-background h-[calc(100vh-30%)] border-liquid-lighter border-[2px] p-[25px] rounded-[20px] text-liquid-white overflow-hidden"
|
||||||
|
onOpenChange={setActive}
|
||||||
|
open={active}
|
||||||
|
backdrop="blur"
|
||||||
|
>
|
||||||
|
<div className="max-w-[1400px] h-full overflow-hidden transition-all duratoin-300">
|
||||||
|
<div className="font-bold text-[30px]">
|
||||||
|
Обновить пост #{post?.id}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
' absolute z-10 h-[calc(100%-100px)] w-[calc(100%-50px)] flex items-center justify-center text-transparent transition-all pointer-events-none ',
|
||||||
|
statusPost == 'loading' && 'text-liquid-white',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div>Загрузка...</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'h-[calc(100%-45px-60px)] opacity-50 pointer-events-none transition-all ',
|
||||||
|
statusPost == 'successful' &&
|
||||||
|
'text-liquid-white pointer-events-auto opacity-100',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<MarkdownEditor
|
||||||
|
defaultValue={
|
||||||
|
statusPost == 'successful'
|
||||||
|
? post?.content
|
||||||
|
: MarkDownPattern
|
||||||
|
}
|
||||||
|
onChange={(v) => {
|
||||||
|
setContent(v);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-row w-full items-center justify-end mt-[20px] gap-[20px]">
|
||||||
|
<PrimaryButton
|
||||||
|
onClick={() => {
|
||||||
|
dispatch(
|
||||||
|
updatePost({
|
||||||
|
name: '',
|
||||||
|
content,
|
||||||
|
groupId,
|
||||||
|
postId,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
text={status == 'idle' ? 'Сохранить' : 'Загрузка...'}
|
||||||
|
disabled={
|
||||||
|
status == 'loading' || statusPost != 'successful'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<ReverseButton
|
||||||
|
onClick={() => {
|
||||||
|
dispatch(deletePost({ groupId, postId }));
|
||||||
|
}}
|
||||||
|
color="error"
|
||||||
|
text={
|
||||||
|
statusDelete == 'idle' ? 'Удалить' : 'Загрузка...'
|
||||||
|
}
|
||||||
|
disabled={
|
||||||
|
statusDelete == 'loading' ||
|
||||||
|
statusPost != 'successful'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<SecondaryButton
|
||||||
|
onClick={() => {
|
||||||
|
setActive(false);
|
||||||
|
}}
|
||||||
|
text="Отмена"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ModalUpdate;
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
import { FC } from 'react';
|
||||||
|
import { useAppSelector } from '../../../../redux/hooks';
|
||||||
|
import MarkdownPreview from '../../../articleeditor/MarckDownPreview';
|
||||||
|
import { Edit } from '../../../../assets/icons/input';
|
||||||
|
|
||||||
|
function convertDate(isoString: string) {
|
||||||
|
const date = new Date(isoString);
|
||||||
|
|
||||||
|
const dd = String(date.getUTCDate()).padStart(2, '0');
|
||||||
|
const mm = String(date.getUTCMonth() + 1).padStart(2, '0');
|
||||||
|
const yyyy = date.getUTCFullYear();
|
||||||
|
|
||||||
|
const hh = String(date.getUTCHours()).padStart(2, '0');
|
||||||
|
const min = String(date.getUTCMinutes()).padStart(2, '0');
|
||||||
|
|
||||||
|
return `${dd}.${mm}.${yyyy} ${hh}:${min}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PostItemProps {
|
||||||
|
id: number;
|
||||||
|
groupId: number;
|
||||||
|
authorId: number;
|
||||||
|
authorUsername: string;
|
||||||
|
name: string;
|
||||||
|
content: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
isAdmin: boolean;
|
||||||
|
setModalUpdateActive: (v: boolean) => void;
|
||||||
|
setUpdatePostId: (v: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PostItem: FC<PostItemProps> = ({
|
||||||
|
id,
|
||||||
|
groupId,
|
||||||
|
authorId,
|
||||||
|
authorUsername,
|
||||||
|
name,
|
||||||
|
content,
|
||||||
|
createdAt,
|
||||||
|
updatedAt,
|
||||||
|
isAdmin,
|
||||||
|
setModalUpdateActive,
|
||||||
|
setUpdatePostId,
|
||||||
|
}) => {
|
||||||
|
const members = useAppSelector(
|
||||||
|
(state) => state.groups.fetchGroupById.group?.members,
|
||||||
|
);
|
||||||
|
const member = members?.find((m) => m.userId === authorId);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="rounded-[10px] flex flex-col gap-[20px]">
|
||||||
|
<div className="h-[40px] w-full flex gap-[10px] relative">
|
||||||
|
<div className="h-[40px] w-[40px] bg-[#D9D9D9] rounded-[10px]"></div>
|
||||||
|
<div className=" leading-[20px] font-bold text-[16px] ">
|
||||||
|
<div>{authorUsername} </div>
|
||||||
|
<div className="text-liquid-light">
|
||||||
|
{member ? member.role : 'роль не найдена'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className=" leading-[20px] font-bold text-[16px] ">
|
||||||
|
<div className="text-liquid-light">
|
||||||
|
{convertDate(createdAt)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isAdmin && (
|
||||||
|
<div
|
||||||
|
className=" h-[40px] w-[40px] absolute top-0 right-0 flex items-center justify-center cursor-pointer
|
||||||
|
rounded-[10px] hover:bg-liquid-lighter transition-all duration-300 active:scale-90"
|
||||||
|
onClick={() => {
|
||||||
|
setUpdatePostId(id);
|
||||||
|
setModalUpdateActive(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img src={Edit} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<MarkdownPreview className="bg-transparent" content={content} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import { SearchInput } from '../../../../components/input/SearchInput';
|
|||||||
import { setMenuActiveGroupPage } from '../../../../redux/slices/store';
|
import { setMenuActiveGroupPage } from '../../../../redux/slices/store';
|
||||||
import { fetchGroupById } from '../../../../redux/slices/groups';
|
import { fetchGroupById } from '../../../../redux/slices/groups';
|
||||||
import { SecondaryButton } from '../../../../components/button/SecondaryButton';
|
import { SecondaryButton } from '../../../../components/button/SecondaryButton';
|
||||||
|
import ModalCreate from './ModalCreate';
|
||||||
|
import { PostItem } from './PostItem';
|
||||||
|
import ModalUpdate from './ModalUpdate';
|
||||||
|
|
||||||
interface PostsProps {
|
interface PostsProps {
|
||||||
groupId: number;
|
groupId: number;
|
||||||
@@ -14,15 +17,16 @@ interface PostsProps {
|
|||||||
export const Posts: FC<PostsProps> = ({ groupId }) => {
|
export const Posts: FC<PostsProps> = ({ groupId }) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const [modalCreateActive, setModalCreateActive] = useState<boolean>(false);
|
||||||
|
const [modalUpdateActive, setModalUpdateActive] = useState<boolean>(false);
|
||||||
|
const [updatePostId, setUpdatePostId] = useState<number>(0);
|
||||||
const [isAdmin, setIsAdmin] = useState<boolean>(false);
|
const [isAdmin, setIsAdmin] = useState<boolean>(false);
|
||||||
const { pages, status } = useAppSelector(
|
const { pages, status } = useAppSelector(
|
||||||
(state) => state.groupfeed.fetchPosts,
|
(state) => state.groupfeed.fetchPosts,
|
||||||
);
|
);
|
||||||
const { id: userId } = useAppSelector((state) => state.auth);
|
const { id: userId } = useAppSelector((state) => state.auth);
|
||||||
|
|
||||||
const { group, status: statusGroup } = useAppSelector(
|
const { group } = useAppSelector((state) => state.groups.fetchGroupById);
|
||||||
(state) => state.groups.fetchGroupById,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Загружаем только первую страницу
|
// Загружаем только первую страницу
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -58,12 +62,12 @@ export const Posts: FC<PostsProps> = ({ groupId }) => {
|
|||||||
placeholder="Поиск сообщений"
|
placeholder="Поиск сообщений"
|
||||||
/>
|
/>
|
||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
<div className=" h-[40px] w-[200px] absolute top-0 right-0 flex items-center">
|
<div className=" h-[40px] w-[180px] absolute top-0 right-0 flex items-center">
|
||||||
<SecondaryButton
|
<SecondaryButton
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// setModalActive(true);
|
setModalCreateActive(true);
|
||||||
}}
|
}}
|
||||||
text="Добавить задачу"
|
text="Создать пост"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -75,42 +79,33 @@ export const Posts: FC<PostsProps> = ({ groupId }) => {
|
|||||||
{status == 'successful' &&
|
{status == 'successful' &&
|
||||||
page0?.items &&
|
page0?.items &&
|
||||||
page0.items.length > 0 ? (
|
page0.items.length > 0 ? (
|
||||||
<div className="space-y-4">
|
<div className="flex flex-col gap-[20px]">
|
||||||
{page0.items.map((post) => (
|
{page0.items.map((post, i) => (
|
||||||
<div
|
<PostItem
|
||||||
key={post.id}
|
{...post}
|
||||||
className="border border-gray-700 rounded p-3"
|
key={i}
|
||||||
>
|
isAdmin={isAdmin}
|
||||||
<div>
|
setModalUpdateActive={setModalUpdateActive}
|
||||||
<b>ID:</b> {post.id}
|
setUpdatePostId={setUpdatePostId}
|
||||||
</div>
|
/>
|
||||||
<div>
|
|
||||||
<b>Название:</b> {post.name}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<b>Содержимое:</b> {post.content}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<b>Автор:</b> {post.authorUsername}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<b>Автор ID:</b> {post.authorId}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<b>Group ID:</b> {post.groupId}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<b>Создан:</b> {post.createdAt}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<b>Обновлён:</b> {post.updatedAt}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : status === 'successful' ? (
|
) : status === 'successful' ? (
|
||||||
<div>Постов пока нет</div>
|
<div>Постов пока нет</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
|
<ModalCreate
|
||||||
|
active={modalCreateActive}
|
||||||
|
setActive={setModalCreateActive}
|
||||||
|
groupId={groupId}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ModalUpdate
|
||||||
|
active={modalUpdateActive}
|
||||||
|
setActive={setModalUpdateActive}
|
||||||
|
groupId={groupId}
|
||||||
|
postId={updatePostId}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ const ModalCreate: FC<ModalCreateProps> = ({ active, setActive }) => {
|
|||||||
|
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
<label className="block mb-2">Файл задачи</label>
|
<label className="block mb-2">Файл задачи</label>
|
||||||
<label className="cursor-pointer inline-flex items-center justify-center px-4 py-2 bg-liquid-lighter hover:bg-liquid-dark transition-colors rounded-[10px] text-liquid-white font-medium shadow-md">
|
<label className="cursor-pointer inline-flex items-center justify-center px-4 py-2 bg-liquid-lighter hover:bg-liquid-dark transition-colors rounded-[10px] text-liquid-white font-medium">
|
||||||
{file ? file.name : 'Выбрать файл'}
|
{file ? file.name : 'Выбрать файл'}
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
|
|||||||
Reference in New Issue
Block a user