enh
This commit is contained in:
parent
db2ee44a5f
commit
6d67a1e759
9 changed files with 67 additions and 14 deletions
|
|
@ -44,6 +44,8 @@ export default function AddProduct() {
|
||||||
images: variantUrls,
|
images: variantUrls,
|
||||||
isFlashAvailable: variant.isFlashAvailable || false,
|
isFlashAvailable: variant.isFlashAvailable || false,
|
||||||
flashPrice: variant.flashPrice ? parseFloat(variant.flashPrice) : undefined,
|
flashPrice: variant.flashPrice ? parseFloat(variant.flashPrice) : undefined,
|
||||||
|
isOffer: variant.isOffer || false,
|
||||||
|
isComboOnly: variant.isComboOnly || false,
|
||||||
features: variant.attributes.map((attr: any) => ({
|
features: variant.attributes.map((attr: any) => ({
|
||||||
featureName: attr.featureName,
|
featureName: attr.featureName,
|
||||||
featureValue: attr.featureValue,
|
featureValue: attr.featureValue,
|
||||||
|
|
@ -85,6 +87,8 @@ export default function AddProduct() {
|
||||||
marketPrice: '',
|
marketPrice: '',
|
||||||
isFlashAvailable: false,
|
isFlashAvailable: false,
|
||||||
flashPrice: '',
|
flashPrice: '',
|
||||||
|
isOffer: false,
|
||||||
|
isComboOnly: false,
|
||||||
attributes: [{ featureName: 'quantity', featureValue: '' }],
|
attributes: [{ featureName: 'quantity', featureValue: '' }],
|
||||||
comboItems: [] as { skuId: number | string }[],
|
comboItems: [] as { skuId: number | string }[],
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,8 @@ export default function EditProduct() {
|
||||||
marketPrice: sku.marketPrice || '',
|
marketPrice: sku.marketPrice || '',
|
||||||
isFlashAvailable: sku.isFlashAvailable || false,
|
isFlashAvailable: sku.isFlashAvailable || false,
|
||||||
flashPrice: sku.flashPrice || '',
|
flashPrice: sku.flashPrice || '',
|
||||||
|
isOffer: sku.isOffer || false,
|
||||||
|
isComboOnly: sku.isComboOnly || false,
|
||||||
attributes: (sku.features || []).map((f) => ({
|
attributes: (sku.features || []).map((f) => ({
|
||||||
featureName: f.featureName,
|
featureName: f.featureName,
|
||||||
featureValue: f.featureValue,
|
featureValue: f.featureValue,
|
||||||
|
|
@ -118,6 +120,8 @@ export default function EditProduct() {
|
||||||
images: allUrls,
|
images: allUrls,
|
||||||
isFlashAvailable: variant.isFlashAvailable || false,
|
isFlashAvailable: variant.isFlashAvailable || false,
|
||||||
flashPrice: variant.flashPrice ? parseFloat(variant.flashPrice) : undefined,
|
flashPrice: variant.flashPrice ? parseFloat(variant.flashPrice) : undefined,
|
||||||
|
isOffer: variant.isOffer || false,
|
||||||
|
isComboOnly: variant.isComboOnly || false,
|
||||||
features: variant.attributes.map((attr: any) => ({
|
features: variant.attributes.map((attr: any) => ({
|
||||||
featureName: attr.featureName,
|
featureName: attr.featureName,
|
||||||
featureValue: attr.featureValue,
|
featureValue: attr.featureValue,
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import MaterialIcons from '@expo/vector-icons/MaterialIcons'
|
||||||
import { trpc } from '../trpc-client'
|
import { trpc } from '../trpc-client'
|
||||||
|
|
||||||
interface Attribute {
|
interface Attribute {
|
||||||
featureName: string
|
featureName: string | null
|
||||||
featureValue: string
|
featureValue: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -17,6 +17,8 @@ interface Variant {
|
||||||
marketPrice: string
|
marketPrice: string
|
||||||
isFlashAvailable: boolean
|
isFlashAvailable: boolean
|
||||||
flashPrice: string
|
flashPrice: string
|
||||||
|
isOffer: boolean
|
||||||
|
isComboOnly: boolean
|
||||||
attributes: Attribute[]
|
attributes: Attribute[]
|
||||||
comboItems: { skuId: number | string }[]
|
comboItems: { skuId: number | string }[]
|
||||||
}
|
}
|
||||||
|
|
@ -52,6 +54,8 @@ const defaultVariant = (): Variant => ({
|
||||||
marketPrice: '',
|
marketPrice: '',
|
||||||
isFlashAvailable: false,
|
isFlashAvailable: false,
|
||||||
flashPrice: '',
|
flashPrice: '',
|
||||||
|
isOffer: false,
|
||||||
|
isComboOnly: false,
|
||||||
attributes: [defaultAttribute()],
|
attributes: [defaultAttribute()],
|
||||||
comboItems: [],
|
comboItems: [],
|
||||||
})
|
})
|
||||||
|
|
@ -210,7 +214,7 @@ const ProductForm = forwardRef<ProductFormRef, ProductFormProps>(({
|
||||||
<View style={tw`flex-1`}>
|
<View style={tw`flex-1`}>
|
||||||
<MyTextInput
|
<MyTextInput
|
||||||
placeholder="Name"
|
placeholder="Name"
|
||||||
value={attr.featureName}
|
value={attr.featureName ?? ''}
|
||||||
onChangeText={handleChange(`variants.${vIndex}.attributes.${aIndex}.featureName`)}
|
onChangeText={handleChange(`variants.${vIndex}.attributes.${aIndex}.featureName`)}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|
@ -265,6 +269,24 @@ const ProductForm = forwardRef<ProductFormRef, ProductFormProps>(({
|
||||||
<MyText style={tw`text-gray-700 font-medium`}>Flash Available</MyText>
|
<MyText style={tw`text-gray-700 font-medium`}>Flash Available</MyText>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
<View style={tw`flex-row items-center mb-3`}>
|
||||||
|
<Checkbox
|
||||||
|
checked={variant.isOffer}
|
||||||
|
onPress={() => setFieldValue(`variants.${vIndex}.isOffer`, !variant.isOffer)}
|
||||||
|
style={tw`mr-3`}
|
||||||
|
/>
|
||||||
|
<MyText style={tw`text-gray-700 font-medium`}>Offer SKU</MyText>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={tw`flex-row items-center mb-3`}>
|
||||||
|
<Checkbox
|
||||||
|
checked={variant.isComboOnly}
|
||||||
|
onPress={() => setFieldValue(`variants.${vIndex}.isComboOnly`, !variant.isComboOnly)}
|
||||||
|
style={tw`mr-3`}
|
||||||
|
/>
|
||||||
|
<MyText style={tw`text-gray-700 font-medium`}>Combo Only SKU</MyText>
|
||||||
|
</View>
|
||||||
|
|
||||||
{variant.isFlashAvailable && (
|
{variant.isFlashAvailable && (
|
||||||
<MyTextInput
|
<MyTextInput
|
||||||
topLabel="Flash Price"
|
topLabel="Flash Price"
|
||||||
|
|
|
||||||
|
|
@ -196,8 +196,10 @@ export const productRouter = router({
|
||||||
images: z.array(z.string()).optional().default([]),
|
images: z.array(z.string()).optional().default([]),
|
||||||
isFlashAvailable: z.boolean().optional().default(false),
|
isFlashAvailable: z.boolean().optional().default(false),
|
||||||
flashPrice: z.number().optional().nullable(),
|
flashPrice: z.number().optional().nullable(),
|
||||||
|
isOffer: z.boolean().optional().default(false),
|
||||||
|
isComboOnly: z.boolean().optional().default(false),
|
||||||
features: z.array(z.object({
|
features: z.array(z.object({
|
||||||
featureName: z.string().min(1, 'Attribute name is required'),
|
featureName: z.string().nullable().optional(),
|
||||||
featureValue: z.string().min(1, 'Value is required'),
|
featureValue: z.string().min(1, 'Value is required'),
|
||||||
})).min(1, 'At least one feature is required'),
|
})).min(1, 'At least one feature is required'),
|
||||||
comboItems: z.array(z.object({
|
comboItems: z.array(z.object({
|
||||||
|
|
@ -222,8 +224,10 @@ export const productRouter = router({
|
||||||
images: sku.images.map((url) => extractKeyFromPresignedUrl(url)),
|
images: sku.images.map((url) => extractKeyFromPresignedUrl(url)),
|
||||||
isFlashAvailable: sku.isFlashAvailable,
|
isFlashAvailable: sku.isFlashAvailable,
|
||||||
flashPrice: sku.flashPrice ?? null,
|
flashPrice: sku.flashPrice ?? null,
|
||||||
|
isOffer: sku.isOffer,
|
||||||
|
isComboOnly: sku.isComboOnly,
|
||||||
features: sku.features.map((f) => ({
|
features: sku.features.map((f) => ({
|
||||||
featureName: f.featureName,
|
featureName: f.featureName?.trim() || null,
|
||||||
featureValue: f.featureValue,
|
featureValue: f.featureValue,
|
||||||
})),
|
})),
|
||||||
comboItems: (sku.comboItems || []).map((ci) => ({ skuId: ci.skuId })),
|
comboItems: (sku.comboItems || []).map((ci) => ({ skuId: ci.skuId })),
|
||||||
|
|
@ -278,8 +282,10 @@ export const productRouter = router({
|
||||||
images: z.array(z.string()).optional().default([]),
|
images: z.array(z.string()).optional().default([]),
|
||||||
isFlashAvailable: z.boolean().optional().default(false),
|
isFlashAvailable: z.boolean().optional().default(false),
|
||||||
flashPrice: z.number().optional().nullable(),
|
flashPrice: z.number().optional().nullable(),
|
||||||
|
isOffer: z.boolean().optional().default(false),
|
||||||
|
isComboOnly: z.boolean().optional().default(false),
|
||||||
features: z.array(z.object({
|
features: z.array(z.object({
|
||||||
featureName: z.string().min(1, 'Attribute name is required'),
|
featureName: z.string().nullable().optional(),
|
||||||
featureValue: z.string().min(1, 'Value is required'),
|
featureValue: z.string().min(1, 'Value is required'),
|
||||||
})).min(1, 'At least one feature is required'),
|
})).min(1, 'At least one feature is required'),
|
||||||
comboItems: z.array(z.object({
|
comboItems: z.array(z.object({
|
||||||
|
|
@ -306,8 +312,10 @@ export const productRouter = router({
|
||||||
images: sku.images.map((url) => extractKeyFromPresignedUrl(url)),
|
images: sku.images.map((url) => extractKeyFromPresignedUrl(url)),
|
||||||
isFlashAvailable: sku.isFlashAvailable,
|
isFlashAvailable: sku.isFlashAvailable,
|
||||||
flashPrice: sku.flashPrice ?? null,
|
flashPrice: sku.flashPrice ?? null,
|
||||||
|
isOffer: sku.isOffer,
|
||||||
|
isComboOnly: sku.isComboOnly,
|
||||||
features: sku.features.map((f) => ({
|
features: sku.features.map((f) => ({
|
||||||
featureName: f.featureName,
|
featureName: f.featureName?.trim() || null,
|
||||||
featureValue: f.featureValue,
|
featureValue: f.featureValue,
|
||||||
})),
|
})),
|
||||||
comboItems: (sku.comboItems || []).map((ci) => ({ skuId: ci.skuId })),
|
comboItems: (sku.comboItems || []).map((ci) => ({ skuId: ci.skuId })),
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ CREATE TABLE `product_skus` (
|
||||||
`is_suspended` integer DEFAULT false NOT NULL,
|
`is_suspended` integer DEFAULT false NOT NULL,
|
||||||
`is_flash_available` integer DEFAULT false NOT NULL,
|
`is_flash_available` integer DEFAULT false NOT NULL,
|
||||||
`flash_price` text,
|
`flash_price` text,
|
||||||
|
`is_offer` integer DEFAULT false NOT NULL,
|
||||||
|
`is_combo_only` integer DEFAULT false NOT NULL,
|
||||||
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
FOREIGN KEY (`product_id`) REFERENCES `product_info`(`id`) ON UPDATE no action ON DELETE no action
|
FOREIGN KEY (`product_id`) REFERENCES `product_info`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
);
|
);
|
||||||
|
|
@ -23,7 +25,7 @@ CREATE TABLE `product_skus` (
|
||||||
CREATE TABLE `sku_features` (
|
CREATE TABLE `sku_features` (
|
||||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
`sku_id` integer NOT NULL,
|
`sku_id` integer NOT NULL,
|
||||||
`feature_name` text NOT NULL,
|
`feature_name` text,
|
||||||
`feature_value` text NOT NULL,
|
`feature_value` text NOT NULL,
|
||||||
FOREIGN KEY (`sku_id`) REFERENCES `product_skus`(`id`) ON UPDATE no action ON DELETE no action
|
FOREIGN KEY (`sku_id`) REFERENCES `product_skus`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,8 @@ const mapSku = (sku: SkuRow, features: SkuFeatureRow[] = [], comboItems: any[] =
|
||||||
isSuspended: sku.isSuspended,
|
isSuspended: sku.isSuspended,
|
||||||
isFlashAvailable: sku.isFlashAvailable,
|
isFlashAvailable: sku.isFlashAvailable,
|
||||||
flashPrice: sku.flashPrice ? String(sku.flashPrice) : null,
|
flashPrice: sku.flashPrice ? String(sku.flashPrice) : null,
|
||||||
|
isOffer: sku.isOffer,
|
||||||
|
isComboOnly: sku.isComboOnly,
|
||||||
createdAt: sku.createdAt,
|
createdAt: sku.createdAt,
|
||||||
features: features.map(mapSkuFeature),
|
features: features.map(mapSkuFeature),
|
||||||
comboItems: comboItems,
|
comboItems: comboItems,
|
||||||
|
|
@ -275,6 +277,8 @@ export async function createProduct(input: CreateProductInput): Promise<AdminPro
|
||||||
images: sku.images ?? null,
|
images: sku.images ?? null,
|
||||||
isFlashAvailable: sku.isFlashAvailable ?? false,
|
isFlashAvailable: sku.isFlashAvailable ?? false,
|
||||||
flashPrice: sku.flashPrice != null ? String(sku.flashPrice) : null,
|
flashPrice: sku.flashPrice != null ? String(sku.flashPrice) : null,
|
||||||
|
isOffer: sku.isOffer ?? false,
|
||||||
|
isComboOnly: sku.isComboOnly ?? false,
|
||||||
}))
|
}))
|
||||||
).returning()
|
).returning()
|
||||||
|
|
||||||
|
|
@ -362,6 +366,8 @@ export async function updateProduct(id: number, input: any): Promise<AdminProduc
|
||||||
images: sku.images ?? null,
|
images: sku.images ?? null,
|
||||||
isFlashAvailable: sku.isFlashAvailable ?? false,
|
isFlashAvailable: sku.isFlashAvailable ?? false,
|
||||||
flashPrice: sku.flashPrice != null ? String(sku.flashPrice) : null,
|
flashPrice: sku.flashPrice != null ? String(sku.flashPrice) : null,
|
||||||
|
isOffer: sku.isOffer ?? false,
|
||||||
|
isComboOnly: sku.isComboOnly ?? false,
|
||||||
})
|
})
|
||||||
.where(eq(productSkus.id, sku.id))
|
.where(eq(productSkus.id, sku.id))
|
||||||
|
|
||||||
|
|
@ -393,6 +399,8 @@ export async function updateProduct(id: number, input: any): Promise<AdminProduc
|
||||||
images: sku.images ?? null,
|
images: sku.images ?? null,
|
||||||
isFlashAvailable: sku.isFlashAvailable ?? false,
|
isFlashAvailable: sku.isFlashAvailable ?? false,
|
||||||
flashPrice: sku.flashPrice != null ? String(sku.flashPrice) : null,
|
flashPrice: sku.flashPrice != null ? String(sku.flashPrice) : null,
|
||||||
|
isOffer: sku.isOffer ?? false,
|
||||||
|
isComboOnly: sku.isComboOnly ?? false,
|
||||||
}).returning()
|
}).returning()
|
||||||
|
|
||||||
await db.insert(skuFeatures).values(
|
await db.insert(skuFeatures).values(
|
||||||
|
|
|
||||||
|
|
@ -208,13 +208,15 @@ export const productSkus = sqliteTable('product_skus', {
|
||||||
isSuspended: integer('is_suspended', { mode: 'boolean' }).notNull().default(false),
|
isSuspended: integer('is_suspended', { mode: 'boolean' }).notNull().default(false),
|
||||||
isFlashAvailable: integer('is_flash_available', { mode: 'boolean' }).notNull().default(false),
|
isFlashAvailable: integer('is_flash_available', { mode: 'boolean' }).notNull().default(false),
|
||||||
flashPrice: numericText('flash_price'),
|
flashPrice: numericText('flash_price'),
|
||||||
|
isOffer: integer('is_offer', { mode: 'boolean' }).notNull().default(false),
|
||||||
|
isComboOnly: integer('is_combo_only', { mode: 'boolean' }).notNull().default(false),
|
||||||
createdAt: timestampText('created_at').notNull().default(sql`CURRENT_TIMESTAMP`),
|
createdAt: timestampText('created_at').notNull().default(sql`CURRENT_TIMESTAMP`),
|
||||||
})
|
})
|
||||||
|
|
||||||
export const skuFeatures = sqliteTable('sku_features', {
|
export const skuFeatures = sqliteTable('sku_features', {
|
||||||
id: integer().primaryKey({ autoIncrement: true }),
|
id: integer().primaryKey({ autoIncrement: true }),
|
||||||
skuId: integer('sku_id').notNull().references(() => productSkus.id),
|
skuId: integer('sku_id').notNull().references(() => productSkus.id),
|
||||||
featureName: text('feature_name').notNull(),
|
featureName: text('feature_name'),
|
||||||
featureValue: text('feature_value').notNull(),
|
featureValue: text('feature_value').notNull(),
|
||||||
}, (t) => ({
|
}, (t) => ({
|
||||||
unq_sku_feature_name: uniqueIndex('unique_sku_feature_name').on(t.skuId, t.featureName),
|
unq_sku_feature_name: uniqueIndex('unique_sku_feature_name').on(t.skuId, t.featureName),
|
||||||
|
|
|
||||||
|
|
@ -181,7 +181,7 @@ export interface ProductSummaryData {
|
||||||
isOutOfStock: boolean
|
isOutOfStock: boolean
|
||||||
unitShortNotation: string
|
unitShortNotation: string
|
||||||
productQuantity: number
|
productQuantity: number
|
||||||
features: { featureName: string; featureValue: string }[]
|
features: { featureName: string | null; featureValue: string }[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getAllProductsWithUnits(tagId?: number): Promise<ProductSummaryData[]> {
|
export async function getAllProductsWithUnits(tagId?: number): Promise<ProductSummaryData[]> {
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,7 @@ export interface AdminOrderDetailsItem {
|
||||||
isPackaged: boolean;
|
isPackaged: boolean;
|
||||||
isPackageVerified: boolean;
|
isPackageVerified: boolean;
|
||||||
skuName?: string | null;
|
skuName?: string | null;
|
||||||
features?: { featureName: string; featureValue: string }[];
|
features?: { featureName: string | null; featureValue: string }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AdminOrderDetailsPayment {
|
export interface AdminOrderDetailsPayment {
|
||||||
|
|
@ -251,7 +251,7 @@ export interface AdminSlotOrderItem {
|
||||||
isPackaged: boolean;
|
isPackaged: boolean;
|
||||||
isPackageVerified: boolean;
|
isPackageVerified: boolean;
|
||||||
skuName?: string | null;
|
skuName?: string | null;
|
||||||
features?: { featureName: string; featureValue: string }[];
|
features?: { featureName: string | null; featureValue: string }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AdminSlotOrder {
|
export interface AdminSlotOrder {
|
||||||
|
|
@ -292,7 +292,7 @@ export interface AdminOrderListItemProduct {
|
||||||
isPackaged: boolean;
|
isPackaged: boolean;
|
||||||
isPackageVerified: boolean;
|
isPackageVerified: boolean;
|
||||||
skuName?: string | null;
|
skuName?: string | null;
|
||||||
features?: { featureName: string; featureValue: string }[];
|
features?: { featureName: string | null; featureValue: string }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AdminOrderListItem {
|
export interface AdminOrderListItem {
|
||||||
|
|
@ -363,7 +363,7 @@ export interface AdminUnit {
|
||||||
export interface AdminSkuFeature {
|
export interface AdminSkuFeature {
|
||||||
id: number;
|
id: number;
|
||||||
skuId: number;
|
skuId: number;
|
||||||
featureName: string;
|
featureName: string | null;
|
||||||
featureValue: string;
|
featureValue: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -388,6 +388,8 @@ export interface AdminSku {
|
||||||
isSuspended: boolean
|
isSuspended: boolean
|
||||||
isFlashAvailable: boolean
|
isFlashAvailable: boolean
|
||||||
flashPrice: string | null
|
flashPrice: string | null
|
||||||
|
isOffer: boolean
|
||||||
|
isComboOnly: boolean
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
features: AdminSkuFeature[]
|
features: AdminSkuFeature[]
|
||||||
comboItems: AdminProductComboItem[]
|
comboItems: AdminProductComboItem[]
|
||||||
|
|
@ -429,6 +431,7 @@ export interface CreateSkuInput {
|
||||||
isSuspended?: boolean
|
isSuspended?: boolean
|
||||||
isFlashAvailable?: boolean
|
isFlashAvailable?: boolean
|
||||||
flashPrice?: number | string | null
|
flashPrice?: number | string | null
|
||||||
|
isOffer?: boolean
|
||||||
isComboOnly?: boolean
|
isComboOnly?: boolean
|
||||||
sortOrder?: number
|
sortOrder?: number
|
||||||
isDefault?: boolean
|
isDefault?: boolean
|
||||||
|
|
@ -573,7 +576,7 @@ export interface AdminSlotProductSummary {
|
||||||
name: string;
|
name: string;
|
||||||
images: string[] | null;
|
images: string[] | null;
|
||||||
skuName?: string | null;
|
skuName?: string | null;
|
||||||
features?: { featureName: string; featureValue: string }[];
|
features?: { featureName: string | null; featureValue: string }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AdminVendorSnippet {
|
export interface AdminVendorSnippet {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue