Skip to content

Online shopping - Revamp Seller Item Registration #365

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 92 additions & 57 deletions src/components/shared/Seller/ShopItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export default function OnlineShopping({ dbSeller }: { dbSeller: ISeller }) {
name: "",
_id: "",
duration: 1,
price: {$numberDecimal: 0.01},
price: {$numberDecimal: '0.01'},
description: "",
image: "",
stock_level: StockLevelType.available_1,
Expand Down Expand Up @@ -190,7 +190,7 @@ export const ShopItem: React.FC<{
name: item.name || '',
description: item.description || '',
duration: item.duration || 1,
price: item.price || 0.01,
price: { $numberDecimal: item.price?.$numberDecimal?.toString()},
image: item.image || '',
stock_level: item.stock_level || translatedStockLevelOptions[0].name,
expired_by: item.expired_by,
Expand All @@ -202,8 +202,34 @@ export const ShopItem: React.FC<{
);
const [showPopup, setShowPopup] = useState(false);
const [showDialog, setShowDialog] = useState<boolean>(false);
const [dialogueMessage, setDialogueMessage] = useState<string>('');
const [file, setFile] = useState<File | null>(null);
const { reload, setReload, showAlert } = useContext(AppContext);
const [sellingStatus, setSellingStatus] = useState('');
const [formattedDate, setFormattedDate] = useState('');

useEffect(() => {
if (item?.expired_by) {
const expiredDate = new Date(item.expired_by);
const isActive = expiredDate > new Date();
setSellingStatus(
isActive
? t('SCREEN.SELLER_REGISTRATION.SELLER_ITEMS_FEATURE.SELLING_STATUS_OPTIONS.ACTIVE')
: t('SCREEN.SELLER_REGISTRATION.SELLER_ITEMS_FEATURE.SELLING_STATUS_OPTIONS.EXPIRED')
);

setFormattedDate(
new Intl.DateTimeFormat(locale || 'en-US', {
day: '2-digit',
month: 'long',
year: 'numeric',
hour: 'numeric',
minute: 'numeric',
hour12: true,
}).format(expiredDate)
);
}
}, [item]); // ✅ Runs when `item` changes

// Handle image upload
const handleAddImage = (e: React.ChangeEvent<HTMLInputElement>) => {
Expand All @@ -229,12 +255,13 @@ export const ShopItem: React.FC<{
// handle such scenarios where the event might not have the typical e.target structure i.e., PhoneInput.
const name = 'target' in e ? e.target.name : e.name;
const value = 'target' in e ? e.target.value : e.value;

// Create a new object with the updated form data
const updatedFormData = {
...formData,
[name]: value,
};
if (name==='price'){
}
setFormData(updatedFormData);

// enable or disable add item button based on form inputs
Expand All @@ -257,40 +284,67 @@ export const ShopItem: React.FC<{
};

const handleSave = async () => {
const duration = Number(formData.duration);
const today = new Date();

if (item.created_at) {
const createdAt = new Date(item.created_at);

// Calculate spent weeks since created_at
const spentWeeks = Math.floor((today.getTime() - createdAt.getTime()) / (7 * 24 * 60 * 60 * 1000));

// Ensure the new duration is not less than already spent weeks
if (duration < spentWeeks) {
setDialogueMessage(`Selling duration cannot be reduced below the ${spentWeeks} weeks already spent`);
setShowDialog(true);
return null;
}
}
// console.log("item price: ", formData.price.$numberDecimal)

const formDataToSend = new FormData();

// Prepare form data
formDataToSend.append('name', removeUrls(formData.name || '').trim());
formDataToSend.append('_id', formData._id || ''); // Ensure `_id` is empty if not provided
formDataToSend.append('_id', formData._id || '');
formDataToSend.append('description', removeUrls(formData.description || '').trim());
formDataToSend.append('duration', formData.duration?.toString() || '1'); // Default to '1' week if not provided
formDataToSend.append('seller_id', formData.seller_id || ''); // Ensure `seller_id` is added at BE
formDataToSend.append('stock_level', formData.stock_level || '1 available'); // Default stock level
formDataToSend.append('price', formData.price?.$numberDecimal?.toString() || '0.01'); // Ensure default price is valid

formDataToSend.append('duration', formData.duration?.toString() || '1');
formDataToSend.append('seller_id', formData.seller_id || '');
formDataToSend.append('stock_level', formData.stock_level || '1 available');
const price = (formData.price as unknown as string) || '0.01';
formDataToSend.append('price', parseFloat(price).toFixed(2));


// for (let [key, value] of formDataToSend.entries()) {
// console.log(`${key}: ${value}`);
// }

// Add file if provided
if (file) {
formDataToSend.append('image', file);
formDataToSend.append('image', file);
}

try {
logger.info('Form data being sent:', Object.fromEntries(formDataToSend.entries()));

// Send data to backend
const data = await addOrUpdateSellerItem(formDataToSend);

if (data) {
logger.info('Saved seller item:', data);
setReload(true);
setShowDialog(true);
setIsAddItemEnabled(false);
showAlert(t('SCREEN.SELLER_REGISTRATION.VALIDATION.SUCCESSFUL_SELLER_ITEM_SAVED'));
}
logger.info('Form data being sent:', Object.fromEntries(formDataToSend.entries()));

// Send data to backend
const data = await addOrUpdateSellerItem(formDataToSend);

if (data) {
logger.info('Saved seller item:', data);
setReload(true);
setDialogueMessage(t('SCREEN.SELLER_REGISTRATION.VALIDATION.SUCCESSFUL_SAVE_MAPPI_ALLOWANCE_SUFFICIENT', {
mappi_count: '99'
}));
setShowDialog(true);
setIsAddItemEnabled(false);
showAlert(t('SCREEN.SELLER_REGISTRATION.VALIDATION.SUCCESSFUL_SELLER_ITEM_SAVED'));
}
} catch (error) {
logger.error('Error saving seller item:', error);
showAlert(t('SCREEN.SELLER_REGISTRATION.VALIDATION.FAILED_SELLER_ITEM_SAVE'));
logger.error('Error saving seller item:', error);
showAlert(t('SCREEN.SELLER_REGISTRATION.VALIDATION.FAILED_SELLER_ITEM_SAVE'));
}
};
};


const handleDelete = async (item_id: string)=> {
if (!item_id || item_id ==='') {
Expand Down Expand Up @@ -318,9 +372,7 @@ export const ShopItem: React.FC<{
className={`relative outline outline-50 outline-gray-600 rounded-lg mb-7 cursor-pointer
${isActive ? '' : 'opacity-50 pointer-events-none'}`}
>
<Notification message={t('SCREEN.SELLER_REGISTRATION.VALIDATION.SUCCESSFUL_SAVE_MAPPI_ALLOWANCE_SUFFICIENT', {
mappi_count: '99'
})} showDialog={showDialog} setShowDialog={setShowDialog} />
<Notification message={dialogueMessage} showDialog={showDialog} setShowDialog={setShowDialog} />
<div className="p-3">
<div className="flex gap-x-4">
<div className="flex-auto w-64">
Expand Down Expand Up @@ -348,7 +400,7 @@ export const ShopItem: React.FC<{
</div>
</div>
</div>
<div className="flex gap-x-4 items-center">
<div className="flex gap-x-4">
<div className="flex-auto w-64">
<TextArea
label={t('SCREEN.SELLER_REGISTRATION.SELLER_ITEMS_FEATURE.DESCRIPTION_LABEL') + ':'}
Expand Down Expand Up @@ -433,31 +485,14 @@ export const ShopItem: React.FC<{
/>
</div>
<div className="mt-3">
{formData?.expired_by && (() => {
const expiredDate = new Date(formData.expired_by);
const isActive = expiredDate > new Date();
const sellingStatus = isActive
? t('SCREEN.SELLER_REGISTRATION.SELLER_ITEMS_FEATURE.SELLING_STATUS_OPTIONS.ACTIVE')
: t('SCREEN.SELLER_REGISTRATION.SELLER_ITEMS_FEATURE.SELLING_STATUS_OPTIONS.EXPIRED');

const formattedDate = new Intl.DateTimeFormat(locale || 'en-US', {
day: '2-digit',
month: 'long',
year: 'numeric',
hour: 'numeric',
minute: 'numeric',
hour12: true,
}).format(expiredDate);

return (
<label className="text-[14px] text-[#333333]">
<span className="fw-bold text-lg">{sellingStatus}: </span>
{t('SCREEN.SELLER_REGISTRATION.SELLER_ITEMS_FEATURE.SELLING_EXPIRATION_DATE', {
expired_by_date: formattedDate,
})}
</label>
);
})()}
{item?.expired_by && (
<label className="text-[14px] text-[#333333]">
<span className="fw-bold text-lg">{sellingStatus}: </span>
{t('SCREEN.SELLER_REGISTRATION.SELLER_ITEMS_FEATURE.SELLING_EXPIRATION_DATE', {
expired_by_date: formattedDate,
})}
</label>
)}
</div>
</div>
</div>
Expand Down Expand Up @@ -554,7 +589,7 @@ export const ListItem: React.FC<{
</div>
</div>

<div className="flex gap-x-4 items-center">
<div className="flex gap-x-4">
<div className="flex-auto w-64">
<TextArea
label={t('SCREEN.BUY_FROM_SELLER.SELLER_ITEMS_FEATURE.DESCRIPTION_LABEL') + ':'}
Expand Down
6 changes: 3 additions & 3 deletions src/constants/demoAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export const SellerItems:SellerItem[] = [
name: "Ham & cheese sandwich",
_id: "01",
duration: 4,
price: {$numberDecimal: 10},
price: {$numberDecimal: '10'},
description: "A filling sandwich made with soft fresh-backed bread, tender, ham, cheese creamy slices, crisp lettuce, juicy tomato and mayo",
image: "/images/business/product.png",
stock_level: StockLevelType.available_1,
Expand All @@ -83,7 +83,7 @@ export const SellerItems:SellerItem[] = [
name: "Coffee",
_id: "02",
duration: 0.5,
price: {$numberDecimal: 10},
price: {$numberDecimal: '10'},
description: "A nice refreshing coffee",
image: "/images/business/product.png",
stock_level: StockLevelType.available_2,
Expand All @@ -96,7 +96,7 @@ export const SellerItems:SellerItem[] = [
name: "Mobile phones",
_id: "03",
duration: 11,
price: {$numberDecimal: 20},
price: {$numberDecimal: '20'},
description: "All kind of mobile phones",
image: "/images/business/product.png",
stock_level: StockLevelType.available_3,
Expand Down
2 changes: 1 addition & 1 deletion src/constants/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export type SellerItem = {
stock_level: StockLevelType;
image?: string;
price: {
$numberDecimal: number;
$numberDecimal: string;
};
created_at?: Date;
updated_at?: Date;
Expand Down