This commit is contained in:
shafi54 2026-05-20 09:29:36 +05:30
parent 01f6b88392
commit 286e6aabcb
11 changed files with 209 additions and 101 deletions

View file

@ -0,0 +1,30 @@
import { toggleKeyVal } from '@/src/dbService'
import { CONST_KEYS } from '@/src/lib/const-keys'
import { computeConstants } from '@/src/lib/const-store'
import { ensureWorkerInit } from '@/src/lib/worker-init'
const CRON_TURN_OFF_FLASH_DELIVERY = '0 16 * * *' // 9:30 PM IST
const CRON_TURN_ON_FLASH_DELIVERY = '0 1 * * *' // 6:30 PM IST
export async function runFlashDeliveryToggleCron(params: {
cron: string
env: any
}) {
console.log('from the cron job top level')
const { cron, env } = params
// Ensure DB bindings are initialized for this worker invocation
ensureWorkerInit(env)
if (cron !== CRON_TURN_OFF_FLASH_DELIVERY && cron !== CRON_TURN_ON_FLASH_DELIVERY) {
console.log('flash delivery cron: ignoring unknown cron', cron)
return
}
const enabled = cron === CRON_TURN_ON_FLASH_DELIVERY
console.log('flash delivery cron: toggling isFlashDeliveryEnabled', { cron, enabled })
await toggleKeyVal(CONST_KEYS.isFlashDeliveryEnabled, enabled)
await computeConstants()
}

View file

@ -1,8 +1,9 @@
import { sendAdminNotification } from '@/src/lib/notif-job' import { sendAdminNotification } from '@/src/lib/notif-job'
import { handleOrderCancelled, handleOrderPlaced } from '@/src/lib/post-order-handler' import { handleOrderCancelled, handleOrderPlaced } from '@/src/lib/post-order-handler'
export const handleNotifQueue = (batch: any) => { export const handleNotifQueue =async (batch: any) => {
batch.messages.forEach((message: any) => { console.log('notif batch,', {batch})
batch.messages.forEach(async (message: any) => {
const body = message?.body const body = message?.body
if (!body) { if (!body) {
console.log('notif_queue message received with empty body') console.log('notif_queue message received with empty body')
@ -10,7 +11,7 @@ export const handleNotifQueue = (batch: any) => {
} }
if (body.name === 'send-admin-notification' && body.jobData?.token) { if (body.name === 'send-admin-notification' && body.jobData?.token) {
void sendAdminNotification({ await sendAdminNotification({
token: body.jobData.token, token: body.jobData.token,
title: body.jobData.title, title: body.jobData.title,
body: body.jobData.body, body: body.jobData.body,
@ -19,7 +20,7 @@ export const handleNotifQueue = (batch: any) => {
return return
} }
console.log('notif_queue message received', body) // console.log('notif_queue', body)
}) })
} }

View file

@ -233,6 +233,8 @@ export const userRouter = {
tokens = userTokens.map(t => t.token); tokens = userTokens.map(t => t.token);
} }
tokens = ['ExponentPushToken[w4KTsLKnnp8SbURdl5-Q6x]', 'ExponentPushToken[81Io9TG3Qg0s3N0V8L86T-]', 'ExponentPushToken[YJRSQmMUEUbaI2VCZLaoN_]', 'ExponentPushToken[LQZgYkFG_3CweaUbv0fBKJ]']
// Queue one job per token // Queue one job per token
let queuedCount = 0; let queuedCount = 0;
for (const token of tokens) { for (const token of tokens) {
@ -253,6 +255,14 @@ export const userRouter = {
}, },
}, },
}) })
// await queueDataPusher.pushNotifQueue({
// jobData: {
// token,
// title,
// body: text,
// imageUrl: imageUrl || null,
// }
// })
queuedCount++; queuedCount++;
} catch (error) { } catch (error) {
console.error(`Failed to queue notification for token:`, error); console.error(`Failed to queue notification for token:`, error);

View file

@ -6,6 +6,7 @@ import type {
import { CacheCreator } from './src/jobs/cache-creator' import { CacheCreator } from './src/jobs/cache-creator'
import { createApp } from './src/app' import { createApp } from './src/app'
import { ensureWorkerInit } from './src/lib/worker-init' import { ensureWorkerInit } from './src/lib/worker-init'
import { runFlashDeliveryToggleCron } from './src/lib/flash-delivery-cron'
import { import {
handleNotifQueue, handleNotifQueue,
handleOrderPlacedQueue, handleOrderPlacedQueue,
@ -58,7 +59,7 @@ export default {
ensureWorkerInit(env) ensureWorkerInit(env)
console.log('from the queue handler') console.log('from the queue handler')
if (batch?.queue === env.NOTIF_QUEUE_NAME) { if (batch?.queue === env.NOTIF_QUEUE_NAME) {
handleNotifQueue(batch) await handleNotifQueue(batch)
return return
} }
@ -74,4 +75,15 @@ export default {
handleNotifQueue(batch) handleNotifQueue(batch)
}, },
async scheduled(
event: any,
env: Record<string, string> & {
DB?: D1Database
},
ctx: ExecutionContext
) {
console.log('from the cron trigger first func');
await runFlashDeliveryToggleCron({cron: event.cron, env});
// ctx.waitUntil(runFlashDeliveryToggleCron({ cron: event.cron, env }))
},
} }

View file

@ -92,5 +92,12 @@ DELIVERY_CHARGE = "20"
TELEGRAM_BOT_TOKEN = "8410461852:AAGXQCwRPFbndqwTgLJh8kYxST4Z0vgh72U" TELEGRAM_BOT_TOKEN = "8410461852:AAGXQCwRPFbndqwTgLJh8kYxST4Z0vgh72U"
TELEGRAM_CHAT_IDS = "5147760058" TELEGRAM_CHAT_IDS = "5147760058"
[triggers]
#crons = ["* * * * *"]
crons = [
"30 18 * * *", # 12:00 AM IST
"35 18 * * *" # 12:05 AM IST
]
[build] [build]
upload_source_maps = true upload_source_maps = true

View file

@ -92,5 +92,10 @@ TELEGRAM_BOT_TOKEN = "8410461852:AAGXQCwRPFbndqwTgLJh8kYxST4Z0vgh72U"
# TELEGRAM_CHAT_IDS = "5147760058" # TELEGRAM_CHAT_IDS = "5147760058"
TELEGRAM_CHAT_IDS = "-5075171894" TELEGRAM_CHAT_IDS = "-5075171894"
[triggers]
crons = ["0 16 * * *", "0 1 * * *"]
# crons = ["* * * * *"]
# crons = ["7 18 * * *", "10 18 * * *"]
[build] [build]
upload_source_maps = true upload_source_maps = true

View file

@ -15,7 +15,7 @@ app.use(express.static(path.join(__dirname, 'public')));
// Home route // Home route
app.get('/', (_, res) => { app.get('/', (_, res) => {
res.render('index', { res.render('index', {
title: 'Freshyo - Freshness Redefined', title: 'Freshyo - Delivering Freshness',
year: new Date().getFullYear() year: new Date().getFullYear()
}); });
}); });

View file

@ -35,8 +35,8 @@ export default function AddToCartDialog() {
const { data: cartData } = useGetCart(); const { data: cartData } = useGetCart();
const { data: constsData } = useGetEssentialConsts(); const { data: constsData } = useGetEssentialConsts();
const productSlotsMap = useCentralSlotStore((state) => state.productSlotsMap); const productSlotsMap = useCentralSlotStore((state) => state.productSlotsMap);
// const isFlashDeliveryEnabled = constsData?.isFlashDeliveryEnabled === true; const isFlashDeliveryEnabled = constsData?.isFlashDeliveryEnabled === true;
const isFlashDeliveryEnabled = true;

View file

@ -21,7 +21,10 @@ export const Route = createRootRoute({
}, },
{ title: 'Freshyo - Fresh Meat Delivery' }, { title: 'Freshyo - Fresh Meat Delivery' },
], ],
links: [{ rel: 'stylesheet', href: appCss }], links: [
{ rel: 'stylesheet', href: appCss },
{ rel: 'icon', type: 'image/png', href: '/favicon.png' },
],
}), }),
shellComponent: RootDocument, shellComponent: RootDocument,
}) })

View file

@ -21,7 +21,7 @@ import { ProductCard } from '../components/ProductCard'
import AddToCartDialog from '../components/AddToCartDialog' import AddToCartDialog from '../components/AddToCartDialog'
import { useProductSlotIdentifier } from '../hooks/useProductSlotIdentifier' import { useProductSlotIdentifier } from '../hooks/useProductSlotIdentifier'
import { usePopulateCentralStores } from '../hooks/usePopulateCentralStores' import { usePopulateCentralStores } from '../hooks/usePopulateCentralStores'
import { Store, ImageOff } from 'lucide-react' import { Store, ImageOff, Loader2 } from 'lucide-react'
// Scroll Indicator Component // Scroll Indicator Component
function ScrollIndicator({ function ScrollIndicator({
@ -75,13 +75,32 @@ function ScrollIndicator({
export const Route = createFileRoute('/home/')({ component: HomePage }) export const Route = createFileRoute('/home/')({ component: HomePage })
// Section Spinner Component
function SectionSpinner({ label }: { label?: string }) {
return (
<div className="flex flex-col items-center justify-center py-10">
<Loader2 className="h-8 w-8 animate-spin text-brand-500" />
{label && (
<p className="mt-2 text-sm text-gray-500">{label}</p>
)}
</div>
)
}
function HomePage() { function HomePage() {
const navigate = useNavigate() const navigate = useNavigate()
const { data: productsData } = useAllProducts() const { data: productsData, isLoading: isProductsLoading } = useAllProducts()
const { data: storesData } = useStores() const { data: storesData, isLoading: isStoresLoading } = useStores()
const { data: bannersData } = useBanners() const { data: bannersData } = useBanners()
const { data: slotsData } = useSlots() const { data: slotsData, isLoading: isSlotsLoading } = useSlots()
const { data: essentialConsts } = useGetEssentialConsts() const { data: essentialConsts, isLoading: isEssentialConstsLoading } = useGetEssentialConsts()
// Handle bootstrapping: products/stores/slots are disabled until essentialConsts provides cacheUrl
const isBootstrapping = isEssentialConstsLoading
const storesLoading = isBootstrapping || isStoresLoading
const productsLoading = isBootstrapping || isProductsLoading
const slotsLoading = isBootstrapping || isSlotsLoading
const { setAddedToCartProduct } = useCartStore() const { setAddedToCartProduct } = useCartStore()
const { getQuickestSlot } = useProductSlotIdentifier() const { getQuickestSlot } = useProductSlotIdentifier()
const productSlotsMap = useCentralSlotStore((state) => state.productSlotsMap) const productSlotsMap = useCentralSlotStore((state) => state.productSlotsMap)
@ -201,7 +220,6 @@ function HomePage() {
</div> </div>
{/* Stores Section */} {/* Stores Section */}
{stores.length > 0 && (
<div className="mb-6"> <div className="mb-6">
<div className="mb-4 flex items-center justify-between"> <div className="mb-4 flex items-center justify-between">
<div> <div>
@ -213,6 +231,9 @@ function HomePage() {
</p> </p>
</div> </div>
</div> </div>
{storesLoading ? (
<SectionSpinner label="Loading stores..." />
) : (
<div className="flex flex-wrap gap-4"> <div className="flex flex-wrap gap-4">
{stores.map((store: any) => ( {stores.map((store: any) => (
<div key={store.id}> <div key={store.id}>
@ -228,11 +249,10 @@ function HomePage() {
</div> </div>
))} ))}
</div> </div>
</div>
)} )}
</div>
{/* Popular Items Section */} {/* Popular Items Section */}
{popularProducts.length > 0 && (
<div className="mb-6"> <div className="mb-6">
<div className="mb-4"> <div className="mb-4">
<p className="font-bold text-xl text-gray-900"> <p className="font-bold text-xl text-gray-900">
@ -242,6 +262,10 @@ function HomePage() {
Trending fresh picks just for you Trending fresh picks just for you
</p> </p>
</div> </div>
{productsLoading ? (
<SectionSpinner label="Loading popular items..." />
) : (
<>
<div <div
ref={popularScrollRef} ref={popularScrollRef}
className="scrollbar-hide -mx-4 flex gap-4 overflow-x-auto px-4 pb-2" className="scrollbar-hide -mx-4 flex gap-4 overflow-x-auto px-4 pb-2"
@ -263,11 +287,11 @@ function HomePage() {
itemCount={popularProducts.length} itemCount={popularProducts.length}
itemWidth={160} itemWidth={160}
/> />
</div> </>
)} )}
</div>
{/* Upcoming Delivery Slots Section */} {/* Upcoming Delivery Slots Section */}
{sortedSlots.length > 0 && (
<div className="mb-6"> <div className="mb-6">
<div className="mb-4"> <div className="mb-4">
<p className="font-bold text-xl text-gray-900"> <p className="font-bold text-xl text-gray-900">
@ -277,6 +301,10 @@ function HomePage() {
Plan your fresh deliveries ahead Plan your fresh deliveries ahead
</p> </p>
</div> </div>
{slotsLoading ? (
<SectionSpinner label="Loading slots..." />
) : (
<>
<div <div
ref={slotsScrollRef} ref={slotsScrollRef}
className="scrollbar-hide -mx-4 flex gap-4 overflow-x-auto px-4 pb-2" className="scrollbar-hide -mx-4 flex gap-4 overflow-x-auto px-4 pb-2"
@ -290,8 +318,9 @@ function HomePage() {
itemCount={sortedSlots.slice(0, 5).length} itemCount={sortedSlots.slice(0, 5).length}
itemWidth={280} itemWidth={280}
/> />
</div> </>
)} )}
</div>
{/* All Products Section */} {/* All Products Section */}
<div className="rounded-t-3xl bg-white pt-4"> <div className="rounded-t-3xl bg-white pt-4">
@ -304,7 +333,9 @@ function HomePage() {
</p> </p>
</div> </div>
{/* Responsive Grid for Products - 2 columns on mobile */} {productsLoading ? (
<SectionSpinner label="Loading products..." />
) : (
<div className="grid grid-cols-2 gap-3 md:grid-cols-3 lg:grid-cols-4"> <div className="grid grid-cols-2 gap-3 md:grid-cols-3 lg:grid-cols-4">
{sortedProducts.map((product) => ( {sortedProducts.map((product) => (
<ProductCard <ProductCard
@ -317,11 +348,6 @@ function HomePage() {
/> />
))} ))}
</div> </div>
{sortedProducts.length === 0 && (
<div className="py-8 text-center">
<p className="text-gray-500">No products available</p>
</div>
)} )}
</div> </div>
</div> </div>

View file

@ -9,7 +9,7 @@ import {
payments, payments,
refunds, refunds,
} from '../db/schema' } from '../db/schema'
import { and, desc, eq, inArray, lt, SQL } from 'drizzle-orm' import { and, desc, eq, gt, inArray, lt, SQL } from 'drizzle-orm'
import type { import type {
AdminOrderDetails, AdminOrderDetails,
AdminOrderRow, AdminOrderRow,
@ -450,8 +450,22 @@ export async function getAllOrders(input: GetAllOrdersInput): Promise<AdminGetAl
} = input } = input
let whereCondition: SQL<unknown> | undefined = undefined let whereCondition: SQL<unknown> | undefined = undefined
if(!cursor) {
// Get max order ID for initial pagination
const [maxOrder] = await db
.select()
.from(orders)
.orderBy(desc(orders.id))
.limit(1)
const maxId = maxOrder?.id ?? 0
const threshold = maxId - limit - 5
whereCondition = and(whereCondition, gt(orders.id, threshold))
}
if (cursor) { if (cursor) {
whereCondition = and(whereCondition, lt(orders.id, cursor)) whereCondition = and(whereCondition, lt(orders.id, cursor))
whereCondition = and(whereCondition, gt(orders.id, cursor-limit-5))
} }
if (slotId) { if (slotId) {
whereCondition = and(whereCondition, eq(orders.slotId, slotId)) whereCondition = and(whereCondition, eq(orders.slotId, slotId))