refactor(doc-item): IDocItem-Typ durch Doc-Modell aus API ersetzen

This commit is contained in:
tekh 2025-07-09 17:15:42 +02:00
parent 8ad250f227
commit a5e32d0d39
5 changed files with 194 additions and 199 deletions

View File

@ -15,14 +15,14 @@ export type DocQuery = {
name?: string | undefined name?: string | undefined
} }
export function getDocuments({ id, name }: DocQuery): Promise<Doc[]> { export function getDocuments(query: DocQuery | undefined = undefined): Promise<Doc[]> {
let documents = _documents; let documents = _documents;
if (id) if (query?.id)
documents = documents.filter(d => d.id = id) documents = documents.filter(d => d.id === query.id);
if (name) if (query?.name)
documents = documents.filter(d => d.name = name) documents = documents.filter(d => d.name === query.name);
return Promise.resolve(documents); return Promise.resolve(documents);
} }

View File

@ -1,16 +1,27 @@
import { useEffect, useState } from 'react';
import { _posts } from 'src/_mock'; import { _posts } from 'src/_mock';
import { CONFIG } from 'src/config-global'; import { CONFIG } from 'src/config-global';
import { Doc, getDocuments } from 'src/api/document-service';
import { DocSearchView } from 'src/sections/document/view'; import { DocSearchView } from 'src/sections/document/view';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
export default function Page() { export default function Page() {
const [docs, setDocs] = useState<Doc[]>([]);
useEffect(() => {
getDocuments({}).then((res) => {
setDocs(res);
});
}, []);
return ( return (
<> <>
<title>{`Document Search - ${CONFIG.appName}`}</title> <title>{`Document Search - ${CONFIG.appName}`}</title>
<DocSearchView posts={_posts} /> <DocSearchView docs={docs} />
</> </>
); );
} }

View File

@ -10,202 +10,187 @@ import Avatar from '@mui/material/Avatar';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import { fDate } from 'src/utils/format-time'; import { fDate } from 'src/utils/format-time';
import { fShortenNumber } from 'src/utils/format-number';
import { Doc } from 'src/api/document-service';
import { Iconify } from 'src/components/iconify'; import { Iconify } from 'src/components/iconify';
import { SvgColor } from 'src/components/svg-color'; 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({ export function DocItem({
sx, sx,
doc, doc,
long, long,
large, large,
...other ...other
}: CardProps & { }: CardProps & {
doc: IDocItem; doc: Doc;
long: boolean; long: boolean;
large: boolean; large: boolean;
}) { }) {
const renderAvatar = ( const renderAvatar = (
<Avatar <Avatar
alt={doc.author.name} alt={doc.addedWho}
src={doc.author.avatarUrl} src={doc.addedWho}
sx={{ sx={{
left: 24, left: 24,
zIndex: 9, zIndex: 9,
bottom: -24, bottom: -24,
position: 'absolute', position: 'absolute',
...((large || long) && { ...((large || long) && {
top: 24, top: 24,
}), }),
}} }}
/> />
); );
const renderTitle = ( const renderTitle = (
<Link <Link
color="inherit" color="inherit"
variant="subtitle2" variant="subtitle2"
underline="hover" underline="hover"
sx={{ sx={{
height: 44, height: 44,
overflow: 'hidden', overflow: 'hidden',
WebkitLineClamp: 2, WebkitLineClamp: 2,
display: '-webkit-box', display: '-webkit-box',
WebkitBoxOrient: 'vertical', WebkitBoxOrient: 'vertical',
...(large && { typography: 'h5', height: 60 }), ...(large && { typography: 'h5', height: 60 }),
...((large || long) && { ...((large || long) && {
color: 'common.white', color: 'common.white',
}), }),
}} }}
>
{doc.title}
</Link>
);
const renderInfo = (
<Box
sx={{
mt: 3,
gap: 1.5,
display: 'flex',
flexWrap: 'wrap',
color: 'text.disabled',
justifyContent: 'flex-end',
}}
>
{[
{ number: doc.totalComments, icon: 'solar:chat-round-dots-bold' },
{ number: doc.totalViews, icon: 'solar:eye-bold' },
{ number: doc.totalShares, icon: 'solar:share-bold' },
].map((info, _index) => (
<Box
key={_index}
sx={{
display: 'flex',
...((large || long) && {
opacity: 0.64,
color: 'common.white',
}),
}}
> >
<Iconify width={16} icon={info.icon as IconifyName} sx={{ mr: 0.5 }} /> {doc.name}
<Typography variant="caption">{fShortenNumber(info.number)}</Typography> </Link>
);
const renderInfo = (
<Box
sx={{
mt: 3,
gap: 1.5,
display: 'flex',
flexWrap: 'wrap',
color: 'text.disabled',
justifyContent: 'flex-end',
}}
>
{[
{ data: doc.addedWho, icon: 'solar:chat-round-dots-bold' },
{ data: doc.changedWho, icon: 'solar:eye-bold' },
{ data: doc.changedWhen, icon: 'solar:share-bold' },
].map((info, _index) => (
<Box
key={_index}
sx={{
display: 'flex',
...((large || long) && {
opacity: 0.64,
color: 'common.white',
}),
}}
>
<Iconify width={16} icon={info.icon as IconifyName} sx={{ mr: 0.5 }} />
<Typography variant="caption">{info.data?.toString()}</Typography>
</Box>
))}
</Box> </Box>
))} );
</Box>
);
const renderCover = ( const renderCover = (
<Box <Box
component="img" component="img"
alt={doc.title} alt={doc.name}
src={doc.coverUrl} src={doc.name}
sx={{ sx={{
top: 0, top: 0,
width: 1, width: 1,
height: 1, height: 1,
objectFit: 'cover', objectFit: 'cover',
position: 'absolute', position: 'absolute',
}} }}
/> />
); );
const renderDate = ( const renderDate = (
<Typography <Typography
variant="caption" variant="caption"
component="div" component="div"
sx={{ sx={{
mb: 1, mb: 1,
color: 'text.disabled', color: 'text.disabled',
...((large || long) && { ...((large || long) && {
opacity: 0.48, opacity: 0.48,
color: 'common.white', color: 'common.white',
}), }),
}} }}
> >
{fDate(doc.postedAt)} {fDate(doc.addedWhen)}
</Typography> </Typography>
); );
const renderShape = ( const renderShape = (
<SvgColor <SvgColor
src="/assets/icons/shape-avatar.svg" src="/assets/icons/shape-avatar.svg"
sx={{ sx={{
left: 0, left: 0,
width: 88, width: 88,
zIndex: 9, zIndex: 9,
height: 36, height: 36,
bottom: -16, bottom: -16,
position: 'absolute', position: 'absolute',
color: 'background.paper', color: 'background.paper',
...((large || long) && { display: 'none' }), ...((large || long) && { display: 'none' }),
}} }}
/> />
); );
return ( return (
<Card sx={sx} {...other}> <Card sx={sx} {...other}>
<Box <Box
sx={(theme) => ({ sx={(theme) => ({
position: 'relative', position: 'relative',
pt: 'calc(100% * 3 / 4)', pt: 'calc(100% * 3 / 4)',
...((large || long) && { ...((large || long) && {
pt: 'calc(100% * 4 / 3)', pt: 'calc(100% * 4 / 3)',
'&:after': { '&:after': {
top: 0, top: 0,
content: "''", content: "''",
width: '100%', width: '100%',
height: '100%', height: '100%',
position: 'absolute', position: 'absolute',
bgcolor: varAlpha(theme.palette.grey['900Channel'], 0.72), bgcolor: varAlpha(theme.palette.grey['900Channel'], 0.72),
}, },
}), }),
...(large && { ...(large && {
pt: { pt: {
xs: 'calc(100% * 4 / 3)', xs: 'calc(100% * 4 / 3)',
sm: 'calc(100% * 3 / 4.66)', sm: 'calc(100% * 3 / 4.66)',
}, },
}), }),
})} })}
> >
{renderShape} {renderShape}
{renderAvatar} {renderAvatar}
{renderCover} {renderCover}
</Box> </Box>
<Box <Box
sx={(theme) => ({ sx={(theme) => ({
p: theme.spacing(6, 3, 3, 3), p: theme.spacing(6, 3, 3, 3),
...((large || long) && { ...((large || long) && {
width: 1, width: 1,
bottom: 0, bottom: 0,
position: 'absolute', position: 'absolute',
}), }),
})} })}
> >
{renderDate} {renderDate}
{renderTitle} {renderTitle}
{renderInfo} {renderInfo}
</Box> </Box>
</Card> </Card>
); );
} }

View File

@ -4,14 +4,14 @@ import TextField from '@mui/material/TextField';
import InputAdornment from '@mui/material/InputAdornment'; import InputAdornment from '@mui/material/InputAdornment';
import Autocomplete, { autocompleteClasses } from '@mui/material/Autocomplete'; import Autocomplete, { autocompleteClasses } from '@mui/material/Autocomplete';
import { Iconify } from 'src/components/iconify'; import { Doc } from 'src/api/document-service';
import type { IDocItem } from './doc-item'; import { Iconify } from 'src/components/iconify';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
type DocSearchProps = { type DocSearchProps = {
posts: IDocItem[]; posts: Doc[];
sx?: SxProps<Theme>; sx?: SxProps<Theme>;
}; };
@ -33,7 +33,7 @@ export function DocSearch({ posts, sx }: DocSearchProps) {
}, },
}} }}
options={posts} options={posts}
getOptionLabel={(post) => post.title} getOptionLabel={(post) => post.name}
isOptionEqualToValue={(option, value) => option.id === value.id} isOptionEqualToValue={(option, value) => option.id === value.id}
renderInput={(params) => ( renderInput={(params) => (
<TextField <TextField

View File

@ -6,6 +6,7 @@ import Button from '@mui/material/Button';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import Pagination from '@mui/material/Pagination'; import Pagination from '@mui/material/Pagination';
import { Doc } from 'src/api/document-service';
import { DashboardContent } from 'src/layouts/dashboard'; import { DashboardContent } from 'src/layouts/dashboard';
import { Filter, getAttributesAsync } from 'src/api/attribute-service'; import { Filter, getAttributesAsync } from 'src/api/attribute-service';
@ -17,15 +18,13 @@ import { TextFilter } from '../text-filter';
import CreateFilterModal from './create-filter-modal'; import CreateFilterModal from './create-filter-modal';
import { DecimalFilter, IntFilter } from '../num-filter'; import { DecimalFilter, IntFilter } from '../num-filter';
import { DateFilter, DateTimeFilter, TimeFilter } from '../date-filter'; import { DateFilter, DateTimeFilter, TimeFilter } from '../date-filter';
import type { IDocItem } from '../doc-item';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
type Props = { type Props = {
posts: IDocItem[]; docs: Doc[];
}; };
export function DocSearchView({ posts }: Props) { export function DocSearchView({ docs }: Props) {
const [sortBy, setSortBy] = useState('latest'); const [sortBy, setSortBy] = useState('latest');
const [filters, setFilters] = useState<Filter[]>([]) const [filters, setFilters] = useState<Filter[]>([])
@ -51,7 +50,7 @@ export function DocSearchView({ posts }: Props) {
// justifyContent: 'space-between', // justifyContent: 'space-between',
// }} // }}
// > // >
// <DocSearch posts={posts} /> // <DocSearch docs={docs} />
// <DocSort // <DocSort
// sortBy={sortBy} // sortBy={sortBy}
// onSort={handleSort} // onSort={handleSort}
@ -145,20 +144,20 @@ export function DocSearchView({ posts }: Props) {
</Grid> </Grid>
<Grid container spacing={3}> <Grid container spacing={3}>
{posts.map((post, index) => { {docs.map((doc, index) => {
const large = false; const large = false;
const long = false; const long = false;
return ( return (
<Grid <Grid
key={post.id} key={doc.id}
size={{ size={{
xs: 12, xs: 12,
sm: large ? 12 : 6, sm: large ? 12 : 6,
md: large ? 6 : 3, md: large ? 6 : 3,
}} }}
> >
<DocItem doc={post} long={long} large={large} /> <DocItem doc={doc} long={long} large={large} />
</Grid> </Grid>
); );
})} })}