changes in inline styles

This commit is contained in:
Malini 2026-01-23 11:26:55 +05:30
parent fce67c8e02
commit 1796b3aea4
18 changed files with 287 additions and 358 deletions

View File

@ -1,16 +1,13 @@
import React from 'react';
const StatCard = ({ icon = null, label, value, tooltipText, width = 200.4 }) => {
const resolvedWidth = typeof width === 'number' ? `${width}px` : width;
return (
<div
className="relative group flex flex-col items-center justify-center rounded-[8px] border border-[#E3E5E8] bg-white px-5 py-5 shadow-[0_8px_20px_rgba(42,47,66,0.06)]"
style={{ width: resolvedWidth, minHeight: '120px' }}
className={`relative group flex flex-col items-center justify-center rounded-lg border border-gray-200 bg-white px-5 py-5 shadow-sm ${typeof width === 'number' ? `w-[${width}px]` : `w-${width}`} min-h-[120px]`}
>
<div className="flex items-center justify-center gap-2 whitespace-nowrap">
{icon && <div className="flex items-center justify-center w-5 h-5">{icon}</div>}
<span className="text-[15px] font-medium text-[#5F646D] leading-[22px] text-center truncate">
<span className="text-sm font-medium text-gray-600 leading-5 text-center truncate">
{label}
</span>
</div>

View File

@ -324,10 +324,9 @@ const tableRows = filtered.map((item) => [
return (
<div
className="border border-[#E5E7EB] bg-white shadow-[0_8px_16px_rgba(15,23,42,0.05)] rounded-[6px] w-full overflow-hidden py-2"
style={{
minHeight: filtered.length === 0 || loading ? 'auto' : '200px',
}}
className={`border border-gray-200 bg-white shadow-sm rounded-md w-full overflow-hidden py-2 ${
filtered.length === 0 || loading ? 'min-h-auto' : 'min-h-[200px]'
}`}
>
<div className="flex justify-between items-center px-4 pr-6 py-2 mt-3">
<h2 className="text-[18px] leading-[28px] font-medium text-[#232528]">

View File

@ -13,19 +13,10 @@ const CustomToast = ({ message, type = 'success', onClose, duration = 4000 }) =>
: 'bg-red-50 border border-red-500 text-red-700';
const Icon = isSuccess ? CheckCircle : XCircle;
// In CustomToast.jsx
// return (
// In CustomToast.jsx
return (
<div
className={`fixed top-4 left-1/2 -translate-x-1/2 flex items-start gap-3 p-4 rounded-lg shadow-md ${bgColor} animate-fade-in`}
style={{
zIndex: 10000,
minWidth: '320px',
maxWidth: '90%',
animation: 'fadeIn 0.3s ease-out',
}}
>
return (
<div
className={`fixed top-4 left-1/2 -translate-x-1/2 flex items-start gap-3 p-4 rounded-lg shadow-md ${bgColor} z-[10000] min-w-[320px] max-w-[90%] animate-[fadeIn_0.3s_ease-out]`}
>
<Icon className="w-5 h-5 mt-0.5 flex-shrink-0" />
<div className="flex-1 text-sm font-medium">{message}</div>
<button

View File

@ -363,14 +363,14 @@ export const SelectField = ({
}
};
return (
<div style={wrapperStyle} className="w-full">
<div className={`w-full ${wrapperStyle ? '' : ''}`} style={wrapperStyle || {}}>
{label && (
<label htmlFor={id || name} className="block text-[14px] leading-[20px] font-medium text-[#232528] mb-1">
<label htmlFor={id || name} className="block text-sm font-medium text-gray-900 mb-1">
{label}
{required && <span className="text-[#B91C1C] ml-1">*</span>}
{required && <span className="text-red-700 ml-1">*</span>}
</label>
)}
<div className="relative" style={{ width: '100%' }}>
<div className="relative w-full">
<select
id={id || name}
name={name}

View File

@ -139,8 +139,12 @@ const Table = ({
<div className={`overflow-hidden rounded-lg bg-white ${className}`}>
{beforeHeader}
<div
className={`${disableHorizontalScroll ? 'overflow-x-hidden' : 'overflow-x-auto'} px-6 pt-2 pb-6`}
style={disableHorizontalScroll ? undefined : { scrollbarWidth: 'thin' }}
className={`
${disableHorizontalScroll
? 'overflow-x-hidden'
: 'overflow-x-auto scrollbar-thin'}
px-6 pt-2 pb-6
`}
>
<table
className={`min-w-[720px] w-full table-fixed rounded-2xl px-4 py-4${
@ -190,13 +194,9 @@ const Table = ({
return (
<td
key={ci}
className={`px-6 py-2 text-sm text-[#232528] border-b border-[#C3C6CB] align-top ${getCellClass ? getCellClass(ri, ci) : ''}`}
style={{
...widthStyle,
whiteSpace: 'normal',
wordWrap: 'break-word',
maxWidth: widthValue || '200px'
}}
className={`px-6 py-2 text-sm text-gray-900 border-b border-gray-300 align-top whitespace-normal break-words ${getCellClass ? getCellClass(ri, ci) : ''} ${
widthValue ? `w-[${typeof widthValue === 'number' ? `${widthValue}px` : widthValue}] min-w-[${typeof widthValue === 'number' ? `${widthValue}px` : widthValue}]` : 'max-w-[200px]'
}`}
>
{renderCell ? renderCell(cell, ri, ci) : cell}
</td>

View File

@ -329,14 +329,11 @@ const Card = ({ children }) => (
</div>
);
const SectionBox = ({ title, children, badgeBg = '#ffffff', badgeText = '#374151' }) => (
const SectionBox = ({ title, children, badgeBg = 'bg-white', badgeText = 'text-gray-700' }) => (
<div className="rounded-md ring-1 ring-gray-200 bg-white">
<div className="p-5">
<div className="font-medium mb-2">
<span
className="inline-flex items-center rounded text-sm font-medium px-2 py-1"
style={{ backgroundColor: badgeBg, color: badgeText }}
>
<span className={`inline-flex items-center rounded text-sm font-medium px-2 py-1 ${badgeBg} ${badgeText}`}>
{title}
</span>
</div>
@ -1501,7 +1498,7 @@ const location = useLocation();
<label className="block text-sm font-medium text-gray-700 mb-1">
{quarterPeriods?.previous_month?.previous_period_one } Qty
</label>
<div className="flex items-center h-10 px-3 py-2 text-sm text-gray-700 rounded-md w-24" style={{ border: '1px solid #E6D7A2' }}>
<div className="flex items-center h-10 px-3 py-2 text-sm text-gray-700 rounded-md w-24 border border-amber-200">
{p.octQuantity || '0'}
</div>
</div>
@ -1509,7 +1506,7 @@ const location = useLocation();
<label className="block text-sm font-medium text-gray-700 mb-1">
{quarterPeriods?.previous_month?.previous_period_two } Qty
</label>
<div className="flex items-center h-10 px-3 py-2 text-sm text-gray-700 rounded-md w-24" style={{ border: '1px solid #E6D7A2' }}>
<div className="flex items-center h-10 px-3 py-2 text-sm text-gray-700 rounded-md w-24 border border-amber-200">
{p.novQuantity || '0'}
</div>
</div>
@ -1517,7 +1514,7 @@ const location = useLocation();
<label className="block text-sm font-medium text-gray-700 mb-1">
{quarterPeriods?.previous_month?.previous_period_three} Qty
</label>
<div className="flex items-center h-10 px-3 py-2 text-sm text-gray-700 rounded-md w-24" style={{ border: '1px solid #E6D7A2' }}>
<div className="flex items-center h-10 px-3 py-2 text-sm text-gray-700 rounded-md w-24 border border-amber-200">
{p.decQuantity || '0'}
</div>
</div>
@ -1527,7 +1524,7 @@ const location = useLocation();
<label className="block text-sm font-medium text-gray-700 mb-1">
{quarterPeriods?.previous_month?.previous_period_one } Cost (<img src={uae_currency} alt="AED" className="inline-block w-5 h-5 -mt-0.5 -mb-2.4 opacity-100 -mr-1 -ml-1" />)
</label>
<div className="flex items-center h-10 px-3 py-2 text-sm text-gray-700 rounded-md w-24" style={{ border: '1px solid #E6D7A2' }}>
<div className="flex items-center h-10 px-3 py-2 text-sm text-gray-700 rounded-md w-24 border border-amber-200">
{p.octCost || '0'}
</div>
</div>
@ -1535,7 +1532,7 @@ const location = useLocation();
<label className="block text-sm font-medium text-gray-700 mb-1">
{quarterPeriods?.previous_month?.previous_period_two } Cost (<img src={uae_currency} alt="AED" className="inline-block w-5 h-5 -mt-0.5 -mb-2.4 opacity-100 -mr-1 -ml-1" />)
</label>
<div className="flex items-center h-10 px-3 py-2 text-sm text-gray-700 rounded-md w-24" style={{ border: '1px solid #E6D7A2' }}>
<div className="flex items-center h-10 px-3 py-2 text-sm text-gray-700 rounded-md w-24 border border-amber-200">
{p.novCost || '0'}
</div>
</div>
@ -1543,7 +1540,7 @@ const location = useLocation();
<label className="block text-sm font-medium text-gray-700 mb-1">
{quarterPeriods?.previous_month?.previous_period_three } Cost (<img src={uae_currency} alt="AED" className="inline-block w-5 h-5 -mt-0.5 -mb-2.4 opacity-100 -mr-1 -ml-1" />)
</label>
<div className="flex items-center h-10 px-3 py-2 text-sm text-gray-700 rounded-md w-24" style={{ border: '1px solid #E6D7A2' }}>
<div className="flex items-center h-10 px-3 py-2 text-sm text-gray-700 rounded-md w-24 border border-amber-200">
{p.decCost || '0'}
</div>
</div>

View File

@ -147,35 +147,40 @@ const AdminDashboard = () => {
<StatCard
label="Total Establishments"
value={loading ? '--' : summary.total_establishments}
width={290}
width={200}
className="flex-1 min-w-0"
icon={<img src={surveyIconSrc} alt="Total Establishments" className="h-5 w-5" />}
tooltipText="Total establishments enrolled in the survey for selected quarter."
/>
<StatCard
label="Submitted"
value={loading ? '--' : summary.submitted}
width={190}
width={200}
className="flex-1 min-w-0"
icon={<img src={submittedIconSrc} alt="Submitted" className="h-5 w-5" />}
tooltipText="Establishments that have submitted all products for this quarter."
/>
<StatCard
label="Approved"
value={loading ? '--' : summary.approved}
width={190}
width={200}
className="flex-1 min-w-0"
icon={<img src={approvedIconSrc} alt="Approved" className="h-5 w-5" />}
tooltipText="Submissions fully validated and approved."
/>
<StatCard
label="Rejected"
value={loading ? '--' : summary.rejected}
width={190}
width={200}
className="flex-1 min-w-0"
icon={<img src={rejectedIconSrc} alt="Rejected" className="h-5 w-5" />}
tooltipText="Submissions rejected and pending resubmission."
/>
<StatCard
label="Not Started"
value={loading ? '--' : summary.not_started}
width={190}
width={200}
className="flex-1 min-w-0"
icon={<img src={overdueIconSrc} alt="Not Started" className="h-5 w-5" />}
tooltipText="Establishments past submission deadline."
/>
@ -186,7 +191,8 @@ const AdminDashboard = () => {
(selectedQuarter === 'All' || selectedYear === 'All') ? 'NA' :
(quarterlyWindows.end_date ? new Date(quarterlyWindows.end_date).toLocaleDateString('en-GB') : '--')}
valueClassName="text-sm"
width={190}
width={200}
className="flex-1 min-w-0"
icon={<img src={dueDateIconSrc} alt="Due Date" className="h-5 w-5" />}
tooltipText={selectedQuarter === 'All' || selectedYear === 'All' ?
"Due date is not applicable when viewing all quarters or years" :

View File

@ -11,6 +11,15 @@ const caretUpSrc = '/assets/images/caret-up.svg';
import apiClient from '@/services/api/apiClient';
import { putRequest } from '@/services/api/CommonService';
const STATUS_CLASSES = {
Approved: 'bg-green-50 text-green-800 border-green-50',
Pending: 'bg-blue-50 text-blue-600 border-blue-50',
Rejected: 'bg-red-50 text-red-700 border-red-50',
Resubmitted: 'bg-amber-50 text-amber-800 border-amber-200',
Submitted: 'bg-blue-50 text-blue-600 border-blue-50',
default: 'bg-gray-50 text-gray-600 border-gray-200'
};
const STATUS_VARIANTS = {
Approved: { background: '#F3FAF4', text: '#2F663C', border: '#F3FAF4' },
Pending: { background: '#E7F5FF', text: '#286CFF', border: '#E7F5FF' },
@ -45,15 +54,11 @@ const formatDateTime = (value) => {
});
return formatted;
};
const Badge = ({ children, status }) => {
const variant = STATUS_VARIANTS[status] || { background: '#F2F4F7', text: '#475467', border: '#EAECF0' };
return (
<span
// className={`inline-flex h-6 min-w-[67px] items-center justify-center rounded px-2 text-xs font-medium border ${variant.background} ${variant.text} ${variant.border}`}
className="inline-flex h-6 min-w-[67px] items-center justify-center rounded px-2 text-xs font-medium"
style={{ background: variant.background, color: variant.text, border: `1px solid ${variant.border}` }}
>
const Badge = ({ children, status }) => {
const variantClass = STATUS_CLASSES[status] || STATUS_CLASSES.default;
return (
<span className={`inline-flex h-6 min-w-[67px] items-center justify-center rounded px-2 text-xs font-medium border ${variantClass}`}>
{children}
</span>
);

View File

@ -35,8 +35,7 @@ const SummaryCard = ({ label, count, icon, iconBg = '#FDF7EB' }) => (
<div className="flex min-h-[96px] min-w-[220px] flex-1 items-center justify-between rounded-[16px] border border-[#E6EAF5] bg-white px-6 py-5 shadow-[0_12px_24px_rgba(15,23,42,0.06)]">
<div className="flex items-center gap-4">
<span
className="flex h-12 w-12 items-center justify-center rounded-full"
style={{ backgroundColor: iconBg }}
className={`flex h-12 w-12 items-center justify-center rounded-full ${iconBg ? `bg-[${iconBg}]` : 'bg-[#FDF7EB]'}`}
>
{icon && <img src={icon} alt={label} className="h-6 w-6" />}
</span>

View File

@ -989,10 +989,7 @@ const CompanyProfile = () => {
<h4 className="text-[16px] font-semibold text-[#232528]">Details</h4>
{/* Responsive grid */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-2 w-full overflow-x-hidden" style={{
msOverflowStyle: 'none', /* IE and Edge */
scrollbarWidth: 'none', /* Firefox */
}}>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-2 w-full overflow-x-hidden [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]">
<div>
<TextField
label="Permanent Factory Code"
@ -1043,7 +1040,7 @@ const CompanyProfile = () => {
</div>
</div>
<div className="mt-4 w-full" style={{ width: 'calc(100% - 1px)' }}>
<div className="mt-4 w-[calc(100%-1px)]">
<TextField
label="Description"
value={form.industryDescription}
@ -1055,7 +1052,7 @@ const CompanyProfile = () => {
</div>
{form.industryCodeProduction && form.industryCodeBusiness != null && form.industryCodeBusiness !== form.industryCodeProduction && (
<div className="mt-4 w-full" style={{ width: 'calc(100% - 1px)' }}>
<div className="mt-4 w-[calc(100%-1px)]">
<TextField
label="Remarks"
value={form.industryCodeMismatchRemarks}
@ -3376,17 +3373,7 @@ const handleImport = async () => {
<div className="fixed inset-0 z-50">
<div className="absolute inset-0 bg-black/40" onClick={closeModal} />
<div
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-[8px] bg-white"
style={{
width: '1300px',
maxWidth: 'calc(100vw - 32px)',
maxHeight: '94vh',
boxShadow: '0 24px 40px rgba(27, 29, 33, 0.16)',
border: '1px solid #CBD5E1',
overflow: 'hidden',
display: 'flex',
flexDirection: 'column',
}}
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-lg bg-white w-[1300px] max-w-[calc(100vw-32px)] max-h-[94vh] shadow-xl border border-slate-200 overflow-hidden flex flex-col"
>
<div className="flex items-center justify-between px-6 py-4 border-b border-[#F1F2F4]">
<h3 className="text-[18px] font-medium text-[#232528]">
@ -3406,7 +3393,7 @@ const handleImport = async () => {
</div>
<div className="flex flex-1 overflow-hidden">
<aside className="w-64 bg-[#F9FAFB] border-r border-[#E5E7EB] overflow-y-auto" style={{ maxHeight: 'calc(94vh - 120px)' }}>
<aside className="w-64 bg-[#F9FAFB] border-r border-[#E5E7EB] overflow-y-auto max-h-[calc(94vh-120px)]">
<ol className="py-6 px-4 space-y-2">
{formSteps.map((step, index) => {
const isActive = index === activeStep;
@ -3447,7 +3434,7 @@ const handleImport = async () => {
})}
</ol>
</aside>
<div className="flex-1 overflow-y-auto" style={{ maxHeight: 'calc(94vh - 120px)' }}>
<div className="flex-1 overflow-y-auto max-h-[calc(94vh-120px)]">
<div className="px-6 py-6">
{renderStepContent()}
</div>
@ -3548,20 +3535,11 @@ const handleImport = async () => {
onClick={isDeletingApi ? undefined : closeDeleteConfirm}
/>
<div
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-lg bg-white border"
style={{
width: '480px',
maxWidth: '92vw',
borderColor: '#CBD5E1',
boxShadow: '0 20px 45px rgba(0,0,0,0.12)',
}}
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-lg bg-white border border-[#CBD5E1] w-[480px] max-w-[92vw] shadow-lg"
>
<div className="px-8 py-6 space-y-6">
<div className="flex items-start gap-3">
<span
className="flex items-center justify-center"
style={{ width: '32px', height: '36px' }}
>
<span className="flex items-center justify-center w-8 h-9">
<img src={deleteIconSrc} alt="Delete" className="h-6 w-6" />
</span>
<div className="flex-1">

View File

@ -67,13 +67,7 @@ const EmailTemplates = () => {
value={form.body}
onChange={handleChange('body')}
rows={10}
className="w-full text-sm focus:outline-none"
style={{
width: '100%',
border: '2px solid #CBA344',
borderRadius: '4px',
padding: '12px 16px',
}}
className="w-full text-sm focus:outline-none border-2 border-[#CBA344] rounded p-3"
/>
</div>
</div>
@ -81,8 +75,7 @@ const EmailTemplates = () => {
<div className="px-6 pb-6 flex items-center justify-end gap-3">
<button
type="button"
className="h-9 px-5 rounded-md border text-[#92722A] bg-white cursor-pointer"
style={{ borderColor: '#92722A' }}
className="h-9 px-5 rounded-md border border-[#92722A] text-[#92722A] bg-white cursor-pointer"
onClick={handleCancel}
>
Cancel

View File

@ -307,8 +307,7 @@ const Establishment = () => {
<div className="px-6 py-4 border-t border-[#E5E7EB] flex items-center justify-end gap-3">
<button
type="button"
className="h-9 px-5 rounded-md border text-[#92722A] bg-white cursor-pointer"
style={{ borderColor: '#92722A' }}
className="h-9 px-5 rounded-md border border-[#92722A] text-[#92722A] bg-white cursor-pointer"
onClick={closeModal}
>
Cancel

View File

@ -1613,9 +1613,8 @@ const handleExportCSV = () => {
onChange={handleFormChange("description")}
disabled={modalMode === "view"}
rows={1}
className="w-full px-3 py-2 text-sm border border-[#CBA344] rounded-md focus:outline-none focus:ring-1 focus:ring-[#92722A] focus:border-[#92722A] min-h-[38px]"
className="w-full px-3 py-2 text-sm border border-[#CBA344] rounded-md focus:outline-none focus:ring-1 focus:ring-[#92722A] focus:border-[#92722A] min-h-[38px] resize-y"
placeholder="Enter product description"
style={{ resize: 'vertical' }}
/>
</div>
{/* <div>
@ -1671,12 +1670,7 @@ const handleExportCSV = () => {
<div className="fixed inset-0 z-50">
<div className="absolute inset-0 bg-black/40" onClick={closeDeleteConfirm} />
<div
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-lg bg-white border"
style={{
width: '480px',
borderColor: '#CBD5E1',
boxShadow: '0 20px 45px rgba(0,0,0,0.12)',
}}
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-lg bg-white border border-[#CBD5E1] w-[480px] shadow-lg"
>
<div className="px-8 py-6 space-y-6">
<div className="flex items-start gap-3">

View File

@ -90,25 +90,25 @@ const QuarterlyWindows = () => {
const [isLoading, setIsLoading] = React.useState(false);
const [validationErrors, setValidationErrors] = React.useState({});
const [toastData, setToastData] = React.useState(null);
const [isSaving, setIsSaving] = React.useState(false); // Add this line
const [isSaving, setIsSaving] = React.useState(false); // Add this line
const [sortConfig, setSortConfig] = React.useState({
key: null,
direction: 'asc'
});
const extractText = (node) => {
if (typeof node === "string") return node;
if (typeof node === "number") return String(node);
if (node?.props?.children) {
if (Array.isArray(node.props.children)) {
return node.props.children.map(extractText).join(" ");
if (typeof node === "string") return node;
if (typeof node === "number") return String(node);
if (node?.props?.children) {
if (Array.isArray(node.props.children)) {
return node.props.children.map(extractText).join(" ");
}
return extractText(node.props.children);
}
return extractText(node.props.children);
}
return "";
};
return "";
};
// Format date to yyyy-MM-dd for input fields
const formatDateForInput = (dateString) => {
@ -172,29 +172,29 @@ const QuarterlyWindows = () => {
const getSortedData = (data) => {
if (!sortConfig.key) return data;
return [...data].sort((a, b) => {
let aValue = a[sortConfig.key];
let bValue = b[sortConfig.key];
// Handle numeric comparisons
if (['year', 'assigned', 'responded', 'not_responded', 'submissionCount'].includes(sortConfig.key)) {
aValue = Number(aValue) || 0;
bValue = Number(bValue) || 0;
return sortConfig.direction === 'asc' ? aValue - bValue : bValue - aValue;
}
// Handle date comparisons
if (['startDate', 'endDate'].includes(sortConfig.key)) {
aValue = aValue ? new Date(aValue).getTime() : 0;
bValue = bValue ? new Date(bValue).getTime() : 0;
return sortConfig.direction === 'asc' ? aValue - bValue : bValue - aValue;
}
// Default string comparison
aValue = String(aValue || '').toLowerCase();
bValue = String(bValue || '').toLowerCase();
if (aValue < bValue) {
return sortConfig.direction === 'asc' ? -1 : 1;
}
@ -206,88 +206,88 @@ const QuarterlyWindows = () => {
};
const headers = [
{
name: 'Survey Name',
{
name: 'Survey Name',
key: 'survey_name',
sortable: true,
className: 'whitespace-nowrap text-left'
className: 'whitespace-nowrap text-left'
},
{
name: 'Year',
{
name: 'Year',
key: 'year',
sortable: true,
className: 'whitespace-nowrap text-left'
className: 'whitespace-nowrap text-left'
},
{
name: 'Quarter',
{
name: 'Quarter',
key: 'quarter',
sortable: true,
className: 'whitespace-nowrap text-left'
className: 'whitespace-nowrap text-left'
},
{
name: 'Start Date',
{
name: 'Start Date',
key: 'startDate',
sortable: true,
className: 'whitespace-nowrap text-left'
className: 'whitespace-nowrap text-left'
},
{
name: 'End Date',
{
name: 'End Date',
key: 'endDate',
sortable: true,
className: 'whitespace-nowrap text-left'
className: 'whitespace-nowrap text-left'
},
{
name: 'Grace Period',
{
name: 'Grace Period',
key: 'gracePeriod',
sortable: false,
className: 'whitespace-nowrap text-left'
className: 'whitespace-nowrap text-left'
},
{
name: 'Assigned',
{
name: 'Assigned',
key: 'assigned',
sortable: false,
className: 'whitespace-nowrap text-left'
className: 'whitespace-nowrap text-left'
},
{
name: 'Responded',
{
name: 'Responded',
key: 'responded',
sortable: false,
className: 'whitespace-nowrap text-left'
className: 'whitespace-nowrap text-left'
},
{
name: 'Not Responded',
{
name: 'Not Responded',
key: 'not_responded',
sortable: false,
className: 'whitespace-nowrap text-left'
className: 'whitespace-nowrap text-left'
},
{
name: 'Submission Count',
{
name: 'Submission Count',
key: 'submissionCount',
sortable: false,
className: 'whitespace-nowrap text-left'
className: 'whitespace-nowrap text-left'
},
{
name: 'Status',
{
name: 'Status',
key: 'status',
sortable: false,
className: 'whitespace-nowrap text-left'
className: 'whitespace-nowrap text-left'
},
].map(header => {
if (!header.sortable) return { name: header.name, className: header.className };
const isActive = sortConfig.key === header.key;
const isAsc = sortConfig.direction === 'asc';
return {
name: (
<div
<div
className="flex items-center cursor-pointer gap-1"
onClick={() => handleSort(header.key)}
>
<span>{header.name}</span>
<img
<img
src={isActive ? "/assets/images/active-sort.svg" : "/assets/images/inactive-sort.svg"}
alt="Sort"
alt="Sort"
className={`w-3 h-3 transition-transform ${isActive && isAsc ? 'transform rotate-180' : ''}`}
/>
</div>
@ -295,7 +295,7 @@ const QuarterlyWindows = () => {
className: header.className
};
});
const columnwidth = [
220, // Survey Name
100, // Year
@ -309,16 +309,16 @@ const QuarterlyWindows = () => {
140, // Submission Count
120 // Actions
];
const filteredRows = React.useMemo(() => {
const term = searchTerm.trim().toLowerCase();
return quarterData.filter((item) => {
// Apply year filter
if (yearFilter !== 'all' && item.year !== yearFilter) return false;
// Apply quarter filter
if (quarterFilter !== 'all' && item.quarter !== quarterFilter) return false;
// Apply search term
if (!term) return true;
const haystack = [
@ -345,18 +345,17 @@ const QuarterlyWindows = () => {
<div className="flex items-center gap-1 group relative">
<span>{children}</span>
<div className="relative group cursor-pointer" title={text}>
<img
src="/assets/images/tooltip.svg"
alt="info"
className="w-4 h-4"
style={{ filter: 'invert(45%) sepia(6%) saturate(323%) hue-rotate(175deg) brightness(93%) contrast(82%)' }}
<img
src="/assets/images/tooltip.svg"
alt="info"
className="w-4 h-4 grayscale opacity-70"
/>
</div>
</div>
);
const sortedRows = getSortedData(filteredRows);
const rows = sortedRows.map((item) => [
item.survey_name || '-',
item.year,
@ -364,13 +363,13 @@ const QuarterlyWindows = () => {
item.startDate ? formatDateForDisplay(item.startDate) : '-',
item.endDate ? formatDateForDisplay(item.endDate) : '-',
item.gracePeriod,
<Tooltip text="Number of establishments assigned this survey">
<Tooltip key="assigned" text="Number of establishments assigned this survey">
{item.assigned || '0'}
</Tooltip>,
<Tooltip text="Number of establishments that have responded">
<Tooltip key="responded" text="Number of establishments that have responded">
{item.responded || '0'}
</Tooltip>,
<Tooltip text="Number of establishments yet to respond">
<Tooltip key="not-responded" text="Number of establishments yet to respond">
{item.not_responded || '0'}
</Tooltip>,
item.submissionCount,
@ -381,19 +380,19 @@ const QuarterlyWindows = () => {
const yearOptions = React.useMemo(() => {
const currentYear = new Date().getFullYear();
const years = new Set(['all']);
// Add years from 2022 to current year
for (let y = 2022; y <= currentYear; y++) {
years.add(y.toString());
}
// Add any additional years that might be in the data
quarterData.forEach(item => {
if (item.year && !years.has(item.year)) {
years.add(item.year);
}
});
// Sort years in descending order (newest first)
return Array.from(years).sort((a, b) => {
if (a === 'all') return -1;
@ -401,7 +400,7 @@ const QuarterlyWindows = () => {
return parseInt(b) - parseInt(a);
});
}, [quarterData]);
const quarterOptions = ['all', 'Q1', 'Q2', 'Q3', 'Q4'];
const openAddModal = () => {
@ -420,18 +419,18 @@ const QuarterlyWindows = () => {
const openEditModal = async (index) => {
try {
setIsLoading(true);
// Calculate the actual index in the filtered data based on pagination
const actualIndex = (currentPage - 1) * pageSize + index;
const selected = filteredRows[actualIndex];
if (!selected || !selected.id) {
throw new Error('Invalid quarterly window selected');
}
// Fetch the latest data for this quarter
const response = await getQuarterlyWindowById(selected.id);
if (response && response.data) {
const quarterData = response.data;
const formData = {
@ -449,7 +448,7 @@ const QuarterlyWindows = () => {
not_responded: quarterData.not_responded?.toString() || '0',
status: quarterData.is_active ? 'Active' : 'Inactive'
};
setEditingRow(actualIndex);
setForm(formData);
setOriginalForm(formData);
@ -525,7 +524,7 @@ const QuarterlyWindows = () => {
const handleFormChange = (field) => (value) => {
setForm(prev => ({ ...prev, [field]: value }));
// Clear validation error when user starts typing
if (validationErrors[field]) {
setValidationErrors(prev => ({
@ -535,125 +534,125 @@ const QuarterlyWindows = () => {
}
};
const handleSave = async () => {
try {
if (!validateForm()) {
return;
}
setIsSaving(true);
const startDate = new Date(form.startDate);
const endDate = new Date(form.endDate);
const formattedData = {
survey_name: form.survey_name || '',
year: form.year ? parseInt(form.year, 10) : 0,
quarter: form.quarter || '',
start_date: startDate.toISOString().split('T')[0],
end_date: endDate.toISOString().split('T')[0],
grace_periods_days: form.gracePeriod ? parseInt(form.gracePeriod.split(' ')[0], 10) || 0 : 0,
assigned: form.assigned || 0,
responded: form.responded || 0,
not_responded: form.not_responded || 0,
is_active: form.status === 'Active',
establishment: form.establishment || '-',
};
if (modalMode === 'add') {
const response = await createQuarterlyWindow(formattedData);
if (response.status === 'success') {
// Fetch the latest data from the server
const latestData = await getQuarterlyWindows();
if (latestData.status === 'success' && Array.isArray(latestData.data)) {
const formattedData = latestData.data.map(item => ({
id: item.id,
survey_name: item.survey_name || '',
establishment: item.establishment || '-',
year: item.year?.toString() || '',
quarter: item.quarter || '',
startDate: item.start_date || '',
endDate: item.end_date || '',
gracePeriod: item.grace_periods_days ? `${item.grace_periods_days} days` : '0 days',
submissionCount: item.submission_count?.toString() || '-',
assigned: item.assigned?.toString() || '0',
responded: item.responded?.toString() || '0',
not_responded: item.not_responded?.toString() || '0',
status: item.is_active ? 'Active' : 'Inactive'
}));
setQuarterData(formattedData);
}
setToastData({
message: 'Quarterly survey created successfully!',
type: 'success'
});
} else {
const errorMessage = response.message || 'Failed to add quarterly window';
setToastData({
message: errorMessage,
type: 'error'
});
console.error('Failed to add quarterly window:', errorMessage);
}
} else if (modalMode === 'edit' && editingRow !== null) {
const itemId = quarterData[editingRow]?.id;
if (!itemId) {
setToastData({
message: 'Error: Could not find the item to update',
type: 'error'
});
console.error('Error: Could not find the item to update');
const handleSave = async () => {
try {
if (!validateForm()) {
return;
}
const response = await updateQuarterlyWindow(itemId, formattedData);
if (response.status === 'success') {
// Fetch the latest data from the server
const latestData = await getQuarterlyWindows();
if (latestData.status === 'success' && Array.isArray(latestData.data)) {
const formattedData = latestData.data.map(item => ({
id: item.id,
survey_name: item.survey_name || '',
establishment: item.establishment || '-',
year: item.year?.toString() || '',
quarter: item.quarter || '',
startDate: item.start_date || '',
endDate: item.end_date || '',
gracePeriod: item.grace_periods_days ? `${item.grace_periods_days} days` : '0 days',
submissionCount: item.submission_count?.toString() || '-',
assigned: item.assigned?.toString() || '0',
responded: item.responded?.toString() || '0',
not_responded: item.not_responded?.toString() || '0',
status: item.is_active ? 'Active' : 'Inactive'
}));
setQuarterData(formattedData);
setIsSaving(true);
const startDate = new Date(form.startDate);
const endDate = new Date(form.endDate);
const formattedData = {
survey_name: form.survey_name || '',
year: form.year ? parseInt(form.year, 10) : 0,
quarter: form.quarter || '',
start_date: startDate.toISOString().split('T')[0],
end_date: endDate.toISOString().split('T')[0],
grace_periods_days: form.gracePeriod ? parseInt(form.gracePeriod.split(' ')[0], 10) || 0 : 0,
assigned: form.assigned || 0,
responded: form.responded || 0,
not_responded: form.not_responded || 0,
is_active: form.status === 'Active',
establishment: form.establishment || '-',
};
if (modalMode === 'add') {
const response = await createQuarterlyWindow(formattedData);
if (response.status === 'success') {
// Fetch the latest data from the server
const latestData = await getQuarterlyWindows();
if (latestData.status === 'success' && Array.isArray(latestData.data)) {
const formattedData = latestData.data.map(item => ({
id: item.id,
survey_name: item.survey_name || '',
establishment: item.establishment || '-',
year: item.year?.toString() || '',
quarter: item.quarter || '',
startDate: item.start_date || '',
endDate: item.end_date || '',
gracePeriod: item.grace_periods_days ? `${item.grace_periods_days} days` : '0 days',
submissionCount: item.submission_count?.toString() || '-',
assigned: item.assigned?.toString() || '0',
responded: item.responded?.toString() || '0',
not_responded: item.not_responded?.toString() || '0',
status: item.is_active ? 'Active' : 'Inactive'
}));
setQuarterData(formattedData);
}
setToastData({
message: 'Quarterly survey created successfully!',
type: 'success'
});
} else {
const errorMessage = response.message || 'Failed to add quarterly window';
setToastData({
message: errorMessage,
type: 'error'
});
console.error('Failed to add quarterly window:', errorMessage);
}
} else if (modalMode === 'edit' && editingRow !== null) {
const itemId = quarterData[editingRow]?.id;
if (!itemId) {
setToastData({
message: 'Error: Could not find the item to update',
type: 'error'
});
console.error('Error: Could not find the item to update');
return;
}
const response = await updateQuarterlyWindow(itemId, formattedData);
if (response.status === 'success') {
// Fetch the latest data from the server
const latestData = await getQuarterlyWindows();
if (latestData.status === 'success' && Array.isArray(latestData.data)) {
const formattedData = latestData.data.map(item => ({
id: item.id,
survey_name: item.survey_name || '',
establishment: item.establishment || '-',
year: item.year?.toString() || '',
quarter: item.quarter || '',
startDate: item.start_date || '',
endDate: item.end_date || '',
gracePeriod: item.grace_periods_days ? `${item.grace_periods_days} days` : '0 days',
submissionCount: item.submission_count?.toString() || '-',
assigned: item.assigned?.toString() || '0',
responded: item.responded?.toString() || '0',
not_responded: item.not_responded?.toString() || '0',
status: item.is_active ? 'Active' : 'Inactive'
}));
setQuarterData(formattedData);
}
setToastData({
message: 'Quarterly survey updated successfully!',
type: 'success'
});
} else {
const errorMessage = response.message || 'Failed to update quarterly window';
setToastData({
message: errorMessage,
type: 'error'
});
console.error('Failed to update quarterly window:', errorMessage);
}
setToastData({
message: 'Quarterly survey updated successfully!',
type: 'success'
});
} else {
const errorMessage = response.message || 'Failed to update quarterly window';
setToastData({
message: errorMessage,
type: 'error'
});
console.error('Failed to update quarterly window:', errorMessage);
}
handleCloseModal();
} catch (error) {
const errorMessage = error.response?.data?.message || error.message || 'An error occurred while saving';
setToastData({
message: errorMessage,
type: 'error'
});
console.error('Error saving quarterly window:', error);
} finally {
setIsSaving(false);
}
handleCloseModal();
} catch (error) {
const errorMessage = error.response?.data?.message || error.message || 'An error occurred while saving';
setToastData({
message: errorMessage,
type: 'error'
});
console.error('Error saving quarterly window:', error);
} finally {
setIsSaving(false);
}
};
};
return (
<div className="w-full max-w-[1280px] rounded-lg border border-[#C3C6CB] bg-white min-h-[348px] pb-6">
@ -669,7 +668,7 @@ const handleSave = async () => {
</div>
</div>
)}
<div className="px-6 py-4">
<div className="flex flex-col gap-3 md:h-12 md:flex-row md:items-center md:justify-between">
<h3 className="text-[18px] leading-[28px] font-medium text-[#232528]">Manage Surveys ({filteredRows.length})</h3>
@ -727,9 +726,9 @@ const handleSave = async () => {
// .map((h) => `"${h.name}"`)
// .join(',');
const csvHeader = headers
.filter((h) => extractText(h.name) !== "") // remove Actions
.map((h) => `"${extractText(h.name)}"` ) // extract plain text
.join(',');
.filter((h) => extractText(h.name) !== "") // remove Actions
.map((h) => `"${extractText(h.name)}"`) // extract plain text
.join(',');
const csvRows = filteredRows.map((item) => {
const rowData = [
item.survey_name || '',
@ -758,21 +757,21 @@ const handleSave = async () => {
document.body.removeChild(link);
URL.revokeObjectURL(url);
}}
className={`h-10 px-5 rounded-[6px] border text-sm inline-flex items-center gap-3 ${
filteredRows.length
className={`h-10 min-w-[143px] px-5 rounded-[6px] border text-sm inline-flex items-center gap-3
${filteredRows.length
? 'bg-[#F7F7F7] border-[#C3C6CB] text-[#232528]'
: 'bg-[#F3F4F6] border-[#E5E7EB] text-[#9CA3AF] cursor-not-allowed'
}`}
}
`}
disabled={!filteredRows.length}
style={{ minWidth: '143px' }}
>
<img src={downloadIconSrc} alt="Export" className="h-5 w-5" />
<span className="font-medium">Export CSV</span>
</button>
<button
onClick={openAddModal}
className="h-10 px-5 rounded-[6px] bg-[#92722A] text-white text-sm inline-flex items-center gap-3 cursor-pointer"
style={{ minWidth: '148px' }}
className="h-10 px-5 rounded-[6px] bg-[#92722A] text-white text-sm inline-flex items-center gap-3 cursor-pointer md:min-w-[148px]"
>
<img src={addIconSrc} alt="Add" className="h-5 w-5" />
<span className="font-medium">Create Survey</span>
@ -833,13 +832,7 @@ const handleSave = async () => {
<div className="fixed inset-0 z-50">
<div className="absolute inset-0 bg-black/40" onClick={handleCloseModal} />
<div
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-[16px] bg-white"
style={{
width: '628px',
maxWidth: '92vw',
boxShadow: '0 24px 40px rgba(27, 29, 33, 0.16)',
border: '1px solid #E5E7EB',
}}
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-[16px] bg-white w-[628px] max-w-[92vw] shadow-[0_24px_40px_rgba(27,29,33,0.16)] border border-[#E5E7EB]"
>
<div className="flex items-center justify-between px-6 py-4 border-b border-[#F1F2F4]">
<h3 className="text-[18px] font-medium text-[#232528]">
@ -893,7 +886,7 @@ const handleSave = async () => {
<p className="mt-1 text-sm text-red-600"></p>
)}
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<SelectField
@ -914,7 +907,7 @@ const handleSave = async () => {
<p className="mt-1 text-sm text-red-600"></p>
)}
</div>
<div>
<SelectField
label={
@ -937,7 +930,7 @@ const handleSave = async () => {
<p className="mt-1 text-sm text-red-600"></p>
)}
</div>
<div>
<DateField
label={
@ -957,7 +950,7 @@ const handleSave = async () => {
<p className="mt-1 text-sm text-red-500">{validationErrors.startDate}</p>
)}
</div>
<div>
<DateField
label={
@ -978,7 +971,7 @@ const handleSave = async () => {
)}
</div>
</div>
<div className="w-full">
<SelectField
label="Grace Period (Days)"

View File

@ -62,8 +62,7 @@ const SubmissionDeadlines = () => {
<div className="px-6 pb-6 flex items-center justify-end gap-3">
<button
type="button"
className="h-9 px-5 rounded-md border text-[#92722A] bg-white cursor-pointer"
style={{ borderColor: '#92722A' }}
className="h-9 px-5 rounded-md border border-[#92722A] text-[#92722A] bg-white cursor-pointer"
onClick={handleCancel}
>
Cancel

View File

@ -964,14 +964,8 @@ const downloadSampleCSV = async () => {
<div className="fixed inset-0 z-50">
<div className="absolute inset-0 bg-black/40" onClick={closeModal} />
<div
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-[16px] bg-white"
style={{
width: '628px',
maxWidth: '92vw',
boxShadow: '0 24px 40px rgba(27, 29, 33, 0.16)',
border: '1px solid #E5E7EB',
}}
>
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-[16px] bg-white w-[628px] max-w-[92vw] shadow-[0_24px_40px_rgba(27,29,33,0.16)] border border-[#E5E7EB]"
>
<div className="flex items-center justify-between px-6 py-4 border-b border-[#F1F2F4]">
<h3 className="text-[18px] font-medium text-[#232528]">
View Unit Master
@ -1028,14 +1022,8 @@ const downloadSampleCSV = async () => {
<div className="fixed inset-0 z-50">
<div className="absolute inset-0 bg-black/40" onClick={closeModal} />
<div
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-xl bg-white"
style={{
width: '628px',
maxWidth: '92vw',
boxShadow: '0 24px 40px rgba(27, 29, 33, 0.16)',
border: '1px solid #E5E7EB',
}}
>
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-xl bg-white w-[628px] max-w-[92vw] shadow-[0_24px_40px_rgba(27,29,33,0.16)] border border-[#E5E7EB]"
>
<div className="flex items-center justify-between px-6 py-4 border-b border-[#F1F2F4]">
<h3 className="text-[18px] font-medium text-[#232528]">
{modalMode === 'edit' ? 'Edit Unit Master' : 'Add Unit Master'}
@ -1108,20 +1096,11 @@ const downloadSampleCSV = async () => {
<div className="fixed inset-0 z-50">
<div className="absolute inset-0 bg-black/40" onClick={closeDeleteConfirm} />
<div
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-lg bg-white border"
style={{
width: '480px',
maxWidth: '92vw',
borderColor: '#CBD5E1',
boxShadow: '0 20px 45px rgba(0,0,0,0.12)',
}}
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-lg bg-white border border-[#CBD5E1] w-[480px] max-w-[92vw] shadow-[0_20px_45px_rgba(0,0,0,0.12)]"
>
<div className="px-8 py-6 space-y-6">
<div className="flex items-start gap-3">
<span
className="flex items-center justify-center"
style={{ width: '32px', height: '36px' }}
>
<span className="flex items-center justify-center w-8 h-9">
<img src={deleteIconSrc} alt="Delete" className="h-6 w-6" />
</span>
<div className="flex-1">

View File

@ -56,17 +56,17 @@ const Faq = () => {
if (isOpen) {
ref.current.style.maxHeight = ref.current.scrollHeight + "px";
} else {
ref.current.style.maxHeight = "0px";
ref.current.classList.remove('max-h-md');
ref.current.classList.add('max-h-0');
}
}, [isOpen]);
return (
<div
ref={ref}
className={`overflow-hidden transition-[max-height] duration-300 ${
className={`overflow-hidden transition-[max-height] duration-300 max-h-0 ${
isOpen ? "mt-3" : ""
}`}
style={{ maxHeight: "0px" }}
>
{children}
</div>

View File

@ -516,7 +516,7 @@ const ManufacturingIndex = () => {
Next scheduled run: {nextScheduledRun}
</div>
<div className="flex text-sm text-gray-600">
<InfoOutlinedIcon className="text-gray-400 mr-1 mt-0.5 flex-shrink-0" style={{ fontSize: '1rem' }} />
<InfoOutlinedIcon className="text-gray-400 text-base mr-1 mt-0.5 flex-shrink-0" />
<span className="whitespace-nowrap">Formula: Manufacturing index<sub>t</sub> = Σ (sector weight<sub>i</sub> × group index<sub>i,t</sub>)</span>
</div>
</div>