Skip to content
Merged
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
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@
"leaflet": "^1.9.4",
"leaflet.heat": "^0.2.0",
"leaflet.markercluster": "^1.5.3",
"lodash": "^4.17.23",
"libphonenumber-js": "^1.12.31",
"lodash": "^4.17.21",
"lucide-react": "^0.484.0",
"micromatch": "^4.0.8",
"moment": "^2.30.1",
Expand Down
4 changes: 3 additions & 1 deletion src/components/Auth/PermissionWatcher.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState, useRef } from 'react';
import { useEffect, useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import axios from 'axios';
import { ENDPOINTS } from '~/utils/URL';
Expand Down Expand Up @@ -82,6 +82,8 @@ function PermissionWatcher() {
if (!isAuthenticated || !flagReady) return;
if (!userProfile) return;
if (isInitialLogin) return;
// Don't trigger when permissions haven't been loaded yet (e.g. on /bmdashboard/login before profile fetch)
if (userProfile?.permissions === undefined || userProfile?.permissions === null) return;

const permissionsChangedMidSession =
isAcknowledged === false && !forceLogoutAt && initialAcknowledgedState !== false;
Expand Down
118 changes: 103 additions & 15 deletions src/components/BMDashboard/AddMaterial/AddMaterial.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
ModalFooter,
} from 'reactstrap';
import PhoneInput from 'react-phone-input-2';
import { parsePhoneNumberFromString } from 'libphonenumber-js/max';
import { toast } from 'react-toastify';
import { useDispatch, useSelector } from 'react-redux';
import Joi from 'joi-browser';
Expand Down Expand Up @@ -47,6 +48,7 @@ export default function AddMaterialForm() {
const [formData, setFormData] = useState(initialFormState);
const [areaCode, setAreaCode] = useState('1');
const [phoneNumber, setPhoneNumber] = useState('');
const [phoneValid, setPhoneValid] = useState(true);
const [uploadedFiles, setUploadedFiles] = useState([]); // log here for correct state snapshot (will show each render)
const [errors, setErrors] = useState({});
const history = useHistory();
Expand All @@ -59,6 +61,7 @@ export default function AddMaterialForm() {
const [showTextbox, setShowTextbox] = useState(false);
const [selectedUnit, setSelectedUnit] = useState('');
const [newUnit, setNewUnit] = useState('');
const [dateError, setDateError] = useState(null);
const units = useSelector(state => state.bmInvUnits.list);
// console.log(materialTypes);
// console.log(units)
Expand Down Expand Up @@ -139,10 +142,27 @@ export default function AddMaterialForm() {
};

const handleInputChange = (name, value) => {
setFormData(prevData => ({
...prevData,
[name]: value,
}));
if (name === 'purchaseDate') {
const today = new Date().toLocaleDateString('en-CA');
if (value && value > today) {
setDateError("Purchase date should be equal or earlier to today's date");
setFormData(prevData => ({
...prevData,
[name]: '',
}));
} else {
setDateError(null);
setFormData(prevData => ({
...prevData,
[name]: value,
}));
}
} else {
setFormData(prevData => ({
...prevData,
[name]: value,
}));
}
};

const { unitPrice, quantity, taxes, shippingFee } = formData;
Expand All @@ -154,16 +174,73 @@ export default function AddMaterialForm() {
const totalTax = calculateTotalTax(Number(taxes), totalPrice);
const totalPriceWithShipping = (totalPrice + totalTax + Number(shippingFee)).toFixed(2);

const phoneChange = (name, phone) => {
const phoneChange = (name, phone, countryData) => {
const dialCode = countryData.dialCode;
const countryCode = countryData.countryCode.toUpperCase();

// Get the national number (remove dial code from beginning)
let nationalNumber = '';
if (phone && phone.startsWith(dialCode)) {
nationalNumber = phone.slice(dialCode.length);
} else if (phone) {
nationalNumber = phone;
}

// Check if country changed (dial code in formData is different from current)
const previousDialCode = formData.areaCode ? formData.areaCode.replace('+', '') : '1';
const countryChanged = previousDialCode !== dialCode;

// If country changed, reset to just the dial code
if (countryChanged && formData.phoneNumber) {
setFormData(prevData => ({
...prevData,
[name]: dialCode,
areaCode: `+${dialCode}`,
}));
setPhoneValid(true);
return;
}

setFormData(prevData => ({
...prevData,
[name]: phone,
areaCode: `+${dialCode}`,
}));

// If no national number entered, consider it valid (optional field)
if (!nationalNumber) {
setPhoneValid(true);
return;
}

// Validate phone number
try {
const fullNumber = `+${dialCode}${nationalNumber}`;
const phoneNumberObj = parsePhoneNumberFromString(fullNumber, countryCode);

if (phoneNumberObj) {
const isValidFormat = phoneNumberObj.isValid();
const numberType = phoneNumberObj.getType();

// Number must be valid AND must have a recognized type (MOBILE, FIXED_LINE, etc.)
const hasValidType = numberType !== undefined;
setPhoneValid(isValidFormat && hasValidType);
} else {
setPhoneValid(false);
}
} catch (error) {
setPhoneValid(false);
}
};

const handleSubmit = async event => {
event.preventDefault();
const validationErrors = validate(formData);
if (!phoneValid) {
toast.error('Invalid phone number for the selected country');
return;
}

setErrors(validationErrors || {});

if (validationErrors) {
Expand Down Expand Up @@ -405,7 +482,7 @@ export default function AddMaterialForm() {
<Input
id="purchase-date"
type="date"
name="purchase-date"
name="purchaseDate"
value={formData.purchaseDate}
onChange={event => handleInputChange('purchaseDate', event.target.value)}
/>
Expand All @@ -414,6 +491,9 @@ export default function AddMaterialForm() {
Enter Date
</Label>
)}
<Label for="purchaseDateErr" sm={12} className={`${styles.materialFormError}`}>
{dateError}
</Label>
</FormGroup>
</div>
<div className={`${styles.addMaterialFlexGroup}`}>
Expand All @@ -440,15 +520,23 @@ export default function AddMaterialForm() {
/>
</FormGroup>
</div>

<PhoneInput
country="US"
regions={['america', 'europe', 'asia', 'oceania', 'africa']}
limitMaxLength="true"
value={formData.phoneNumber}
onChange={phone => phoneChange('phoneNumber', phone)}
inputStyle={{ height: 'auto', width: '40%', fontSize: 'inherit' }}
/>
<FormGroup>
<Label for="Phone Number">Phone Number</Label>
<div>
<PhoneInput
country="us"
value={formData.phoneNumber}
onChange={(phone, countryData) => phoneChange('phoneNumber', phone, countryData)}
enableLongNumbers={false}
inputStyle={{ height: 'auto', width: '40%', fontSize: 'inherit' }}
/>
{!phoneValid && formData.phoneNumber && (
<Label className={`${styles.materialFormError}`}>
Invalid phone number for the selected country
</Label>
)}
</div>
</FormGroup>
<FormGroup>
<Label for="imageUpload">Upload Material Picture</Label>
<DragAndDrop
Expand Down
2 changes: 1 addition & 1 deletion src/components/BMDashboard/_tests_/AddMaterial.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ vi.mock('react-phone-input-2', () => ({
<input
data-testid="phone-input"
value={props.value}
onChange={e => props.onChange(e.target.value)}
onChange={e => props.onChange(e.target.value, { dialCode: '1', countryCode: 'us' })}
placeholder="Phone number"
/>
);
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8645,6 +8645,11 @@ levn@^0.4.1:
prelude-ls "^1.2.1"
type-check "~0.4.0"

libphonenumber-js@^1.12.31:
version "1.12.31"
resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.12.31.tgz#3cdb45641c6b77228dd1238f3d810c3bb5d91199"
integrity sha512-Z3IhgVgrqO1S5xPYM3K5XwbkDasU67/Vys4heW+lfSBALcUZjeIIzI8zCLifY+OCzSq+fpDdywMDa7z+4srJPQ==

lines-and-columns@^1.1.6:
version "1.2.4"
resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz"
Expand Down
Loading