This commit is contained in:
shafi54 2026-08-01 22:02:39 +05:30
parent db2ee44a5f
commit 6d67a1e759
9 changed files with 67 additions and 14 deletions

View file

@ -44,6 +44,8 @@ export default function AddProduct() {
images: variantUrls,
isFlashAvailable: variant.isFlashAvailable || false,
flashPrice: variant.flashPrice ? parseFloat(variant.flashPrice) : undefined,
isOffer: variant.isOffer || false,
isComboOnly: variant.isComboOnly || false,
features: variant.attributes.map((attr: any) => ({
featureName: attr.featureName,
featureValue: attr.featureValue,
@ -85,6 +87,8 @@ export default function AddProduct() {
marketPrice: '',
isFlashAvailable: false,
flashPrice: '',
isOffer: false,
isComboOnly: false,
attributes: [{ featureName: 'quantity', featureValue: '' }],
comboItems: [] as { skuId: number | string }[],
},

View file

@ -49,6 +49,8 @@ export default function EditProduct() {
marketPrice: sku.marketPrice || '',
isFlashAvailable: sku.isFlashAvailable || false,
flashPrice: sku.flashPrice || '',
isOffer: sku.isOffer || false,
isComboOnly: sku.isComboOnly || false,
attributes: (sku.features || []).map((f) => ({
featureName: f.featureName,
featureValue: f.featureValue,
@ -118,6 +120,8 @@ export default function EditProduct() {
images: allUrls,
isFlashAvailable: variant.isFlashAvailable || false,
flashPrice: variant.flashPrice ? parseFloat(variant.flashPrice) : undefined,
isOffer: variant.isOffer || false,
isComboOnly: variant.isComboOnly || false,
features: variant.attributes.map((attr: any) => ({
featureName: attr.featureName,
featureValue: attr.featureValue,

View file

@ -6,7 +6,7 @@ import MaterialIcons from '@expo/vector-icons/MaterialIcons'
import { trpc } from '../trpc-client'
interface Attribute {
featureName: string
featureName: string | null
featureValue: string
}
@ -17,6 +17,8 @@ interface Variant {
marketPrice: string
isFlashAvailable: boolean
flashPrice: string
isOffer: boolean
isComboOnly: boolean
attributes: Attribute[]
comboItems: { skuId: number | string }[]
}
@ -52,6 +54,8 @@ const defaultVariant = (): Variant => ({
marketPrice: '',
isFlashAvailable: false,
flashPrice: '',
isOffer: false,
isComboOnly: false,
attributes: [defaultAttribute()],
comboItems: [],
})
@ -210,7 +214,7 @@ const ProductForm = forwardRef<ProductFormRef, ProductFormProps>(({
<View style={tw`flex-1`}>
<MyTextInput
placeholder="Name"
value={attr.featureName}
value={attr.featureName ?? ''}
onChangeText={handleChange(`variants.${vIndex}.attributes.${aIndex}.featureName`)}
/>
</View>
@ -265,6 +269,24 @@ const ProductForm = forwardRef<ProductFormRef, ProductFormProps>(({
<MyText style={tw`text-gray-700 font-medium`}>Flash Available</MyText>
</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 && (
<MyTextInput
topLabel="Flash Price"

View file

@ -196,8 +196,10 @@ export const productRouter = router({
images: z.array(z.string()).optional().default([]),
isFlashAvailable: z.boolean().optional().default(false),
flashPrice: z.number().optional().nullable(),
isOffer: z.boolean().optional().default(false),
isComboOnly: z.boolean().optional().default(false),
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'),
})).min(1, 'At least one feature is required'),
comboItems: z.array(z.object({
@ -222,8 +224,10 @@ export const productRouter = router({
images: sku.images.map((url) => extractKeyFromPresignedUrl(url)),
isFlashAvailable: sku.isFlashAvailable,
flashPrice: sku.flashPrice ?? null,
isOffer: sku.isOffer,
isComboOnly: sku.isComboOnly,
features: sku.features.map((f) => ({
featureName: f.featureName,
featureName: f.featureName?.trim() || null,
featureValue: f.featureValue,
})),
comboItems: (sku.comboItems || []).map((ci) => ({ skuId: ci.skuId })),
@ -278,8 +282,10 @@ export const productRouter = router({
images: z.array(z.string()).optional().default([]),
isFlashAvailable: z.boolean().optional().default(false),
flashPrice: z.number().optional().nullable(),
isOffer: z.boolean().optional().default(false),
isComboOnly: z.boolean().optional().default(false),
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'),
})).min(1, 'At least one feature is required'),
comboItems: z.array(z.object({
@ -306,8 +312,10 @@ export const productRouter = router({
images: sku.images.map((url) => extractKeyFromPresignedUrl(url)),
isFlashAvailable: sku.isFlashAvailable,
flashPrice: sku.flashPrice ?? null,
isOffer: sku.isOffer,
isComboOnly: sku.isComboOnly,
features: sku.features.map((f) => ({
featureName: f.featureName,
featureName: f.featureName?.trim() || null,
featureValue: f.featureValue,
})),
comboItems: (sku.comboItems || []).map((ci) => ({ skuId: ci.skuId })),

View file

@ -16,6 +16,8 @@ CREATE TABLE `product_skus` (
`is_suspended` integer DEFAULT false NOT NULL,
`is_flash_available` integer DEFAULT false NOT NULL,
`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,
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` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`sku_id` integer NOT NULL,
`feature_name` text NOT NULL,
`feature_name` text,
`feature_value` text NOT NULL,
FOREIGN KEY (`sku_id`) REFERENCES `product_skus`(`id`) ON UPDATE no action ON DELETE no action
);

View file

@ -106,6 +106,8 @@ const mapSku = (sku: SkuRow, features: SkuFeatureRow[] = [], comboItems: any[] =
isSuspended: sku.isSuspended,
isFlashAvailable: sku.isFlashAvailable,
flashPrice: sku.flashPrice ? String(sku.flashPrice) : null,
isOffer: sku.isOffer,
isComboOnly: sku.isComboOnly,
createdAt: sku.createdAt,
features: features.map(mapSkuFeature),
comboItems: comboItems,
@ -275,6 +277,8 @@ export async function createProduct(input: CreateProductInput): Promise<AdminPro
images: sku.images ?? null,
isFlashAvailable: sku.isFlashAvailable ?? false,
flashPrice: sku.flashPrice != null ? String(sku.flashPrice) : null,
isOffer: sku.isOffer ?? false,
isComboOnly: sku.isComboOnly ?? false,
}))
).returning()
@ -362,6 +366,8 @@ export async function updateProduct(id: number, input: any): Promise<AdminProduc
images: sku.images ?? null,
isFlashAvailable: sku.isFlashAvailable ?? false,
flashPrice: sku.flashPrice != null ? String(sku.flashPrice) : null,
isOffer: sku.isOffer ?? false,
isComboOnly: sku.isComboOnly ?? false,
})
.where(eq(productSkus.id, sku.id))
@ -393,6 +399,8 @@ export async function updateProduct(id: number, input: any): Promise<AdminProduc
images: sku.images ?? null,
isFlashAvailable: sku.isFlashAvailable ?? false,
flashPrice: sku.flashPrice != null ? String(sku.flashPrice) : null,
isOffer: sku.isOffer ?? false,
isComboOnly: sku.isComboOnly ?? false,
}).returning()
await db.insert(skuFeatures).values(

View file

@ -208,13 +208,15 @@ export const productSkus = sqliteTable('product_skus', {
isSuspended: integer('is_suspended', { mode: 'boolean' }).notNull().default(false),
isFlashAvailable: integer('is_flash_available', { mode: 'boolean' }).notNull().default(false),
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`),
})
export const skuFeatures = sqliteTable('sku_features', {
id: integer().primaryKey({ autoIncrement: true }),
skuId: integer('sku_id').notNull().references(() => productSkus.id),
featureName: text('feature_name').notNull(),
featureName: text('feature_name'),
featureValue: text('feature_value').notNull(),
}, (t) => ({
unq_sku_feature_name: uniqueIndex('unique_sku_feature_name').on(t.skuId, t.featureName),

View file

@ -181,7 +181,7 @@ export interface ProductSummaryData {
isOutOfStock: boolean
unitShortNotation: string
productQuantity: number
features: { featureName: string; featureValue: string }[]
features: { featureName: string | null; featureValue: string }[]
}
export async function getAllProductsWithUnits(tagId?: number): Promise<ProductSummaryData[]> {

View file

@ -171,7 +171,7 @@ export interface AdminOrderDetailsItem {
isPackaged: boolean;
isPackageVerified: boolean;
skuName?: string | null;
features?: { featureName: string; featureValue: string }[];
features?: { featureName: string | null; featureValue: string }[];
}
export interface AdminOrderDetailsPayment {
@ -251,7 +251,7 @@ export interface AdminSlotOrderItem {
isPackaged: boolean;
isPackageVerified: boolean;
skuName?: string | null;
features?: { featureName: string; featureValue: string }[];
features?: { featureName: string | null; featureValue: string }[];
}
export interface AdminSlotOrder {
@ -292,7 +292,7 @@ export interface AdminOrderListItemProduct {
isPackaged: boolean;
isPackageVerified: boolean;
skuName?: string | null;
features?: { featureName: string; featureValue: string }[];
features?: { featureName: string | null; featureValue: string }[];
}
export interface AdminOrderListItem {
@ -363,7 +363,7 @@ export interface AdminUnit {
export interface AdminSkuFeature {
id: number;
skuId: number;
featureName: string;
featureName: string | null;
featureValue: string;
}
@ -388,6 +388,8 @@ export interface AdminSku {
isSuspended: boolean
isFlashAvailable: boolean
flashPrice: string | null
isOffer: boolean
isComboOnly: boolean
createdAt: Date
features: AdminSkuFeature[]
comboItems: AdminProductComboItem[]
@ -429,6 +431,7 @@ export interface CreateSkuInput {
isSuspended?: boolean
isFlashAvailable?: boolean
flashPrice?: number | string | null
isOffer?: boolean
isComboOnly?: boolean
sortOrder?: number
isDefault?: boolean
@ -573,7 +576,7 @@ export interface AdminSlotProductSummary {
name: string;
images: string[] | null;
skuName?: string | null;
features?: { featureName: string; featureValue: string }[];
features?: { featureName: string | null; featureValue: string }[];
}
export interface AdminVendorSnippet {