init project
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
import type { Category, Product } from '@/entities/product/model/types'
|
||||
import { apiClient } from '@/shared/api/client'
|
||||
|
||||
export async function fetchPublicProducts(categorySlug?: string): Promise<Product[]> {
|
||||
const { data } = await apiClient.get<Product[]>('products', {
|
||||
params: categorySlug ? { categorySlug } : undefined,
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
export async function fetchCategories(): Promise<Category[]> {
|
||||
const { data } = await apiClient.get<Category[]>('categories')
|
||||
return data
|
||||
}
|
||||
|
||||
export async function fetchAdminProducts(token: string): Promise<Product[]> {
|
||||
const { data } = await apiClient.get<Product[]>('admin/products', {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
export async function createProduct(
|
||||
token: string,
|
||||
body: {
|
||||
title: string
|
||||
slug?: string
|
||||
description?: string | null
|
||||
priceCents: number
|
||||
imageUrl?: string | null
|
||||
published: boolean
|
||||
categoryId: string
|
||||
},
|
||||
): Promise<Product> {
|
||||
const { data } = await apiClient.post<Product>('admin/products', body, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
export async function updateProduct(
|
||||
token: string,
|
||||
id: string,
|
||||
body: Partial<{
|
||||
title: string
|
||||
slug: string
|
||||
description: string | null
|
||||
priceCents: number
|
||||
imageUrl: string | null
|
||||
published: boolean
|
||||
categoryId: string
|
||||
}>,
|
||||
): Promise<Product> {
|
||||
const { data } = await apiClient.patch<Product>(`admin/products/${id}`, body, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
export async function deleteProduct(token: string, id: string): Promise<void> {
|
||||
await apiClient.delete(`admin/products/${id}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
})
|
||||
}
|
||||
|
||||
export async function createCategory(
|
||||
token: string,
|
||||
body: { name: string; slug?: string; sort?: number },
|
||||
): Promise<Category> {
|
||||
const { data } = await apiClient.post<Category>('admin/categories', body, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
})
|
||||
return data
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
export type Category = {
|
||||
id: string
|
||||
name: string
|
||||
slug: string
|
||||
sort: number
|
||||
}
|
||||
|
||||
export type Product = {
|
||||
id: string
|
||||
title: string
|
||||
slug: string
|
||||
description: string | null
|
||||
priceCents: number
|
||||
imageUrl: string | null
|
||||
published: boolean
|
||||
categoryId: string
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
category?: Category
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import Card from '@mui/material/Card'
|
||||
import CardContent from '@mui/material/CardContent'
|
||||
import CardMedia from '@mui/material/CardMedia'
|
||||
import Chip from '@mui/material/Chip'
|
||||
import Stack from '@mui/material/Stack'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import type { Product } from '@/entities/product/model/types'
|
||||
import { formatPriceRub } from '@/shared/lib/format-price'
|
||||
|
||||
type Props = { product: Product }
|
||||
|
||||
export function ProductCard({ product }: Props) {
|
||||
return (
|
||||
<Card variant="outlined" sx={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
||||
{product.imageUrl ? (
|
||||
<CardMedia
|
||||
component="img"
|
||||
height="200"
|
||||
image={product.imageUrl}
|
||||
alt={product.title}
|
||||
sx={{ objectFit: 'cover' }}
|
||||
/>
|
||||
) : (
|
||||
<CardMedia
|
||||
component="div"
|
||||
sx={{
|
||||
height: 200,
|
||||
bgcolor: 'grey.100',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography color="text.secondary">Нет фото</Typography>
|
||||
</CardMedia>
|
||||
)}
|
||||
<CardContent sx={{ flexGrow: 1 }}>
|
||||
<Stack spacing={1}>
|
||||
{product.category && <Chip label={product.category.name} size="small" />}
|
||||
<Typography variant="h6" component="h2">
|
||||
{product.title}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="text.secondary"
|
||||
sx={{
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
WebkitLineClamp: 3,
|
||||
display: '-webkit-box',
|
||||
WebkitBoxOrient: 'vertical',
|
||||
}}
|
||||
>
|
||||
{product.description ?? 'Описание появится позже.'}
|
||||
</Typography>
|
||||
<Typography variant="h6" color="primary">
|
||||
{formatPriceRub(product.priceCents)}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user