diff --git a/src/client/dd-hub-react/src/sections/document/doc-item.tsx b/src/client/dd-hub-react/src/sections/document/doc-item.tsx
new file mode 100644
index 0000000..7365186
--- /dev/null
+++ b/src/client/dd-hub-react/src/sections/document/doc-item.tsx
@@ -0,0 +1,211 @@
+import type { CardProps } from '@mui/material/Card';
+import type { IconifyName } from 'src/components/iconify';
+
+import { varAlpha } from 'minimal-shared/utils';
+
+import Box from '@mui/material/Box';
+import Link from '@mui/material/Link';
+import Card from '@mui/material/Card';
+import Avatar from '@mui/material/Avatar';
+import Typography from '@mui/material/Typography';
+
+import { fDate } from 'src/utils/format-time';
+import { fShortenNumber } from 'src/utils/format-number';
+
+import { Iconify } from 'src/components/iconify';
+import { SvgColor } from 'src/components/svg-color';
+
+// ----------------------------------------------------------------------
+
+export type IDocItem = {
+ id: string;
+ title: string;
+ coverUrl: string;
+ totalViews: number;
+ description: string;
+ totalShares: number;
+ totalComments: number;
+ totalFavorites: number;
+ postedAt: string | number | null;
+ author: {
+ name: string;
+ avatarUrl: string;
+ };
+};
+
+export function DocItem({
+ sx,
+ post,
+ latestDoc,
+ latestDocLarge,
+ ...other
+}: CardProps & {
+ post: IDocItem;
+ latestDoc: boolean;
+ latestDocLarge: boolean;
+}) {
+ const renderAvatar = (
+
+ );
+
+ const renderTitle = (
+
+ {post.title}
+
+ );
+
+ const renderInfo = (
+
+ {[
+ { number: post.totalComments, icon: 'solar:chat-round-dots-bold' },
+ { number: post.totalViews, icon: 'solar:eye-bold' },
+ { number: post.totalShares, icon: 'solar:share-bold' },
+ ].map((info, _index) => (
+
+
+ {fShortenNumber(info.number)}
+
+ ))}
+
+ );
+
+ const renderCover = (
+
+ );
+
+ const renderDate = (
+
+ {fDate(post.postedAt)}
+
+ );
+
+ const renderShape = (
+
+ );
+
+ return (
+
+ ({
+ position: 'relative',
+ pt: 'calc(100% * 3 / 4)',
+ ...((latestDocLarge || latestDoc) && {
+ pt: 'calc(100% * 4 / 3)',
+ '&:after': {
+ top: 0,
+ content: "''",
+ width: '100%',
+ height: '100%',
+ position: 'absolute',
+ bgcolor: varAlpha(theme.palette.grey['900Channel'], 0.72),
+ },
+ }),
+ ...(latestDocLarge && {
+ pt: {
+ xs: 'calc(100% * 4 / 3)',
+ sm: 'calc(100% * 3 / 4.66)',
+ },
+ }),
+ })}
+ >
+ {renderShape}
+ {renderAvatar}
+ {renderCover}
+
+
+ ({
+ p: theme.spacing(6, 3, 3, 3),
+ ...((latestDocLarge || latestDoc) && {
+ width: 1,
+ bottom: 0,
+ position: 'absolute',
+ }),
+ })}
+ >
+ {renderDate}
+ {renderTitle}
+ {renderInfo}
+
+
+ );
+}
diff --git a/src/client/dd-hub-react/src/sections/document/doc-search.tsx b/src/client/dd-hub-react/src/sections/document/doc-search.tsx
new file mode 100644
index 0000000..4d4d753
--- /dev/null
+++ b/src/client/dd-hub-react/src/sections/document/doc-search.tsx
@@ -0,0 +1,59 @@
+import type { Theme, SxProps } from '@mui/material/styles';
+
+import TextField from '@mui/material/TextField';
+import InputAdornment from '@mui/material/InputAdornment';
+import Autocomplete, { autocompleteClasses } from '@mui/material/Autocomplete';
+
+import { Iconify } from 'src/components/iconify';
+
+import type { IDocItem } from './doc-item';
+
+// ----------------------------------------------------------------------
+
+type DocSearchProps = {
+ posts: IDocItem[];
+ sx?: SxProps;
+};
+
+export function DocSearch({ posts, sx }: DocSearchProps) {
+ return (
+ post.title}
+ isOptionEqualToValue={(option, value) => option.id === value.id}
+ renderInput={(params) => (
+
+
+
+ ),
+ },
+ }}
+ />
+ )}
+ />
+ );
+}
diff --git a/src/client/dd-hub-react/src/sections/document/doc-sort.tsx b/src/client/dd-hub-react/src/sections/document/doc-sort.tsx
new file mode 100644
index 0000000..78df396
--- /dev/null
+++ b/src/client/dd-hub-react/src/sections/document/doc-sort.tsx
@@ -0,0 +1,96 @@
+import type { ButtonProps } from '@mui/material/Button';
+
+import { useState, useCallback } from 'react';
+import { varAlpha } from 'minimal-shared/utils';
+
+import Button from '@mui/material/Button';
+import Popover from '@mui/material/Popover';
+import MenuList from '@mui/material/MenuList';
+import MenuItem, { menuItemClasses } from '@mui/material/MenuItem';
+
+import { Iconify } from 'src/components/iconify';
+
+// ----------------------------------------------------------------------
+
+type DocSortProps = ButtonProps & {
+ sortBy: string;
+ onSort: (newSort: string) => void;
+ options: { value: string; label: string }[];
+};
+
+export function DocSort({ options, sortBy, onSort, sx, ...other }: DocSortProps) {
+ const [openPopover, setOpenPopover] = useState(null);
+
+ const handleOpenPopover = useCallback((event: React.MouseEvent) => {
+ setOpenPopover(event.currentTarget);
+ }, []);
+
+ const handleClosePopover = useCallback(() => {
+ setOpenPopover(null);
+ }, []);
+
+ return (
+ <>
+
+ }
+ sx={[
+ {
+ bgcolor: (theme) => varAlpha(theme.vars.palette.grey['500Channel'], 0.08),
+ },
+ ...(Array.isArray(sx) ? sx : [sx]),
+ ]}
+ {...other}
+ >
+ {options.find((option) => option.value === sortBy)?.label}
+
+
+
+
+ {options.map((option) => (
+
+ ))}
+
+
+ >
+ );
+}
diff --git a/src/client/dd-hub-react/src/sections/document/view/doc-search-view.tsx b/src/client/dd-hub-react/src/sections/document/view/doc-search-view.tsx
new file mode 100644
index 0000000..c105da6
--- /dev/null
+++ b/src/client/dd-hub-react/src/sections/document/view/doc-search-view.tsx
@@ -0,0 +1,96 @@
+import { useState, useCallback } from 'react';
+
+import Box from '@mui/material/Box';
+import Grid from '@mui/material/Grid';
+import Button from '@mui/material/Button';
+import Typography from '@mui/material/Typography';
+import Pagination from '@mui/material/Pagination';
+
+import { DashboardContent } from 'src/layouts/dashboard';
+
+import { Iconify } from 'src/components/iconify';
+
+import { DocItem } from '../doc-item';
+import { DocSort } from '../doc-sort';
+import { DocSearch } from '../doc-search';
+
+import type { IDocItem } from '../doc-item';
+
+// ----------------------------------------------------------------------
+
+type Props = {
+ posts: IDocItem[];
+};
+
+export function DocSearchView({ posts }: Props) {
+ const [sortBy, setSortBy] = useState('latest');
+
+ const handleSort = useCallback((newSort: string) => {
+ setSortBy(newSort);
+ }, []);
+
+ return (
+
+
+
+ Document Search
+
+ }
+ >
+ New post
+
+
+
+
+
+
+
+
+
+ {posts.map((post, index) => {
+ const latestDocLarge = index === 0;
+ const latestDoc = index === 1 || index === 2;
+
+ return (
+
+
+
+ );
+ })}
+
+
+
+
+ );
+}
diff --git a/src/client/dd-hub-react/src/sections/document/view/index.ts b/src/client/dd-hub-react/src/sections/document/view/index.ts
new file mode 100644
index 0000000..15992c6
--- /dev/null
+++ b/src/client/dd-hub-react/src/sections/document/view/index.ts
@@ -0,0 +1 @@
+export * from './doc-search-view';