implemented api intergration for add,edit,delete for company profile
This commit is contained in:
parent
c75cc5dffd
commit
ca887ceb80
4
ipi-survey-platform/src/assets/images/resetpassword.svg
Normal file
4
ipi-survey-platform/src/assets/images/resetpassword.svg
Normal 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 |
@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -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,
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user