implemented api intergration for add,edit,delete for company profile

This commit is contained in:
Malini 2025-10-28 23:33:39 +05:30
parent c75cc5dffd
commit ca887ceb80
5 changed files with 1799 additions and 487 deletions

View File

@ -0,0 +1,4 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.5833 8.66667C16.5833 10.2324 16.119 11.763 15.2491 13.0649C14.3792 14.3668 13.1428 15.3815 11.6962 15.9807C10.2497 16.5799 8.65789 16.7367 7.12221 16.4312C5.58652 16.1258 4.17591 15.3718 3.06874 14.2646C1.96158 13.1574 1.20759 11.7468 0.90212 10.2111C0.596654 8.67545 0.75343 7.08367 1.35262 5.63709C1.95182 4.19051 2.96652 2.95409 4.26841 2.0842C5.5703 1.2143 7.1009 0.75 8.66667 0.75C10.2075 0.749497 11.715 1.19866 13.0043 2.0424C14.2936 2.88614 15.3087 4.08779 15.925 5.5M16.5833 3.25L16.1875 5.89583L13.6667 5.33333" stroke="#92722A" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7 7.83341V6.58341C7 6.14139 7.1756 5.71746 7.48816 5.4049C7.80072 5.09234 8.22464 4.91675 8.66667 4.91675C9.10869 4.91675 9.53262 5.09234 9.84518 5.4049C10.1577 5.71746 10.3333 6.14139 10.3333 6.58341V7.83341M8.04167 12.4167H9.29167C10.2692 12.4167 10.7583 12.4167 11.0942 12.1584C11.1809 12.0919 11.2585 12.0143 11.325 11.9276C11.5833 11.5909 11.5833 11.1026 11.5833 10.1251C11.5833 9.14758 11.5833 8.65841 11.325 8.32258C11.2585 8.23588 11.1809 8.15827 11.0942 8.09175C10.7575 7.83341 10.2692 7.83341 9.29167 7.83341H8.04167C7.06417 7.83341 6.575 7.83341 6.23917 8.09175C6.15246 8.15827 6.07486 8.23588 6.00833 8.32258C5.75 8.65925 5.75 9.14758 5.75 10.1251C5.75 11.1026 5.75 11.5917 6.00833 11.9276C6.07486 12.0143 6.15246 12.0919 6.23917 12.1584C6.57583 12.4167 7.06417 12.4167 8.04167 12.4167Z" stroke="#92722A" stroke-width="1.5" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -49,12 +49,24 @@ export const TextField = ({
onFocus,
onBlur,
variant = 'default',
required = false,
showToggle = false,
toggleLabels = { show: 'Show', hide: 'Hide' },
error = '',
readOnly = false,
}) => {
const wrapperStyle = {};
if (width !== 'auto') {
wrapperStyle.width = typeof width === 'number' ? `${width}px` : width;
}
const [focused, setFocused] = React.useState(false);
const isPasswordField = type === 'password';
const [showPassword, setShowPassword] = React.useState(false);
React.useEffect(() => {
if (!isPasswordField) {
setShowPassword(false);
}
}, [isPasswordField]);
const handleFocus = (event) => {
setFocused(true);
onFocus?.(event);
@ -68,7 +80,18 @@ export const TextField = ({
if (leftIcon) {
inputStyle.paddingLeft = '40px';
}
inputStyle.borderColor = focused ? focusBorderColors[variant] : idleBorderColors[variant] || idleBorderColors.default;
if (isPasswordField && showToggle) {
inputStyle.paddingRight = '44px';
}
const hasError = Boolean(error);
inputStyle.borderColor = hasError
? '#B91C1C'
: focused && !readOnly
? focusBorderColors[variant]
: idleBorderColors[variant] || idleBorderColors.default;
if (readOnly) {
inputStyle.backgroundColor = '#F9FAFB';
}
if (variant !== 'toolbar') {
inputStyle.color = '#232528';
}
@ -77,13 +100,14 @@ export const TextField = ({
{label && (
<label htmlFor={id || name} className="block text-[14px] leading-[20px] font-medium text-[#232528] mb-1">
{label}
{required && <span className="text-[#B91C1C] ml-1">*</span>}
</label>
)}
<div className="relative">
<input
id={id || name}
name={name}
type={type}
type={isPasswordField ? (showPassword ? 'text' : 'password') : type}
value={value}
onChange={onChange}
placeholder={placeholder}
@ -91,11 +115,35 @@ export const TextField = ({
style={inputStyle}
onFocus={handleFocus}
onBlur={handleBlur}
required={required}
readOnly={readOnly}
/>
{leftIcon && (
<img src={leftIcon} alt="" className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4" />
)}
{isPasswordField && showToggle && (
<button
type="button"
onClick={() => setShowPassword((prev) => !prev)}
className="absolute right-3 top-1/2 -translate-y-1/2 text-[#92722A] hover:text-[#7b5c1f]"
aria-label={showPassword ? toggleLabels.hide : toggleLabels.show}
title={showPassword ? toggleLabels.hide : toggleLabels.show}
>
{showPassword ? (
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path strokeLinecap="round" strokeLinejoin="round" d="M3 3l18 18" />
<path strokeLinecap="round" strokeLinejoin="round" d="M10.58 10.58A2 2 0 0012 14a2 2 0 001.42-.58M16.68 16.68C15.28 17.54 13.69 18 12 18 7 18 3.73 14.82 2 12c.58-.9 1.34-1.83 2.26-2.68M9.88 4.13C10.56 4.05 11.27 4 12 4c5 0 8.27 3.18 10 6-.46.72-1.03 1.43-1.71 2.1" />
</svg>
) : (
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path strokeLinecap="round" strokeLinejoin="round" d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8S1 12 1 12z" />
<circle cx="12" cy="12" r="3" />
</svg>
)}
</button>
)}
</div>
{error && <p className="mt-1 text-xs text-[#B91C1C]">{error}</p>}
</div>
);
};
@ -118,6 +166,8 @@ export const SelectField = ({
allowClear = false,
onClear,
clearValue = '',
error = '',
readOnly = false,
}) => {
const wrapperStyle = {};
if (width !== 'auto') {
@ -128,7 +178,7 @@ export const SelectField = ({
const [focused, setFocused] = React.useState(false);
const normalizedClear = clearValue ?? '';
const hasValue = value !== undefined && value !== null && value !== '' && value !== normalizedClear;
const showClear = allowClear && hasValue;
const showClear = allowClear && hasValue && !readOnly;
if (rightIcon) {
inputStyle.paddingRight = '40px';
}
@ -140,12 +190,21 @@ export const SelectField = ({
setFocused(false);
onBlur?.(event);
};
inputStyle.borderColor = focused ? focusBorderColors[variant] : idleBorderColors[variant] || idleBorderColors.default;
const hasError = Boolean(error);
inputStyle.borderColor = hasError
? '#B91C1C'
: focused
? focusBorderColors[variant]
: idleBorderColors[variant] || idleBorderColors.default;
if (variant === 'toolbar') {
inputStyle.color = hasValue && value !== 'All' && value !== 'Status' && value !== 'Year' && value !== 'Quarter' && value !== 'Emirates' ? '#232528' : '#5F646D';
} else {
inputStyle.color = hasValue ? '#232528' : '#232528';
}
if (readOnly) {
inputStyle.backgroundColor = '#F9FAFB';
inputStyle.cursor = 'not-allowed';
}
const handleClear = (event) => {
event.preventDefault();
event.stopPropagation();
@ -167,7 +226,11 @@ export const SelectField = ({
id={id || name}
name={name}
value={value}
onChange={onChange}
onChange={(event) => {
if (!readOnly) {
onChange(event);
}
}}
className={`w-full appearance-none text-sm focus:outline-none ${className}`}
style={inputStyle}
onFocus={handleFocus}
@ -197,6 +260,132 @@ export const SelectField = ({
rightIcon && <img src={rightIcon} alt="" className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4" />
)}
</div>
{readOnly && <div className="absolute inset-0 pointer-events-none" />}
{error && <p className="mt-1 text-xs text-[#B91C1C]">{error}</p>}
</div>
);
};
const formatPhoneDigits = (digits) => {
if (!digits) return '';
const cleaned = digits.replace(/\D/g, '').slice(0, 8);
const part1 = cleaned.slice(0, 2);
const part2 = cleaned.slice(2, 5);
const part3 = cleaned.slice(5, 8);
return [part1, part2, part3].filter(Boolean).join(' ');
};
export const PhoneField = ({
label,
countryCode = '+971',
onCountryCodeChange,
countryOptions = [{ label: '+971', value: '+971' }],
placeholder = 'XX XXX XXX',
value,
onChange,
width = 'auto',
required = false,
error = '',
readOnly = false,
className = '',
}) => {
const wrapperStyle = {};
if (width !== 'auto') {
wrapperStyle.width = typeof width === 'number' ? `${width}px` : width;
}
const [focused, setFocused] = React.useState(false);
const handleFocus = () => {
if (!readOnly) setFocused(true);
};
const handleBlur = () => {
setFocused(false);
};
const hasError = Boolean(error);
const borderColor = hasError ? '#B91C1C' : focused ? '#92722A' : '#CBA344';
const containerStyle = {
display: 'flex',
alignItems: 'center',
border: `2px solid ${borderColor}`,
borderRadius: '4px',
backgroundColor: readOnly ? '#F9FAFB' : '#FFFFFF',
transition: 'border-color 0.2s ease',
height: '40px',
paddingRight: '8px',
pointerEvents: readOnly ? 'none' : 'auto',
};
const selectStyle = {
border: 'none',
background: 'transparent',
paddingLeft: '12px',
paddingRight: '24px',
fontSize: '14px',
color: '#232528',
appearance: 'none',
WebkitAppearance: 'none',
MozAppearance: 'none',
cursor: readOnly ? 'not-allowed' : 'pointer',
height: '100%',
};
const inputStyle = {
flex: 1,
border: 'none',
outline: 'none',
background: 'transparent',
fontSize: '14px',
color: '#232528',
padding: '0 0 0 8px',
};
const arrowStyle = {
position: 'absolute',
right: '8px',
top: '50%',
transform: 'translateY(-50%)',
pointerEvents: 'none',
height: '12px',
width: '12px',
};
const handleSelectChange = (event) => {
onCountryCodeChange?.(event.target.value);
};
const handleInputChange = (event) => {
const digits = event.target.value.replace(/\D/g, '') || '';
const formatted = formatPhoneDigits(digits);
onChange?.(formatted);
};
const displayValue = formatPhoneDigits((value || '').replace(/\D/g, ''));
return (
<div style={wrapperStyle} className="w-full">
{label && (
<label className="block text-[14px] leading-[20px] font-medium text-[#232528] mb-1">
{label}
{required && <span className="text-[#B91C1C] ml-1">*</span>}
</label>
)}
<div style={containerStyle} className={`relative ${className}`} onFocus={handleFocus} onBlur={handleBlur}>
<div className="relative h-full flex items-center">
<select value={countryCode} onChange={handleSelectChange} style={selectStyle} disabled={readOnly}>
{countryOptions.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
<img src={CaretDownActive} alt="" style={arrowStyle} />
</div>
<input
type="text"
value={displayValue}
onChange={handleInputChange}
placeholder={placeholder}
style={inputStyle}
disabled={readOnly}
/>
</div>
{error && <p className="mt-1 text-xs text-[#B91C1C]">{error}</p>}
</div>
);
};

View File

@ -21,7 +21,8 @@ import CaretDoubleRightIcon from '../../assets/images/CaretDoubleRight.svg';
// onPageChange?: (page: number) => void,
// pageSize?: number,
// totalItems?: number,
// pageSizeOptions?: number[], // reserved for future use
// pageSizeOptions?: number[],
// onPageSizeChange?: (size: number) => void,
// }
const Table = ({
headers = [],
@ -44,6 +45,17 @@ const Table = ({
const totalPages = enablePagination ? Math.max(1, Math.ceil(totalItems / pageSize)) : 1;
const isControlled = enablePagination && typeof pagination?.currentPage === 'number' && typeof pagination?.onPageChange === 'function';
const [internalPage, setInternalPage] = React.useState(1);
const pageSizeOptions = enablePagination ? pagination?.pageSizeOptions ?? [] : [];
const handlePageSizeChange = React.useCallback(
(event) => {
if (!enablePagination) return;
const nextSize = Number(event.target.value);
if (Number.isNaN(nextSize) || nextSize <= 0) return;
pagination?.onPageSizeChange?.(nextSize);
},
[enablePagination, pagination]
);
React.useEffect(() => {
if (!enablePagination || isControlled) return;
@ -189,8 +201,8 @@ const Table = ({
{enablePagination && (
<div className="px-6 pb-6">
<div className="flex flex-col gap-1 md:flex-row md:items-center md:justify-start">
<div className="flex items-centergap-3 text-sm text-[#4B5563]">
<div className="flex flex-col gap-2 md:flex-row md:items-center md:justify-between">
<div className="flex items-center gap-3 text-sm text-[#4B5563]">
<button
type="button"
className={arrowButtonClass}
@ -218,7 +230,24 @@ const Table = ({
</button>
</div>
<div className="flex pl-50 items-start justify-start gap-3 text-sm text-[#4B5563]">
<div className="flex flex-wrap items-center gap-3 text-sm text-[#4B5563]">
{pageSizeOptions.length > 0 && pagination?.onPageSizeChange && (
<label className="flex items-center gap-2 text-sm text-[#232528]">
<span>Rows per page</span>
<select
value={pageSize}
onChange={handlePageSizeChange}
className="h-9 rounded-md border border-[#C3C6CB] bg-white px-3 text-sm text-[#232528] focus:border-[#92722A] focus:outline-none"
>
{pageSizeOptions.map((option) => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
</label>
)}
<button
type="button"
className={navButtonClass}

View File

@ -1,9 +1,39 @@
import { getRequest } from '../api/CommonService.js';
import { getRequest, postRequest, putRequest, deleteRequest } from '../api/CommonService.js';
import resolveEstablishmentId from '../utils/establishment.js';
const dashboardEndpoint = '/establishment_dashboard';
const establishmentsEndpoint = '/establishments';
export const fetchEstablishments = async (config = {}) => {
const response = await getRequest(establishmentsEndpoint, config);
return response.data;
};
export const createEstablishment = async (payload, config = {}) => {
const response = await postRequest(establishmentsEndpoint, payload, config);
return response.data;
};
export const deleteEstablishment = async (establishmentId, config = {}) => {
const resolvedId = resolveEstablishmentId(establishmentId);
if (resolvedId === undefined) {
throw new Error('Missing establishment ID for delete request.');
}
const url = `${establishmentsEndpoint}/${resolvedId}`;
const response = await deleteRequest(url, config);
return response.data;
};
export const updateEstablishment = async (establishmentId, payload, config = {}) => {
const resolvedId = resolveEstablishmentId(establishmentId);
if (resolvedId === undefined) {
throw new Error('Missing establishment ID for update request.');
}
const url = `${establishmentsEndpoint}/${resolvedId}`;
const response = await putRequest(url, payload, config);
return response.data;
};
export const fetchEstablishmentDashboard = async (establishmentId, config = {}) => {
const resolvedId = resolveEstablishmentId(establishmentId);
if (resolvedId === undefined) {
@ -25,6 +55,10 @@ export const fetchEstablishmentDetail = async (establishmentId, config = {}) =>
};
export default {
fetchEstablishments,
fetchEstablishmentDashboard,
fetchEstablishmentDetail,
createEstablishment,
updateEstablishment,
deleteEstablishment,
};