Implement dynamic header and table color functionality

This commit is contained in:
Srinath 2026-03-03 19:53:48 +05:30
parent ac975f6c69
commit 0f223636c5
1 changed files with 503 additions and 22 deletions

View File

@ -6,7 +6,7 @@ import React, {
forwardRef,
useImperativeHandle,
} from 'react';
import { useDispatch } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import {
Form,
Checkbox,
@ -69,12 +69,34 @@ import {
changeSelectedCredit,
changeSelectedTax,
changeSelectedHeaderColor,
changeSelectedTableHeaderColor,
changeSelectedTableBodyColor,
changeSelectedFooterColor,
changeRowType,
GlobalSelectedPrintHeaderColor,
GlobalSelectedPrintHeaderFontColor,
GlobalSelectedTableHeaderColor,
GlobalSelectedTableHeaderFontColor,
GlobalSelectedTableBodyColor,
GlobalSelectedTableBodyFontColor,
GlobalSelectedFooterColor,
GlobalSelectedFooterFontColor,
GlobalRowType,
changeSelectedNetAmountColor,
GlobalSelectedNetAmountColor,
GlobalSelectedNetAmountFontColor,
changeSelectedHeaderColour,
changeSelectedTableHeaderColour,
changeSelectedTableBodyColour,
changeSelectedFooterColour,
changeSelectedNetAmountColour,
} from '../../../../Features/ThemeChange/ThemeChange';
import '../../../../Styles/BookingScreen/Components/SelectionComponent/SelectionComponent.scss';
import { useAuth } from '../../../../AuthContext.jsx';
import { InputField } from '../../../../Components/Forms/InputField.jsx';
import ColorPicker from '../../../../Components/Forms/ColorPicker.jsx';
import { FaChevronDown, FaChevronUp } from 'react-icons/fa';
import ColorsSelected from '../UtillComponents/ColorsSelected.jsx';
const subDirectory = import.meta.env.BASE_URL;
@ -92,6 +114,13 @@ const SelectionComponent = forwardRef((props, ref) => {
}
};
// helper to read a color object from the newer `PrintColors` array
const getColor = (printColors, key) => {
if (!printColors || !Array.isArray(printColors)) return {};
const entry = printColors.find((c) => c && c[key]);
return entry ? entry[key] : {};
};
useImperativeHandle(ref, () => ({
doSomething,
}));
@ -138,6 +167,10 @@ const SelectionComponent = forwardRef((props, ref) => {
const [isTermscondChecking, setisTermscondChecking] = useState(false);
const [isSignatureChecking, setisSignatureChecking] = useState(false);
const [isheadercolour, setisheadercolour] = useState(false);
const [isTableHeaderColour, setisTableHeaderColour] = useState(false);
const [isTableBodyColour, setisTableBodyColour] = useState(false);
const [isFooterColour, setisFooterColour] = useState(false);
const [isNetAmountColour, setisNetAmountColour] = useState(false);
const [showMore, setShowMore] = useState(false);
const visibleConfigs = showMore ? styleData : styleData.slice(0, 4);
const defaultImage =
@ -150,6 +183,25 @@ const SelectionComponent = forwardRef((props, ref) => {
const [previewTitle, setPreviewTitle] = useState('');
const [Notes, setNote] = useState('');
const [BillName, setBillName] = useState('');
const [openColorModal, setOpenColorModal] = useState(false);
const [bgColor, setBgColor] = useState("#ffffff");
const [fontColor, setFontColor] = useState("#000000");
const [selectedHeaderFontColor, setSelectedHeaderFontColor] = useState("#ffffff");
const [colorModalTarget, setColorModalTarget] = useState(null); // 'header' | 'tableHeader' | 'tableBody' | 'footer'
const rowType = useSelector(GlobalRowType);
// derived from global store
const headerColor = useSelector(GlobalSelectedPrintHeaderColor);
const headerFontColor = useSelector(GlobalSelectedPrintHeaderFontColor);
const tableHeaderColor = useSelector(GlobalSelectedTableHeaderColor);
const tableHeaderFontColor = useSelector(GlobalSelectedTableHeaderFontColor);
const tableBodyColor = useSelector(GlobalSelectedTableBodyColor);
const tableBodyFontColor = useSelector(GlobalSelectedTableBodyFontColor);
const footerColor = useSelector(GlobalSelectedFooterColor);
const footerFontColor = useSelector(GlobalSelectedFooterFontColor);
const netAmountColor = useSelector(GlobalSelectedNetAmountColor);
const netAmountFontColor = useSelector(GlobalSelectedNetAmountFontColor);
const items = [
{
@ -358,6 +410,66 @@ const SelectionComponent = forwardRef((props, ref) => {
);
setisSignatureChecking(checkedList?.includes(CheckSignature?.ConfigId));
setisheadercolour(checkedList?.includes(headerColour?.ConfigId));
setisTableHeaderColour(
checkedList?.includes(
sizeCheckList.find((item) => item.ConfigName === 'Table Header Colour')
?.ConfigId
)
);
setisTableBodyColour(
checkedList?.includes(
sizeCheckList.find((item) => item.ConfigName === 'Table Body Colour')
?.ConfigId
)
);
setisFooterColour(
checkedList?.includes(
sizeCheckList.find((item) => item.ConfigName === 'Footer Colour')
?.ConfigId
)
);
setisNetAmountColour(
checkedList?.includes(
sizeCheckList.find((item) => item.ConfigName === 'Net Amount Colour')
?.ConfigId
)
);
// update global toggle states as well so print components can read them
if (checkedList?.includes(headerColour?.ConfigId)) {
dispatch(changeSelectedHeaderColour(true));
} else {
dispatch(changeSelectedHeaderColour(false));
dispatch(changeSelectedHeaderColor({ background: '#ffffff', font: '#000000' }));
}
const tblHdr2 = sizeCheckList.find((item) => item.ConfigName === 'Table Header Colour')?.ConfigId;
if (checkedList?.includes(tblHdr2)) {
dispatch(changeSelectedTableHeaderColour(true));
} else {
dispatch(changeSelectedTableHeaderColour(false));
dispatch(changeSelectedTableHeaderColor({ background: '', font: '' }));
}
const tblBody2 = sizeCheckList.find((item) => item.ConfigName === 'Table Body Colour')?.ConfigId;
if (checkedList?.includes(tblBody2)) {
dispatch(changeSelectedTableBodyColour(true));
} else {
dispatch(changeSelectedTableBodyColour(false));
dispatch(changeSelectedTableBodyColor({ background: '', font: '' }));
}
const ftr2 = sizeCheckList.find((item) => item.ConfigName === 'Footer Colour')?.ConfigId;
if (checkedList?.includes(ftr2)) {
dispatch(changeSelectedFooterColour(true));
} else {
dispatch(changeSelectedFooterColour(false));
dispatch(changeSelectedFooterColor({ background: '', font: '' }));
}
const net2 = sizeCheckList.find((item) => item.ConfigName === 'Net Amount Colour')?.ConfigId;
if (checkedList?.includes(net2)) {
dispatch(changeSelectedNetAmountColour(true));
} else {
dispatch(changeSelectedNetAmountColour(false));
dispatch(changeSelectedNetAmountColor({ background: '', font: '' }));
}
};
const fetchInitialData = async () => {
@ -422,7 +534,30 @@ const SelectionComponent = forwardRef((props, ref) => {
Signature: item.Signature,
PrintType: item.PrintType,
PrintHdrName: item.PrintHdrName,
PrintHdrColor: item.PrintHdrColor,
// legacy fields are filled from the array so older code still works
PrintHdrColor:
item.PrintHdrColor || getColor(item.PrintColors, 'headerColor')?.background,
PrintHdrFontColor:
item.PrintHdrFontColor || getColor(item.PrintColors, 'headerColor')?.font,
TableHeaderColor:
item.TableHeaderColor || getColor(item.PrintColors, 'tableHeaderColor')?.background,
TableHeaderFontColor:
item.TableHeaderFontColor || getColor(item.PrintColors, 'tableHeaderColor')?.font,
TableBodyColor:
item.TableBodyColor || getColor(item.PrintColors, 'tableBodyColor')?.background,
TableBodyFontColor:
item.TableBodyFontColor || getColor(item.PrintColors, 'tableBodyColor')?.font,
footerColor:
item.footerColor || getColor(item.PrintColors, 'footerColor')?.background,
footerFontColor:
item.footerFontColor || getColor(item.PrintColors, 'footerColor')?.font,
netAmountColor:
item.netAmountColor || getColor(item.PrintColors, 'netAmtColor')?.background,
netAmountFontColor:
item.netAmountFontColor || getColor(item.PrintColors, 'netAmtColor')?.font,
rowType:
item.rowType || getColor(item.PrintColors, 'tableBodyColor')?.rowType || 'odd',
PrintColors: item.PrintColors,
Notes: item?.Notes,
PageSize: item?.PageSize?.trim() || '3inch',
PrintTypeId: item?.PrintTypeId,
@ -474,6 +609,7 @@ const SelectionComponent = forwardRef((props, ref) => {
OptionDetails,
PrintTypeId,
PrintHdrColor,
PrintColors
} = row;
if (index >= 4) setShowMore(true);
if (index <= 4) setShowMore(false);
@ -487,7 +623,11 @@ const SelectionComponent = forwardRef((props, ref) => {
setNote(Notes);
setBillName(PrintHdrName);
setEdit(true);
setSelectedColour(PrintHdrColor);
// load existing header color, falling back to array
setSelectedColour(
PrintHdrColor || getColor(PrintColors, 'headerColor')?.background || '#000000'
);
// setSelectedHeaderFontColor(PrintHdrFontColor);
setSelectedStyleId(StyleId);
if (Signature) {
@ -538,6 +678,44 @@ const SelectionComponent = forwardRef((props, ref) => {
dispatch(changeNotes(helperFunction('Notes')));
dispatch(changeSignatureImage(Signature));
dispatch(changeBillName(PrintHdrName));
// derive color objects; prefer the legacy fields but fall back to the PrintColors array
const hdr = getColor(PrintColors, 'headerColor');
const tblHdr = getColor(PrintColors, 'tableHeaderColor');
const tblBody = getColor(PrintColors, 'tableBodyColor');
const ftr = getColor(PrintColors, 'footerColor');
const net = getColor(PrintColors, 'netAmtColor');
dispatch(
changeSelectedHeaderColor({
background: PrintHdrColor || hdr.background,
font: hdr.font,
})
);
dispatch(
changeSelectedTableHeaderColor({
background: tblHdr.background || row.TableHeaderColor,
font: tblHdr.font || row.TableHeaderFontColor,
})
);
dispatch(
changeSelectedTableBodyColor({
background: tblBody.background || row.TableBodyColor,
font: tblBody.font || row.TableBodyFontColor,
})
);
dispatch(changeRowType(tblBody.rowType || row.rowType || 'odd'));
dispatch(
changeSelectedFooterColor({
background: ftr.background || row.footerColor,
font: ftr.font || row.footerFontColor,
})
);
dispatch(
changeSelectedNetAmountColor({
background: net.background || row.netAmountColor,
font: net.font || row.netAmountFontColor,
})
);
dispatch(
changeSelectedprintStyle({
ConfigId: StyleId,
@ -613,6 +791,8 @@ const SelectionComponent = forwardRef((props, ref) => {
setSelectedStyleName(
styleData.find((item) => item.ConfigId === e)?.ConfigName
);
// reset rowType when selecting a new style
dispatch(changeRowType('odd'));
dispatch(
changeSelectedprintStyle({
ConfigId: e,
@ -733,13 +913,42 @@ const SelectionComponent = forwardRef((props, ref) => {
)
? dispatch(changeNotes(true))
: dispatch(changeNotes(false));
checkedValues?.includes(
sizeCheckList.find((item) => item.ConfigName === 'Header Colour')
?.ConfigId
)
? setisheadercolour(true)
: setisheadercolour(false);
const hdrId = sizeCheckList.find((item) => item.ConfigName === 'Header Colour')?.ConfigId;
if (checkedValues?.includes(hdrId)) {
dispatch(changeSelectedHeaderColour(true));
} else {
dispatch(changeSelectedHeaderColour(false));
// reset colours when option disabled
dispatch(changeSelectedHeaderColor({ background: '#ffffff', font: '#000000' }));
}
const tblHdrId = sizeCheckList.find((item) => item.ConfigName === 'Table Header Colour')?.ConfigId;
if (checkedValues?.includes(tblHdrId)) {
dispatch(changeSelectedTableHeaderColour(true));
} else {
dispatch(changeSelectedTableHeaderColour(false));
dispatch(changeSelectedTableHeaderColor({ background: '', font: '' }));
}
const tblBodyId = sizeCheckList.find((item) => item.ConfigName === 'Table Body Colour')?.ConfigId;
if (checkedValues?.includes(tblBodyId)) {
dispatch(changeSelectedTableBodyColour(true));
} else {
dispatch(changeSelectedTableBodyColour(false));
dispatch(changeSelectedTableBodyColor({ background: '', font: '' }));
}
const ftrId = sizeCheckList.find((item) => item.ConfigName === 'Footer Colour')?.ConfigId;
if (checkedValues?.includes(ftrId)) {
dispatch(changeSelectedFooterColour(true));
} else {
dispatch(changeSelectedFooterColour(false));
dispatch(changeSelectedFooterColor({ background: '', font: '' }));
}
const netId = sizeCheckList.find((item) => item.ConfigName === 'Net Amount Colour')?.ConfigId;
if (checkedValues?.includes(netId)) {
dispatch(changeSelectedNetAmountColour(true));
} else {
dispatch(changeSelectedNetAmountColour(false));
dispatch(changeSelectedNetAmountColor({ background: '', font: '' }));
}
};
const handleOptions = async () => {
@ -790,13 +999,28 @@ const SelectionComponent = forwardRef((props, ref) => {
PrintHdrName: BillName,
PrintType: PrintType ? 'S' : 'C',
PrintTypeId: segmentValue,
PrintHdrColor: getConcatenatedArray().some(
// Header color (background) and font
PrintHdrColor: getConcatenatedArray()?.some(
(e) => e?.ConfigName?.toLowerCase() == 'header colour'
)
? selectedColour
? (headerColor || selectedColour)
: null,
PrintHdrFontColor: getConcatenatedArray()?.some(
(e) => e?.ConfigName?.toLowerCase() == 'header colour'
)
? (headerFontColor || selectedHeaderFontColor)
: null,
// Table header / body / footer / net amount colors
TableHeaderColor: tableHeaderColor || null,
TableHeaderFontColor: tableHeaderFontColor || null,
TableBodyColor: tableBodyColor || null,
TableBodyFontColor: tableBodyFontColor || null,
footerColor: footerColor || null,
footerFontColor: footerFontColor || null,
netAmountColor: netAmountColor || null,
netAmountFontColor: netAmountFontColor || null,
rowType: rowType || 'odd',
});
// Validation
if (isSignatureChecking && fileList?.length === 0)
return message.warning('Please upload Signature');
@ -865,7 +1089,39 @@ const SelectionComponent = forwardRef((props, ref) => {
StyleId: item.StyleId,
SetasDefault: item.SetasDefault,
SetasDefaultInvoice: item.SetasDefaultInvoice,
PrintHdrColor: item.PrintHdrColor,
PrintHdrColor: [
{
headerColor: {
background: item.PrintHdrColor,
font: item.PrintHdrFontColor,
},
},
{
tableHeaderColor: {
background: item?.TableHeaderColor,
font: item?.TableHeaderFontColor,
},
},
{
tableBodyColor: {
background: item?.TableBodyColor,
font: item?.TableBodyFontColor,
rowType: rowType,
},
},
{
footerColor: {
background: item?.footerColor,
font: item?.footerFontColor,
},
},
{
netAmtColor: {
background: item?.netAmountColor,
font: item?.netAmountFontColor,
},
},
],
PrintTypeId: item?.PrintTypeId,
SizeDetails: item.OptionDetails.map((item) => ({
OptionId: item.ConfigId,
@ -1045,6 +1301,37 @@ const SelectionComponent = forwardRef((props, ref) => {
// }
const handleColorSave = (colors) => {
// common handler called when ColorsSelected modal returns values
console.log("Received from modal:", colors);
switch (colorModalTarget) {
case 'header':
dispatch(changeSelectedHeaderColor({ background: colors.background, font: colors.font }));
setSelectedColour(colors.background);
setSelectedHeaderFontColor(colors.font);
break;
case 'tableHeader':
dispatch(changeSelectedTableHeaderColor({ background: colors.background, font: colors.font }));
break;
case 'tableBody':
dispatch(changeSelectedTableBodyColor({ background: colors.background, font: colors.font }));
break;
case 'footer':
dispatch(changeSelectedFooterColor({ background: colors.background, font: colors.font }));
break;
case 'netAmount':
dispatch(changeSelectedNetAmountColor({ background: colors.background, font: colors.font }));
break;
default:
dispatch(changeSelectedHeaderColor({ background: colors.background, font: colors.font }));
break;
}
// update local previews as well (not strictly necessary if UI reads from store)
setBgColor(colors.background);
setFontColor(colors.font);
setOpenColorModal(false);
setColorModalTarget(null);
};
return (
<>
<Messages
@ -1387,21 +1674,215 @@ const SelectionComponent = forwardRef((props, ref) => {
</div>
)}
{isheadercolour && (
// {isheadercolour && SelectedStyleName == 'TaxInvoice' && (
<div style={{ marginTop: '1rem' }}>
<Title level={5} style={{ marginBottom: 12 }}>
Header Color
</Title>
<ColorPicker
value={selectedColour}
onChange={(color) => {
setSelectedColour(color);
dispatch(changeSelectedHeaderColor(color));
<button
onClick={() => {
setColorModalTarget('header');
setOpenColorModal(true);
}}
placeholder="Select color"
/>
className="handleAddBTN"
>
<PlusOutlined /> select color
<span
style={{
display: 'inline-block',
width: 16,
height: 16,
backgroundColor: headerColor || '#ffffff',
border: '1px solid #000',
marginLeft: 6,
verticalAlign: 'middle',
}}
/>
<span
style={{
display: 'inline-block', width: 16, height: 16, backgroundColor: headerFontColor,
border: '1px solid #000', marginLeft: 6, verticalAlign: 'middle',
}}
/>
</button>
</div>
)}
{isTableHeaderColour && (
<div style={{ marginTop: '1rem' }}>
<Title level={5} style={{ marginBottom: 12 }}>
Table Header Colour
</Title>
<button
onClick={() => {
setColorModalTarget('tableHeader');
setOpenColorModal(true);
}}
className="handleAddBTN"
>
<PlusOutlined /> select color
<span
style={{
display: 'inline-block',
width: 16,
height: 16,
backgroundColor: tableHeaderColor,
border: '1px solid #000',
marginLeft: 6,
verticalAlign: 'middle',
}}
/>
<span
style={{
display: 'inline-block', width: 16, height: 16, backgroundColor: tableHeaderFontColor,
border: '1px solid #000', marginLeft: 6, verticalAlign: 'middle',
}}
/>
</button>
</div>
)}
{isTableBodyColour && (
<div style={{ marginTop: '1rem' }}>
<Title level={5} style={{ marginBottom: 12 }}>
Table Body Colour
</Title>
<button
onClick={() => {
setColorModalTarget('tableBody');
setOpenColorModal(true);
}}
className="handleAddBTN"
>
<PlusOutlined /> select color
<span
style={{
display: 'inline-block',
width: 16,
height: 16,
backgroundColor: tableBodyColor,
border: '1px solid #000',
marginLeft: 6,
verticalAlign: 'middle',
}}
/>
<span
style={{
display: 'inline-block', width: 16, height: 16, backgroundColor: tableBodyFontColor,
border: '1px solid #000', marginLeft: 6, verticalAlign: 'middle',
}}
/>
</button>
<span style={{ margin: '0px 10px' }} className="handleAddBTN">
<Switch
checked={rowType === "even"}
checkedChildren="Even"
unCheckedChildren="Odd"
onChange={(checked) => dispatch(changeRowType(checked ? "even" : "odd"))}
/>
</span>
</div>
)}
{isFooterColour && (
<div style={{ marginTop: '1rem' }}>
<Title level={5} style={{ marginBottom: 12 }}>
Footer Colour
</Title>
<button
onClick={() => {
setColorModalTarget('footer');
setOpenColorModal(true);
}}
className="handleAddBTN"
>
<PlusOutlined /> select color
<span
style={{
display: 'inline-block',
width: 16,
height: 16,
backgroundColor: footerColor || '#ffffff',
border: '1px solid #000',
marginLeft: 6,
verticalAlign: 'middle',
}}
/>
<span
style={{
display: 'inline-block', width: 16, height: 16, backgroundColor: footerFontColor || '#000000',
border: '1px solid #000', marginLeft: 6, verticalAlign: 'middle',
}}
/>
</button>
</div>
)}
{isNetAmountColour && (
<div style={{ marginTop: '1rem' }}>
<Title level={5} style={{ marginBottom: 12 }}>
Net Amount Colour
</Title>
<button
onClick={() => {
setColorModalTarget('netAmount');
setOpenColorModal(true);
}}
className="handleAddBTN"
>
<PlusOutlined /> select color
<span
style={{
display: 'inline-block',
width: 16,
height: 16,
backgroundColor: netAmountColor || '#ffffff',
border: '1px solid #000',
marginLeft: 6,
verticalAlign: 'middle',
}}
/>
<span
style={{
display: 'inline-block', width: 16, height: 16, backgroundColor: netAmountFontColor || '#000000',
border: '1px solid #000', marginLeft: 6, verticalAlign: 'middle',
}}
/>
</button>
</div>
)}
{openColorModal && (
<ColorsSelected
open={openColorModal}
onClose={() => setOpenColorModal(false)}
onSave={handleColorSave}
initial={{
background:
colorModalTarget === 'header'
? headerColor
: colorModalTarget === 'tableHeader'
? tableHeaderColor
: colorModalTarget === 'tableBody'
? tableBodyColor
: colorModalTarget === 'footer'
? footerColor
: colorModalTarget === 'netAmount'
? netAmountColor
: bgColor,
font:
colorModalTarget === 'header'
? headerFontColor
: colorModalTarget === 'tableHeader'
? tableHeaderFontColor
: colorModalTarget === 'tableBody'
? tableBodyFontColor
: colorModalTarget === 'footer'
? footerFontColor
: colorModalTarget === 'netAmount'
? netAmountFontColor
: fontColor,
}}
/>
)}
{isTermscondChecking && (
<div style={{ marginTop: '1rem' }}>
<Title level={5} style={{ marginBottom: 12 }}>