grand commit

This commit is contained in:
2025-02-07 08:34:42 +02:00
parent f594f001f6
commit c6c34f0453
53 changed files with 1283 additions and 625 deletions

67
lib/config/resources.ts Normal file
View File

@@ -0,0 +1,67 @@
import {ResourceType} from '@prisma/client'
import im, {Features} from 'imagemagick'
import fs from 'node:fs'
import path from 'node:path'
import {db} from '@/lib/db/prisma/client'
export const PUBLIC_UPLOAD_PRODUCTS_DIR = '/uploads/products'
export const UPLOAD_PRODUCTS_DIR = path.resolve(
`./public${PUBLIC_UPLOAD_PRODUCTS_DIR}`
)
export function getFilesByProductId(id: number, fullPath: boolean = true) {
const files = fs
.readdirSync(UPLOAD_PRODUCTS_DIR)
.filter(file => file.startsWith(`${id}-`))
return !fullPath
? files
: files.map(file => path.join(UPLOAD_PRODUCTS_DIR, file))
}
interface ProductImage {
type: string
productId: number
filesize: number
height: number
width: number
'mime type'?: string
mimeType: string
quality: number
properties: {
signature: string
}
signature: string
}
export async function getMetaOfFile(id: number) {
getFilesByProductId(id).forEach(file => {
im.identify(file, async (err, features) => {
if (err) throw err
// { format: 'JPEG', width: 3904, height: 2622, depth: 8 }
const data = features as ProductImage
try {
const result = await db.productResource.create({
data: {
type: 'IMAGE' as ResourceType,
productId: id,
uri: PUBLIC_UPLOAD_PRODUCTS_DIR + '/' + path.basename(file),
filesize: parseInt(data.filesize as unknown as string),
height: data.height,
width: data.width,
quality: data.quality,
mimeType: data['mime type'] || 'image/*',
signature: data.properties.signature
//meta: features
}
})
//return result
} catch (e) {
//console.log(e)
}
})
})
}

View File

@@ -5,7 +5,7 @@ export const data = {
{
name: 'Про нас',
slug: slug('Про нас'),
href: '/search?tag='
href: '/about-us'
},
{
name: "Цікаво про здоров'я",

View File

@@ -0,0 +1,22 @@
'use server'
import {CategoryLocale} from '@prisma/client'
import {STORE_ID} from '@/lib/config/constants'
import {db} from '@/lib/db/prisma/client'
export const getCategoryBySlug = async (slug: string) => {
return db.categoryLocale.findFirst({
where: {
slug
},
select: {
category: {
include: {
locales: true,
categoriesOnProducts: true
}
}
}
})
}

View File

@@ -1,12 +1,23 @@
'use server'
import {Lang, Product, ProductLocale, ProductToStore} from '@prisma/client'
import {
CategoriesOnProducts,
CategoryLocale,
Lang,
Product,
ProductLocale,
ProductResource,
ProductToStore
} from '@prisma/client'
import {STORE_ID} from '@/lib/config/constants'
import {db, dbQueryLog} from '@/lib/db/prisma/client'
export interface ProductProps extends Product {
locales: ProductLocale[]
toStore: ProductToStore[]
categoriesOnProducts: CategoriesOnProducts[]
resources: ProductResource[]
}
export const getProductBySlug = async (data: {
@@ -25,7 +36,27 @@ export const getProductById = async (id: unknown): Promise<Product | null> => {
return db.product.findUnique({
where: {id: id as number},
include: {
resources: true,
categoriesOnProducts: {
where: {
storeId: STORE_ID
},
include: {
category: {
include: {
locales: {
orderBy: {
lang: 'asc'
}
}
}
}
}
},
locales: {
orderBy: {
lang: 'asc'
},
include: {
meta: true
}
@@ -39,6 +70,9 @@ export const getProducts = async (): Promise<Product[] | null> => {
return db.product.findMany({
include: {
locales: {
orderBy: {
lang: 'asc'
},
omit: {
description: true,
content: true,
@@ -52,3 +86,13 @@ export const getProducts = async (): Promise<Product[] | null> => {
}
})
}
export const getProductResources = async (
productId: number | null
): Promise<ProductResource[] | null> => {
return db.productResource.findMany({
where: {
productId: productId
}
})
}

View File

@@ -0,0 +1,23 @@
import Decimal from 'decimal.js'
import {i18nLocalesCodes} from '@/i18n-config'
import {locales} from '@/i18n/routing'
export type CategoryPageSqlSchema = {
productId: number
lang: string
slug?: string | null | undefined
image?: string | null | undefined
imageWidth?: number | null | undefined
imageHeight?: number | null | undefined
categoryId?: number
title: string
shortTitle?: string | null | undefined
description?: string | null | undefined
content?: string | null | undefined
headingTitle?: string | null | undefined
instruction?: string | null | undefined
categorySlug?: string | null | undefined
categoryTitle?: string | null | undefined
price?: string | null | Decimal
}

View File

@@ -6,7 +6,7 @@ model Category {
storeId Int @map("store_id")
locales CategoryLocale[]
createdAt DateTime @default(now()) @map("created_at")
categoriesOnPruducts CategoriesOnProducts[]
categoriesOnProducts CategoriesOnProducts[]
@@map("categories")
}
@@ -36,5 +36,5 @@ model CategoriesOnProducts {
categoryId Int @map("category_id")
@@id([storeId, productId, categoryId])
@@map("categories_on_pruducts")
@@map("categories_on_products")
}

View File

@@ -25,7 +25,7 @@ model ProductLocale {
shortTitle String? @map("short_title")
headingTitle String? @map("heading_title")
description String? @db.MediumText
content String @db.MediumText
content String? @db.MediumText
instruction String? @db.MediumText
product Product @relation(fields: [productId], references: [id])
productId Int @map("product_id")
@@ -40,12 +40,13 @@ model ProductResource {
id Int @id @default(autoincrement())
type ResourceType
mimeType String? @map("mime_type")
isFeature Boolean? @default(false)
filesize Int? @db.UnsignedInt
width Int? @db.UnsignedSmallInt
height Int? @db.UnsignedSmallInt
quality Int? @db.UnsignedTinyInt
signature String? @db.Char(64)
uri String @db.Text
quality Float? @db.Float
signature String @db.Char(64)
uri String @db.VarChar(1024)
title String? @db.VarChar(255)
description String? @db.Text
meta Json? @db.Json
@@ -54,6 +55,7 @@ model ProductResource {
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
updatedAt DateTime? @default(dbgenerated("NULL DEFAULT NULL ON UPDATE current_timestamp(3)")) @map("updated_at") @db.Timestamp(3)
@@unique([productId, signature])
@@map("product_resources")
}
@@ -85,15 +87,16 @@ model ProductAttribute {
}
model ProductToStore {
id Int @id @default(autoincrement())
position Int @default(0)
published Boolean @default(false)
available Boolean @default(false)
price Decimal @default(0) @db.Decimal(7, 2)
pricePromotional Decimal @default(0) @map("price_promotional") @db.Decimal(7, 2)
productId Int @map("product_id")
storeId Int @map("store_id")
product Product @relation(fields: [productId], references: [id])
id Int @id @default(autoincrement())
position Int @default(0)
published Boolean @default(false)
available Boolean @default(false)
price Decimal @default(0) @db.Decimal(7, 2)
pricePromotional Decimal @default(0) @map("price_promotional") @db.Decimal(7, 2)
productId Int @map("product_id")
storeId Int @map("store_id")
product Product @relation(fields: [productId], references: [id])
createdAt DateTime @default(now()) @map("created_at") @db.Timestamp(3)
@@unique([storeId, productId])
@@map("product_to_store")

View File

@@ -0,0 +1,20 @@
SELECT DISTINCT
p.id as productId,
pl.lang,
pl.slug,
pr.uri as image,
pr.width as imageWidth,
pr.height as imageHeight,
pts.price,
pts.price_promotional as pricePromotional,
pl.title,
pl.short_title as shortTitle,
pl.description
-- pl.content, pl.heading_title as headingTitle, pl.instruction
FROM products p
JOIN categories_on_products cop ON p.id = cop.product_id
JOIN product_locale pl ON pl.product_id = p.id
JOIN product_to_store pts ON pts.product_id = p.id AND pts.store_id = cop.store_id
LEFT JOIN product_resources pr ON p.id = pr.product_id AND pr.isFeature = TRUE
WHERE cop.store_id = 1 AND pl.lang = ?
ORDER BY p.id DESC;

View File

@@ -0,0 +1,26 @@
SELECT
p.id as productId,
pl.lang,
pl.slug,
pr.uri as image,
pr.width as imageWidth,
pr.height as imageHeight,
pts.price,
pts.price_promotional as pricePromotional,
cl.category_id as categoryId,
pl.title,
pl.short_title as shortTitle,
pl.description,
-- pl.content,
pl.heading_title as headingTitle,
-- pl.instruction,
cl.slug as categorySlug,
cl.title as categoryTitle
FROM products p
JOIN categories_on_products cop ON p.id = cop.product_id
JOIN product_locale pl ON pl.product_id = p.id
JOIN category_locales cl ON cl.category_id = cop.category_id
JOIN product_to_store pts ON pts.product_id = p.id AND pts.store_id = cop.store_id
LEFT JOIN product_resources pr ON p.id = pr.product_id AND pr.isFeature = TRUE
WHERE cop.store_id = 1 AND cl.slug = ?
ORDER BY pl.lang;

View File

@@ -0,0 +1,26 @@
SELECT DISTINCT
p.id as productId,
pl.lang,
pl.slug,
pr.uri as image,
pr.width as imageWidth,
pr.height as imageHeight,
pts.price,
pts.price_promotional as pricePromotional,
cl.category_id as categoryId,
pl.title,
pl.short_title as shortTitle,
pl.description,
pl.content,
pl.heading_title as headingTitle,
pl.instruction,
cl.slug as categorySlug,
cl.title as categoryTitle
FROM products p
JOIN categories_on_products cop ON p.id = cop.product_id
JOIN product_locale pl ON pl.product_id = p.id
JOIN category_locales cl ON cl.category_id = cop.category_id
JOIN product_to_store pts ON pts.product_id = p.id AND pts.store_id = cop.store_id
LEFT JOIN product_resources pr ON p.id = pr.product_id AND pr.isFeature = TRUE
WHERE cop.store_id = 1 AND p.id = ?
ORDER BY pl.lang

View File

@@ -1,9 +1,12 @@
import {Prisma} from '@prisma/client'
import bcrypt from 'bcryptjs'
import {type ClassValue, clsx} from 'clsx'
import {getLocale} from 'next-intl/server'
import slugify from 'slugify'
import {twMerge} from 'tailwind-merge'
import {i18nDefaultLocale} from '@/i18n-config'
/**
* Just output dump using pretty output
*
@@ -16,6 +19,8 @@ export function dump(variable: any): [string, string] {
]
}
export const toPrice = (price: any) => parseFloat(price).toFixed(2)
/**
* Create fallback avatar for showing during login process or in case if empty
*
@@ -118,6 +123,16 @@ export const toEmptyParams = (data: object | object[]) => {
return result
}
export const thisLocales = async (locales: any) => {
const loc = await getLocale()
return locales.filter((locale: any) => locale.lang === loc)
}
export const thisLocale = async (locales: any) => {
const loc = await getLocale()
return locales.find((locale: any) => locale.lang === loc)
}
export const dbErrorHandling = (e: unknown, message?: string | null) => {
if (e instanceof Prisma.PrismaClientKnownRequestError) {
return {error: `${e.code}: ${e.message}`}