changes in inline styles
This commit is contained in:
parent
fce67c8e02
commit
1796b3aea4
@ -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>
|
||||
|
||||
@ -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]">
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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" :
|
||||
|
||||
@ -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>
|
||||
);
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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)"
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user