mirror of
https://github.com/TautvydasDerzinskas/Thingport.git
synced 2026-10-20 13:49:56 -05:00
feat: add category banned and update ui for sorting
This commit is contained in:
1 parent
312c6be706
commit
b4e4c2aa9b
4 files changed
+99
-53
No files matched your search
@@ -139,7 +139,9 @@ export default function CollectionDetailPage({ theme, previewMode, onUnauthorize
|
||||
{collection.description && (
|
||||
<Typography variant="body2" color="text.secondary">{collection.description}</Typography>
|
||||
)}
|
||||
<SortTabs value={sortMode} onChange={setSortMode} />
|
||||
<Box sx={{ display: "flex", justifyContent: "flex-end" }}>
|
||||
<SortTabs value={sortMode} onChange={setSortMode} />
|
||||
</Box>
|
||||
{items.length ? (
|
||||
<Stack spacing={2}>
|
||||
<Box sx={{ display: "grid", gridTemplateColumns: "repeat(5, 1fr)", columnGap: "20px", rowGap: "20px" }}>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import { alpha } from "@mui/material/styles";
|
||||
import { type Folder } from "../../api/folders";
|
||||
|
||||
type Props = {
|
||||
folder: Folder;
|
||||
};
|
||||
|
||||
/** The rounded title+description banner shown above the grid when the selected category has
|
||||
* admin-set meta text (Folder.metaTitle/metaDescription, edited via CategoryMetaDialog) --
|
||||
* mirrors MakerWorld's category banner, a light rounded box with the category's display title
|
||||
* and blurb sitting above its model grid. Models page only: a plain Collection has no
|
||||
* analogous meta text today. Renders nothing without a title -- the description alone isn't
|
||||
* worth a banner. */
|
||||
export default function CategoryBanner({ folder }: Props) {
|
||||
if (!folder.meta_title) return null;
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
borderRadius: 2,
|
||||
px: 2.5,
|
||||
py: 2,
|
||||
bgcolor: (theme) => alpha(theme.palette.primary.main, 0.08),
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6">{folder.meta_title}</Typography>
|
||||
{folder.meta_description && (
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mt: 0.5 }}>
|
||||
{folder.meta_description}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@@ -25,7 +25,8 @@ export default function SortTabs({ value, onChange }: Props) {
|
||||
onClick={() => onChange(mode)}
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
fontWeight: value === mode ? 700 : 400,
|
||||
fontSize: 14,
|
||||
fontWeight: value === mode ? 700 : 500,
|
||||
color: value === mode ? "primary.main" : "text.secondary",
|
||||
"&:hover": { color: "primary.main" },
|
||||
}}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { type ResolvedTheme } from "../../constants/settingsOptions";
|
||||
import { usePageHeader } from "../../components/Layout/PageHeaderContext";
|
||||
import { useInfiniteScroll } from "../../hooks/useInfiniteScroll";
|
||||
import CategoriesPanel from "./CategoriesPanel";
|
||||
import CategoryBanner from "./CategoryBanner";
|
||||
import ModelCard from "./ModelCard";
|
||||
import SortTabs from "./SortTabs";
|
||||
|
||||
@@ -185,58 +186,65 @@ export default function ModelsPage({ folderId, onSelectFolder, foldersVersion, o
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack direction="row" spacing={2} alignItems="flex-start">
|
||||
<CategoriesPanel
|
||||
folders={folders}
|
||||
loading={foldersLoading}
|
||||
selectedId={folderId}
|
||||
onSelect={onSelectFolder}
|
||||
onCreate={createCategory}
|
||||
onRename={renameCategory}
|
||||
onDelete={deleteCategory}
|
||||
onReorder={reorderCategories}
|
||||
onUpdateMeta={updateCategoryMeta}
|
||||
/>
|
||||
<Box sx={{ flex: 1, minWidth: 0 }}>
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<SortTabs value={sortMode} onChange={setSortMode} />
|
||||
</Box>
|
||||
{loading ? (
|
||||
<Stack alignItems="center" sx={{ py: 8 }}>
|
||||
<CircularProgress size={22} />
|
||||
</Stack>
|
||||
) : items.length ? (
|
||||
<Stack spacing={2}>
|
||||
<Box sx={{ display: "grid", gridTemplateColumns: "repeat(5, 1fr)", columnGap: "20px", rowGap: "20px" }}>
|
||||
{items.map(item => (
|
||||
<ModelCard
|
||||
key={item.id}
|
||||
item={item}
|
||||
theme={theme}
|
||||
previewMode={previewMode}
|
||||
onDeleted={deletedId => setItems(prev => prev.filter(i => i.id !== deletedId))}
|
||||
onFavoriteChange={updated => setItems(prev => prev.map(i => (i.id === updated.id ? updated : i)))}
|
||||
onUnauthorized={onUnauthorized}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
{hasMore && (
|
||||
<Stack ref={loadMoreSentinelRef} direction="row" justifyContent="center" sx={{ py: 1 }}>
|
||||
{loadingMore && (
|
||||
<Stack direction="row" alignItems="center" spacing={1} sx={{ color: "text.secondary" }}>
|
||||
<CircularProgress size={14} />
|
||||
<Typography variant="caption">{t("models:grid.loadingMore")}</Typography>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
) : (
|
||||
<Stack alignItems="center" spacing={1} sx={{ py: 8, color: "text.secondary" }}>
|
||||
<Typography variant="body2">{t("models:grid.empty")}</Typography>
|
||||
</Stack>
|
||||
)}
|
||||
<Stack spacing={2}>
|
||||
<Box sx={{ display: "flex", justifyContent: "flex-end" }}>
|
||||
<SortTabs value={sortMode} onChange={setSortMode} />
|
||||
</Box>
|
||||
<Stack direction="row" spacing={2} alignItems="flex-start">
|
||||
<CategoriesPanel
|
||||
folders={folders}
|
||||
loading={foldersLoading}
|
||||
selectedId={folderId}
|
||||
onSelect={onSelectFolder}
|
||||
onCreate={createCategory}
|
||||
onRename={renameCategory}
|
||||
onDelete={deleteCategory}
|
||||
onReorder={reorderCategories}
|
||||
onUpdateMeta={updateCategoryMeta}
|
||||
/>
|
||||
<Box sx={{ flex: 1, minWidth: 0 }}>
|
||||
{selectedFolder?.meta_title && (
|
||||
<Box sx={{ mb: 2 }}>
|
||||
<CategoryBanner folder={selectedFolder} />
|
||||
</Box>
|
||||
)}
|
||||
{loading ? (
|
||||
<Stack alignItems="center" sx={{ py: 8 }}>
|
||||
<CircularProgress size={22} />
|
||||
</Stack>
|
||||
) : items.length ? (
|
||||
<Stack spacing={2}>
|
||||
<Box sx={{ display: "grid", gridTemplateColumns: "repeat(5, 1fr)", columnGap: "20px", rowGap: "20px" }}>
|
||||
{items.map(item => (
|
||||
<ModelCard
|
||||
key={item.id}
|
||||
item={item}
|
||||
theme={theme}
|
||||
previewMode={previewMode}
|
||||
onDeleted={deletedId => setItems(prev => prev.filter(i => i.id !== deletedId))}
|
||||
onFavoriteChange={updated => setItems(prev => prev.map(i => (i.id === updated.id ? updated : i)))}
|
||||
onUnauthorized={onUnauthorized}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
{hasMore && (
|
||||
<Stack ref={loadMoreSentinelRef} direction="row" justifyContent="center" sx={{ py: 1 }}>
|
||||
{loadingMore && (
|
||||
<Stack direction="row" alignItems="center" spacing={1} sx={{ color: "text.secondary" }}>
|
||||
<CircularProgress size={14} />
|
||||
<Typography variant="caption">{t("models:grid.loadingMore")}</Typography>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
) : (
|
||||
<Stack alignItems="center" spacing={1} sx={{ py: 8, color: "text.secondary" }}>
|
||||
<Typography variant="body2">{t("models:grid.empty")}</Typography>
|
||||
</Stack>
|
||||
)}
|
||||
</Box>
|
||||
</Stack>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user