This commit is contained in:
shafi54 2026-04-11 15:39:54 +05:30
parent 55bfd1aafa
commit dc21636b3f
17 changed files with 57006 additions and 9 deletions

View file

@ -63,7 +63,21 @@
"backgroundColor": "#fff0f6"
},
"edgeToEdgeEnabled": true,
"package": "in.freshyo.adminui"
"package": "in.freshyo.adminui",
"intentFilters": [
{
"action": "VIEW",
"autoVerify": true,
"data": [
{
"scheme": "https",
"host": "ui.freshyo.in",
"pathPrefix": "/manage-orders/order-details"
}
],
"category": ["BROWSABLE", "DEFAULT"]
}
]
},
"web": {
"bundler": "metro",

View file

@ -5,8 +5,8 @@
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
"distribution": "internal",
"channel": "development"
},
"preview": {
"distribution": "internal",

56362
apps/backend/dumps/latest.sql Normal file

File diff suppressed because it is too large Load diff

View file

@ -14,6 +14,7 @@
"deploy": "wrangler deploy --config wrangler.prod.toml",
"wrangler:dev": "wrangler dev worker.ts --config wrangler.toml",
"wrangler:deploy": "wrangler deploy worker.ts --config wrangler.toml",
"pull_db": "wrangler d1 export freshyo-dev --config wrangler.prod.toml --remote --output ./dumps/latest.sql && bash ./scripts/populate_localdb.sh",
"docker:build": "cd .. && docker buildx build --platform linux/amd64 -t mohdshafiuddin54/health_petal:latest --progress=plain -f backend/Dockerfile .",
"docker:push": "docker push mohdshafiuddin54/health_petal:latest"
},

View file

@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
DUMP_FILE="$ROOT_DIR/dumps/latest.sql"
WRANGLER_CONFIG="$ROOT_DIR/wrangler.dev.toml"
DB_NAME="freshyo-dev"
if [ ! -f "$DUMP_FILE" ]; then
echo "Dump file not found: $DUMP_FILE"
exit 1
fi
if [ ! -f "$WRANGLER_CONFIG" ]; then
echo "Wrangler config not found: $WRANGLER_CONFIG"
exit 1
fi
wrangler d1 execute "$DB_NAME" --local --file "$DUMP_FILE" --config "$WRANGLER_CONFIG"
echo "Local D1 database populated from $DUMP_FILE"

View file

@ -5,6 +5,7 @@ import {
import { sendTelegramMessage } from '@/src/lib/telegram-service'
import { queueDataPusher } from '@/src/lib/queue-data-pusher'
import { ensureWorkerInit } from './worker-init';
import { getAppUrl } from '@/src/lib/env-exporter'
interface OrderIdMessage {
orderIds: number[];
@ -26,6 +27,18 @@ const formatDateTime = (dateStr: string | null | undefined): string => {
});
};
const buildTelegramLinks = (orderId: number, userId?: number | null): string => {
const baseUrl = getAppUrl() || 'https://ui.freshyo.in'
const orderUrl = `${baseUrl}/manage-orders/order-details/${orderId}`
const orderLink = `↪ <a href="${orderUrl}">Order</a>`
if (!userId) {
return orderLink
}
const userUrl = `${baseUrl}/user-management/${userId}`
const userLink = `↪ <a href="${userUrl}">User</a>`
return `${orderLink} | ${userLink}`
}
const formatOrderMessageWithFullData = (ordersData: any[]): string => {
console.log('formatting the msg')
let message = '🛒 <b>New Order Placed</b>\n\n';
@ -55,6 +68,8 @@ const formatOrderMessageWithFullData = (ordersData: any[]): string => {
message += ` 📞 ${order.address.phone}\n`;
}
message += `\n${buildTelegramLinks(order.id, order.userId)}\n`
if (index < ordersData.length - 1) {
message += '\n---\n\n';
}
@ -80,6 +95,8 @@ ${orderData.orderItems?.map((item: any) => ` • ${item.product?.name || 'Unkno
<b>Reason:</b> ${cancellationData.reason}
👤 <b>Cancelled by:</b> ${cancellationData.cancelledBy === 'admin' ? 'Admin' : 'User'}
<b>Time:</b> ${formatDateTime(cancellationData.cancelledAt)}
${buildTelegramLinks(orderData.id, orderData.userId)}
`;
return message;

View file

@ -51,8 +51,7 @@ export const scheduleStoreInitialization = async (): Promise<void> => {
}
const id = env.CACHE_CREATOR.idFromName('store-init')
// const stub = env.CACHE_CREATOR.get(id)
const stub = env.CACHE_CREATOR.getByName('store-init')
const stub = env.CACHE_CREATOR.get(id)
try {
await stub.fetch('https://cache-creator/schedule', { method: 'POST' })
} catch (error) {

View file

@ -32,6 +32,7 @@ export default {
},
ctx: ExecutionContext
) {
console.log(env)
ensureWorkerInit(env)
const app = createApp()
return app.fetch(request, env, ctx)

View file

@ -5,6 +5,7 @@
"type": "module",
"scripts": {
"dev": "vite",
"deploy": "wrangler pages deploy dist --project-name=freshyo-fallbackui",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint \"src/**/*.{ts,tsx}\""

View file

@ -0,0 +1,12 @@
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "in.freshyo.adminui",
"sha256_cert_fingerprints": [
"49:65:C6:4F:DE:31:95:1B:69:B1:15:1E:71:26:39:33:56:37:9E:A6:4B:29:59:F4:16:24:18:62:7A:9A:60:A3"
]
}
}
]

View file

@ -12,6 +12,8 @@ import { LocationMarkerRoute } from './routes/location-marker'
import { UserConnectRoute } from './routes/user-connect'
import Inauguration from './routes/inauguration'
import { DemoRoute } from './routes/demo'
import { OrderDetailsRoute } from './routes/order-details'
import { UserDetailsRoute } from './routes/user-details'
import { AuthWrapper } from './components/AuthWrapper'
import { SuperAdminGuard } from './components/SuperAdminGuard'
import { cn } from '@/lib/utils'
@ -135,6 +137,30 @@ const demoRoute = new Route({
)
})
const orderDetailsRoute = new Route({
getParentRoute: () => rootRoute,
path: '/manage-orders/order-details/$id',
component: () => (
<Suspense fallback={<p>Loading order details</p>}>
<AuthWrapper>
<OrderDetailsRoute />
</AuthWrapper>
</Suspense>
)
})
const userDetailsRoute = new Route({
getParentRoute: () => rootRoute,
path: '/user-management/$id',
component: () => (
<Suspense fallback={<p>Loading user details</p>}>
<AuthWrapper>
<UserDetailsRoute />
</AuthWrapper>
</Suspense>
)
})
const routeTree = rootRoute.addChildren([
dashboardRoute,
vendorOrderListRoute,
@ -145,7 +171,9 @@ const routeTree = rootRoute.addChildren([
userConnectRoute,
locationMarkerRoute,
inaugurationRoute,
demoRoute
demoRoute,
orderDetailsRoute,
userDetailsRoute
])
export function createAppRouter() {

View file

@ -0,0 +1,282 @@
import { useMemo, useState } from "react";
import { useParams, useNavigate, Link } from "@tanstack/react-router";
import dayjs from "dayjs";
import { trpc } from "../trpc/client";
import { Button } from "@/components/ui/button";
const getStatusBadge = (status?: string) => {
if (status === "delivered") {
return "bg-emerald-50 text-emerald-700 border-emerald-200";
}
if (status === "cancelled") {
return "bg-red-50 text-red-700 border-red-200";
}
return "bg-amber-50 text-amber-700 border-amber-200";
};
export function OrderDetailsRoute() {
const { id } = useParams({ from: "/manage-orders/order-details/$id" });
const navigate = useNavigate();
const [incidentComment, setIncidentComment] = useState("");
const [negativityScore, setNegativityScore] = useState("");
const orderId = Number(id);
const {
data: orderData,
isLoading,
error,
} = trpc.admin.order.getOrderDetails.useQuery(
{ orderId: Number.isFinite(orderId) ? orderId : 0 },
{ enabled: Number.isFinite(orderId) }
);
const order = orderData as any;
const { data: incidentsData, refetch: refetchIncidents } =
trpc.admin.user.getUserIncidents.useQuery(
{ userId: order?.userId ?? 0 },
{ enabled: Boolean(order?.userId) }
);
const addIncidentMutation = trpc.admin.user.addUserIncident.useMutation({
onSuccess: () => {
setIncidentComment("");
setNegativityScore("");
refetchIncidents();
},
});
const subtotal = useMemo(() => {
if (!order?.items?.length) return 0;
return order.items.reduce((sum: number, item: any) => sum + item.amount, 0);
}, [order]);
if (isLoading) {
return (
<div className="min-h-[60vh] flex items-center justify-center">
<div className="rounded-2xl border border-slate-200 bg-white px-6 py-8 text-sm text-slate-600 shadow-sm">
Loading order details...
</div>
</div>
);
}
if (error || !order) {
return (
<div className="min-h-[60vh] flex items-center justify-center">
<div className="rounded-2xl border border-red-200 bg-red-50 px-6 py-8 text-sm text-red-700 shadow-sm">
{error?.message || "Failed to load order details"}
</div>
</div>
);
}
const statusBadge = getStatusBadge(order.status);
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<div>
<p className="text-xs font-medium text-slate-500">Order Details</p>
<h1 className="text-2xl font-semibold text-slate-900">#{order.readableId}</h1>
</div>
<Button
variant="outline"
onClick={() => navigate({ to: "/vendor-order-list" })}
>
Back to Orders
</Button>
</div>
<div className="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm">
<div className="flex flex-wrap items-start justify-between gap-4">
<div>
<p className="text-sm text-slate-500">Status</p>
<div className={`inline-flex items-center rounded-full border px-3 py-1 text-xs font-semibold uppercase ${statusBadge}`}>
{order.status}
</div>
</div>
<div className="text-sm text-slate-600">
<p className="font-medium text-slate-700">Placed on</p>
<p>{dayjs(order.createdAt).format("MMM DD, YYYY • h:mm A")}</p>
</div>
<div className="text-sm text-slate-600">
<p className="font-medium text-slate-700">Delivery</p>
<p>
{order.isFlashDelivery
? dayjs(order.createdAt).add(30, "minutes").format("MMM DD, YYYY • h:mm A")
: order.slotInfo?.time
? dayjs(order.slotInfo.time).format("MMM DD, YYYY • h:mm A")
: "Not scheduled"}
</p>
</div>
</div>
</div>
{order.status === "cancelled" && order.cancelReason && (
<div className="rounded-2xl border border-red-200 bg-red-50 p-5 text-sm text-red-700">
<p className="font-semibold">Cancellation Reason</p>
<p className="mt-1 text-red-800">{order.cancelReason}</p>
</div>
)}
<div className="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm">
<h2 className="text-lg font-semibold text-slate-900 mb-4">Customer Details</h2>
<div className="grid gap-2 text-sm text-slate-600">
{order.userId ? (
<Link
to="/user-management/$id"
params={{ id: String(order.userId) }}
className="font-semibold text-blue-700 hover:text-blue-800"
>
{order.customerName || "Unknown User"}
</Link>
) : (
<p className="font-medium text-slate-900">{order.customerName || "Unknown User"}</p>
)}
<p>{order.customerMobile}</p>
{order.customerEmail && <p>{order.customerEmail}</p>}
<p className="whitespace-pre-line">
{order.address?.name} {order.address?.line1}
{order.address?.line2 ? `, ${order.address.line2}` : ""}
{`\n${order.address?.city}, ${order.address?.state} - ${order.address?.pincode}`}
</p>
</div>
</div>
{order.userId && (
<div className="rounded-2xl border border-amber-200 bg-amber-50 p-6 shadow-sm">
<div className="flex flex-wrap items-center justify-between gap-3 mb-4">
<h2 className="text-lg font-semibold text-amber-900">User Incidents</h2>
</div>
{incidentsData?.incidents?.length ? (
<div className="space-y-3">
{incidentsData.incidents.map((incident: any) => (
<div key={incident.id} className="rounded-xl border border-amber-200 bg-white p-4">
<div className="flex flex-wrap items-center justify-between gap-2 text-xs text-slate-500">
<span>{dayjs(incident.dateAdded).format("MMM DD, YYYY • h:mm A")}</span>
{incident.negativityScore ? (
<span className="rounded-md bg-red-100 px-2 py-1 text-red-700 font-semibold">
Score: {incident.negativityScore}
</span>
) : null}
</div>
{incident.adminComment && (
<p className="mt-2 text-sm text-slate-800">{incident.adminComment}</p>
)}
<div className="mt-3 border-t border-slate-100 pt-2 text-xs text-slate-500">
Added by {incident.addedBy}
{incident.orderId ? ` • Order #${incident.orderId}` : ""}
</div>
</div>
))}
</div>
) : (
<div className="rounded-xl border border-amber-100 bg-white p-4 text-sm text-amber-800">
No incidents recorded for this user
</div>
)}
<div className="mt-6 rounded-xl border border-amber-100 bg-white p-4">
<p className="text-sm font-semibold text-slate-900">Add Incident</p>
<p className="text-xs text-slate-500 mt-1">
Record an incident for this user. Higher negativity scores indicate more serious incidents.
</p>
<div className="mt-3 grid gap-3">
<textarea
value={incidentComment}
onChange={(event) => setIncidentComment(event.target.value)}
placeholder="Enter details about the incident..."
className="min-h-[96px] w-full rounded-lg border border-slate-200 px-3 py-2 text-sm focus:border-blue-400 focus:outline-none"
/>
<input
type="number"
value={negativityScore}
onChange={(event) => setNegativityScore(event.target.value)}
placeholder="Negativity score (optional)"
className="w-full rounded-lg border border-slate-200 px-3 py-2 text-sm focus:border-blue-400 focus:outline-none"
/>
<div className="flex justify-end">
<Button
onClick={() => {
addIncidentMutation.mutate({
userId: order.userId,
orderId: order.id,
adminComment: incidentComment || undefined,
negativityScore: negativityScore ? Number(negativityScore) : undefined,
});
}}
disabled={addIncidentMutation.isPending}
>
{addIncidentMutation.isPending ? "Adding..." : "Add Incident"}
</Button>
</div>
</div>
</div>
</div>
)}
<div className="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm">
<h2 className="text-lg font-semibold text-slate-900 mb-4">Items Ordered</h2>
<div className="space-y-3">
{order.items?.map((item: any, index: number) => (
<div
key={`${item.id}-${index}`}
className="flex items-center justify-between border-b border-slate-100 pb-3"
>
<div>
<p className="text-sm font-semibold text-slate-900">{item.name}</p>
<p className="text-xs text-slate-500">
{Number(item.quantity) * item.productSize} {item.unit} × {item.price}
</p>
</div>
<div className="text-sm font-semibold text-slate-900">{item.amount}</div>
</div>
))}
</div>
<div className="mt-6 border-t border-slate-100 pt-4 text-sm text-slate-600">
<div className="flex items-center justify-between">
<span>Subtotal</span>
<span className="font-semibold text-slate-900">{subtotal}</span>
</div>
{order.discountAmount > 0 && (
<div className="flex items-center justify-between mt-2 text-emerald-600">
<span>Discount</span>
<span className="font-semibold">-{order.discountAmount}</span>
</div>
)}
{order.deliveryCharge > 0 && (
<div className="flex items-center justify-between mt-2">
<span>Delivery Charge</span>
<span className="font-semibold text-slate-900">{order.deliveryCharge}</span>
</div>
)}
<div className="flex items-center justify-between mt-4 text-base font-semibold text-slate-900">
<span>Total Amount</span>
<span>{order.totalAmount}</span>
</div>
</div>
</div>
{order.adminNotes && (
<div className="rounded-2xl border border-amber-200 bg-amber-50 p-5 text-sm text-amber-900">
<p className="font-semibold">Admin Notes</p>
<p className="mt-1">{order.adminNotes}</p>
</div>
)}
{order.couponCode && (
<div className="rounded-2xl border border-emerald-200 bg-emerald-50 p-5">
<p className="text-sm font-semibold text-emerald-800">Coupon Applied</p>
<p className="mt-1 text-sm text-emerald-700">{order.couponCode}</p>
{order.couponDescription && (
<p className="mt-1 text-xs text-emerald-600">{order.couponDescription}</p>
)}
<p className="mt-2 text-sm font-semibold text-emerald-800">-{order.discountAmount}</p>
</div>
)}
</div>
);
}

View file

@ -0,0 +1,257 @@
import { useMemo, useState } from "react";
import { useNavigate, useParams } from "@tanstack/react-router";
import dayjs from "dayjs";
import { trpc } from "../trpc/client";
import { Button } from "@/components/ui/button";
const getStatusBadge = (status?: string) => {
if (status === "delivered") {
return "bg-emerald-50 text-emerald-700 border-emerald-200";
}
if (status === "cancelled") {
return "bg-red-50 text-red-700 border-red-200";
}
return "bg-amber-50 text-amber-700 border-amber-200";
};
export function UserDetailsRoute() {
const { id } = useParams({ from: "/user-management/$id" });
const navigate = useNavigate();
const [incidentComment, setIncidentComment] = useState("");
const [negativityScore, setNegativityScore] = useState("");
const userId = Number(id);
const { data, isLoading, error } = trpc.admin.user.getUserDetails.useQuery(
{ userId: Number.isFinite(userId) ? userId : 0 },
{ enabled: Number.isFinite(userId) }
);
const { data: incidentsData, refetch: refetchIncidents } =
trpc.admin.user.getUserIncidents.useQuery(
{ userId },
{ enabled: Number.isFinite(userId) }
);
const updateSuspension = trpc.admin.user.updateUserSuspension.useMutation();
const addIncidentMutation = trpc.admin.user.addUserIncident.useMutation({
onSuccess: () => {
setIncidentComment("");
setNegativityScore("");
refetchIncidents();
},
});
const user = data?.user as any;
const orders = (data?.orders ?? []) as any[];
const orderCount = orders.length;
const statusBadge = useMemo(
() =>
user?.isSuspended
? "bg-red-50 text-red-700 border-red-200"
: "bg-emerald-50 text-emerald-700 border-emerald-200",
[user?.isSuspended]
);
if (isLoading) {
return (
<div className="min-h-[60vh] flex items-center justify-center">
<div className="rounded-2xl border border-slate-200 bg-white px-6 py-8 text-sm text-slate-600 shadow-sm">
Loading user details...
</div>
</div>
);
}
if (error || !data) {
return (
<div className="min-h-[60vh] flex items-center justify-center">
<div className="rounded-2xl border border-red-200 bg-red-50 px-6 py-8 text-sm text-red-700 shadow-sm">
{error?.message || "Failed to load user details"}
</div>
</div>
);
}
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<div>
<p className="text-xs font-medium text-slate-500">User Details</p>
<h1 className="text-2xl font-semibold text-slate-900">
{user?.name || "Unnamed User"}
</h1>
</div>
<Button variant="outline" onClick={() => navigate({ to: "/" })}>
Back
</Button>
</div>
<div className="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm">
<div className="flex flex-wrap items-start justify-between gap-4">
<div>
<p className="text-sm text-slate-500">Mobile</p>
<p className="text-lg font-semibold text-slate-900">{user?.mobile || "No Mobile"}</p>
<p className="text-sm text-slate-500 mt-1">{user?.name || "Unnamed User"}</p>
</div>
<div>
<p className="text-sm text-slate-500">Status</p>
<div
className={`inline-flex items-center rounded-full border px-3 py-1 text-xs font-semibold uppercase ${statusBadge}`}
>
{user?.isSuspended ? "Suspended" : "Active"}
</div>
</div>
<div className="text-sm text-slate-600">
<p className="font-medium text-slate-700">Registered</p>
<p>{user?.createdAt ? dayjs(user.createdAt).format("MMM DD, YYYY • h:mm A") : "-"}</p>
</div>
</div>
<div className="mt-6 border-t border-slate-100 pt-4 flex flex-wrap items-center justify-between gap-4">
<div>
<p className="text-sm font-semibold text-slate-900">Suspend User</p>
<p className="text-xs text-slate-500">Prevent user from placing orders</p>
</div>
<label className="flex items-center gap-2 text-sm text-slate-700">
<input
type="checkbox"
checked={Boolean(user?.isSuspended)}
onChange={() =>
updateSuspension.mutate({
userId,
isSuspended: !user?.isSuspended,
})
}
disabled={updateSuspension.isPending}
className="h-4 w-4 rounded border-slate-300 text-blue-600 focus:ring-blue-500"
/>
{updateSuspension.isPending ? "Updating..." : "Suspended"}
</label>
</div>
</div>
<div className="rounded-2xl border border-amber-200 bg-amber-50 p-6 shadow-sm">
<div className="flex flex-wrap items-center justify-between gap-3 mb-4">
<h2 className="text-lg font-semibold text-amber-900">User Incidents</h2>
</div>
{incidentsData?.incidents?.length ? (
<div className="space-y-3">
{incidentsData.incidents.map((incident: any) => (
<div key={incident.id} className="rounded-xl border border-amber-200 bg-white p-4">
<div className="flex flex-wrap items-center justify-between gap-2 text-xs text-slate-500">
<span>{dayjs(incident.dateAdded).format("MMM DD, YYYY • h:mm A")}</span>
{incident.negativityScore ? (
<span className="rounded-md bg-red-100 px-2 py-1 text-red-700 font-semibold">
Score: {incident.negativityScore}
</span>
) : null}
</div>
{incident.adminComment && (
<p className="mt-2 text-sm text-slate-800">{incident.adminComment}</p>
)}
<div className="mt-3 border-t border-slate-100 pt-2 text-xs text-slate-500">
Added by {incident.addedBy}
{incident.orderId ? ` • Order #${incident.orderId}` : ""}
</div>
</div>
))}
</div>
) : (
<div className="rounded-xl border border-amber-100 bg-white p-4 text-sm text-amber-800">
No incidents recorded for this user
</div>
)}
<div className="mt-6 rounded-xl border border-amber-100 bg-white p-4">
<p className="text-sm font-semibold text-slate-900">Add Incident</p>
<p className="text-xs text-slate-500 mt-1">
Record an incident for this user. Higher negativity scores indicate more serious incidents.
</p>
<div className="mt-3 grid gap-3">
<textarea
value={incidentComment}
onChange={(event) => setIncidentComment(event.target.value)}
placeholder="Enter details about the incident..."
className="min-h-[96px] w-full rounded-lg border border-slate-200 px-3 py-2 text-sm focus:border-blue-400 focus:outline-none"
/>
<input
type="number"
value={negativityScore}
onChange={(event) => setNegativityScore(event.target.value)}
placeholder="Negativity score (optional)"
className="w-full rounded-lg border border-slate-200 px-3 py-2 text-sm focus:border-blue-400 focus:outline-none"
/>
<div className="flex justify-end">
<Button
onClick={() => {
addIncidentMutation.mutate({
userId,
orderId: undefined,
adminComment: incidentComment || undefined,
negativityScore: negativityScore ? Number(negativityScore) : undefined,
});
}}
disabled={addIncidentMutation.isPending}
>
{addIncidentMutation.isPending ? "Adding..." : "Add Incident"}
</Button>
</div>
</div>
</div>
</div>
<div className="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm">
<div className="flex items-center justify-between mb-4">
<h2 className="text-lg font-semibold text-slate-900">Order History</h2>
<span className="text-sm text-slate-500">
{orderCount} {orderCount === 1 ? "order" : "orders"}
</span>
</div>
{orders.length === 0 ? (
<div className="rounded-xl border border-slate-100 bg-slate-50 p-6 text-sm text-slate-600">
No orders yet
</div>
) : (
<div className="space-y-3">
{orders.map((order) => (
<div
key={order.id}
className="rounded-xl border border-slate-100 bg-white p-4 shadow-sm"
>
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-2">
<span className="text-lg font-semibold text-slate-900">#{order.readableId}</span>
{order.isFlashDelivery && (
<span className="rounded-full border border-amber-200 bg-amber-100 px-2 py-0.5 text-[10px] font-black uppercase text-amber-700">
</span>
)}
</div>
<span className={`rounded-full border px-2 py-1 text-xs font-semibold uppercase ${getStatusBadge(order.status)}`}>
{order.status}
</span>
</div>
<div className="flex flex-wrap items-center justify-between gap-3 text-sm text-slate-600">
<span>{dayjs(order.createdAt).format("MMM DD, YYYY • h:mm A")}</span>
<span>{order.itemCount} {order.itemCount === 1 ? "item" : "items"}</span>
<span className="text-base font-semibold text-slate-900">{order.totalAmount}</span>
</div>
<div className="mt-3">
<Button
variant="link"
onClick={() => navigate({ to: "/manage-orders/order-details/$id", params: { id: String(order.id) } })}
>
View Order Details
</Button>
</div>
</div>
))}
</div>
)}
</div>
</div>
);
}

View file

@ -629,6 +629,7 @@ export async function getProductsForRecentOrders(
export interface OrderWithFullData {
id: number
userId: number
totalAmount: string
isFlashDelivery: boolean
address: {

View file

@ -667,6 +667,7 @@ export async function getProductsForRecentOrders(
export interface OrderWithFullData {
id: number
userId: number
totalAmount: string
isFlashDelivery: boolean
address: {

View file

@ -65,9 +65,9 @@ const isDevMode = Constants.executionEnvironment !== "standalone";
// const BASE_API_URL = 'http://10.0.2.2:4000';
// const BASE_API_URL = 'http://192.168.100.101:4000';
// const BASE_API_URL = 'http://192.168.1.5:4000';
// const BASE_API_URL = 'http://192.168.1.5:8787';
const BASE_API_URL = 'http://192.168.1.5:8787';
// let BASE_API_URL = "https://raw.freshyo.in";
let BASE_API_URL = "https://worker.freshyo.in";
// let BASE_API_URL = "https://worker.freshyo.in";
// let BASE_API_URL = "https://freshyo.technocracy.ovh";
// let BASE_API_URL = 'http://192.168.100.109:8787';
// let BASE_API_URL = 'http://192.168.29.176:4000';