enh
This commit is contained in:
parent
489d0905c5
commit
b953774fbc
9 changed files with 114 additions and 29 deletions
|
|
@ -22,6 +22,7 @@ react-native. They are available in the common-ui as MyText, MyTextInput, MyTouc
|
||||||
- Local imports last
|
- Local imports last
|
||||||
- Use absolute imports with path aliases when possible
|
- Use absolute imports with path aliases when possible
|
||||||
- imports from /packages/ui are aliased as `common-ui` in apps/user-ui and apps/admin-ui
|
- imports from /packages/ui are aliased as `common-ui` in apps/user-ui and apps/admin-ui
|
||||||
|
- For backend, use `@/*` to import from the backend root directory (e.g., `@/src/lib/file-name`)
|
||||||
|
|
||||||
### Error Handling
|
### Error Handling
|
||||||
- API errors handled via axios interceptors
|
- API errors handled via axios interceptors
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -7,6 +7,7 @@ import { authenticateUser } from "./middleware/auth.middleware";
|
||||||
import { raiseComplaint } from "./uv-apis/user-rest.controller";
|
import { raiseComplaint } from "./uv-apis/user-rest.controller";
|
||||||
import uploadHandler from "./lib/upload-handler";
|
import uploadHandler from "./lib/upload-handler";
|
||||||
|
|
||||||
|
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
// Health check endpoints (no auth required)
|
// Health check endpoints (no auth required)
|
||||||
|
|
|
||||||
|
|
@ -29,9 +29,10 @@
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
|
"@/*": ["./*"],
|
||||||
"shared-types": ["../shared-types"],
|
"shared-types": ["../shared-types"],
|
||||||
"@commonTypes": ["../../packages/ui/shared-types"],
|
"@commonTypes": ["../../packages/ui/shared-types"],
|
||||||
"@commonTypes/*": ["../../packages/ui/shared-types/*"]
|
"@commonTypes/*": ["../../packages/ui/shared-types/*"],
|
||||||
},
|
},
|
||||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||||
// "typeRoots": [""], /* Specify multiple folders that act like './node_modules/@types'. */
|
// "typeRoots": [""], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,20 @@ const RenderStore = ({
|
||||||
|
|
||||||
const headerColor = colors.secondaryPink;
|
const headerColor = colors.secondaryPink;
|
||||||
|
|
||||||
|
// Format time range helper
|
||||||
|
const formatTimeRange = (deliveryTime: string) => {
|
||||||
|
const time = dayjs(deliveryTime);
|
||||||
|
const endTime = time.add(1, 'hour');
|
||||||
|
const startPeriod = time.format('A');
|
||||||
|
const endPeriod = endTime.format('A');
|
||||||
|
|
||||||
|
if (startPeriod === endPeriod) {
|
||||||
|
return `${time.format('h')}-${endTime.format('h')} ${startPeriod}`;
|
||||||
|
} else {
|
||||||
|
return `${time.format('h:mm')} ${startPeriod} - ${endTime.format('h:mm')} ${endPeriod}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export default function Dashboard() {
|
export default function Dashboard() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const userDetails = useUserDetails();
|
const userDetails = useUserDetails();
|
||||||
|
|
@ -479,12 +493,12 @@ export default function Dashboard() {
|
||||||
<MyText
|
<MyText
|
||||||
style={tw`text-sm font-extrabold text-slate-900`}
|
style={tw`text-sm font-extrabold text-slate-900`}
|
||||||
>
|
>
|
||||||
{dayjs(slot.deliveryTime).format("h:mm A")}
|
{formatTimeRange(slot.deliveryTime)}
|
||||||
</MyText>
|
</MyText>
|
||||||
<MyText
|
<MyText
|
||||||
style={tw`text-[11px] font-bold text-slate-500`}
|
style={tw`text-[11px] font-bold text-slate-500`}
|
||||||
>
|
>
|
||||||
{dayjs(slot.deliveryTime).format("ddd, MMM DD")}
|
Delivery Slot
|
||||||
</MyText>
|
</MyText>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,23 @@ const QuickDeliveryAddressSelector: React.FC<QuickDeliveryAddressSelectorProps>
|
||||||
const defaultAddress = defaultAddressData?.data;
|
const defaultAddress = defaultAddressData?.data;
|
||||||
const addresses = addressesData?.data || [];
|
const addresses = addressesData?.data || [];
|
||||||
|
|
||||||
|
// Format time range helper
|
||||||
|
const formatTimeRange = (deliveryTime: string) => {
|
||||||
|
const time = dayjs(deliveryTime);
|
||||||
|
const endTime = time.add(1, 'hour');
|
||||||
|
const startPeriod = time.format('A');
|
||||||
|
const endPeriod = endTime.format('A');
|
||||||
|
|
||||||
|
let timeRange;
|
||||||
|
if (startPeriod === endPeriod) {
|
||||||
|
timeRange = `${time.format('h')}-${endTime.format('h')} ${startPeriod}`;
|
||||||
|
} else {
|
||||||
|
timeRange = `${time.format('h:mm')} ${startPeriod} - ${endTime.format('h:mm')} ${endPeriod}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${time.format('ddd, DD MMM ')}${timeRange}`;
|
||||||
|
};
|
||||||
|
|
||||||
// Find earliest slot for pre-selection
|
// Find earliest slot for pre-selection
|
||||||
const earliestSlot = slotsData?.slots?.sort((a, b) =>
|
const earliestSlot = slotsData?.slots?.sort((a, b) =>
|
||||||
dayjs(a.deliveryTime).diff(dayjs(b.deliveryTime))
|
dayjs(a.deliveryTime).diff(dayjs(b.deliveryTime))
|
||||||
|
|
@ -50,21 +67,23 @@ const QuickDeliveryAddressSelector: React.FC<QuickDeliveryAddressSelectorProps>
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Transform slots for display
|
// Transform slots for display
|
||||||
const slotOptions = slotsData?.slots?.map(slot => ({
|
const slotOptions = slotsData?.slots?.map(slot => {
|
||||||
|
return {
|
||||||
id: slot.id,
|
id: slot.id,
|
||||||
deliveryTime: dayjs(slot.deliveryTime).format('MMM DD, h:mm A'),
|
deliveryTime: formatTimeRange(slot.deliveryTime),
|
||||||
closeTime: dayjs(slot.freezeTime).format('h:mm A'),
|
closeTime: dayjs(slot.freezeTime).format('h:mm A'),
|
||||||
})) || [];
|
};
|
||||||
|
}) || [];
|
||||||
|
|
||||||
// Get current selected slot display
|
// Get current selected slot display
|
||||||
const getCurrentSlotDisplay = () => {
|
const getCurrentSlotDisplay = () => {
|
||||||
if (isForFlashDelivery) return '30 minutes';
|
if (isForFlashDelivery) return '30 minutes';
|
||||||
if (slotId) {
|
if (slotId) {
|
||||||
const slot = slotsData?.slots?.find(s => s.id === slotId);
|
const slot = slotsData?.slots?.find(s => s.id === slotId);
|
||||||
return slot ? dayjs(slot.deliveryTime).format('MMM DD, h:mm A') : 'Select time';
|
return slot ? formatTimeRange(slot.deliveryTime) : 'Select time';
|
||||||
}
|
}
|
||||||
if (earliestSlot) {
|
if (earliestSlot) {
|
||||||
return dayjs(earliestSlot.deliveryTime).format('MMM DD, h:mm A');
|
return formatTimeRange(earliestSlot.deliveryTime);
|
||||||
}
|
}
|
||||||
return 'Select time';
|
return 'Select time';
|
||||||
};
|
};
|
||||||
|
|
@ -118,7 +137,7 @@ const QuickDeliveryAddressSelector: React.FC<QuickDeliveryAddressSelectorProps>
|
||||||
{/* Trigger Component with Separate Chevrons */}
|
{/* Trigger Component with Separate Chevrons */}
|
||||||
<View style={tw`bg-brand50 border border-brand100 rounded-lg p-3`}>
|
<View style={tw`bg-brand50 border border-brand100 rounded-lg p-3`}>
|
||||||
|
|
||||||
/* Regular Delivery Time Section */
|
{/* Regular Delivery Time Section */}
|
||||||
<MyTouchableOpacity
|
<MyTouchableOpacity
|
||||||
onPress={() => setDialogOpen(true)}
|
onPress={() => setDialogOpen(true)}
|
||||||
style={tw`flex-row items-center justify-between mb-2`}
|
style={tw`flex-row items-center justify-between mb-2`}
|
||||||
|
|
|
||||||
|
|
@ -318,7 +318,7 @@ const CompactProductCard = ({
|
||||||
{item.marketPrice && Number(item.marketPrice) > Number(item.price) && (
|
{item.marketPrice && Number(item.marketPrice) > Number(item.price) && (
|
||||||
<MyText style={tw`text-gray-400 text-xs ml-1 line-through`}>₹{item.marketPrice}</MyText>
|
<MyText style={tw`text-gray-400 text-xs ml-1 line-through`}>₹{item.marketPrice}</MyText>
|
||||||
)}
|
)}
|
||||||
<MyText style={tw`text-gray-600 text-xs ml-1`}>Quantity: <MyText style={tw`text-[#f81260] font-semibold`}>{formatQuantity(item.productQuantity || 1, item.unitNotation).display}</MyText></MyText>
|
<MyText style={tw`text-gray-600 text-xs ml-1`}>Quantity: <MyText style={tw`text-[#f81260] font-semibold`}>{formatQuantity(item.productQuantity || 1, item.unit || item.unitNotation).display}</MyText></MyText>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
||||||
|
|
@ -183,10 +183,26 @@ export default function CartPage({ isFlashDelivery = false }: CartPageProps) {
|
||||||
const uniqueSlots = allSlots.filter(
|
const uniqueSlots = allSlots.filter(
|
||||||
(slot, index, self) => index === self.findIndex((s) => s.id === slot.id)
|
(slot, index, self) => index === self.findIndex((s) => s.id === slot.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Smart time window formatting function
|
||||||
|
const formatTimeRange = (deliveryTime: string) => {
|
||||||
|
const time = dayjs(deliveryTime);
|
||||||
|
const endTime = time.add(1, 'hour');
|
||||||
|
const startPeriod = time.format('A');
|
||||||
|
const endPeriod = endTime.format('A');
|
||||||
|
|
||||||
|
let timeRange;
|
||||||
|
if (startPeriod === endPeriod) {
|
||||||
|
timeRange = `${time.format('h')}-${endTime.format('h')} ${startPeriod}`;
|
||||||
|
} else {
|
||||||
|
timeRange = `${time.format('h:mm')} ${startPeriod} - ${endTime.format('h:mm')} ${endPeriod}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${time.format('ddd, DD MMM ')}${timeRange}`;
|
||||||
|
};
|
||||||
|
|
||||||
return uniqueSlots.map((slot) => ({
|
return uniqueSlots.map((slot) => ({
|
||||||
label: `Delivery: ${dayjs(slot.deliveryTime).format(
|
label: `Delivery: ${formatTimeRange(slot.deliveryTime)} - Close time: ${dayjs(slot.freezeTime).format("h:mm a")}`,
|
||||||
"ddd DD MMM, h:mm a"
|
|
||||||
)} - Close time: ${dayjs(slot.freezeTime).format("h:mm a")}`,
|
|
||||||
value: slot.id,
|
value: slot.id,
|
||||||
}));
|
}));
|
||||||
}, [slotsData]);
|
}, [slotsData]);
|
||||||
|
|
@ -195,12 +211,28 @@ export default function CartPage({ isFlashDelivery = false }: CartPageProps) {
|
||||||
const getAvailableSlotsForProduct = React.useMemo(() => {
|
const getAvailableSlotsForProduct = React.useMemo(() => {
|
||||||
return (productId: number) => {
|
return (productId: number) => {
|
||||||
if (!slotsData || !slotsData[productId]) return [];
|
if (!slotsData || !slotsData[productId]) return [];
|
||||||
return slotsData[productId].map((slot) => ({
|
return slotsData[productId].map((slot) => {
|
||||||
label: `Delivery: ${dayjs(slot.deliveryTime).format(
|
const formatTimeRange = (deliveryTime: string) => {
|
||||||
"ddd DD MMM, h:mm a"
|
const time = dayjs(deliveryTime);
|
||||||
)} - Close time: ${dayjs(slot.freezeTime).format("h:mm a")}`,
|
const endTime = time.add(1, 'hour');
|
||||||
|
const startPeriod = time.format('A');
|
||||||
|
const endPeriod = endTime.format('A');
|
||||||
|
|
||||||
|
let timeRange;
|
||||||
|
if (startPeriod === endPeriod) {
|
||||||
|
timeRange = `${time.format('h')}-${endTime.format('h')} ${startPeriod}`;
|
||||||
|
} else {
|
||||||
|
timeRange = `${time.format('h:mm')} ${startPeriod} - ${endTime.format('h:mm')} ${endPeriod}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${time.format('ddd, DD MMM ')}${timeRange}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
label: `Delivery: ${formatTimeRange(slot.deliveryTime)} - Close time: ${dayjs(slot.freezeTime).format("h:mm a")}`,
|
||||||
value: slot.id,
|
value: slot.id,
|
||||||
}));
|
};
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}, [slotsData]);
|
}, [slotsData]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,23 @@ interface FloatingCartBarProps {
|
||||||
setIsExpanded?: (value: boolean) => void;
|
setIsExpanded?: (value: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Smart time window formatting function
|
||||||
|
const formatTimeRange = (deliveryTime: string) => {
|
||||||
|
const time = dayjs(deliveryTime);
|
||||||
|
const endTime = time.add(1, 'hour');
|
||||||
|
const startPeriod = time.format('A');
|
||||||
|
const endPeriod = endTime.format('A');
|
||||||
|
|
||||||
|
let timeRange;
|
||||||
|
if (startPeriod === endPeriod) {
|
||||||
|
timeRange = `${time.format('h')}-${endTime.format('h')} ${startPeriod}`;
|
||||||
|
} else {
|
||||||
|
timeRange = `${time.format('h:mm')} ${startPeriod} - ${endTime.format('h:mm')} ${endPeriod}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${time.format('ddd, DD MMM ')}${timeRange}`;
|
||||||
|
};
|
||||||
|
|
||||||
const FloatingCartBar: React.FC<FloatingCartBarProps> = ({
|
const FloatingCartBar: React.FC<FloatingCartBarProps> = ({
|
||||||
isFlashDelivery = false,
|
isFlashDelivery = false,
|
||||||
isExpanded: controlledIsExpanded,
|
isExpanded: controlledIsExpanded,
|
||||||
|
|
@ -257,16 +274,16 @@ const FloatingCartBar: React.FC<FloatingCartBarProps> = ({
|
||||||
<BottomDropdown
|
<BottomDropdown
|
||||||
label="Select Delivery Slot"
|
label="Select Delivery Slot"
|
||||||
value={item.slotId}
|
value={item.slotId}
|
||||||
options={(productSlotsMap.get(item.productId) || []).map(slotId => {
|
options={(productSlotsMap.get(item.productId) || []).map(slotId => {
|
||||||
const slot = slotsData.slots.find(s => s.id === slotId);
|
const slot = slotsData.slots.find(s => s.id === slotId);
|
||||||
return {
|
return {
|
||||||
label: slot ? dayjs(slot.deliveryTime).format("ddd, MMM DD • h:mm A") : "N/A",
|
label: slot ? formatTimeRange(slot.deliveryTime) : "N/A",
|
||||||
value: slotId,
|
value: slotId,
|
||||||
};
|
};
|
||||||
})}
|
})}
|
||||||
onValueChange={(val) => {
|
onValueChange={(val) => {
|
||||||
const newSlot = slotsData.slots.find(s => s.id === val);
|
const newSlot = slotsData.slots.find(s => s.id === val);
|
||||||
Alert.alert("Delivery Updated", `Scheduled for ${dayjs(newSlot?.deliveryTime).format("MMM DD, h:mm A")}`);
|
Alert.alert("Delivery Updated", `Scheduled for ${formatTimeRange(newSlot?.deliveryTime || '')}`);
|
||||||
}}
|
}}
|
||||||
triggerComponent={({ onPress, displayText }) => (
|
triggerComponent={({ onPress, displayText }) => (
|
||||||
<MyTouchableOpacity
|
<MyTouchableOpacity
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue