Android_Retail/src/Pages/SlotManagement/SlotManagement.jsx

1628 lines
53 KiB
React
Raw Normal View History

2026-01-27 18:27:29 +05:30
import React, { useState, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import {
Form,
Input,
Select,
InputNumber,
Button,
DatePicker,
Space,
Card,
Row,
Col,
Checkbox,
Collapse,
Tooltip,
Modal,
Table,
Tag,
Switch,
Radio,
TimePicker,
Divider,
TreeSelect,
Avatar,
List
} from 'antd';
import {
PlusOutlined,
DeleteOutlined,
InfoCircleOutlined,
ArrowRightOutlined,
BlockOutlined,
DollarOutlined,
FlagOutlined,
CalendarOutlined,
ShopOutlined,
TeamOutlined,
CloseCircleOutlined,
UserOutlined,
EditOutlined
} from '@ant-design/icons';
import moment from 'moment';
import { getCategories, getProductListBasedSubcat, getSubCategories, GlobalCategories, GlobalProductBasedSubcat, GlobalSubCategories, PutSlotBlock } from '../../Features/SlotManagement/SlotManagement';
import { getSession } from '../../Services/Others';
import { DropDowns } from '../../Components/Forms/DropDown';
import { InputField } from '../../Components/Forms/InputField';
import "./SlotManagement.scss"
import { changeBreadCrumb } from '../../Features/AppPage/CenterPage';
import Buttons from '../../Components/Forms/Buttons';
const { Option } = Select;
const { Panel } = Collapse;
const { RangePicker } = DatePicker;
const { TextArea } = Input;
const { TreeNode } = TreeSelect;
const subDirectory = import.meta.env.BASE_URL;
const SlotManagementForm = () => {
const CompId = getSession('CompId');
const BranchId = getSession('BranchId');
const AppId = getSession('AppId');
const UserId = getSession('UserId');
const dispatch = useDispatch();
const navigate = useNavigate();
// UseSlectors
const CategoriesData = useSelector(GlobalCategories);
const SubCategoriesData = useSelector(GlobalSubCategories);
const ProductList = useSelector(GlobalProductBasedSubcat)
// States
const [form] = Form.useForm();
const [selectedCategory, setSelectedCategory] = useState(null);
const [selectedSubCategory, setSelectedSubCategory] = useState(null);
const [selectedFacility, setSelectedFacility] = useState(null);
const [selectedCoach, setSelectedCoach] = useState(null);
const [actionMode, setActionMode] = useState(null);
const [closureType, setClosureType] = useState(null);
const [dateRange, setDateRange] = useState([]);
console.log(closureType, "closureType")
const [selectedDays, setSelectedDays] = useState({
monday: false,
tuesday: false,
wednesday: false,
thursday: false,
friday: false,
saturday: false,
sunday: false
});
const [priceChangeType, setPriceChangeType] = useState('full');
const [SlotChangeType, setSlotChangeType] = useState('all');
const [weekendPremium, setWeekendPremium] = useState(false);
const [weekendPrice, setWeekendPrice] = useState(0);
const [newPrice, setNewPrice] = useState(null);
const [selectAllDays, setSelectAllDays] = useState(false);
const [selectedSlots, setSelectedSlots] = useState([]);
const [managementHistory, setManagementHistory] = useState([]);
const [selectedBranches, setSelectedBranches] = useState([]);
const [closureReason, setClosureReason] = useState(null);
const [individualSlotPrices, setIndividualSlotPrices] = useState({}); // NEW: Store individual slot prices
const [Slots, setSlots] = useState([]);
const [individualPricing, setIndividualPricing] = useState(false);
const [individualDefaultPrice, setIndividualDefaultPrice] = useState(0);
const [slotPrices, setSlotPrices] = useState({});
const [types, setTypes] = useState([]);
const items = [
{
name: 'Home',
link: `${subDirectory}app-page/home`,
},
{
name: 'Slot Management',
link: `${subDirectory}setting/slot-management-list`,
},
];
useEffect(() => {
try {
dispatch(changeBreadCrumb({ items: items }));
} catch (err) {
console.log(err, 'err');
}
}, []);
// Mock data for branches
const branches = [
{ id: 1, name: 'Main Sports Center', address: '123 Main St, City' },
{ id: 2, name: 'North Branch', address: '456 North Ave, City' },
{ id: 3, name: 'South Complex', address: '789 South Rd, City' },
{ id: 4, name: 'East Arena', address: '321 East Blvd, City' },
{ id: 5, name: 'West Facility', address: '654 West St, City' }
];
// Mock data for categories and sub-categories
const categories = [
{ id: 1, name: 'Sports' },
{ id: 2, name: 'Fitness' },
{ id: 3, name: 'Wellness' },
{ id: 4, name: 'Entertainment' }
];
const subCategories = {
1: [ // Sports
{ id: 101, name: 'Cricket' },
{ id: 102, name: 'Football' },
{ id: 103, name: 'Tennis' },
{ id: 104, name: 'Badminton' },
{ id: 105, name: 'Swimming' }
],
2: [ // Fitness
{ id: 201, name: 'Gym' },
{ id: 202, name: 'Yoga' },
{ id: 203, name: 'Zumba' },
{ id: 204, name: 'Pilates' }
],
3: [ // Wellness
{ id: 301, name: 'Spa' },
{ id: 302, name: 'Massage' },
{ id: 303, name: 'Meditation' }
],
4: [ // Entertainment
{ id: 401, name: 'Movie' },
{ id: 402, name: 'Games' }
]
};
// Mock data for facilities
const facilities = [
{ id: 1, name: 'Cricket Ground A', categoryId: 1, subCategoryId: 101, branchId: 1 },
{ id: 2, name: 'Cricket Ground B', categoryId: 1, subCategoryId: 101, branchId: 1 },
{ id: 3, name: 'Football Field', categoryId: 1, subCategoryId: 102, branchId: 2 },
{ id: 4, name: 'Tennis Court 1', categoryId: 1, subCategoryId: 103, branchId: 1 },
{ id: 5, name: 'Tennis Court 2', categoryId: 1, subCategoryId: 103, branchId: 3 },
{ id: 6, name: 'Badminton Court', categoryId: 1, subCategoryId: 104, branchId: 2 },
{ id: 7, name: 'Swimming Pool', categoryId: 1, subCategoryId: 105, branchId: 4 },
{ id: 8, name: 'Gym Area', categoryId: 2, subCategoryId: 201, branchId: 1 },
{ id: 9, name: 'Yoga Studio', categoryId: 2, subCategoryId: 202, branchId: 2 },
{ id: 10, name: 'Spa Center', categoryId: 3, subCategoryId: 301, branchId: 1 }
];
// Mock data for coaches
const coaches = [
{
id: 1,
name: 'John Smith',
sport: 'Cricket',
specialization: 'Batting',
categoryId: 1,
subCategoryId: 101,
facilities: [1, 2],
branchId: 1
},
{
id: 2,
name: 'Mike Johnson',
sport: 'Cricket',
specialization: 'Bowling',
categoryId: 1,
subCategoryId: 101,
facilities: [1],
branchId: 1
},
{
id: 3,
name: 'Sarah Wilson',
sport: 'Tennis',
specialization: 'Singles',
categoryId: 1,
subCategoryId: 103,
facilities: [4, 5],
branchId: 1
},
{
id: 4,
name: 'David Brown',
sport: 'Football',
specialization: 'Goal Keeping',
categoryId: 1,
subCategoryId: 102,
facilities: [3],
branchId: 2
},
{
id: 5,
name: 'Emma Davis',
sport: 'Swimming',
specialization: 'Competitive',
categoryId: 1,
subCategoryId: 105,
facilities: [7],
branchId: 4
}
];
// Closure types configuration
const closureTypes = {
block: [
{ value: 'full_branch', label: 'Full Branch Block', icon: <ShopOutlined />, color: 'red' },
{ value: 'category', label: 'Category Block', icon: <TeamOutlined />, color: 'orange' },
{ value: 'subcategory', label: 'Sub-Category Block', icon: <TeamOutlined />, color: 'volcano' },
{ value: 'product', label: 'Facility Block', color: 'gold' }
],
priceChange: [
{ value: 'product', label: 'Facility Price Change', icon: <DollarOutlined />, color: 'purple' },
],
holiday: [
{ value: 'full_branch', label: 'Full Branch Holiday', icon: <FlagOutlined />, color: 'red' },
{ value: 'category', label: 'Category Holiday', icon: <FlagOutlined />, color: 'orange' },
{ value: 'subcategory', label: 'Sub-Category Holiday', icon: <TeamOutlined />, color: 'volcano' },
{ value: 'product', label: 'Facility Holiday', icon: <FlagOutlined />, color: 'gold' }
]
};
// const types = closureTypes[actionMode] || [];
useEffect(() => {
// Fetch categories on component mount
let baseData = {
CompId,
BranchId,
AppId,
};
dispatch(getCategories(baseData));
}
, []);
useEffect(() => {
if(types?.length == 1){
const singleOption = types[0];
form.setFieldsValue({ closureType: singleOption.value });
handleClosureTypeChange(singleOption.value);
}
}, [types]);
// Slot Price update
useEffect(() => {
setSlotPrices(prev => {
const updated = { ...prev };
selectedSlots.forEach(slot => {
if (!updated[slot]) {
updated[slot] = {
fullSlotPrice: Number(newPrice) || 0,
individualPrice: Number(individualDefaultPrice) || 0
};
}
});
// Remove prices for deselected slots
Object.keys(updated).forEach(slot => {
if (!selectedSlots.includes(slot)) {
delete updated[slot];
}
});
return updated;
});
}, [selectedSlots, newPrice, individualDefaultPrice]);
const handleFullSlotPriceChange = (slot, value) => {
setSlotPrices(prev => ({
...prev,
[slot]: {
...prev[slot],
fullSlotPrice: value
}
}));
};
const handleIndividualPriceChange = (slot, value) => {
setSlotPrices(prev => ({
...prev,
[slot]: {
...prev[slot],
individualPrice: value
}
}));
};
// Day selection handlers
const handleDayChange = (day) => {
setSelectedDays(prev => ({
...prev,
[day]: !prev[day]
}));
};
const handleSelectAllDays = (checked) => {
setSelectAllDays(checked);
if (checked) {
setSelectedDays({
monday: true,
tuesday: true,
wednesday: true,
thursday: true,
friday: true,
saturday: true,
sunday: true
});
} else {
setSelectedDays({
monday: false,
tuesday: false,
wednesday: false,
thursday: false,
friday: false,
saturday: false,
sunday: false
});
}
};
// Update select all when individual days change
useEffect(() => {
const allSelected = Object.values(selectedDays).every(day => day);
setSelectAllDays(allSelected);
}, [selectedDays]);
// Handle category change
const handleCategoryChange = async (value) => {
console.log("Category value", value);
setSelectedCategory(value);
form.setFieldsValue({
category: value,
});
// Fetch sub-categories based on selected category
let data = { CompId: CompId, BranchId: BranchId, AppId: AppId, ProdCat: value };
await dispatch(getSubCategories(data));
setSelectedSubCategory(null);
setSelectedFacility(null);
form.setFieldsValue({
subCategory: undefined,
facility: undefined
});
};
// Handle sub category change
const handleSubCategoryChange = async (value) => {
setSelectedSubCategory(value);
form.setFieldsValue({
subCategory: value,
});
let data = { CompId: CompId, BranchId: BranchId, AppId: AppId, ProdSubCat: value };
await dispatch(getProductListBasedSubcat(data));
setSelectedFacility(null);
form.setFieldsValue({ facility: undefined });
};
// Handle facility change
const handleFacilityChange = (value) => {
setSelectedFacility(value);
form.setFieldsValue({
facility: value,
});
// Auto-populate category and sub-category based on facility
const facilityData = ProductList.find(f => f.ProdId === value);
let datawithinward = facilityData?.ProductDetail?.[0]?.ProdVariantDetails?.flatMap(item => item.StockDetails.map(stock => ({
...item,
ProdInwardDtlId: stock.InwardDtlId,
})))
setSlots(facilityData?.ProductDetail?.[0]?.ProdVariantDetails?.length > 0 ? datawithinward : []);
};
// Handle coach change
const handleCoachChange = (value) => {
setSelectedCoach(value);
// Auto-populate category, sub-category, and facility based on coach
const coachData = coaches.find(c => c.id === value);
if (coachData) {
setSelectedCategory(coachData.categoryId);
setSelectedSubCategory(coachData.subCategoryId);
form.setFieldsValue({
category: coachData.categoryId,
subCategory: coachData.subCategoryId
});
}
};
// Handle action mode change
const handleActionModeChange = (value) => {
setActionMode(value);
form.setFieldsValue({ actionMode: value });
setTypes(closureTypes[value] || []);
setClosureType(null);
setSlotChangeType('all');
setDateRange([]);
form.setFieldsValue({ dateRange: [] });
form.setFieldsValue({
category: undefined,
subCategory: undefined,
product: undefined,
closureType: undefined,
});
// Reset related states when mode changes
if (value !== 'priceChange') {
setNewPrice(null);
setWeekendPremium(false);
setWeekendPrice(0);
setIndividualSlotPrices({});
}
if (value !== 'block' && value !== 'priceChange') {
setSelectedSlots([]);
}
setClosureReason(null);
};
// Handle closure type change
const handleClosureTypeChange = (value) => {
setClosureType(value);
form.setFieldsValue({
closureType: value
});
// Reset selections when closure type changes
setSelectedCategory(null);
setSelectedSubCategory(null);
setSelectedFacility(null);
setSelectedCoach(null);
setSelectedBranches([]);
setSelectedSlots([]);
setIndividualSlotPrices({});
setClosureReason(null);
form.setFieldsValue({
category: undefined,
subCategory: undefined,
product: undefined,
coach: undefined,
branches: undefined
});
};
// Handle slot selection
const handleSlotSelection = (slot) => {
if (selectedSlots.includes(slot)) {
setSelectedSlots(selectedSlots.filter(s => s !== slot));
// Remove from individual prices when deselected
setIndividualSlotPrices(prev => {
const newPrices = { ...prev };
delete newPrices[slot];
return newPrices;
});
} else {
setSelectedSlots([...selectedSlots, slot]);
// Initialize with base price when selected
setIndividualSlotPrices(prev => ({
...prev,
[slot]: newPrice || 0
}));
}
};
const to12Hour = (timeStr) => {
if (!timeStr) return '';
let [h, m] = timeStr.split(':');
h = parseInt(h, 10);
const suffix = h >= 12 ? 'PM' : 'AM';
h = h % 12 || 12;
return `${h}:${m}${suffix}`;
};
// Select all slots
const handleSelectAllSlots = () => {
if (selectedSlots.length === Slots.length) {
setSelectedSlots([]);
setIndividualSlotPrices({});
} else {
setSelectedSlots([...Slots?.map(slot => slot.ProdInwardDtlId)]);
// Initialize all with base price
const initialPrices = {};
Slots.forEach(slot => {
initialPrices[slot] = newPrice || 0;
});
setIndividualSlotPrices(initialPrices);
}
};
// Handle individual slot price change
const handleIndividualSlotPriceChange = (slot, price) => {
setIndividualSlotPrices(prev => ({
...prev,
[slot]: price
}));
};
// Apply base price to all selected slots
const applyBasePriceToAllSlots = () => {
const updatedPrices = {};
selectedSlots.forEach(slot => {
updatedPrices[slot] = newPrice;
});
setIndividualSlotPrices(updatedPrices);
};
// Get selected days display text
const getSelectedDaysText = () => {
const daysMap = {
monday: 'Mon',
tuesday: 'Tue',
wednesday: 'Wed',
thursday: 'Thu',
friday: 'Fri',
saturday: 'Sat',
sunday: 'Sun'
};
const selected = Object.entries(selectedDays)
.filter(([_, isSelected]) => isSelected)
.map(([day]) => daysMap[day]);
return selected.length > 0 ? selected.join(', ') : 'No days selected';
};
// Get action mode display text
const getActionModeText = () => {
switch (actionMode) {
case 'block':
return 'Block';
case 'priceChange':
return 'Price Change';
case 'holiday':
return 'Holiday';
default:
return '';
}
};
// Get closure type display text
const getClosureTypeText = () => {
const allClosureTypes = [...closureTypes.block, ...closureTypes.priceChange, ...closureTypes.holiday];
const type = allClosureTypes.find(t => t.value === closureType);
return type ? type.label : 'No Type Selected';
};
// Filter facilities based on category and sub-category
const getFilteredFacilities = () => {
let filtered = facilities;
if (selectedCategory) {
filtered = filtered.filter(f => f.categoryId === selectedCategory);
}
if (selectedSubCategory) {
filtered = filtered.filter(f => f.subCategoryId === selectedSubCategory);
}
return filtered;
};
// Filter coaches based on category and sub-category
const getFilteredCoaches = () => {
let filtered = coaches;
if (selectedCategory) {
filtered = filtered.filter(c => c.categoryId === selectedCategory);
}
if (selectedSubCategory) {
filtered = filtered.filter(c => c.subCategoryId === selectedSubCategory);
}
return filtered;
};
// Check if time slot selection should be shown
const ShouldCategoryShow = () => {
if (closureType !== 'full_branch' && actionMode && closureType) {
return true;
}
return false;
}
const ShouldSubCategoryShow = () => {
if (actionMode && closureType && selectedCategory) {
if (closureType === 'subcategory' || closureType === 'product') {
return true;
} else {
return false;
}
}
return false;
}
const ShouldFacilityShow = () => {
if (actionMode && closureType && selectedSubCategory) {
if (closureType === 'product') {
return true;
} else {
return false;
}
}
return false;
}
// Check if time slot selection should be shown
const shouldShowTimeSlotSelection = () => {
if (actionMode === 'block' || actionMode === 'priceChange') {
return SlotChangeType === 'individual';
}
return false;
};
// Check if day selection should be shown
const shouldShowDaySelection = () => {
if (actionMode === 'block' || actionMode === 'priceChange') {
return SlotChangeType === 'individual';
}
return false;
};
const showslotselection = () => {
if (actionMode === 'block' || actionMode === 'priceChange') {
return dateRange?.length > 0 && closureType == 'product' && selectedFacility;
}
return false;
}
// Check if reason input should be shown
const shouldShowReasonInput = () => {
return actionMode === 'block' && closureType && closureType !== 'priceChange';
};
// Check if individual slot price table should be shown
const shouldShowIndividualSlotPriceTable = () => {
return actionMode === 'priceChange' &&
selectedSlots.length > 0;
};
// Apply management action
const applyManagementAction = async () => {
if (!actionMode || !closureType || !dateRange || dateRange.length === 0) {
Modal.error({
title: 'Validation Error',
content: 'Please fill all required fields: Slot Action, Activity Type, and Date Range',
});
return;
}
// Validation based on closure type
// if (closureType === 'full_branch' && selectedBranches.length === 0) {
// Modal.error({
// title: 'Validation Error',
// content: 'Please select at least one branch for full branch closure',
// });
// return;
// }
if ((closureType === 'category' || closureType === 'subcategory') && !selectedCategory) {
Modal.error({
title: 'Validation Error',
content: `Please select category for ${closureType} `,
});
return;
}
if (closureType === 'subcategory' && !selectedSubCategory) {
Modal.error({
title: 'Validation Error',
content: 'Please select sub-category ',
});
return;
}
if ((closureType === 'product' || closureType === 'individual_slot') && !selectedFacility) {
Modal.error({
title: 'Validation Error',
content: 'Please select facility',
});
return;
}
if (closureType === 'coach_leave' && !selectedCoach) {
Modal.error({
title: 'Validation Error',
content: 'Please select coach for coach leave',
});
return;
}
// Check for day selection for time slot operations
if (shouldShowDaySelection() && !Object.values(selectedDays).some(day => day)) {
Modal.error({
title: 'Validation Error',
content: 'Please select at least one day for time slot operations',
});
return;
}
// Check for slot selection for individual time slot operations
if ((SlotChangeType === 'individual') && selectedSlots.length === 0) {
Modal.error({
title: 'Validation Error',
content: 'Please select at least one time slot',
});
return;
}
// Check for price in price change operations
if (actionMode === 'priceChange') {
if (priceChangeType === 'full' && newPrice <= 0) {
Modal.error({
title: 'Validation Error',
content: 'Please enter a valid price for price change',
});
return;
}
// if (priceChangeType === 'full') {
// Check if all selected slots have valid prices
const invalidSlots = selectedSlots?.filter(slot =>
!slotPrices[slot] || Number(slotPrices[slot]?.fullSlotPrice) <= 0
);
console.log(selectedSlots?.filter(slot =>
!slotPrices[slot] || Number(slotPrices[slot]?.fullSlotPrice) <= 0
), "selectedSlots?.filter")
if (invalidSlots.length > 0) {
Modal.error({
title: 'Validation Error',
content: `Please enter valid prices for all selected slots. Missing or invalid prices `,
});
return;
}
}
// }
// Check for reason in block operations
if (actionMode === 'block' && !closureReason?.trim()) {
Modal.error({
title: 'Validation Error',
content: 'Please provide a reason for the block',
});
return;
}
let slotDetails = {
"AppId": AppId,
"CompId": CompId,
"BranchId": BranchId,
"ModeOfException": SlotChangeType == 'individual' ? 'slot' : closureType,
"ExceptionType": actionMode,
"Reason": closureReason,
"IsHoliday": actionMode == "holiday" ? 'Y' : 'N',
"DayOfWeek":
Object.entries(selectedDays).filter(([_, selected]) => selected).map(([day]) => day),
"SlotDetails":
SlotChangeType === 'individual'
? selectedSlots?.map(inwardId => ({
ProdCat: selectedCategory || null,
ProdSubCat: selectedSubCategory || null,
ProdId: selectedFacility || null,
InwardDtlId: inwardId,
ExceptionFrom: dateRange[0]?.format('YYYY-MM-DD'),
ExceptionTo: dateRange[1]?.format('YYYY-MM-DD'),
NewPrice: slotPrices[inwardId]?.fullSlotPrice || 0,
SinglePrice: slotPrices[inwardId]?.individualPrice || 0
}))
: [
{
ProdCat: selectedCategory || null,
ProdSubCat: selectedSubCategory || null,
ProdId: selectedFacility || null,
InwardDtlId: null,
ExceptionFrom: dateRange[0]?.format('YYYY-MM-DD'),
ExceptionTo: dateRange[1]?.format('YYYY-MM-DD'),
NewPrice: newPrice || 0,
SinglePrice: individualDefaultPrice || 0
}
],
"UpdatedBy": UserId
}
console.log("Management Record", slotDetails);
let response = await dispatch(PutSlotBlock(slotDetails)).unwrap()
console.log("response", response);
if (response?.data?.statusCode == 1) {
navigate(`${subDirectory}setting/slot-management-list`, {
state: {
Notiffy: {
messageType: 'success',
messageData:`${getActionModeText()} - ${getClosureTypeText()} applied successfully!`,
},
},
});
// Modal.success({
// title: 'Success',
// content: `${getActionModeText()} - ${getClosureTypeText()} applied successfully!`,
// });
}
// Modal.success({
// title: 'Success',
// content: `${getActionModeText()} - ${getClosureTypeText()} applied successfully!`,
// });
// Reset form
form.resetFields();
setSelectedCategory(null);
setSelectedSubCategory(null);
setSelectedFacility(null);
setSelectedCoach(null);
setActionMode(null);
setClosureType(null);
setDateRange([]);
setSelectedDays({
monday: false, tuesday: false, wednesday: false, thursday: false,
friday: false, saturday: false, sunday: false
});
setSelectedSlots([]);
setSelectedBranches([]);
setNewPrice(null);
setWeekendPremium(false);
setWeekendPrice(0);
setClosureReason(null);
setIndividualSlotPrices({});
setSlotChangeType('all');
setPriceChangeType('full');
setSelectAllDays(false);
};
// Remove management record
const removeManagementRecord = (id) => {
Modal.confirm({
title: 'Confirm Removal',
content: 'Are you sure you want to remove this management record?',
okText: 'Yes',
cancelText: 'No',
onOk() {
setManagementHistory(prev => prev.filter(record => record.id !== id));
}
});
};
// Toggle management record status
const toggleManagementStatus = (id) => {
setManagementHistory(prev => prev.map(record =>
record.id === id
? { ...record, status: record.status === 'active' ? 'inactive' : 'active' }
: record
));
};
// Calculate summary statistics
const calculateSummary = () => {
const activeRecords = managementHistory.filter(record => record.status === 'active');
const blockedSlots = activeRecords.filter(record => record.action === 'block').length;
const priceChanges = activeRecords.filter(record => record.action === 'priceChange').length;
const holidayBlocks = activeRecords.filter(record => record.action === 'holiday').length;
// Count by closure type
const closureTypeCounts = {};
activeRecords.forEach(record => {
closureTypeCounts[record.closureType] = (closureTypeCounts[record.closureType] || 0) + 1;
});
return {
blockedSlots,
priceChanges,
holidayBlocks,
total: activeRecords.length,
closureTypeCounts
};
};
const summary = calculateSummary();
// Slot price management columns
const slotPriceColumns = [
{
title: 'Sl.No',
key: 'sno',
align: 'center',
width: 60,
render: (text, record, index) => index + 1,
},
{
title: 'Slot',
dataIndex: 'time',
key: 'time',
align: 'center',
render: (slot) => <strong>{slot}</strong>,
},
{
title: 'Full Slot Price (₹)',
key: 'fullSlotPrice',
align: 'center',
render: (record) => (
<InputNumber
value={slotPrices[record.slot]?.fullSlotPrice || 0}
min={0}
style={{ width: 120 }}
formatter={value => `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
parser={value => value.replace(/₹\s?|(,*)/g, '')}
onChange={value => handleFullSlotPriceChange(record.slot, value)}
placeholder="Full Slot Price"
/>
),
},
{
title: 'Individual Price (₹)',
key: 'individualPrice',
align: 'center',
render: (record) => (
<InputNumber
value={slotPrices[record.slot]?.individualPrice || 0}
min={0}
style={{ width: 120 }}
formatter={value => `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
parser={value => value.replace(/₹\s?|(,*)/g, '')}
onChange={value => handleIndividualPriceChange(record.slot, value)}
placeholder="Individual Price"
/>
),
},
];
const BasicActionType = [
{ label: 'Block', value: 'block', icon: <BlockOutlined /> },
{ label: 'Price Change', value: 'priceChange', icon: <DollarOutlined /> },
{ label: 'Holiday', value: 'holiday', icon: <FlagOutlined /> },
];
const PriceTypeChange = (e) => {
setPriceChangeType(e.target.value)
setNewPrice(null);
}
const SlotTypeChange = (e) => {
setSlotChangeType(e.target.value)
}
const navigatelist = () => {
navigate(`${subDirectory}setting/slot-management-list`)
}
return (
<div className="slot-management">
<div className="slot-management__header">
<div>
<h1 className="slot-management__header-title">
Slot Management
</h1>
</div>
{/* <div className="slot-management__header-link" onClick={navigatelist}>
View SlotDetails
</div> */}
</div>
<Form
form={form}
layout="vertical"
initialValues={{
priceChangeType: 'full',
SlotSelectionType: 'all',
}}
>
{/* Basic Configuration Section */}
<div className="slot-management__config-section">
<div className="slot-management__form-row">
<div className="slot-management__form-col">
<Form.Item
name="actionMode"
// label="Action Type"
rules={[{ required: true, message: 'Please select action type' }]}
>
<DropDowns
options={BasicActionType?.map((option) => ({
value: option.value,
label: option.label
}))}
label={<label class="required">Slot Action</label>}
className="field-DropDown"
// isOnchanges={
// editstate?.UOM || SelectedUom ? true : false
// }
onChangeFunction={handleActionModeChange}
// valueData={SelectedUom}
// disabled={formType == 'edit' ? true : false}
/>
</Form.Item>
</div>
{actionMode &&
<div className="slot-management__form-col">
<Form.Item
name="closureType"
// label="Closure Type"
rules={[{ required: true, message: 'Please select closure type' }]}
>
<DropDowns
options={types?.map((option) => ({
value: option.value,
label: option.label
}))}
label={<label className="required">Activity Type</label>}
className="field-DropDown"
isOnchanges={
closureType ? true : false
}
onChangeFunction={handleClosureTypeChange}
valueData={closureType}
// disabled={formType == 'edit' ? true : false}
/>
{/* <Select
placeholder={`-- Select ${getActionModeText()} Type --`}
onChange={handleClosureTypeChange}
allowClear
>
{types.map(type => (
<Option key={type.value} value={type.value}>
<Space>
{type.icon}
{type.label}
</Space>
</Option>
))}
</Select> */}
</Form.Item>
</div>
}
{closureType &&
<div className="slot-management__form-col">
<Form.Item
name="dateRange"
label="Date Range"
rules={[{ required: true, message: 'Please select date range' }]}
>
<RangePicker
style={{ width: '100%' }}
onChange={setDateRange}
format="DD-MMM-YYYY"
value={dateRange}
disabledDate={current => current && current < moment().startOf('day')}
/>
</Form.Item>
</div>
}
{/* <div style={{ marginLeft: 'auto' }}>
<Tooltip title="Configure slot management settings">
<InfoCircleOutlined style={{ color: '#1890ff', fontSize: 20 }} />
</Tooltip>
</div> */}
</div>
{/* Closure Specific Fields */}
<div className='seletcCategorylabel'>
{ShouldCategoryShow() &&
<Form.Item
name="category"
// label="Select Category"
rules={[{ required: true, message: 'Please select category' }]}
>
<DropDowns
options={CategoriesData?.map((option) => ({
value: option.ProdCat,
label: option.ProdCatName
}))}
label={<label className="required">Category</label>}
className="field-DropDown"
isOnchanges={
selectedCategory ? true : false
}
onChangeFunction={handleCategoryChange}
valueData={selectedCategory}
// disabled={formType == 'edit' ? true : false}
/>
</Form.Item>
}
{ShouldSubCategoryShow() && (
<Form.Item
name="subCategory"
rules={[{ required: true, message: 'Please select sub category' }]}
>
<DropDowns
options={SubCategoriesData?.map((option) => ({
value: option.ProdSubCat,
label: option.ProdSubCatName
}))}
label={<label className="required">Sub Category</label>}
className="field-DropDown"
isOnchanges={
selectedSubCategory ? true : false
}
onChangeFunction={handleSubCategoryChange}
valueData={selectedSubCategory}
// disabled={formType == 'edit' ? true : false}
/>
</Form.Item>
)}
{ShouldFacilityShow() &&
<Form.Item
name="facility"
// label="Select Facility"
rules={[{ required: true, message: 'Please select facility' }]}
>
<DropDowns
options={ProductList?.map((option) => ({
value: option.ProdId,
label: option.ProdName
}))}
label={<label className="required">Facility</label>}
className="field-DropDown"
isOnchanges={
selectedFacility ? true : false
}
onChangeFunction={handleFacilityChange}
valueData={selectedFacility}
// disabled={formType == 'edit' ? true : false}
/>
{/* <Select
placeholder="-- Select Facility --"
onChange={handleFacilityChange}
allowClear
showSearch
optionFilterProp="children"
filterOption={(input, option) =>
option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
>
{facilities.map(facility => (
<Option key={facility.id} value={facility.id}>
{facility.name} - {branches.find(b => b.id === facility.branchId)?.name}
({CategoriesData?.find(c => c.ProdCat === facility.ProdCat)?.ProdCatName} -
{SubCategoriesData?.find(s => s.ProdSubCat === facility.subCategoryId)?.ProdSubCatName})
</Option>
))}
</Select> */}
</Form.Item>
}
{showslotselection() &&
<div style={{ flex: '1 1 250px', minWidth: 220 }}>
<Form.Item
name="SlotSelectionType"
label="Slot Selection Type"
>
<Radio.Group
onChange={(e) => SlotTypeChange(e)}
value={SlotChangeType}
>
<Radio value="all">All Slots</Radio>
<Radio value="individual">Individual Slots</Radio>
</Radio.Group>
</Form.Item>
</div>
}
</div>
{/* {renderClosureSpecificFields()} */}
{/* Reason Input for Block Actions */}
{shouldShowReasonInput() && (
<Form.Item
label= {<label className="required">Reason for Block</label>}
rules={[{ required: true, message: 'Please provide a reason for the block' }]}
>
<TextArea
value={closureReason}
onChange={(e) => setClosureReason(e.target.value)}
placeholder="Enter the reason for this block (e.g., Maintenance, Repair, Special Event, etc.)"
rows={3}
showCount
maxLength={500}
/>
</Form.Item>
)}
</div>
{/* Day Selection Section for Time Slot Operations */}
{shouldShowDaySelection() && (
<div
style={{
marginBottom: 20,
padding: 24,
background: '#fff',
borderRadius: 8,
boxShadow: '0 2px 8px #f0f1f2',
border: '1px solid #f0f0f0'
}}
>
<div style={{ fontWeight: 600, fontSize: 18, marginBottom: 12 }}>
Day Selection
</div>
<Form.Item >
{/* label="Select Days for Action" required */}
<div
style={{
display: 'flex',
flexWrap: 'wrap',
padding: '16px',
border: '1px solid #d9d9d9',
borderRadius: '6px',
backgroundColor: '#fafafa'
}}
>
<div style={{
minWidth: 120,
padding: '4px 0'
}}>
<Checkbox
checked={selectAllDays}
onChange={(e) => handleSelectAllDays(e.target.checked)}
>
<strong>Select All Days</strong>
</Checkbox>
</div>
<div
style={{
display: 'flex',
flexWrap: 'wrap',
gap: '12px 0',
marginBottom: '12px'
}}
>
{Object.entries(selectedDays)?.map(([day, isSelected]) => (
<div
key={day}
style={{
minWidth: 120,
padding: '4px 0'
}}
>
<Checkbox
checked={isSelected}
onChange={() => handleDayChange(day)}
>
{day === 'saturday' || day === 'sunday' ? (
<strong style={{ color: '#ff4d4f' }}>
{day.charAt(0).toUpperCase() + day.slice(1)}
</strong>
) : (
day.charAt(0).toUpperCase() + day.slice(1)
)}
</Checkbox>
</div>
))}
</div>
{/* <div className="slot-management__day-selection-summary">
<strong>Selected Days:</strong> {getSelectedDaysText()}
</div> */}
</div>
</Form.Item>
{shouldShowTimeSlotSelection() && (
<div>
<div className="slot-management__section-title">
Time Slot Selection
</div>
<div style={{ marginBottom: '12px', display: 'flex', alignItems: 'center' }}>
<Button
type="dashed"
onClick={handleSelectAllSlots}
size="small"
>
{selectedSlots.length === Slots?.length ? 'Deselect All' : 'Select All'} Slots
</Button>
<span style={{ marginLeft: '12px', color: '#666' }}>
{selectedSlots.length} slots selected
</span>
</div>
<div className="slot-management__slot-grid">
{Slots?.map(slot => (
<div
key={slot?.ProdInwardDtlId}
onClick={() => handleSlotSelection(slot?.ProdInwardDtlId)}
className={`slot-management__slot-item ${selectedSlots.includes(slot?.ProdInwardDtlId) ? 'slot-management__slot-item--selected' : ''
}`}
>
{to12Hour(slot?.AvailableFrom)} - {to12Hour(slot?.AvailableTo)}
</div>
))}
</div>
</div>
)}
</div>
)}
{/* Time Slot Selection for Block and Price Change Operations */}
{/* Price Change Configuration */}
{(actionMode === 'priceChange' && selectedFacility) && (
<div className="slot-management__config-section">
<div className="slot-management__section-title">
Price Change Configuration
</div>
<div className="slot-management__price-config-row">
{/* <div style={{ flex: '1 1 250px', minWidth: 220 }}>
<Form.Item
name="priceChangeType"
label="Price Change Type"
>
<Radio.Group
onChange={(e) => PriceTypeChange(e) }
value={priceChangeType}
>
<Radio value="full">Full Slot Price</Radio>
<Radio value="individual">Individual Slot Price</Radio>
</Radio.Group>
</Form.Item>
</div> */}
<div className="slot-management__form-col">
<div className='IndividualInput'>
<Form.Item
// label={"New Price (₹)" }
rules={[{ required: true, message: 'Please Give Price' }]}
>
<InputField
value={newPrice}
onChange={(e) => setNewPrice(e.target.value)}
min={0}
style={{ width: '100%' }}
placeholder={"Enter new price"}
inputMode="decimal"
onInput={(e) => {
let cleanedValue = e.target.value.replace(
/[^0-9.]/g,
''
); // Remove invalid characters
const parts = cleanedValue.split('.');
if (cleanedValue.startsWith('.')) {
cleanedValue = '0' + cleanedValue;
}
e.target.value =
parts.length > 2
? `${parts[0]}.${parts.slice(1).join('')}`
: cleanedValue;
}}
/>
</Form.Item>
<Form.Item label="Individual Pricing">
<Checkbox
checked={individualPricing}
onChange={(e) => setIndividualPricing(e.target.checked)}
>
Enable Individual Pricing
</Checkbox>
</Form.Item>
</div>
</div>
{individualPricing && (
<Form.Item
name="individualDefaultPrice"
// label="Individual Default Price (₹)"
>
<InputField
min={0}
// className="sports-master__input-number"
formatter={value => `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
parser={value => value.replace(/₹\s?|(,*)/g, '')}
onChange={(e) => setIndividualDefaultPrice(e.target.value)}
value={individualDefaultPrice}
placeholder={"Enter individual price"}
inputMode="decimal"
onInput={(e) => {
let cleanedValue = e.target.value.replace(
/[^0-9.]/g,
''
); // Remove invalid characters
const parts = cleanedValue.split('.');
if (cleanedValue.startsWith('.')) {
cleanedValue = '0' + cleanedValue;
}
e.target.value =
parts.length > 2
? `${parts[0]}.${parts.slice(1).join('')}`
: cleanedValue;
}}
/>
</Form.Item>
)}
{SlotChangeType == 'individual' ?
<Button
className="slot-management__apply-price-btn"
onClick={() => {
// Apply newPrice to all selected slots
const updated = { ...slotPrices };
selectedSlots.forEach(slot => {
updated[slot] = {
...updated[slot],
fullSlotPrice: Number(newPrice) || 0,
individualPrice:
individualDefaultPrice !== undefined && individualDefaultPrice !== null && individualDefaultPrice !== ''
? Number(individualDefaultPrice)
: updated[slot]?.individualPrice || 0
};
});
setSlotPrices(updated);
}}
disabled={!newPrice}
>
Apply Price
</Button>
: ''
}
</div>
{/* Individual Slot Price Table */}
{shouldShowIndividualSlotPriceTable() && (
<div style={{ marginTop: '16px' }}>
<Divider orientation="left">
Individual Slot Prices ({selectedSlots.length} slots selected)
</Divider>
{/* <div style={{ marginBottom: '12px' }}>
<Button
type="dashed"
onClick={applyBasePriceToAllSlots}
icon={<EditOutlined />}
size="small"
>
Apply Base Price to All Selected Slots
</Button>
<span style={{ marginLeft: '12px', color: '#666', fontSize: '12px' }}>
Base Price: {newPrice || 0}
</span>
</div> */}
{/* <Table
columns={individualSlotPriceColumns}
dataSource={selectedSlots.map(slot => ({ slot }))}
pagination={{ pageSize: 10 }}
rowKey="slot"
size="small"
scroll={{ x: 600 }}
/> */}
<Table
columns={slotPriceColumns}
// dataSource={selectedSlots.map(slot => ({ slot }))}
dataSource={selectedSlots.map(slotId => {
const slotObj = Slots.find(s => s.ProdInwardDtlId === slotId);
return {
slot: slotId,
time: slotObj ? `${to12Hour(slotObj.AvailableFrom)} - ${to12Hour(slotObj.AvailableTo)}` : ''
};
})}
pagination={{ pageSize: 10 }}
rowKey="slot"
size="small"
scroll={{ x: 600 }}
/>
</div>
)}
</div>
)}
{/* Action Summary */}
{(actionMode && closureType && dateRange?.length > 0) && (
<Card
title="Action Summary"
style={{ marginBottom: 20 }}
type="inner"
>
<Row gutter={16}>
<Col xs={24} md={4}>
<div style={{ textAlign: 'center' }}>
<div style={{ fontSize: '12px', color: '#666' }}>Slot Action</div>
<div style={{ fontSize: '14px', fontWeight: 'bold', color: '#1890ff' }}>
{getActionModeText()}
</div>
</div>
</Col>
<Col xs={24} md={4}>
<div style={{ textAlign: 'center' }}>
<div style={{ fontSize: '12px', color: '#666' }}>Activity Type</div>
<div style={{ fontSize: '14px', fontWeight: 'bold', color: '#52c41a' }}>
{getClosureTypeText()}
</div>
</div>
</Col>
<Col xs={24} md={4}>
<div style={{ textAlign: 'center' }}>
<div style={{ fontSize: '12px', color: '#666' }}>Category</div>
<div style={{ fontSize: '14px', fontWeight: 'bold', color: '#fa8c16' }}>
{selectedCategory ? CategoriesData?.find(c => c.ProdCat === selectedCategory)?.ProdCatName : '-'}
</div>
</div>
</Col>
<Col xs={24} md={4}>
<div style={{ textAlign: 'center' }}>
<div style={{ fontSize: '12px', color: '#666' }}>Sub Category</div>
<div style={{ fontSize: '14px', fontWeight: 'bold', color: '#eb2f96' }}>
{selectedSubCategory ? SubCategoriesData?.find(s => s.ProdSubCat === selectedSubCategory)?.ProdSubCatName : '-'}
</div>
</div>
</Col>
<Col xs={24} md={4}>
<div style={{ textAlign: 'center' }}>
<div style={{ fontSize: '12px', color: '#666' }}>Date Range</div>
<div style={{ fontSize: '14px', fontWeight: 'bold', color: '#722ed1' }}>
{dateRange[0]?.format('DD-MMM-YY')} - {dateRange[1]?.format('DD-MMM-YY')}
</div>
</div>
</Col>
</Row>
{shouldShowDaySelection() && (
<div style={{ marginTop: '26px', textAlign: 'left' }}>
<div style={{ fontSize: '14px', color: '#666' }}>
<strong>Days:</strong> {getSelectedDaysText()}
</div>
{shouldShowTimeSlotSelection() && (
<div style={{ fontSize: '14px', color: '#666' }}>
<strong>Time Slots:</strong> {selectedSlots.length} selected
</div>
)}
</div>
)}
{actionMode === 'block' && closureReason && (
<div style={{ marginTop: '12px', textAlign: 'left' }}>
<div style={{ fontSize: '14px', color: '#666' }}>
<strong>Reason:</strong> {closureReason}
</div>
</div>
)}
{/* {actionMode === 'priceChange' && (
<div style={{ marginTop: '12px', textAlign: 'left' }}>
<div style={{ fontSize: '14px', color: '#666' }}>
<strong>Price Type:</strong> {priceChangeType === 'individual' ? 'Individual Slots' : 'Full Day'}
</div>
{priceChangeType === 'full' && (
<div style={{ fontSize: '14px', color: '#666' }}>
<strong>Price:</strong> {newPrice}
{weekendPremium && (
<span style={{ marginLeft: '12px' }}>
<strong>Weekend Price:</strong> {weekendPrice}
</span>
)}
</div>
)}
{priceChangeType === 'individual' && (
<div style={{ fontSize: '14px', color: '#666' }}>
<strong>Individual Prices Set for:</strong> {selectedSlots.length} slots
</div>
)}
</div>
)} */}
</Card>
)}
{/* Apply Action Button */}
<Form.Item>
<Buttons
buttonText={`Apply ${getActionModeText()}`}
disabled={!actionMode || !closureType || !dateRange || dateRange.length === 0}
handleSubmit={() => applyManagementAction()}
color="901D77"
icon={<ArrowRightOutlined />}
/>
{/* <Buttons
type="primary"
onClick={applyManagementAction}
size="large"
icon={< />}
disabled={!actionMode || !closureType || !dateRange || dateRange.length === 0}
style={{
height: '50px',
fontSize: '16px',
padding: '0 30px'
}}
>
Apply {getActionModeText()}
</Buttons> */}
</Form.Item>
</Form>
</div>
);
};
export default SlotManagementForm;