From acfe9598cbe14d6ee3fd61ad5466bfa926f0f8fb Mon Sep 17 00:00:00 2001 From: Sidharth Prabhu Date: Fri, 17 Apr 2026 11:19:18 +0530 Subject: [PATCH] Repeat orders added --- ordering_site/package-lock.json | 47 +++- ordering_site/package.json | 1 + ordering_site/src/contexts/AuthContext.tsx | 43 +++- ordering_site/src/contexts/FoodContext.tsx | 6 +- ordering_site/src/pages/MyOrdersScreen.css | 145 +++++++++++ ordering_site/src/pages/MyOrdersScreen.tsx | 267 ++++++++++++++++----- ordering_site/src/pages/ProfileScreen.css | 137 +++++++++++ ordering_site/src/pages/ProfileScreen.tsx | 123 +++++++++- 8 files changed, 686 insertions(+), 83 deletions(-) diff --git a/ordering_site/package-lock.json b/ordering_site/package-lock.json index a2cc4041..7cd9da4d 100644 --- a/ordering_site/package-lock.json +++ b/ordering_site/package-lock.json @@ -8,6 +8,7 @@ "name": "ordering_site", "version": "0.0.0", "dependencies": { + "framer-motion": "^12.38.0", "lucide-react": "^1.7.0", "qrcode.react": "^4.2.0", "react": "^19.2.4", @@ -1834,6 +1835,33 @@ "dev": true, "license": "ISC" }, + "node_modules/framer-motion": { + "version": "12.38.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.38.0.tgz", + "integrity": "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.38.0", + "motion-utils": "^12.36.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2398,6 +2426,21 @@ "node": "*" } }, + "node_modules/motion-dom": { + "version": "12.38.0", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.38.0.tgz", + "integrity": "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.36.0" + } + }, + "node_modules/motion-utils": { + "version": "12.36.0", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.36.0.tgz", + "integrity": "sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -2824,9 +2867,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD", - "optional": true + "license": "0BSD" }, "node_modules/type-check": { "version": "0.4.0", diff --git a/ordering_site/package.json b/ordering_site/package.json index 0ec0761d..28d33cca 100644 --- a/ordering_site/package.json +++ b/ordering_site/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "framer-motion": "^12.38.0", "lucide-react": "^1.7.0", "qrcode.react": "^4.2.0", "react": "^19.2.4", diff --git a/ordering_site/src/contexts/AuthContext.tsx b/ordering_site/src/contexts/AuthContext.tsx index 43372c45..bd2aa2d4 100644 --- a/ordering_site/src/contexts/AuthContext.tsx +++ b/ordering_site/src/contexts/AuthContext.tsx @@ -9,6 +9,7 @@ interface AuthContextType { register: (mobileNumber: string, name: string, pin: string) => Promise<{ success: boolean; message: string }>; logout: () => void; changePin: (currentPin: string, newPin: string) => Promise<{ success: boolean; message: string }>; + updateProfile: (name: string, mobileNumber: string) => Promise<{ success: boolean; message: string }>; } const AuthContext = createContext(undefined); @@ -29,7 +30,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children const checkUserExists = async (mobileNumber: string) => { try { - const response = await fetch(`${API_BASE_URL}/check`, { + const response = await fetch(`${API_BASE_URL}/check`, { cache: 'no-store', method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ mobileNumber }), @@ -42,7 +43,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children const login = async (mobileNumber: string, pin: string) => { try { - const response = await fetch(`${API_BASE_URL}/login`, { + const response = await fetch(`${API_BASE_URL}/login`, { cache: 'no-store', method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ mobileNumber, pin }), @@ -61,7 +62,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children const register = async (mobileNumber: string, name: string, pin: string) => { try { - const response = await fetch(`${API_BASE_URL}/register`, { + const response = await fetch(`${API_BASE_URL}/register`, { cache: 'no-store', method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ mobileNumber, name, pin }), @@ -80,7 +81,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children const logout = () => { if (user) { - fetch(`${API_BASE_URL}/logout`, { + fetch(`${API_BASE_URL}/logout`, { cache: 'no-store', method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ mobileNumber: user.mobileNumber }), @@ -93,7 +94,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children const changePin = async (currentPin: string, newPin: string) => { if (!user) return { success: false, message: 'Not logged in' }; try { - const response = await fetch(`${API_BASE_URL}/change-pin`, { + const response = await fetch(`${API_BASE_URL}/change-pin`, { cache: 'no-store', method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ mobileNumber: user.mobileNumber, currentPin, newPin }), @@ -105,8 +106,38 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children } }; + const updateProfile = async (name: string, mobileNumber: string) => { + if (!user) return { success: false, message: 'Not logged in' }; + try { + const response = await fetch(`${API_BASE_URL}/users/${user.id}`, { cache: 'no-store', + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ name, mobileNumber }), + }); + + if (response.ok) { + const updatedUserDto = await response.json(); + // The DTO from backend might have different structure, let's map it back to User type + const updatedUser: User = { + id: updatedUserDto.id, + name: updatedUserDto.name, + mobileNumber: updatedUserDto.mobileNumber, + isLoggedIn: updatedUserDto.loggedIn + }; + setUser(updatedUser); + localStorage.setItem('user', JSON.stringify(updatedUser)); + return { success: true, message: 'Profile updated successfully' }; + } else { + const errorData = await response.json(); + return { success: false, message: errorData.message || 'Failed to update profile' }; + } + } catch (error) { + return { success: false, message: 'Network error. Please try again.' }; + } + }; + return ( - + {children} ); diff --git a/ordering_site/src/contexts/FoodContext.tsx b/ordering_site/src/contexts/FoodContext.tsx index 0c07a304..8389bd9a 100644 --- a/ordering_site/src/contexts/FoodContext.tsx +++ b/ordering_site/src/contexts/FoodContext.tsx @@ -29,9 +29,9 @@ export const FoodProvider: React.FC<{ children: React.ReactNode }> = ({ children setError(null); try { const [baseItemsRes, productsRes, stallsRes] = await Promise.all([ - fetch(`${API_BASE_URL}/base-items?size=100`), - fetch(`${API_BASE_URL}/products?size=100`), - fetch(`${API_BASE_URL}/stalls/active`) + fetch(`${API_BASE_URL}/base-items?size=100`, { cache: 'no-store' }), + fetch(`${API_BASE_URL}/products?size=100`, { cache: 'no-store' }), + fetch(`${API_BASE_URL}/stalls/active`, { cache: 'no-store' }) ]); if (!baseItemsRes.ok || !productsRes.ok || !stallsRes.ok) { diff --git a/ordering_site/src/pages/MyOrdersScreen.css b/ordering_site/src/pages/MyOrdersScreen.css index 93c2b72a..ff151647 100644 --- a/ordering_site/src/pages/MyOrdersScreen.css +++ b/ordering_site/src/pages/MyOrdersScreen.css @@ -319,3 +319,148 @@ margin-bottom: 20px; opacity: 0.5; } + +/* Repeat Order Button */ +.repeat-order-btn { + width: 100%; + padding: 16px; + background-color: var(--primary); + color: white; + border-radius: 16px; + font-size: 15px; + font-weight: 700; + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + margin-top: 24px; + border: none; + cursor: pointer; + box-shadow: 0 4px 15px rgba(35, 22, 81, 0.2); + transition: transform 0.2s; +} + +.repeat-order-btn:active { + transform: scale(0.98); +} + +.repeat-order-btn.mini { + margin-top: 12px; + padding: 12px; + font-size: 13px; + background-color: var(--primary-light); + color: var(--primary); + box-shadow: none; +} + +/* Stock Alert Modal */ +.stock-alert-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(8px); + z-index: 3000; + display: flex; + align-items: center; + justify-content: center; + padding: 24px; +} + +.stock-alert-modal { + background: white; + width: 100%; + max-width: 400px; + border-radius: 28px; + padding: 32px 24px; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + box-shadow: 0 20px 50px rgba(0, 0, 0, 0.2); + animation: modalPop 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); +} + +@keyframes modalPop { + from { transform: scale(0.8); opacity: 0; } + to { transform: scale(1); opacity: 1; } +} + +.alert-icon-wrapper { + width: 64px; + height: 64px; + background: #fff1f2; + color: #e11d48; + border-radius: 20px; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 20px; +} + +.stock-alert-modal h3 { + font-size: 20px; + font-weight: 800; + color: var(--text-dark); + margin-bottom: 12px; +} + +.stock-alert-modal p { + font-size: 14px; + color: var(--text-mid); + margin-bottom: 20px; + line-height: 1.5; +} + +.unavailable-items-list { + width: 100%; + background: var(--bg); + border-radius: 16px; + padding: 16px; + margin-bottom: 24px; + display: flex; + flex-direction: column; + gap: 8px; +} + +.unavailable-item-chip { + display: flex; + align-items: center; + gap: 8px; + font-size: 13px; + font-weight: 700; + color: #e11d48; + background: white; + padding: 8px 12px; + border-radius: 10px; + border: 1px solid #fecdd3; +} + +.alert-actions { + display: flex; + flex-direction: column; + gap: 12px; + width: 100%; +} + +.btn-proceed { + width: 100%; + padding: 16px; + background: var(--primary); + color: white; + border-radius: 16px; + font-weight: 700; + border: none; +} + +.btn-discard { + width: 100%; + padding: 16px; + background: var(--bg); + color: var(--text-mid); + border-radius: 16px; + font-weight: 700; + border: 1px solid var(--border); +} diff --git a/ordering_site/src/pages/MyOrdersScreen.tsx b/ordering_site/src/pages/MyOrdersScreen.tsx index 6a31861b..93ba2c5c 100644 --- a/ordering_site/src/pages/MyOrdersScreen.tsx +++ b/ordering_site/src/pages/MyOrdersScreen.tsx @@ -1,9 +1,14 @@ import React, { useState, useEffect } from 'react'; -import { Clock, ShoppingBag as ShoppingBagIcon, ChevronRight as ChevronRightIcon, X } from 'lucide-react'; +import { useNavigate } from 'react-router-dom'; +import { Clock, ShoppingBag as ShoppingBagIcon, ChevronRight as ChevronRightIcon, X, RefreshCcw, AlertCircle, ShoppingCart } from 'lucide-react'; import { QRCodeCanvas } from 'qrcode.react'; +import { motion, AnimatePresence } from 'framer-motion'; import Header from '../components/Header'; import BottomNav from '../components/BottomNav'; import { useAuth } from '../contexts/AuthContext'; +import { useFood } from '../contexts/FoodContext'; +import { useCart } from '../contexts/CartContext'; +import type { FoodItem } from '../types'; import './MyOrdersScreen.css'; interface Order { @@ -23,10 +28,19 @@ interface Order { } const MyOrdersScreen: React.FC = () => { + const navigate = useNavigate(); const { user } = useAuth(); + const { foodItems } = useFood(); + const { addToCart, clearCart } = useCart(); + const [orders, setOrders] = useState([]); const [loading, setLoading] = useState(true); const [selectedOrder, setSelectedOrder] = useState(null); + + // State for stock alert + const [showStockAlert, setShowStockAlert] = useState(false); + const [unavailableItems, setUnavailableItems] = useState([]); + const [pendingItems, setPendingItems] = useState([]); useEffect(() => { if (user?.id) { @@ -46,6 +60,50 @@ const MyOrdersScreen: React.FC = () => { } }; + const handleRepeatOrder = (order: Order) => { + const missing: string[] = []; + const available: FoodItem[] = []; + + order.items.forEach(orderItem => { + // Find the current food item by name (since orders store names) + // Robustness: matching by name is fallback if IDs aren't directly available in OrderItem DTO + const currentItem = foodItems.find(fi => fi.name === orderItem.productName); + + if (currentItem) { + if (currentItem.stock && currentItem.stock > 0) { + // Add multiple times based on original quantity + for (let i = 0; i < orderItem.quantity; i++) { + available.push(currentItem); + } + } else { + missing.push(orderItem.productName); + } + } else { + missing.push(orderItem.productName); // Item no longer exists in catalog + } + }); + + if (missing.length > 0) { + setUnavailableItems(missing); + setPendingItems(available); + setShowStockAlert(true); + } else { + // All items available + performRepeat(available); + } + }; + + const performRepeat = (items: FoodItem[]) => { + clearCart(); + items.forEach(item => addToCart(item)); + navigate('/cart'); + }; + + const handleProceedPartial = () => { + performRepeat(pendingItems); + setShowStockAlert(false); + }; + const latestOrder = orders.length > 0 ? orders[0] : null; const previousOrders = orders.length > 1 ? orders.slice(1) : []; @@ -108,6 +166,16 @@ const MyOrdersScreen: React.FC = () => { Total Amount ₹{latestOrder.totalAmount.toFixed(2)} + + )} @@ -143,74 +211,141 @@ const MyOrdersScreen: React.FC = () => { {/* Order Detail Modal / Overlay */} - {selectedOrder && ( -
setSelectedOrder(null)}> -
e.stopPropagation()}> - - -
-

Order Details

-

Order #{selectedOrder.displayOrderId}

-
- -
-
- - {selectedOrder.status.toUpperCase() === 'COMPLETED' && ( -
- COMPLETED -
- )} - {selectedOrder.archived && ( -
- EXPIRED -
- )} + + {selectedOrder && ( + setSelectedOrder(null)} + > + e.stopPropagation()} + > + + +
+

Order Details

+

Order #{selectedOrder.displayOrderId}

-

- {selectedOrder.archived - ? 'This order has expired' - : selectedOrder.status.toUpperCase() === 'COMPLETED' - ? 'This order has been fulfilled' - : 'Show this QR code at the counter'} -

-
- -
-
- Date - {new Date(selectedOrder.createdAt).toLocaleString()} -
-
- Payment - {selectedOrder.paymentMethod} -
-
- Status - - {selectedOrder.status} - -
-
- -
-

Items Ordered

- {selectedOrder.items.map((item, idx) => ( -
- {item.quantity} x {item.productName} - ₹{(item.price * item.quantity).toFixed(0)} + +
+
+ + {selectedOrder.status.toUpperCase() === 'COMPLETED' && ( +
+ COMPLETED +
+ )} + {selectedOrder.archived && ( +
+ EXPIRED +
+ )}
- ))} -
- Grand Total - ₹{selectedOrder.totalAmount.toFixed(2)} +

+ {selectedOrder.archived + ? 'This order has expired' + : selectedOrder.status.toUpperCase() === 'COMPLETED' + ? 'This order has been fulfilled' + : 'Show this QR code at the counter'} +

-
-
-
- )} + +
+
+ Date + {new Date(selectedOrder.createdAt).toLocaleString()} +
+
+ Payment + {selectedOrder.paymentMethod} +
+
+ Status + + {selectedOrder.status} + +
+
+ +
+

Items Ordered

+ {selectedOrder.items.map((item, idx) => ( +
+ {item.quantity} x {item.productName} + ₹{(item.price * item.quantity).toFixed(0)} +
+ ))} +
+ Grand Total + ₹{selectedOrder.totalAmount.toFixed(2)} +
+
+ + + + + )} + + + {/* Stock Alert Dialog */} + + {showStockAlert && ( + + +
+ +
+

Items Unavailable

+

Some items from your previous order are currently out of stock or unavailable.

+ +
+ {unavailableItems.map((item, idx) => ( +
+ {item} +
+ ))} +
+ +
+ {pendingItems.length > 0 ? ( + + ) : ( +
+ All items in this order are currently unavailable. +
+ )} + +
+
+
+ )} +
diff --git a/ordering_site/src/pages/ProfileScreen.css b/ordering_site/src/pages/ProfileScreen.css index 9a556530..fa55404a 100644 --- a/ordering_site/src/pages/ProfileScreen.css +++ b/ordering_site/src/pages/ProfileScreen.css @@ -205,3 +205,140 @@ font-size: 0.75rem; color: var(--text-light); } + +/* Modal Styles */ +.modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + padding: 20px; + backdrop-filter: blur(4px); +} + +.modal-container { + background: var(--surface); + width: 100%; + max-width: 400px; + border-radius: 24px; + overflow: hidden; + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2); + border: 1px solid var(--border); + animation: modalIn 0.3s ease-out; +} + +@keyframes modalIn { + from { transform: translateY(20px); opacity: 0; } + to { transform: translateY(0); opacity: 1; } +} + +.modal-header { + padding: 24px 24px 12px; + display: flex; + align-items: center; + justify-content: space-between; +} + +.modal-header h2 { + font-size: 1.5rem; + font-weight: 800; + color: var(--text-dark); +} + +.close-btn { + background: var(--bg); + border: none; + width: 36px; + height: 36px; + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + color: var(--text-mid); + cursor: pointer; +} + +.modal-body { + padding: 12px 24px 24px; +} + +.form-group { + margin-bottom: 20px; +} + +.form-group label { + display: block; + font-size: 0.85rem; + font-weight: 700; + color: var(--text-mid); + margin-bottom: 8px; + margin-left: 4px; +} + +.input-wrapper { + position: relative; + display: flex; + align-items: center; +} + +.input-icon { + position: absolute; + left: 16px; + color: var(--primary); +} + +.form-group input { + width: 100%; + padding: 16px 16px 16px 48px; + background: var(--bg); + border: 2px solid var(--border); + border-radius: 16px; + font-size: 1rem; + font-weight: 600; + color: var(--text-dark); + transition: all 0.2s; +} + +.form-group input:focus { + border-color: var(--primary); + background: var(--surface); + box-shadow: 0 0 0 4px var(--primary-light); + outline: none; +} + +.modal-footer { + padding: 0 24px 24px; + display: flex; + gap: 12px; +} + +.btn-cancel { + flex: 1; + padding: 16px; + background: var(--bg); + color: var(--text-mid); + border-radius: 16px; + font-weight: 700; + border: 1px solid var(--border); +} + +.btn-save { + flex: 2; + padding: 16px; + background: var(--primary); + color: white; + border-radius: 16px; + font-weight: 700; + box-shadow: 0 4px 15px rgba(35, 22, 81, 0.2); +} + +.btn-save:disabled { + opacity: 0.7; + cursor: not-allowed; +} diff --git a/ordering_site/src/pages/ProfileScreen.tsx b/ordering_site/src/pages/ProfileScreen.tsx index e1778260..4ba8be3a 100644 --- a/ordering_site/src/pages/ProfileScreen.tsx +++ b/ordering_site/src/pages/ProfileScreen.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { ShoppingBag, @@ -7,16 +7,33 @@ import { LogOut, ChevronRight, Phone, - ShieldCheck + ShieldCheck, + User, + X, + User as UserIcon } from 'lucide-react'; import { useAuth } from '../contexts/AuthContext'; +import { motion, AnimatePresence } from 'framer-motion'; import Header from '../components/Header'; import BottomNav from '../components/BottomNav'; import './ProfileScreen.css'; const ProfileScreen: React.FC = () => { const navigate = useNavigate(); - const { user, logout } = useAuth(); + const { user, logout, updateProfile } = useAuth(); + + const [isEditModalOpen, setIsEditModalOpen] = useState(false); + const [editName, setEditName] = useState(user?.name || ''); + const [editMobile, setEditMobile] = useState(user?.mobileNumber || ''); + const [isUpdating, setIsUpdating] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + if (user) { + setEditName(user.name); + setEditMobile(user.mobileNumber); + } + }, [user]); const handleLogout = () => { if (window.confirm('Are you sure you want to logout?')) { @@ -25,6 +42,39 @@ const ProfileScreen: React.FC = () => { } }; + const handleEditClick = () => { + setEditName(user?.name || ''); + setEditMobile(user?.mobileNumber || ''); + setError(null); + setIsEditModalOpen(true); + }; + + const handleUpdateProfile = async (e: React.FormEvent) => { + e.preventDefault(); + if (!editName.trim() || !editMobile.trim()) { + setError('Name and mobile number are required'); + return; + } + + if (!/^[0-9]{10}$/.test(editMobile)) { + setError('Mobile number must be 10 digits'); + return; + } + + setIsUpdating(true); + setError(null); + + const result = await updateProfile(editName, editMobile); + setIsUpdating(false); + + if (result.success) { + setIsEditModalOpen(false); + // Optional: Show success message + } else { + setError(result.message); + } + }; + const menuItems = [ { icon: ShoppingBag, label: 'My Orders', sub: 'View order history', path: '/orders' }, { icon: ShieldCheck, label: 'Account Security', sub: 'Change Security PIN', path: '/change-pin' }, @@ -49,10 +99,9 @@ const ProfileScreen: React.FC = () => {

{user?.name || 'User Name'}

{user?.mobileNumber || '+91 XXXXX XXXXX'} - {/* user@example.com */}
- +
@@ -98,6 +147,70 @@ const ProfileScreen: React.FC = () => { + {/* Edit Profile Modal */} + + {isEditModalOpen && ( + + +
+

Edit Profile

+ +
+
+
+ {error &&
{error}
} + +
+ +
+ + setEditName(e.target.value)} + placeholder="Enter your name" + /> +
+
+ +
+ +
+ + setEditMobile(e.target.value)} + placeholder="10-digit mobile number" + maxLength={10} + /> +
+
+
+
+ + +
+
+
+
+ )} +
+ );