stuff done
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
export const BaseEditorConfig = {
|
||||
readonly: false, // all options from https://xdsoft.net/jodit/docs/,
|
||||
placeholder: 'Start typings...',
|
||||
placeholder: 'Почніть введення...',
|
||||
spellcheck: true,
|
||||
language: 'ua',
|
||||
//toolbarAdaptive: false,
|
||||
@@ -10,9 +10,9 @@ export const BaseEditorConfig = {
|
||||
//defaultActionOnPaste: 'insert_as_text',
|
||||
//defaultActionOnPaste: 'insert_only_text',
|
||||
//disablePlugins: 'ai-assistant,mobile,print,speech-recognize,table,table-keyboard-navigation,powered-by-jodit,iframe',
|
||||
minHeight: 240,
|
||||
maxHeight: 640,
|
||||
maxWidth: 890,
|
||||
minHeight: '240',
|
||||
maxHeight: '640',
|
||||
maxWidth: '890',
|
||||
uploader: {
|
||||
insertImageAsBase64URI: true,
|
||||
imagesExtensions: ['jpg', 'png', 'jpeg', 'gif', 'svg', 'webp']
|
||||
|
||||
32
lib/db/prisma/schema/entity.prisma
Normal file
32
lib/db/prisma/schema/entity.prisma
Normal file
@@ -0,0 +1,32 @@
|
||||
model Entity {
|
||||
id Int @id @default(autoincrement())
|
||||
type EntityType
|
||||
published Boolean? @default(false)
|
||||
scopes Json?
|
||||
position Int? @default(0) @db.UnsignedSmallInt
|
||||
slug String? @db.VarChar(512)
|
||||
media String? @db.VarChar(512)
|
||||
storeId Int @default(1) @map("store_id")
|
||||
locales EntityLocale[]
|
||||
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([storeId, type, slug])
|
||||
@@map("entities")
|
||||
}
|
||||
|
||||
model EntityLocale {
|
||||
id Int @id @default(autoincrement())
|
||||
lang Lang @default(uk)
|
||||
slug String? @db.VarChar(512)
|
||||
title String @db.VarChar(384)
|
||||
annotation String? @db.MediumText
|
||||
body String? @db.MediumText
|
||||
entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade)
|
||||
entityId Int @map("entity_id")
|
||||
meta Meta? @relation(fields: [metaId], references: [id], onDelete: Cascade)
|
||||
metaId Int? @map("meta_id")
|
||||
|
||||
@@unique([slug, lang])
|
||||
@@map("entity_locale")
|
||||
}
|
||||
@@ -1,8 +1,20 @@
|
||||
enum DeliveryOption {
|
||||
NP
|
||||
PICKUP
|
||||
COURIER
|
||||
}
|
||||
|
||||
enum Lang {
|
||||
uk
|
||||
ru
|
||||
}
|
||||
|
||||
enum EntityType {
|
||||
article
|
||||
block
|
||||
page
|
||||
}
|
||||
|
||||
enum Unit {
|
||||
mkg
|
||||
mg
|
||||
|
||||
@@ -23,6 +23,7 @@ model Meta {
|
||||
openGraph OpenGraph?
|
||||
storeLocale StoreLocale[]
|
||||
productLocale ProductLocale[]
|
||||
entityLocale EntityLocale[]
|
||||
//vendorLocale VendorLocale[]
|
||||
|
||||
@@map("meta")
|
||||
|
||||
26
lib/db/prisma/schema/order.prisma
Normal file
26
lib/db/prisma/schema/order.prisma
Normal file
@@ -0,0 +1,26 @@
|
||||
model Order {
|
||||
id Int @id @default(autoincrement())
|
||||
storeId Int @default(1) @map("store_id")
|
||||
lang Lang
|
||||
orderNo String @map("order_no") @db.VarChar(45)
|
||||
isQuick Boolean @map("is_quick")
|
||||
user User? @relation(fields: [userId], references: [id])
|
||||
userId Int? @map("user_id")
|
||||
firstName String @map("first_name") @db.VarChar(255)
|
||||
patronymic String? @db.VarChar(255)
|
||||
surname String? @db.VarChar(255)
|
||||
deliveryOption DeliveryOption? @map("delivery_option")
|
||||
phone String? @db.Char(24)
|
||||
email String? @db.VarChar(320)
|
||||
emailSent Boolean? @map("email_sent")
|
||||
/// [OrderAddressType]
|
||||
address Json?
|
||||
notes String? @db.MediumText
|
||||
/// [OrderDetailsType]
|
||||
details Json
|
||||
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([orderNo, storeId])
|
||||
@@map("orders")
|
||||
}
|
||||
@@ -27,6 +27,7 @@ model User {
|
||||
extendedData Json? @map("extended_data") @db.Json
|
||||
// orders Order[]
|
||||
favorites UserFavouriteProduct[]
|
||||
orders Order[]
|
||||
reviews UserProductReview[]
|
||||
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)
|
||||
|
||||
88
lib/nova-post-helper.ts
Normal file
88
lib/nova-post-helper.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import cache from 'next/cache'
|
||||
|
||||
export const NP_SETTLEMENT_KYIV_REF = 'e718a680-4b33-11e4-ab6d-005056801329'
|
||||
|
||||
export type Street = {
|
||||
SettlementStreetRef: string
|
||||
Present: string
|
||||
StreetsTypeDescription: string
|
||||
SettlementStreetDescription: string
|
||||
SettlementStreetDescriptionRu: string
|
||||
}
|
||||
|
||||
export type Settlement = {
|
||||
Ref: string
|
||||
Description: string
|
||||
DescriptionRu: string
|
||||
AreaDescription: string
|
||||
AreaDescriptionRu: string
|
||||
SettlementTypeDescription: string
|
||||
SettlementTypeDescriptionRu: string
|
||||
}
|
||||
|
||||
export type Warehouse = {
|
||||
Ref: string
|
||||
Description: string
|
||||
DescriptionRu: string
|
||||
CityDescription: string
|
||||
CityDescriptionRu: string
|
||||
SettlementDescription: string
|
||||
AreaDescription: string
|
||||
SettlementRegionsDescription: string
|
||||
SettlementTypeDescription: string
|
||||
SettlementTypeDescriptionRu: string
|
||||
Longitude: string
|
||||
Latitude: string
|
||||
}
|
||||
|
||||
export const getApi = async (url: string) => {
|
||||
//TODO: need implement caching
|
||||
return await fetch(url)
|
||||
}
|
||||
|
||||
export const formatSettlement = (
|
||||
city: Settlement,
|
||||
locale: string = 'uk'
|
||||
): string => {
|
||||
if (!city) return ''
|
||||
|
||||
if (locale === 'ru') {
|
||||
const {DescriptionRu, AreaDescriptionRu, SettlementTypeDescriptionRu} = city
|
||||
// https://www.alta.ru/fias/socrname/
|
||||
const type = SettlementTypeDescriptionRu.replace(
|
||||
'поселок городского типа',
|
||||
'пгт '
|
||||
)
|
||||
.replace('поселок', 'п. ')
|
||||
.replace('село', 'с. ')
|
||||
.replace('город', 'г. ')
|
||||
|
||||
return (
|
||||
type +
|
||||
DescriptionRu.replace(`(${AreaDescriptionRu} обл.)`, '')
|
||||
.replace(`(${AreaDescriptionRu} обл)`, '')
|
||||
.replace(`${AreaDescriptionRu} обл., `, '')
|
||||
.trim() +
|
||||
` (${AreaDescriptionRu} обл.)`
|
||||
)
|
||||
} else {
|
||||
const {Description, AreaDescription, SettlementTypeDescription} = city
|
||||
const type = SettlementTypeDescription.replace(
|
||||
'селище міського типу',
|
||||
'смт '
|
||||
)
|
||||
.replace('село', 'с. ')
|
||||
.replace('селище', 'с-ще ')
|
||||
.replace('місто', 'м. ')
|
||||
|
||||
return (
|
||||
type +
|
||||
Description.replace(`(${AreaDescription} обл.)`, '')
|
||||
.replace(`(${AreaDescription} обл)`, '')
|
||||
.replace(`(село)`, '')
|
||||
.replace(`${AreaDescription} обл., `, '')
|
||||
.trim() +
|
||||
` (${AreaDescription} обл.)`
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
import {z} from 'zod'
|
||||
|
||||
import {db} from '@/lib/db/prisma/client'
|
||||
|
||||
export const categoryLocaleSchema = z.object({
|
||||
lang: z.enum(['uk', 'ru']),
|
||||
title: z.string().trim().min(1).max(384),
|
||||
|
||||
43
lib/schemas/admin/entity.ts
Normal file
43
lib/schemas/admin/entity.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import {Entity, EntityLocale, EntityType} from '@prisma/client'
|
||||
import {z} from 'zod'
|
||||
|
||||
import {i18nLocalesCodes} from '@/i18n-config'
|
||||
import {metaFormSchema} from '@/lib/schemas/meta'
|
||||
|
||||
interface Map {
|
||||
[key: string]: string | undefined
|
||||
}
|
||||
|
||||
export const EntityTypeDescription: Map = {
|
||||
article: 'Стаття',
|
||||
page: 'Сторінка',
|
||||
block: 'Блок'
|
||||
}
|
||||
//
|
||||
export type EntityTerm = Entity & {locales: EntityLocale}
|
||||
|
||||
export const entityLocaleSchema = z.object({
|
||||
lang: z.enum(i18nLocalesCodes),
|
||||
title: z.coerce.string().trim().min(1).max(384),
|
||||
annotation: z.coerce.string().trim().optional(),
|
||||
body: z.coerce.string().trim().optional()
|
||||
})
|
||||
|
||||
export const createEntityFormSchema = z.object({
|
||||
type: z.enum(Object.keys(EntityType) as [string, ...string[]], {
|
||||
message: "Обов'язкове до вибору"
|
||||
}), // ProductToStore
|
||||
published: z.coerce.boolean().default(false).optional(), // ProductToStore
|
||||
scopes: z.coerce.string().trim().optional(),
|
||||
slug: z.coerce
|
||||
.string()
|
||||
.trim()
|
||||
.max(512)
|
||||
.regex(/^[a-z0-9-]+$/, {
|
||||
message: 'тільки латинські символи, цифри та дефіс'
|
||||
})
|
||||
.optional(),
|
||||
media: z.coerce.string().trim().max(512).optional(),
|
||||
locales: z.array(entityLocaleSchema),
|
||||
meta: z.array(metaFormSchema)
|
||||
})
|
||||
38
lib/schemas/admin/order.ts
Normal file
38
lib/schemas/admin/order.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import {DeliveryOption, Lang, Order} from '@prisma/client'
|
||||
import {z} from 'zod'
|
||||
|
||||
interface Map {
|
||||
[key: string]: string | undefined
|
||||
}
|
||||
|
||||
export const DeliveryOptionTypeDescription: Map = {
|
||||
NP: 'До відділення «Нової Пошти»',
|
||||
PICKUP: 'Самовивіз (для Києва)',
|
||||
COURIER: "Кур'єр (для Києва)"
|
||||
}
|
||||
|
||||
export const createOrderFormSchema = z.object({
|
||||
user_id: z.string().optional(),
|
||||
is_quick: z.boolean(),
|
||||
lang: z.enum(Object.keys(Lang) as [string, ...string[]]),
|
||||
first_name: z.coerce.string().trim().min(1).max(255),
|
||||
//patronymic: z.coerce.string().trim().min(1).max(255),
|
||||
surname: z.coerce.string().trim().min(1).max(255),
|
||||
delivery_option: z.enum(
|
||||
Object.keys(DeliveryOption) as [string, ...string[]],
|
||||
{
|
||||
message: "Обов'язкове до вибору"
|
||||
}
|
||||
),
|
||||
phone: z.coerce.string().trim().min(1).max(24),
|
||||
email: z.coerce
|
||||
.string()
|
||||
.trim()
|
||||
.regex(/^[A-Za-z0-9\._%+\-]+@[A-Za-z0-9\.\-]+\.[A-Za-z]{2,}$/, {
|
||||
message: 'Ведуть коректну e-mail адресу'
|
||||
})
|
||||
.max(320),
|
||||
address: z.coerce.string().trim(),
|
||||
notes: z.coerce.string().trim().max(1024).optional(),
|
||||
details: z.coerce.string().trim()
|
||||
})
|
||||
38
lib/utils.ts
38
lib/utils.ts
@@ -3,6 +3,7 @@ import bcrypt from 'bcryptjs'
|
||||
import {type ClassValue, clsx} from 'clsx'
|
||||
import {getLocale} from 'next-intl/server'
|
||||
import slugify from 'slugify'
|
||||
import striptags from 'striptags'
|
||||
import {twMerge} from 'tailwind-merge'
|
||||
|
||||
import {i18nDefaultLocale} from '@/i18n-config'
|
||||
@@ -157,3 +158,40 @@ export const dbErrorHandling = (e: unknown, message?: string | null) => {
|
||||
*/
|
||||
export const isEmptyObj = (obj: object): boolean =>
|
||||
Object.keys(obj).length === 0
|
||||
|
||||
/**
|
||||
* To convert letter on typing from latin to cyr
|
||||
* TODO: need to complete
|
||||
*
|
||||
* @param term
|
||||
*/
|
||||
export const replacerLat2Cyr = (term: string): string => {
|
||||
const obj = {g: 'п', e: 'у', o: 'щ', f: 'а'}
|
||||
|
||||
return term
|
||||
.split('')
|
||||
.map((ch: PropertyKey): string => {
|
||||
return obj.hasOwnProperty(ch as PropertyKey)
|
||||
? (obj[ch as never] as string)
|
||||
: (ch as string)
|
||||
})
|
||||
.join('')
|
||||
}
|
||||
|
||||
type NormalizeConfigType =
|
||||
| {
|
||||
stripTags?: boolean
|
||||
}
|
||||
| undefined
|
||||
| null
|
||||
|
||||
export function normalizeData(
|
||||
data: string | null,
|
||||
config?: NormalizeConfigType
|
||||
): string {
|
||||
if (config?.stripTags) {
|
||||
data = striptags(data as string)
|
||||
}
|
||||
|
||||
return data as string
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user