From 86de06078ffc5ddf00b0c179b7cf39a2cdec69d4 Mon Sep 17 00:00:00 2001
From: shafi54 <108669266+shafi-aviz@users.noreply.github.com>
Date: Sun, 1 Feb 2026 21:01:05 +0530
Subject: [PATCH] enh
---
apps/admin-ui/app/(drawer)/add-slot/index.tsx | 29 +-
apps/admin-ui/app/(drawer)/slots/index.tsx | 40 +-
apps/admin-ui/components/SlotForm.tsx | 36 +-
apps/backend/drizzle/0070_known_ares.sql | 1 +
apps/backend/drizzle/meta/0070_snapshot.json | 3624 +++++++++++++++++
apps/backend/drizzle/meta/_journal.json | 7 +
apps/backend/src/db/schema.ts | 1 +
apps/backend/src/trpc/admin-apis/slots.ts | 21 +-
.../src/components/3d/Characters.tsx | 26 +-
9 files changed, 3749 insertions(+), 36 deletions(-)
create mode 100644 apps/backend/drizzle/0070_known_ares.sql
create mode 100644 apps/backend/drizzle/meta/0070_snapshot.json
diff --git a/apps/admin-ui/app/(drawer)/add-slot/index.tsx b/apps/admin-ui/app/(drawer)/add-slot/index.tsx
index 5ee2002..ac69049 100644
--- a/apps/admin-ui/app/(drawer)/add-slot/index.tsx
+++ b/apps/admin-ui/app/(drawer)/add-slot/index.tsx
@@ -1,22 +1,43 @@
import React from 'react';
-import { View } from 'react-native';
+import { View, Text } from 'react-native';
import { AppContainer } from 'common-ui';
import SlotForm from '../../../components/SlotForm';
-import { useRouter } from 'expo-router';
+import { useRouter, useLocalSearchParams } from 'expo-router';
import { trpc } from '../../../src/trpc-client';
export default function AddSlot() {
const router = useRouter();
+ const { baseslot } = useLocalSearchParams();
+ const baseSlotId = baseslot ? parseInt(baseslot as string) : null;
+
const { refetch } = trpc.admin.slots.getAll.useQuery();
+ const { data: baseSlotData, isLoading } = trpc.admin.slots.getSlotById.useQuery(
+ { id: baseSlotId! },
+ { enabled: !!baseSlotId }
+ );
const handleSlotAdded = () => {
refetch();
router.back();
};
+ if (isLoading && baseSlotId) {
+ return (
+
+
+ Loading base slot...
+
+
+ );
+ }
+
return (
-
+ p.id) || []}
+ initialGroupIds={baseSlotData?.slot?.groupIds || []}
+ />
);
-}
\ No newline at end of file
+}
diff --git a/apps/admin-ui/app/(drawer)/slots/index.tsx b/apps/admin-ui/app/(drawer)/slots/index.tsx
index 139161c..a46ad63 100644
--- a/apps/admin-ui/app/(drawer)/slots/index.tsx
+++ b/apps/admin-ui/app/(drawer)/slots/index.tsx
@@ -1,6 +1,6 @@
import React, { useState } from 'react';
-import { MaterialCommunityIcons } from '@expo/vector-icons';
-import { View, TouchableOpacity, FlatList } from 'react-native';
+import { MaterialCommunityIcons, Entypo } from '@expo/vector-icons';
+import { View, TouchableOpacity, FlatList, Alert } from 'react-native';
import { AppContainer, MyText, tw, MyFlatList , BottomDialog, MyTouchableOpacity } from 'common-ui';
import { trpc } from '../../../src/trpc-client';
import { useRouter } from 'expo-router';
@@ -20,6 +20,7 @@ const SlotItemComponent: React.FC = ({
setDialogProducts,
setDialogOpen,
}) => {
+ const [menuOpen, setMenuOpen] = useState(false);
const slotProducts = slot.products?.map((p: any) => p.name).filter(Boolean) || [];
const displayProducts = slotProducts.slice(0, 2).join(', ');
@@ -57,9 +58,44 @@ const SlotItemComponent: React.FC = ({
{statusText}
+ setMenuOpen(true)}
+ style={tw`ml-2 p-1`}
+ hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
+ >
+
+
+ {/* Replicate Menu Dialog */}
+ setMenuOpen(false)}>
+
+ Slot #{slot.id} Actions
+ {
+ setMenuOpen(false);
+ router.push(`/add-slot?baseslot=${slot.id}` as any);
+ }}
+ style={tw`py-4 border-b border-gray-200`}
+ >
+
+
+ Replicate Slot
+
+
+ setMenuOpen(false)}
+ style={tw`py-4 mt-2`}
+ >
+
+
+ Cancel
+
+
+
+
+
{/* Divider */}
diff --git a/apps/admin-ui/components/SlotForm.tsx b/apps/admin-ui/components/SlotForm.tsx
index c3880b7..e7fda24 100644
--- a/apps/admin-ui/components/SlotForm.tsx
+++ b/apps/admin-ui/components/SlotForm.tsx
@@ -20,6 +20,7 @@ interface SlotFormProps {
initialIsActive?: boolean;
slotId?: number;
initialProductIds?: number[];
+ initialGroupIds?: number[];
}
export default function SlotForm({
@@ -29,13 +30,26 @@ export default function SlotForm({
initialIsActive = true,
slotId,
initialProductIds = [],
+ initialGroupIds = [],
}: SlotFormProps) {
+ const { data: slotData } = trpc.admin.slots.getSlotById.useQuery(
+ { id: slotId! },
+ { enabled: !!slotId }
+ );
+
+ const vendorSnippetsFromSlot = (slotData?.slot?.vendorSnippets || []).map((snippet: any) => ({
+ name: snippet.name || '',
+ groupIds: snippet.groupIds || [],
+ productIds: snippet.productIds || [],
+ validTill: snippet.validTill || undefined,
+ })) as VendorSnippet[];
+
const initialValues = {
- deliveryTime: initialDeliveryTime || null,
- freezeTime: initialFreezeTime || null,
- selectedGroupIds: [] as number[],
- selectedProductIds: initialProductIds,
- vendorSnippetList: [] as VendorSnippet[],
+ deliveryTime: initialDeliveryTime || (slotData?.slot?.deliveryTime ? new Date(slotData.slot.deliveryTime) : null),
+ freezeTime: initialFreezeTime || (slotData?.slot?.freezeTime ? new Date(slotData.slot.freezeTime) : null),
+ selectedGroupIds: initialGroupIds.length > 0 ? initialGroupIds : (slotData?.slot?.groupIds || []),
+ selectedProductIds: initialProductIds.length > 0 ? initialProductIds : (slotData?.slot?.products?.map((p: any) => p.id) || []),
+ vendorSnippetList: vendorSnippetsFromSlot,
};
const { mutate: createSlot, isPending: isCreating } = trpc.admin.slots.createSlot.useMutation();
@@ -63,20 +77,14 @@ export default function SlotForm({
deliveryTime: values.deliveryTime.toISOString(),
freezeTime: values.freezeTime.toISOString(),
isActive: initialIsActive,
+ groupIds: values.selectedGroupIds,
productIds: values.selectedProductIds,
- vendorSnippets: values.vendorSnippetList.map(snippet => ({
+ vendorSnippets: values.vendorSnippetList.map((snippet: VendorSnippet) => ({
name: snippet.name,
productIds: snippet.productIds,
validTill: snippet.validTill,
})),
};
-
- console.log({snippetList: values.vendorSnippetList})
-
- values.vendorSnippetList.forEach((snippet, index) => {
- console.log({snippet})
-
- });
if (isEditMode && slotId) {
@@ -160,7 +168,7 @@ export default function SlotForm({
{({ push, remove }) => (
Vendor Snippets
- {values.vendorSnippetList.map((snippet, index) => (
+ {values.vendorSnippetList.map((snippet: VendorSnippet, index: number) => (
= 1 AND \"mf\".\"product_reviews\".\"ratings\" <= 5"
+ }
+ },
+ "isRLSEnabled": false
+ },
+ "mf.product_slots": {
+ "name": "product_slots",
+ "schema": "mf",
+ "columns": {
+ "product_id": {
+ "name": "product_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slot_id": {
+ "name": "slot_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "product_slots_product_id_product_info_id_fk": {
+ "name": "product_slots_product_id_product_info_id_fk",
+ "tableFrom": "product_slots",
+ "tableTo": "product_info",
+ "schemaTo": "mf",
+ "columnsFrom": [
+ "product_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "product_slots_slot_id_delivery_slot_info_id_fk": {
+ "name": "product_slots_slot_id_delivery_slot_info_id_fk",
+ "tableFrom": "product_slots",
+ "tableTo": "delivery_slot_info",
+ "schemaTo": "mf",
+ "columnsFrom": [
+ "slot_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "product_slot_pk": {
+ "name": "product_slot_pk",
+ "nullsNotDistinct": false,
+ "columns": [
+ "product_id",
+ "slot_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "mf.product_tag_info": {
+ "name": "product_tag_info",
+ "schema": "mf",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "product_tag_info_id_seq",
+ "schema": "mf",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "tag_name": {
+ "name": "tag_name",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tag_description": {
+ "name": "tag_description",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "image_url": {
+ "name": "image_url",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_dashboard_tag": {
+ "name": "is_dashboard_tag",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "product_tag_info_tag_name_unique": {
+ "name": "product_tag_info_tag_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "tag_name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "mf.product_tags": {
+ "name": "product_tags",
+ "schema": "mf",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "product_tags_id_seq",
+ "schema": "mf",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "product_id": {
+ "name": "product_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "tag_id": {
+ "name": "tag_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "assigned_at": {
+ "name": "assigned_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "product_tags_product_id_product_info_id_fk": {
+ "name": "product_tags_product_id_product_info_id_fk",
+ "tableFrom": "product_tags",
+ "tableTo": "product_info",
+ "schemaTo": "mf",
+ "columnsFrom": [
+ "product_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "product_tags_tag_id_product_tag_info_id_fk": {
+ "name": "product_tags_tag_id_product_tag_info_id_fk",
+ "tableFrom": "product_tags",
+ "tableTo": "product_tag_info",
+ "schemaTo": "mf",
+ "columnsFrom": [
+ "tag_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "unique_product_tag": {
+ "name": "unique_product_tag",
+ "nullsNotDistinct": false,
+ "columns": [
+ "product_id",
+ "tag_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "mf.refunds": {
+ "name": "refunds",
+ "schema": "mf",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "refunds_id_seq",
+ "schema": "mf",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "order_id": {
+ "name": "order_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "refund_amount": {
+ "name": "refund_amount",
+ "type": "numeric(10, 2)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refund_status": {
+ "name": "refund_status",
+ "type": "varchar(50)",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'none'"
+ },
+ "merchant_refund_id": {
+ "name": "merchant_refund_id",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refund_processed_at": {
+ "name": "refund_processed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "refunds_order_id_orders_id_fk": {
+ "name": "refunds_order_id_orders_id_fk",
+ "tableFrom": "refunds",
+ "tableTo": "orders",
+ "schemaTo": "mf",
+ "columnsFrom": [
+ "order_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "mf.reserved_coupons": {
+ "name": "reserved_coupons",
+ "schema": "mf",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "reserved_coupons_id_seq",
+ "schema": "mf",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "secret_code": {
+ "name": "secret_code",
+ "type": "varchar(50)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "coupon_code": {
+ "name": "coupon_code",
+ "type": "varchar(50)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "discount_percent": {
+ "name": "discount_percent",
+ "type": "numeric(5, 2)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "flat_discount": {
+ "name": "flat_discount",
+ "type": "numeric(10, 2)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "min_order": {
+ "name": "min_order",
+ "type": "numeric(10, 2)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "product_ids": {
+ "name": "product_ids",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "max_value": {
+ "name": "max_value",
+ "type": "numeric(10, 2)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "valid_till": {
+ "name": "valid_till",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "max_limit_for_user": {
+ "name": "max_limit_for_user",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "exclusive_apply": {
+ "name": "exclusive_apply",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "is_redeemed": {
+ "name": "is_redeemed",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "redeemed_by": {
+ "name": "redeemed_by",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "redeemed_at": {
+ "name": "redeemed_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_by": {
+ "name": "created_by",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "reserved_coupons_redeemed_by_users_id_fk": {
+ "name": "reserved_coupons_redeemed_by_users_id_fk",
+ "tableFrom": "reserved_coupons",
+ "tableTo": "users",
+ "schemaTo": "mf",
+ "columnsFrom": [
+ "redeemed_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "reserved_coupons_created_by_staff_users_id_fk": {
+ "name": "reserved_coupons_created_by_staff_users_id_fk",
+ "tableFrom": "reserved_coupons",
+ "tableTo": "staff_users",
+ "schemaTo": "mf",
+ "columnsFrom": [
+ "created_by"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "reserved_coupons_secret_code_unique": {
+ "name": "reserved_coupons_secret_code_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "secret_code"
+ ]
+ },
+ "unique_secret_code": {
+ "name": "unique_secret_code",
+ "nullsNotDistinct": false,
+ "columns": [
+ "secret_code"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "mf.special_deals": {
+ "name": "special_deals",
+ "schema": "mf",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "special_deals_id_seq",
+ "schema": "mf",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "product_id": {
+ "name": "product_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "quantity": {
+ "name": "quantity",
+ "type": "numeric(10, 2)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "price": {
+ "name": "price",
+ "type": "numeric(10, 2)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "valid_till": {
+ "name": "valid_till",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "special_deals_product_id_product_info_id_fk": {
+ "name": "special_deals_product_id_product_info_id_fk",
+ "tableFrom": "special_deals",
+ "tableTo": "product_info",
+ "schemaTo": "mf",
+ "columnsFrom": [
+ "product_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "mf.staff_permissions": {
+ "name": "staff_permissions",
+ "schema": "mf",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "staff_permissions_id_seq",
+ "schema": "mf",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "permission_name": {
+ "name": "permission_name",
+ "type": "staff_permission",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "unique_permission_name": {
+ "name": "unique_permission_name",
+ "nullsNotDistinct": false,
+ "columns": [
+ "permission_name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "mf.staff_role_permissions": {
+ "name": "staff_role_permissions",
+ "schema": "mf",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "staff_role_permissions_id_seq",
+ "schema": "mf",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "staff_role_id": {
+ "name": "staff_role_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "staff_permission_id": {
+ "name": "staff_permission_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "staff_role_permissions_staff_role_id_staff_roles_id_fk": {
+ "name": "staff_role_permissions_staff_role_id_staff_roles_id_fk",
+ "tableFrom": "staff_role_permissions",
+ "tableTo": "staff_roles",
+ "schemaTo": "mf",
+ "columnsFrom": [
+ "staff_role_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "staff_role_permissions_staff_permission_id_staff_permissions_id_fk": {
+ "name": "staff_role_permissions_staff_permission_id_staff_permissions_id_fk",
+ "tableFrom": "staff_role_permissions",
+ "tableTo": "staff_permissions",
+ "schemaTo": "mf",
+ "columnsFrom": [
+ "staff_permission_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "unique_role_permission": {
+ "name": "unique_role_permission",
+ "nullsNotDistinct": false,
+ "columns": [
+ "staff_role_id",
+ "staff_permission_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "mf.staff_roles": {
+ "name": "staff_roles",
+ "schema": "mf",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "staff_roles_id_seq",
+ "schema": "mf",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "role_name": {
+ "name": "role_name",
+ "type": "staff_role",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "unique_role_name": {
+ "name": "unique_role_name",
+ "nullsNotDistinct": false,
+ "columns": [
+ "role_name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "mf.staff_users": {
+ "name": "staff_users",
+ "schema": "mf",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "staff_users_id_seq",
+ "schema": "mf",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "password": {
+ "name": "password",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "staff_role_id": {
+ "name": "staff_role_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "staff_users_staff_role_id_staff_roles_id_fk": {
+ "name": "staff_users_staff_role_id_staff_roles_id_fk",
+ "tableFrom": "staff_users",
+ "tableTo": "staff_roles",
+ "schemaTo": "mf",
+ "columnsFrom": [
+ "staff_role_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "mf.store_info": {
+ "name": "store_info",
+ "schema": "mf",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "store_info_id_seq",
+ "schema": "mf",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "image_url": {
+ "name": "image_url",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "owner": {
+ "name": "owner",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "store_info_owner_staff_users_id_fk": {
+ "name": "store_info_owner_staff_users_id_fk",
+ "tableFrom": "store_info",
+ "tableTo": "staff_users",
+ "schemaTo": "mf",
+ "columnsFrom": [
+ "owner"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "mf.units": {
+ "name": "units",
+ "schema": "mf",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "units_id_seq",
+ "schema": "mf",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "short_notation": {
+ "name": "short_notation",
+ "type": "varchar(50)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "full_name": {
+ "name": "full_name",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "unique_short_notation": {
+ "name": "unique_short_notation",
+ "nullsNotDistinct": false,
+ "columns": [
+ "short_notation"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "mf.upload_url_status": {
+ "name": "upload_url_status",
+ "schema": "mf",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "upload_url_status_id_seq",
+ "schema": "mf",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "key": {
+ "name": "key",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "upload_status",
+ "typeSchema": "public",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "mf.user_creds": {
+ "name": "user_creds",
+ "schema": "mf",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "user_creds_id_seq",
+ "schema": "mf",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_password": {
+ "name": "user_password",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_creds_user_id_users_id_fk": {
+ "name": "user_creds_user_id_users_id_fk",
+ "tableFrom": "user_creds",
+ "tableTo": "users",
+ "schemaTo": "mf",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "mf.user_details": {
+ "name": "user_details",
+ "schema": "mf",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "user_details_id_seq",
+ "schema": "mf",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "bio": {
+ "name": "bio",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "date_of_birth": {
+ "name": "date_of_birth",
+ "type": "date",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "gender": {
+ "name": "gender",
+ "type": "varchar(20)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "occupation": {
+ "name": "occupation",
+ "type": "varchar(100)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "profile_image": {
+ "name": "profile_image",
+ "type": "varchar(500)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_suspended": {
+ "name": "is_suspended",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "user_details_user_id_users_id_fk": {
+ "name": "user_details_user_id_users_id_fk",
+ "tableFrom": "user_details",
+ "tableTo": "users",
+ "schemaTo": "mf",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_details_user_id_unique": {
+ "name": "user_details_user_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "user_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "mf.users": {
+ "name": "users",
+ "schema": "mf",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "users_id_seq",
+ "schema": "mf",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "name": {
+ "name": "name",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "email": {
+ "name": "email",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "mobile": {
+ "name": "mobile",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "unique_email": {
+ "name": "unique_email",
+ "nullsNotDistinct": false,
+ "columns": [
+ "email"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "mf.vendor_snippets": {
+ "name": "vendor_snippets",
+ "schema": "mf",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "vendor_snippets_id_seq",
+ "schema": "mf",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "snippet_code": {
+ "name": "snippet_code",
+ "type": "varchar(255)",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slot_id": {
+ "name": "slot_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_permanent": {
+ "name": "is_permanent",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "product_ids": {
+ "name": "product_ids",
+ "type": "integer[]",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "valid_till": {
+ "name": "valid_till",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "vendor_snippets_slot_id_delivery_slot_info_id_fk": {
+ "name": "vendor_snippets_slot_id_delivery_slot_info_id_fk",
+ "tableFrom": "vendor_snippets",
+ "tableTo": "delivery_slot_info",
+ "schemaTo": "mf",
+ "columnsFrom": [
+ "slot_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "vendor_snippets_snippet_code_unique": {
+ "name": "vendor_snippets_snippet_code_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "snippet_code"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {
+ "public.payment_status": {
+ "name": "payment_status",
+ "schema": "public",
+ "values": [
+ "pending",
+ "success",
+ "cod",
+ "failed"
+ ]
+ },
+ "public.staff_permission": {
+ "name": "staff_permission",
+ "schema": "public",
+ "values": [
+ "crud_product",
+ "make_coupon",
+ "crud_staff_users"
+ ]
+ },
+ "public.staff_role": {
+ "name": "staff_role",
+ "schema": "public",
+ "values": [
+ "super_admin",
+ "admin",
+ "marketer",
+ "delivery_staff"
+ ]
+ },
+ "public.upload_status": {
+ "name": "upload_status",
+ "schema": "public",
+ "values": [
+ "pending",
+ "claimed"
+ ]
+ }
+ },
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/apps/backend/drizzle/meta/_journal.json b/apps/backend/drizzle/meta/_journal.json
index 18e947c..6dbc0d1 100644
--- a/apps/backend/drizzle/meta/_journal.json
+++ b/apps/backend/drizzle/meta/_journal.json
@@ -491,6 +491,13 @@
"when": 1769718702463,
"tag": "0069_violet_smiling_tiger",
"breakpoints": true
+ },
+ {
+ "idx": 70,
+ "version": "7",
+ "when": 1769958949864,
+ "tag": "0070_known_ares",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/apps/backend/src/db/schema.ts b/apps/backend/src/db/schema.ts
index ed63934..4e92e55 100755
--- a/apps/backend/src/db/schema.ts
+++ b/apps/backend/src/db/schema.ts
@@ -192,6 +192,7 @@ export const deliverySlotInfo = mf.table('delivery_slot_info', {
isActive: boolean('is_active').notNull().default(true),
isFlash: boolean('is_flash').notNull().default(false),
deliverySequence: jsonb('delivery_sequence').$defaultFn(() => {}),
+ groupIds: jsonb('group_ids').$defaultFn(() => []),
});
export const vendorSnippets = mf.table('vendor_snippets', {
diff --git a/apps/backend/src/trpc/admin-apis/slots.ts b/apps/backend/src/trpc/admin-apis/slots.ts
index 5ed73d0..6cb020c 100644
--- a/apps/backend/src/trpc/admin-apis/slots.ts
+++ b/apps/backend/src/trpc/admin-apis/slots.ts
@@ -2,7 +2,7 @@ import { router, protectedProcedure } from "../trpc-index";
import { TRPCError } from "@trpc/server";
import { z } from "zod";
import { db } from "../../db/db_index";
-import { deliverySlotInfo, productSlots, productInfo, vendorSnippets } from "../../db/schema";
+import { deliverySlotInfo, productSlots, productInfo, vendorSnippets, productGroupInfo } from "../../db/schema";
import { eq, inArray, and, desc } from "drizzle-orm";
import { ApiError } from "../../lib/api-error";
import { appUrl } from "../../lib/env-exporter";
@@ -26,6 +26,7 @@ const createSlotSchema = z.object({
productIds: z.array(z.number().int().positive()).min(1),
validTill: z.string().optional(),
})).optional(),
+ groupIds: z.array(z.number()).optional(),
});
const getSlotByIdSchema = z.object({
@@ -43,6 +44,7 @@ const updateSlotSchema = z.object({
productIds: z.array(z.number().int().positive()).min(1),
validTill: z.string().optional(),
})).optional(),
+ groupIds: z.array(z.number()).optional(),
});
const deleteSlotSchema = z.object({
@@ -229,7 +231,7 @@ export const slotsRouter = router({
throw new TRPCError({ code: "UNAUTHORIZED", message: "Access denied" });
}
- const { deliveryTime, freezeTime, isActive, productIds, vendorSnippets: snippets } = input;
+ const { deliveryTime, freezeTime, isActive, productIds, vendorSnippets: snippets, groupIds } = input;
// Validate required fields
if (!deliveryTime || !freezeTime) {
@@ -244,6 +246,7 @@ export const slotsRouter = router({
deliveryTime: new Date(deliveryTime),
freezeTime: new Date(freezeTime),
isActive: isActive !== undefined ? isActive : true,
+ groupIds: groupIds !== undefined ? groupIds : [],
})
.returning();
@@ -348,6 +351,7 @@ export const slotsRouter = router({
slot: {
...slot,
deliverySequence: slot.deliverySequence as number[],
+ groupIds: slot.groupIds as number[],
products: slot.productSlots.map((ps) => ps.product),
vendorSnippets: slot.vendorSnippets?.map(snippet => ({
...snippet,
@@ -364,12 +368,22 @@ export const slotsRouter = router({
throw new TRPCError({ code: "UNAUTHORIZED", message: "Access denied" });
}
try{
- const { id, deliveryTime, freezeTime, isActive, productIds, vendorSnippets: snippets } = input;
+ const { id, deliveryTime, freezeTime, isActive, productIds, vendorSnippets: snippets, groupIds } = input;
if (!deliveryTime || !freezeTime) {
throw new ApiError("Delivery time and orders close time are required", 400);
}
+ // Filter groupIds to only include valid (existing) groups
+ let validGroupIds = groupIds;
+ if (groupIds && groupIds.length > 0) {
+ const existingGroups = await db.query.productGroupInfo.findMany({
+ where: inArray(productGroupInfo.id, groupIds),
+ columns: { id: true },
+ });
+ validGroupIds = existingGroups.map(g => g.id);
+ }
+
return await db.transaction(async (tx) => {
const [updatedSlot] = await tx
.update(deliverySlotInfo)
@@ -377,6 +391,7 @@ export const slotsRouter = router({
deliveryTime: new Date(deliveryTime),
freezeTime: new Date(freezeTime),
isActive: isActive !== undefined ? isActive : true,
+ groupIds: validGroupIds !== undefined ? validGroupIds : [],
})
.where(eq(deliverySlotInfo.id, id))
.returning();
diff --git a/apps/fallback-ui/src/components/3d/Characters.tsx b/apps/fallback-ui/src/components/3d/Characters.tsx
index 6d88971..0b919a1 100644
--- a/apps/fallback-ui/src/components/3d/Characters.tsx
+++ b/apps/fallback-ui/src/components/3d/Characters.tsx
@@ -54,7 +54,7 @@ function AnimatedFlag({ flagTexture, isCelebrating, time }: { flagTexture: THREE
// Increase segmentation for better cloth simulation
geometry.dispose();
- const newGeometry = new THREE.PlaneGeometry(1.5, 0.75, 24, 12);
+ const newGeometry = new THREE.PlaneGeometry(2.5, 1.25, 32, 16);
flagMeshRef.current.geometry = newGeometry;
originalPositions.current = new Float32Array(newGeometry.attributes.position.array);
}
@@ -77,15 +77,15 @@ function AnimatedFlag({ flagTexture, isCelebrating, time }: { flagTexture: THREE
const y = originalPos[i + 1];
const z = originalPos[i + 2];
- // Distance from flag pole
- const distanceFromPole = (x + 0.75) / 1.5;
+ // Distance from flag pole (adjusted for larger flag width of 2.5)
+ const distanceFromPole = (x + 1.25) / 2.5;
// Multiple wave layers
const wave1 = Math.sin(elapsed * flutterSpeed * 0.5 + distanceFromPole * 4) * 0.15 * distanceFromPole * windStrength;
const wave2 = Math.sin(elapsed * flutterSpeed + distanceFromPole * 8 + y * 3) * 0.08 * distanceFromPole * windStrength;
const wave3 = Math.sin(elapsed * flutterSpeed * 2 + distanceFromPole * 12 + y * 5) * 0.04 * distanceFromPole * windStrength;
const turbulence = noise(elapsed * 2 + distanceFromPole * 6) * 0.06 * distanceFromPole * windStrength;
- const verticalFlutter = Math.sin(elapsed * flutterSpeed * 0.7 + distanceFromPole * 3) * 0.05 * (1 - Math.abs(y) / 0.375) * distanceFromPole;
+ const verticalFlutter = Math.sin(elapsed * flutterSpeed * 0.7 + distanceFromPole * 3) * 0.05 * (1 - Math.abs(y) / 0.625) * distanceFromPole;
const zOffset = (wave1 + wave2 + wave3 + turbulence) * (0.3 + distanceFromPole * 0.7);
const yOffset = verticalFlutter * windStrength;
@@ -103,8 +103,8 @@ function AnimatedFlag({ flagTexture, isCelebrating, time }: { flagTexture: THREE
});
return (
-
-
+
+