Enhance dashboard components with theme integration and UI improvements
- Updated FarmAiAssistantChat, PlantPestDetection, SmartFertilizationRecommendation, and SmartIrrigationRecommendation components to utilize theme-based styling for better consistency and visual appeal. - Refactored background gradients, box shadows, and hover effects to align with the primary color palette. - Improved layout classes for better responsiveness and user experience across various dashboard sections. - Added new button styles and loading indicators to enhance interactivity and feedback during user actions.
This commit is contained in:
@@ -8,7 +8,13 @@ import Card from '@mui/material/Card'
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
import TextField from '@mui/material/TextField'
|
||||
import Collapse from '@mui/material/Collapse'
|
||||
import { useTheme } from '@mui/material/styles'
|
||||
import { alpha } from '@mui/material/styles'
|
||||
import classnames from 'classnames'
|
||||
|
||||
// Util Imports
|
||||
import { commonLayoutClasses } from '@layouts/utils/layoutClasses'
|
||||
|
||||
import type { FarmContext, FarmAIMessage, AIResponseSection } from './farmAiAssistantTypes'
|
||||
|
||||
// ─── Constants ─────────────────────────────────────────────────────────────
|
||||
@@ -62,6 +68,7 @@ const DEMO_AI_RESPONSE_SECTIONS: AIResponseSection[] = [
|
||||
|
||||
export default function FarmAiAssistantChat() {
|
||||
const t = useTranslations('farmAiAssistant')
|
||||
const theme = useTheme()
|
||||
const [messages, setMessages] = useState<FarmAIMessage[]>([])
|
||||
const [inputValue, setInputValue] = useState('')
|
||||
const [isContextExpanded, setIsContextExpanded] = useState(true)
|
||||
@@ -71,6 +78,7 @@ export default function FarmAiAssistantChat() {
|
||||
const scrollRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
const farmContext = DEFAULT_FARM_CONTEXT
|
||||
const { primary, info, warning } = theme.palette
|
||||
|
||||
// Scroll to bottom on new messages
|
||||
useEffect(() => {
|
||||
@@ -127,35 +135,36 @@ export default function FarmAiAssistantChat() {
|
||||
|
||||
return (
|
||||
<Box
|
||||
className='flex flex-col min-bs-screen max-w-md mx-auto'
|
||||
className={classnames(commonLayoutClasses.contentHeightFixed, 'flex flex-col is-full overflow-hidden rounded')}
|
||||
sx={{
|
||||
background: 'linear-gradient(180deg, #f8fcf8 0%, #f0f7f4 30%, #e8f4ef 100%)',
|
||||
minHeight: '100dvh'
|
||||
background: `linear-gradient(180deg, ${theme.palette.background.default} 0%, ${alpha(primary.main, 0.04)} 30%, ${alpha(primary.main, 0.08)} 100%)`,
|
||||
minHeight: '100%'
|
||||
}}
|
||||
>
|
||||
{/* 1) Smart Header */}
|
||||
<Box
|
||||
className='flex items-center gap-3 px-4 pt-4 pb-3 flex-shrink-0'
|
||||
sx={{
|
||||
background: 'linear-gradient(135deg, rgba(34, 197, 94, 0.06) 0%, rgba(14, 165, 233, 0.06) 100%)',
|
||||
borderBottom: '1px solid rgba(34, 197, 94, 0.12)'
|
||||
background: `linear-gradient(135deg, ${alpha(primary.main, 0.06)} 0%, ${alpha(info.main, 0.06)} 100%)`,
|
||||
borderBottom: `1px solid ${alpha(primary.main, 0.12)}`
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
className='w-12 h-12 rounded-2xl flex items-center justify-center shrink-0'
|
||||
sx={{
|
||||
background: 'linear-gradient(145deg, #22c55e 0%, #0ea5e9 100%)',
|
||||
boxShadow: '0 4px 12px rgba(34, 197, 94, 0.3)'
|
||||
background: `linear-gradient(145deg, ${primary.main} 0%, ${info.main} 100%)`,
|
||||
boxShadow: `0 4px 12px ${alpha(primary.main, 0.3)}`,
|
||||
color: 'primary.contrastText'
|
||||
}}
|
||||
>
|
||||
<i className='tabler-leaf text-2xl text-white' />
|
||||
<i className='tabler-leaf text-2xl' style={{ color: 'inherit' }} />
|
||||
</Box>
|
||||
<Box className='min-w-0 flex-1'>
|
||||
<Typography
|
||||
variant='h6'
|
||||
className='font-bold truncate'
|
||||
sx={{
|
||||
background: 'linear-gradient(135deg, #16a34a 0%, #0ea5e9 100%)',
|
||||
background: `linear-gradient(135deg, ${primary.dark} 0%, ${info.main} 100%)`,
|
||||
backgroundClip: 'text',
|
||||
WebkitBackgroundClip: 'text',
|
||||
color: 'transparent',
|
||||
@@ -174,9 +183,9 @@ export default function FarmAiAssistantChat() {
|
||||
<Box
|
||||
className='mx-4 mt-3 flex-shrink-0 rounded-2xl overflow-hidden'
|
||||
sx={{
|
||||
background: 'linear-gradient(145deg, #ffffff 0%, rgba(34, 197, 94, 0.04) 100%)',
|
||||
border: '1px solid rgba(34, 197, 94, 0.15)',
|
||||
boxShadow: '0 2px 12px rgba(0,0,0,0.04)'
|
||||
background: `linear-gradient(145deg, ${theme.palette.background.paper} 0%, ${alpha(primary.main, 0.04)} 100%)`,
|
||||
border: `1px solid ${alpha(primary.main, 0.15)}`,
|
||||
boxShadow: theme.shadows[1]
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
@@ -185,7 +194,7 @@ export default function FarmAiAssistantChat() {
|
||||
onClick={() => setIsContextExpanded(!isContextExpanded)}
|
||||
className='w-full flex items-center justify-between px-4 py-3 text-start'
|
||||
sx={{
|
||||
'&:hover': { bgcolor: 'rgba(34, 197, 94, 0.04)' },
|
||||
'&:hover': { bgcolor: alpha(primary.main, 0.04) },
|
||||
transition: 'background 0.2s'
|
||||
}}
|
||||
>
|
||||
@@ -193,9 +202,10 @@ export default function FarmAiAssistantChat() {
|
||||
{t('context.title')}
|
||||
</Typography>
|
||||
<i
|
||||
className={classnames('tabler-chevron-down text-xl text-[#22c55e] transition-transform duration-300', {
|
||||
className={classnames('tabler-chevron-down text-xl transition-transform duration-300', {
|
||||
'rotate-180': isContextExpanded
|
||||
})}
|
||||
style={{ color: primary.main }}
|
||||
/>
|
||||
</Box>
|
||||
<Collapse in={isContextExpanded}>
|
||||
@@ -225,10 +235,10 @@ export default function FarmAiAssistantChat() {
|
||||
<Box
|
||||
className='w-20 h-20 rounded-3xl flex items-center justify-center mb-4'
|
||||
sx={{
|
||||
background: 'linear-gradient(145deg, rgba(34, 197, 94, 0.1) 0%, rgba(14, 165, 233, 0.1) 100%)'
|
||||
background: `linear-gradient(145deg, ${alpha(primary.main, 0.1)} 0%, ${alpha(info.main, 0.1)} 100%)`
|
||||
}}
|
||||
>
|
||||
<i className='tabler-message-circle text-4xl text-[#22c55e]' />
|
||||
<i className='tabler-message-circle text-4xl' style={{ color: primary.main }} />
|
||||
</Box>
|
||||
<Typography variant='body1' color='text.secondary' className='mb-1'>
|
||||
{t('emptyState.title')}
|
||||
@@ -255,17 +265,18 @@ export default function FarmAiAssistantChat() {
|
||||
<Box
|
||||
className='w-9 h-9 rounded-xl flex items-center justify-center shrink-0'
|
||||
sx={{
|
||||
background: 'linear-gradient(145deg, #22c55e 0%, #0ea5e9 100%)'
|
||||
background: `linear-gradient(145deg, ${primary.main} 0%, ${info.main} 100%)`,
|
||||
color: 'primary.contrastText'
|
||||
}}
|
||||
>
|
||||
<i className='tabler-leaf text-lg text-white' />
|
||||
<i className='tabler-leaf text-lg' style={{ color: 'inherit' }} />
|
||||
</Box>
|
||||
<Box
|
||||
className='px-4 py-3 rounded-2xl rounded-tl-md'
|
||||
sx={{
|
||||
background: 'linear-gradient(145deg, #ffffff 0%, rgba(34, 197, 94, 0.05) 100%)',
|
||||
border: '1px solid rgba(34, 197, 94, 0.15)',
|
||||
boxShadow: '0 2px 12px rgba(0,0,0,0.04)'
|
||||
background: `linear-gradient(145deg, ${theme.palette.background.paper} 0%, ${alpha(primary.main, 0.05)} 100%)`,
|
||||
border: `1px solid ${alpha(primary.main, 0.15)}`,
|
||||
boxShadow: theme.shadows[1]
|
||||
}}
|
||||
>
|
||||
<TypingIndicator />
|
||||
@@ -290,14 +301,12 @@ export default function FarmAiAssistantChat() {
|
||||
sx={{
|
||||
background:
|
||||
selectedChip === chip.id
|
||||
? 'linear-gradient(135deg, #22c55e 0%, #16a34a 100%)'
|
||||
: 'linear-gradient(145deg, #ffffff 0%, rgba(34, 197, 94, 0.08) 100%)',
|
||||
color: selectedChip === chip.id ? '#ffffff' : 'text.primary',
|
||||
border: selectedChip === chip.id ? 'none' : '1px solid rgba(34, 197, 94, 0.2)',
|
||||
? `linear-gradient(135deg, ${primary.main} 0%, ${primary.dark} 100%)`
|
||||
: `linear-gradient(145deg, ${theme.palette.background.paper} 0%, ${alpha(primary.main, 0.08)} 100%)`,
|
||||
color: selectedChip === chip.id ? 'primary.contrastText' : 'text.primary',
|
||||
border: selectedChip === chip.id ? 'none' : `1px solid ${alpha(primary.main, 0.2)}`,
|
||||
boxShadow:
|
||||
selectedChip === chip.id
|
||||
? '0 4px 12px rgba(34, 197, 94, 0.35)'
|
||||
: '0 2px 8px rgba(0,0,0,0.04)'
|
||||
selectedChip === chip.id ? `0 4px 12px ${alpha(primary.main, 0.35)}` : theme.shadows[1]
|
||||
}}
|
||||
>
|
||||
{t(chip.labelKey)}
|
||||
@@ -310,17 +319,17 @@ export default function FarmAiAssistantChat() {
|
||||
<Box
|
||||
className='px-4 py-3 pb-6 flex-shrink-0'
|
||||
sx={{
|
||||
background: 'linear-gradient(to top, rgba(248,252,248,0.98) 0%, rgba(248,252,248,0.9) 70%, transparent 100%)',
|
||||
background: `linear-gradient(to top, ${alpha(theme.palette.background.default, 0.98)} 0%, ${alpha(theme.palette.background.default, 0.9)} 70%, transparent 100%)`,
|
||||
backdropFilter: 'blur(12px)'
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
className='flex items-end gap-2'
|
||||
sx={{
|
||||
background: '#ffffff',
|
||||
bgcolor: 'background.paper',
|
||||
borderRadius: '24px',
|
||||
border: '1px solid rgba(34, 197, 94, 0.2)',
|
||||
boxShadow: '0 4px 20px rgba(34, 197, 94, 0.08)',
|
||||
border: `1px solid ${alpha(primary.main, 0.2)}`,
|
||||
boxShadow: `0 4px 20px ${alpha(primary.main, 0.08)}`,
|
||||
p: 1
|
||||
}}
|
||||
>
|
||||
@@ -331,7 +340,7 @@ export default function FarmAiAssistantChat() {
|
||||
className='shrink-0'
|
||||
sx={{
|
||||
color: 'text.secondary',
|
||||
'&:hover': { color: '#22c55e', bgcolor: 'rgba(34, 197, 94, 0.08)' }
|
||||
'&:hover': { color: 'primary.main', bgcolor: 'primary.lighterOpacity' }
|
||||
}}
|
||||
>
|
||||
<i className='tabler-camera text-xl' />
|
||||
@@ -342,7 +351,7 @@ export default function FarmAiAssistantChat() {
|
||||
className='shrink-0'
|
||||
sx={{
|
||||
color: 'text.secondary',
|
||||
'&:hover': { color: '#0ea5e9', bgcolor: 'rgba(14, 165, 233, 0.08)' }
|
||||
'&:hover': { color: 'info.main', bgcolor: 'info.lighterOpacity' }
|
||||
}}
|
||||
>
|
||||
<i className='tabler-microphone text-xl' />
|
||||
@@ -377,13 +386,13 @@ export default function FarmAiAssistantChat() {
|
||||
className='shrink-0'
|
||||
sx={{
|
||||
background: inputValue.trim()
|
||||
? 'linear-gradient(135deg, #22c55e 0%, #16a34a 100%)'
|
||||
? `linear-gradient(135deg, ${primary.main} 0%, ${primary.dark} 100%)`
|
||||
: 'action.disabledBackground',
|
||||
color: inputValue.trim() ? '#ffffff' : 'action.disabled',
|
||||
color: inputValue.trim() ? 'primary.contrastText' : 'action.disabled',
|
||||
'&:hover': inputValue.trim()
|
||||
? {
|
||||
background: 'linear-gradient(135deg, #16a34a 0%, #15803d 100%)',
|
||||
boxShadow: '0 4px 12px rgba(34, 197, 94, 0.4)'
|
||||
background: `linear-gradient(135deg, ${primary.dark} 0%, ${primary.dark} 100%)`,
|
||||
boxShadow: `0 4px 12px ${alpha(primary.main, 0.4)}`
|
||||
}
|
||||
: {},
|
||||
'&.Mui-disabled': { background: 'action.disabledBackground', color: 'action.disabled' }
|
||||
@@ -410,15 +419,18 @@ function ContextBadge({
|
||||
value: string
|
||||
colSpan?: number
|
||||
}) {
|
||||
const theme = useTheme()
|
||||
const primary = theme.palette.primary
|
||||
|
||||
return (
|
||||
<Box
|
||||
className={classnames('flex items-center gap-2 px-3 py-2 rounded-xl', colSpan === 2 ? 'col-span-2' : '')}
|
||||
sx={{
|
||||
background: 'rgba(34, 197, 94, 0.06)',
|
||||
border: '1px solid rgba(34, 197, 94, 0.12)'
|
||||
background: alpha(primary.main, 0.06),
|
||||
border: `1px solid ${alpha(primary.main, 0.12)}`
|
||||
}}
|
||||
>
|
||||
<i className={`${icon} text-lg text-[#22c55e] shrink-0`} />
|
||||
<i className={`${icon} text-lg shrink-0`} style={{ color: primary.main }} />
|
||||
<Box className='min-w-0'>
|
||||
<Typography variant='caption' color='text.secondary' display='block' lineHeight={1.2}>
|
||||
{label}
|
||||
@@ -442,6 +454,8 @@ function MessageBubble({
|
||||
onToggleExplanation: (id: string) => void
|
||||
t: (key: string) => string
|
||||
}) {
|
||||
const theme = useTheme()
|
||||
const { primary, info } = theme.palette
|
||||
const isUser = message.role === 'user'
|
||||
|
||||
if (isUser) {
|
||||
@@ -450,8 +464,8 @@ function MessageBubble({
|
||||
<Box
|
||||
className='max-w-[85%] px-4 py-2.5 rounded-2xl rounded-tr-md'
|
||||
sx={{
|
||||
background: 'linear-gradient(145deg, rgba(34, 197, 94, 0.15) 0%, rgba(34, 197, 94, 0.08) 100%)',
|
||||
border: '1px solid rgba(34, 197, 94, 0.2)'
|
||||
background: `linear-gradient(145deg, ${alpha(primary.main, 0.15)} 0%, ${alpha(primary.main, 0.08)} 100%)`,
|
||||
border: `1px solid ${alpha(primary.main, 0.2)}`
|
||||
}}
|
||||
>
|
||||
<Typography variant='body2' color='text.primary'>
|
||||
@@ -468,10 +482,11 @@ function MessageBubble({
|
||||
<Box
|
||||
className='w-9 h-9 rounded-xl flex items-center justify-center shrink-0'
|
||||
sx={{
|
||||
background: 'linear-gradient(145deg, #22c55e 0%, #0ea5e9 100%)'
|
||||
background: `linear-gradient(145deg, ${primary.main} 0%, ${info.main} 100%)`,
|
||||
color: 'primary.contrastText'
|
||||
}}
|
||||
>
|
||||
<i className='tabler-leaf text-lg text-white' />
|
||||
<i className='tabler-leaf text-lg' style={{ color: 'inherit' }} />
|
||||
</Box>
|
||||
<Box className='flex-1 min-w-0 space-y-3'>
|
||||
{message.sections?.map((section, idx) => (
|
||||
@@ -505,6 +520,8 @@ function AISectionCard({
|
||||
idx: number
|
||||
t: (key: string) => string
|
||||
}) {
|
||||
const theme = useTheme()
|
||||
const { primary, warning: warningPalette } = theme.palette
|
||||
const expId = `${messageId}-exp-${idx}`
|
||||
|
||||
const iconMap = {
|
||||
@@ -523,14 +540,14 @@ function AISectionCard({
|
||||
className='overflow-hidden'
|
||||
sx={{
|
||||
borderRadius: '20px',
|
||||
background: 'linear-gradient(160deg, #ffffff 0%, rgba(34, 197, 94, 0.06) 100%)',
|
||||
border: '1px solid rgba(34, 197, 94, 0.2)',
|
||||
boxShadow: '0 4px 20px rgba(34, 197, 94, 0.12)'
|
||||
background: `linear-gradient(160deg, ${theme.palette.background.paper} 0%, ${alpha(primary.main, 0.06)} 100%)`,
|
||||
border: `1px solid ${alpha(primary.main, 0.2)}`,
|
||||
boxShadow: `0 4px 20px ${alpha(primary.main, 0.12)}`
|
||||
}}
|
||||
>
|
||||
<Box className='p-4'>
|
||||
<Box className='flex items-center gap-2 mb-3'>
|
||||
<i className={`${iconClass} text-xl text-[#22c55e]`} />
|
||||
<i className={`${iconClass} text-xl`} style={{ color: primary.main }} />
|
||||
<Typography variant='subtitle2' fontWeight={700} color='primary'>
|
||||
{section.title}
|
||||
</Typography>
|
||||
@@ -574,7 +591,7 @@ function AISectionCard({
|
||||
type='button'
|
||||
onClick={() => onToggleExplanation(expId)}
|
||||
className='flex items-center gap-1 text-sm font-medium'
|
||||
sx={{ color: '#22c55e', '&:hover': { color: '#16a34a' } }}
|
||||
sx={{ color: 'primary.main', '&:hover': { color: 'primary.dark' } }}
|
||||
>
|
||||
{t('recommendation.whyThis')}
|
||||
<i
|
||||
@@ -600,12 +617,15 @@ function AISectionCard({
|
||||
<Box
|
||||
className='p-4 rounded-2xl'
|
||||
sx={{
|
||||
background: 'linear-gradient(135deg, rgba(251, 191, 36, 0.15) 0%, rgba(245, 158, 11, 0.08) 100%)',
|
||||
border: '1px solid rgba(251, 191, 36, 0.35)'
|
||||
background: `linear-gradient(135deg, ${alpha(warningPalette.main, 0.15)} 0%, ${alpha(warningPalette.main, 0.08)} 100%)`,
|
||||
border: `1px solid ${alpha(warningPalette.main, 0.35)}`
|
||||
}}
|
||||
>
|
||||
<Box className='flex gap-2'>
|
||||
<i className='tabler-alert-triangle text-xl text-amber-600 shrink-0 mt-0.5' />
|
||||
<i
|
||||
className='tabler-alert-triangle text-xl shrink-0 mt-0.5'
|
||||
style={{ color: warningPalette.dark }}
|
||||
/>
|
||||
<Box>
|
||||
{section.title && (
|
||||
<Typography variant='subtitle2' fontWeight={600} color='warning.dark' className='mb-1'>
|
||||
@@ -626,13 +646,13 @@ function AISectionCard({
|
||||
<Box
|
||||
className='p-4 rounded-2xl'
|
||||
sx={{
|
||||
background: 'linear-gradient(145deg, #ffffff 0%, rgba(34, 197, 94, 0.04) 100%)',
|
||||
border: '1px solid rgba(34, 197, 94, 0.15)'
|
||||
background: `linear-gradient(145deg, ${theme.palette.background.paper} 0%, ${alpha(primary.main, 0.04)} 100%)`,
|
||||
border: `1px solid ${alpha(primary.main, 0.15)}`
|
||||
}}
|
||||
>
|
||||
{section.title && (
|
||||
<Box className='flex items-center gap-2 mb-2'>
|
||||
<i className={`${iconClass} text-lg text-[#22c55e]`} />
|
||||
<i className={`${iconClass} text-lg`} style={{ color: primary.main }} />
|
||||
<Typography variant='subtitle2' fontWeight={600} color='text.primary'>
|
||||
{section.title}
|
||||
</Typography>
|
||||
@@ -660,8 +680,9 @@ function TypingIndicator() {
|
||||
{[0, 1, 2].map(i => (
|
||||
<Box
|
||||
key={i}
|
||||
className='w-2 h-2 rounded-full bg-[#22c55e]'
|
||||
className='w-2 h-2 rounded-full'
|
||||
sx={{
|
||||
bgcolor: 'primary.main',
|
||||
animation: `typing-bounce 1.4s ease-in-out ${i * 0.16}s infinite both`
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -9,6 +9,13 @@ import Typography from '@mui/material/Typography'
|
||||
import Button from '@mui/material/Button'
|
||||
import CircularProgress from '@mui/material/CircularProgress'
|
||||
import Collapse from '@mui/material/Collapse'
|
||||
import { useTheme } from '@mui/material/styles'
|
||||
import { alpha } from '@mui/material/styles'
|
||||
import classnames from 'classnames'
|
||||
|
||||
// Util Imports
|
||||
import { commonLayoutClasses } from '@layouts/utils/layoutClasses'
|
||||
|
||||
import UploadBox from './components/UploadBox'
|
||||
import ResultCard from './components/ResultCard'
|
||||
import type { UploadedFile } from './components/UploadBox'
|
||||
@@ -16,6 +23,8 @@ import type { PestResult } from './components/ResultCard'
|
||||
|
||||
export default function PlantPestDetection() {
|
||||
const t = useTranslations('pestDetection')
|
||||
const theme = useTheme()
|
||||
const primary = theme.palette.primary
|
||||
const [file, setFile] = useState<UploadedFile | null>(null)
|
||||
const [result, setResult] = useState<PestResult | null>(null)
|
||||
const [loading, setLoading] = useState(false)
|
||||
@@ -58,19 +67,17 @@ export default function PlantPestDetection() {
|
||||
|
||||
return (
|
||||
<Box
|
||||
className="min-bs-screen pb-24"
|
||||
sx={{
|
||||
minHeight: '100vh',
|
||||
}}
|
||||
className={classnames(commonLayoutClasses.contentHeightFixed, 'flex flex-col is-full overflow-hidden rounded')}
|
||||
sx={{ minHeight: '100%' }}
|
||||
>
|
||||
<Box className="max-w-2xl mx-auto px-4 py-6 sm:py-8">
|
||||
<Box className="is-full py-6 sm:py-8 flex flex-col">
|
||||
{/* Header */}
|
||||
<Box className="mb-8">
|
||||
<Typography
|
||||
variant="h4"
|
||||
className="font-bold tracking-tight"
|
||||
sx={{
|
||||
background: 'linear-gradient(135deg, #22c55e 0%, #16a34a 50%, #15803d 100%)',
|
||||
background: `linear-gradient(135deg, ${primary.main} 0%, ${primary.dark} 50%, ${primary.dark} 100%)`,
|
||||
backgroundClip: 'text',
|
||||
WebkitBackgroundClip: 'text',
|
||||
color: 'transparent',
|
||||
@@ -90,9 +97,9 @@ export default function PlantPestDetection() {
|
||||
className="mb-6 overflow-hidden transition-all duration-300 hover:shadow-lg"
|
||||
sx={{
|
||||
borderRadius: '24px',
|
||||
background: 'linear-gradient(145deg, #ffffff 0%, #f8fcf8 100%)',
|
||||
boxShadow: '0 4px 24px rgba(34, 197, 94, 0.08), 0 1px 3px rgba(0,0,0,0.04)',
|
||||
border: '1px solid rgba(34, 197, 94, 0.12)',
|
||||
background: `linear-gradient(145deg, ${theme.palette.background.paper} 0%, ${alpha(primary.main, 0.04)} 100%)`,
|
||||
boxShadow: `0 4px 24px ${alpha(primary.main, 0.08)}, ${theme.shadows[1]}`,
|
||||
border: `1px solid ${alpha(primary.main, 0.12)}`,
|
||||
}}
|
||||
>
|
||||
<CardContent className="p-5 sm:p-6">
|
||||
@@ -111,18 +118,18 @@ export default function PlantPestDetection() {
|
||||
onClick={handleAnalyze}
|
||||
startIcon={
|
||||
loading ? (
|
||||
<CircularProgress size={20} color="inherit" sx={{ color: 'white' }} />
|
||||
<CircularProgress size={20} color="inherit" />
|
||||
) : (
|
||||
<i className="tabler-scan text-xl" />
|
||||
)
|
||||
}
|
||||
className="rounded-xl py-3 px-8 font-semibold shadow-md transition-all duration-300 hover:shadow-lg hover:scale-[1.02] active:scale-[0.98]"
|
||||
sx={{
|
||||
background: 'linear-gradient(135deg, #22c55e 0%, #16a34a 50%, #15803d 100%)',
|
||||
boxShadow: '0 4px 20px rgba(34, 197, 94, 0.35)',
|
||||
background: `linear-gradient(135deg, ${primary.main} 0%, ${primary.dark} 50%, ${primary.dark} 100%)`,
|
||||
boxShadow: `0 4px 20px ${alpha(primary.main, 0.35)}`,
|
||||
'&:hover': {
|
||||
background: 'linear-gradient(135deg, #4ade80 0%, #22c55e 50%, #16a34a 100%)',
|
||||
boxShadow: '0 6px 24px rgba(34, 197, 94, 0.45)',
|
||||
background: `linear-gradient(135deg, ${primary.light} 0%, ${primary.main} 50%, ${primary.dark} 100%)`,
|
||||
boxShadow: `0 6px 24px ${alpha(primary.main, 0.45)}`,
|
||||
},
|
||||
'&:disabled': {
|
||||
background: 'action.disabledBackground',
|
||||
@@ -162,13 +169,13 @@ export default function PlantPestDetection() {
|
||||
className="mb-6"
|
||||
sx={{
|
||||
borderRadius: '24px',
|
||||
background: 'linear-gradient(160deg, #ffffff 0%, #f0fdf4 100%)',
|
||||
boxShadow: '0 8px 32px rgba(34, 197, 94, 0.1)',
|
||||
border: '1px solid rgba(34, 197, 94, 0.12)',
|
||||
background: `linear-gradient(160deg, ${theme.palette.background.paper} 0%, ${alpha(primary.main, 0.06)} 100%)`,
|
||||
boxShadow: `0 8px 32px ${alpha(primary.main, 0.1)}`,
|
||||
border: `1px solid ${alpha(primary.main, 0.12)}`,
|
||||
}}
|
||||
>
|
||||
<CardContent className="p-12 flex flex-col items-center gap-4">
|
||||
<CircularProgress size={48} sx={{ color: '#22c55e' }} />
|
||||
<CircularProgress size={48} sx={{ color: 'primary.main' }} />
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{t('analyzing')}
|
||||
</Typography>
|
||||
|
||||
@@ -8,6 +8,7 @@ import CardContent from '@mui/material/CardContent'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import Button from '@mui/material/Button'
|
||||
import Collapse from '@mui/material/Collapse'
|
||||
import { useTheme, alpha } from '@mui/material/styles'
|
||||
|
||||
// Types
|
||||
interface FarmData {
|
||||
@@ -77,6 +78,11 @@ function generateFertilizationPlan(
|
||||
|
||||
export default function SmartFertilizationRecommendation() {
|
||||
const t = useTranslations('fertilization')
|
||||
const theme = useTheme()
|
||||
const primaryMain = theme.palette.primary.main
|
||||
const primaryLight = theme.palette.primary.light
|
||||
const primaryDark = theme.palette.primary.dark
|
||||
const paperBg = theme.palette.background.paper
|
||||
const [farmData] = useState<FarmData>(DEFAULT_FARM_DATA)
|
||||
const [growthStage, setGrowthStage] = useState<string>(GROWTH_STAGES[0].id)
|
||||
const [selectedCrop, setSelectedCrop] = useState<string | null>(null)
|
||||
@@ -101,10 +107,10 @@ export default function SmartFertilizationRecommendation() {
|
||||
|
||||
return (
|
||||
<Box
|
||||
className='min-bs-screen pb-28'
|
||||
className='min-bs-screen'
|
||||
sx={{
|
||||
background:
|
||||
'linear-gradient(165deg, #f0fdf4 0%, #ecfdf5 25%, #faf5ff 60%, var(--mui-palette-background-default) 100%)',
|
||||
background: (th) =>
|
||||
`linear-gradient(165deg, ${alpha(th.palette.primary.main, 0.08)} 0%, ${alpha(th.palette.primary.main, 0.05)} 25%, ${alpha(th.palette.primary.main, 0.03)} 60%, ${th.palette.background.default} 100%)`,
|
||||
minHeight: '100vh'
|
||||
}}
|
||||
>
|
||||
@@ -115,8 +121,7 @@ export default function SmartFertilizationRecommendation() {
|
||||
variant='h4'
|
||||
className='font-bold tracking-tight'
|
||||
sx={{
|
||||
background:
|
||||
'linear-gradient(135deg, #16a34a 0%, #22c55e 40%, #7c3aed 100%)',
|
||||
background: `linear-gradient(135deg, ${primaryDark} 0%, ${primaryMain} 40%, ${primaryLight} 100%)`,
|
||||
backgroundClip: 'text',
|
||||
WebkitBackgroundClip: 'text',
|
||||
color: 'transparent',
|
||||
@@ -140,11 +145,9 @@ export default function SmartFertilizationRecommendation() {
|
||||
className='mb-6 overflow-hidden transition-all duration-300 hover:shadow-lg animate-fade-in'
|
||||
sx={{
|
||||
borderRadius: '28px',
|
||||
background:
|
||||
'linear-gradient(145deg, #ffffff 0%, #faf5ff 50%, #f0fdf4 100%)',
|
||||
boxShadow:
|
||||
'0 4px 24px rgba(34, 197, 94, 0.08), 0 4px 12px rgba(124, 58, 237, 0.04), 0 1px 3px rgba(0,0,0,0.04)',
|
||||
border: '1px solid rgba(34, 197, 94, 0.12)'
|
||||
background: `linear-gradient(145deg, ${paperBg} 0%, ${alpha(primaryMain, 0.06)} 50%, ${alpha(primaryMain, 0.04)} 100%)`,
|
||||
boxShadow: `0 4px 24px ${alpha(primaryMain, 0.08)}, 0 4px 12px ${alpha(primaryMain, 0.04)}, 0 1px 3px rgba(0,0,0,0.04)`,
|
||||
border: `1px solid ${alpha(primaryMain, 0.12)}`
|
||||
}}
|
||||
>
|
||||
<CardContent className='p-5'>
|
||||
@@ -155,10 +158,9 @@ export default function SmartFertilizationRecommendation() {
|
||||
<Box
|
||||
className='px-2.5 py-1 rounded-full text-xs font-medium flex items-center gap-1.5'
|
||||
sx={{
|
||||
background:
|
||||
'linear-gradient(135deg, #22c55e 0%, #16a34a 100%)',
|
||||
background: (th) => `linear-gradient(135deg, ${th.palette.success.main} 0%, ${th.palette.success.dark} 100%)`,
|
||||
color: 'white',
|
||||
boxShadow: '0 2px 8px rgba(34, 197, 94, 0.3)'
|
||||
boxShadow: (th) => `0 2px 8px ${alpha(th.palette.success.main, 0.3)}`
|
||||
}}
|
||||
>
|
||||
<i className='tabler-circle-check text-sm' />
|
||||
@@ -193,18 +195,18 @@ export default function SmartFertilizationRecommendation() {
|
||||
onClick={() => setGrowthStage(stage.id)}
|
||||
className='flex flex-col items-center gap-1.5 px-4 py-3 rounded-2xl shrink-0 transition-all duration-300 ease-out border-2 cursor-pointer min-w-[72px]'
|
||||
sx={{
|
||||
borderColor: isSelected ? '#22c55e' : 'transparent',
|
||||
borderColor: isSelected ? primaryMain : 'transparent',
|
||||
background: isSelected
|
||||
? 'linear-gradient(145deg, rgba(34, 197, 94, 0.15) 0%, rgba(124, 58, 237, 0.06) 100%)'
|
||||
: 'linear-gradient(145deg, #ffffff 0%, #faf5ff 100%)',
|
||||
? `linear-gradient(145deg, ${alpha(primaryMain, 0.15)} 0%, ${alpha(primaryMain, 0.06)} 100%)`
|
||||
: `linear-gradient(145deg, ${paperBg} 0%, ${alpha(primaryMain, 0.04)} 100%)`,
|
||||
boxShadow: isSelected
|
||||
? '0 4px 20px rgba(34, 197, 94, 0.2), inset 0 1px 0 rgba(255,255,255,0.8)'
|
||||
? `0 4px 20px ${alpha(primaryMain, 0.2)}, inset 0 1px 0 rgba(255,255,255,0.8)`
|
||||
: '0 2px 8px rgba(0,0,0,0.04)',
|
||||
'&:hover': {
|
||||
transform: 'translateY(-2px)',
|
||||
boxShadow: isSelected
|
||||
? '0 6px 24px rgba(34, 197, 94, 0.25)'
|
||||
: '0 4px 16px rgba(124, 58, 237, 0.1)'
|
||||
? `0 6px 24px ${alpha(primaryMain, 0.25)}`
|
||||
: `0 4px 16px ${alpha(primaryMain, 0.1)}`
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -212,23 +214,22 @@ export default function SmartFertilizationRecommendation() {
|
||||
className='w-10 h-10 rounded-xl flex items-center justify-center transition-all duration-300'
|
||||
sx={{
|
||||
background: isSelected
|
||||
? 'linear-gradient(135deg, #22c55e 0%, #16a34a 100%)'
|
||||
? `linear-gradient(135deg, ${primaryMain} 0%, ${primaryDark} 100%)`
|
||||
: isPast
|
||||
? 'linear-gradient(145deg, rgba(34, 197, 94, 0.2) 0%, rgba(34, 197, 94, 0.1) 100%)'
|
||||
: 'rgba(124, 58, 237, 0.08)'
|
||||
? `linear-gradient(145deg, ${alpha(primaryMain, 0.2)} 0%, ${alpha(primaryMain, 0.1)} 100%)`
|
||||
: alpha(primaryMain, 0.08)
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className={`${stage.icon} text-xl transition-colors duration-300 ${
|
||||
isSelected ? 'text-white' : isPast ? 'text-emerald-600' : 'text-violet-500'
|
||||
}`}
|
||||
className={`${stage.icon} text-xl transition-colors duration-300 ${isSelected ? 'text-white' : ''}`}
|
||||
style={!isSelected ? { color: primaryMain } : undefined}
|
||||
/>
|
||||
</Box>
|
||||
<Typography
|
||||
variant='caption'
|
||||
fontWeight={600}
|
||||
sx={{
|
||||
color: isSelected ? '#16a34a' : 'text.secondary',
|
||||
color: isSelected ? 'primary.main' : 'text.secondary',
|
||||
textAlign: 'center',
|
||||
lineHeight: 1.2
|
||||
}}
|
||||
@@ -244,7 +245,7 @@ export default function SmartFertilizationRecommendation() {
|
||||
<Typography variant='subtitle2' fontWeight={600} color='text.secondary' className='mbe-3'>
|
||||
{t('plantSelection.title')}
|
||||
</Typography>
|
||||
<Box className='flex flex-wrap gap-3 mb-8'>
|
||||
<Box className='flex flex-wrap gap-3 mb-6'>
|
||||
{CROP_OPTIONS.map(crop => (
|
||||
<CropCard
|
||||
key={crop.id}
|
||||
@@ -258,6 +259,33 @@ export default function SmartFertilizationRecommendation() {
|
||||
))}
|
||||
</Box>
|
||||
|
||||
{/* 5) Primary CTA Button - End of form */}
|
||||
<Box className='mb-8'>
|
||||
<Button
|
||||
fullWidth
|
||||
variant='contained'
|
||||
disabled={!selectedCrop || loading}
|
||||
onClick={handleGenerate}
|
||||
startIcon={<i className='tabler-sparkles text-xl' />}
|
||||
className='rounded-2xl py-3.5 text-base font-semibold shadow-lg transition-all duration-300 hover:shadow-xl hover:scale-[1.01] active:scale-[0.99]'
|
||||
sx={{
|
||||
background: `linear-gradient(135deg, ${primaryLight} 0%, ${primaryMain} 50%, ${primaryDark} 100%)`,
|
||||
boxShadow: `0 4px 20px ${alpha(primaryMain, 0.4)}`,
|
||||
'&:hover': {
|
||||
background: `linear-gradient(135deg, ${primaryLight} 0%, ${primaryMain} 50%, ${primaryDark} 100%)`,
|
||||
boxShadow: `0 6px 28px ${alpha(primaryMain, 0.5)}`,
|
||||
filter: 'brightness(1.05)'
|
||||
},
|
||||
'&:disabled': {
|
||||
background: 'action.disabledBackground',
|
||||
color: 'action.disabled'
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('generateCta')}
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
{/* 6) Result Section - Prescription style */}
|
||||
{plan && (
|
||||
<Box className='mb-6 animate-fade-in'>
|
||||
@@ -265,17 +293,15 @@ export default function SmartFertilizationRecommendation() {
|
||||
elevation={0}
|
||||
sx={{
|
||||
borderRadius: '28px',
|
||||
background:
|
||||
'linear-gradient(160deg, #ffffff 0%, #faf5ff 40%, #f0fdf4 100%)',
|
||||
boxShadow:
|
||||
'0 8px 32px rgba(34, 197, 94, 0.12), 0 4px 16px rgba(124, 58, 237, 0.06), 0 2px 8px rgba(0,0,0,0.04)',
|
||||
border: '1px solid rgba(34, 197, 94, 0.15)',
|
||||
background: `linear-gradient(160deg, ${paperBg} 0%, ${alpha(primaryMain, 0.06)} 40%, ${alpha(primaryMain, 0.04)} 100%)`,
|
||||
boxShadow: `0 8px 32px ${alpha(primaryMain, 0.12)}, 0 4px 16px ${alpha(primaryMain, 0.06)}, 0 2px 8px rgba(0,0,0,0.04)`,
|
||||
border: `1px solid ${alpha(primaryMain, 0.15)}`,
|
||||
overflow: 'visible'
|
||||
}}
|
||||
>
|
||||
<CardContent className='p-6'>
|
||||
<Box className='flex items-center gap-2 mbe-5'>
|
||||
<i className='tabler-prescription text-2xl text-emerald-600' />
|
||||
<i className='tabler-prescription text-2xl' style={{ color: primaryMain }} />
|
||||
<Typography variant='h6' fontWeight={700} color='text.primary'>
|
||||
{t('result.title')}
|
||||
</Typography>
|
||||
@@ -308,8 +334,8 @@ export default function SmartFertilizationRecommendation() {
|
||||
<Box
|
||||
className='mt-5 rounded-2xl overflow-hidden transition-all duration-300'
|
||||
sx={{
|
||||
border: '1px solid rgba(34, 197, 94, 0.15)',
|
||||
background: 'rgba(34, 197, 94, 0.04)'
|
||||
border: `1px solid ${alpha(primaryMain, 0.15)}`,
|
||||
background: alpha(primaryMain, 0.04)
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
@@ -317,18 +343,19 @@ export default function SmartFertilizationRecommendation() {
|
||||
type='button'
|
||||
onClick={() => setReasoningExpanded(!reasoningExpanded)}
|
||||
className='w-full flex items-center justify-between px-4 py-3 text-start cursor-pointer'
|
||||
sx={{ '&:hover': { bgcolor: 'rgba(34, 197, 94, 0.06)' } }}
|
||||
sx={{ '&:hover': { bgcolor: alpha(primaryMain, 0.06) } }}
|
||||
>
|
||||
<Box className='flex items-center gap-2'>
|
||||
<i className='tabler-brain text-lg text-emerald-600' />
|
||||
<i className='tabler-brain text-lg' style={{ color: primaryMain }} />
|
||||
<Typography variant='subtitle2' fontWeight={600} color='text.primary'>
|
||||
{t('result.whyRecommendation')}
|
||||
</Typography>
|
||||
</Box>
|
||||
<i
|
||||
className={`tabler-chevron-down text-xl text-emerald-600 transition-transform duration-300 ${
|
||||
className={`tabler-chevron-down text-xl transition-transform duration-300 ${
|
||||
reasoningExpanded ? 'rotate-180' : ''
|
||||
}`}
|
||||
style={{ color: primaryMain }}
|
||||
/>
|
||||
</Box>
|
||||
<Collapse in={reasoningExpanded}>
|
||||
@@ -355,21 +382,19 @@ export default function SmartFertilizationRecommendation() {
|
||||
className='mb-6 animate-fade-in'
|
||||
sx={{
|
||||
borderRadius: '28px',
|
||||
background:
|
||||
'linear-gradient(160deg, #ffffff 0%, #f0fdf4 100%)',
|
||||
boxShadow: '0 8px 32px rgba(34, 197, 94, 0.1)',
|
||||
border: '1px solid rgba(34, 197, 94, 0.12)'
|
||||
background: `linear-gradient(160deg, ${paperBg} 0%, ${alpha(primaryMain, 0.06)} 100%)`,
|
||||
boxShadow: `0 8px 32px ${alpha(primaryMain, 0.1)}`,
|
||||
border: `1px solid ${alpha(primaryMain, 0.12)}`
|
||||
}}
|
||||
>
|
||||
<CardContent className='p-12 flex flex-col items-center gap-4'>
|
||||
<Box
|
||||
className='w-14 h-14 rounded-2xl flex items-center justify-center animate-pulse'
|
||||
sx={{
|
||||
background:
|
||||
'linear-gradient(135deg, rgba(34, 197, 94, 0.15) 0%, rgba(124, 58, 237, 0.08) 100%)'
|
||||
background: `linear-gradient(135deg, ${alpha(primaryMain, 0.15)} 0%, ${alpha(primaryMain, 0.08)} 100%)`
|
||||
}}
|
||||
>
|
||||
<i className='tabler-sparkles text-2xl text-emerald-600' />
|
||||
<i className='tabler-sparkles text-2xl' style={{ color: primaryMain }} />
|
||||
</Box>
|
||||
<Typography variant='body2' color='text.secondary'>
|
||||
{t('generating')}
|
||||
@@ -378,41 +403,6 @@ export default function SmartFertilizationRecommendation() {
|
||||
</Card>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* 5) Primary CTA Button - Sticky */}
|
||||
<Box
|
||||
className='fixed bottom-0 start-0 end-0 p-4 z-10'
|
||||
sx={{
|
||||
background:
|
||||
'linear-gradient(to top, rgba(255,255,255,0.98) 0%, rgba(255,255,255,0.9) 70%, transparent 100%)',
|
||||
backdropFilter: 'blur(8px)'
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
fullWidth
|
||||
variant='contained'
|
||||
disabled={!selectedCrop || loading}
|
||||
onClick={handleGenerate}
|
||||
startIcon={<i className='tabler-sparkles text-xl' />}
|
||||
className='rounded-2xl py-3.5 text-base font-semibold shadow-lg transition-all duration-300 hover:shadow-xl hover:scale-[1.01] active:scale-[0.99]'
|
||||
sx={{
|
||||
background:
|
||||
'linear-gradient(135deg, #22c55e 0%, #16a34a 50%, #15803d 100%)',
|
||||
boxShadow: '0 4px 20px rgba(34, 197, 94, 0.4)',
|
||||
'&:hover': {
|
||||
background:
|
||||
'linear-gradient(135deg, #4ade80 0%, #22c55e 50%, #16a34a 100%)',
|
||||
boxShadow: '0 6px 28px rgba(34, 197, 94, 0.5)'
|
||||
},
|
||||
'&:disabled': {
|
||||
background: 'action.disabledBackground',
|
||||
color: 'action.disabled'
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('generateCta')}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -428,17 +418,18 @@ function FarmBadge({
|
||||
label: string
|
||||
value: string
|
||||
}) {
|
||||
const theme = useTheme()
|
||||
const primaryMain = theme.palette.primary.main
|
||||
return (
|
||||
<Box
|
||||
className='flex items-center gap-2 px-4 py-2.5 rounded-2xl transition-all duration-200 hover:scale-[1.02] hover:shadow-md'
|
||||
sx={{
|
||||
background:
|
||||
'linear-gradient(145deg, rgba(34, 197, 94, 0.1) 0%, rgba(124, 58, 237, 0.04) 100%)',
|
||||
border: '1px solid rgba(34, 197, 94, 0.15)',
|
||||
background: `linear-gradient(145deg, ${alpha(primaryMain, 0.1)} 0%, ${alpha(primaryMain, 0.04)} 100%)`,
|
||||
border: `1px solid ${alpha(primaryMain, 0.15)}`,
|
||||
boxShadow: 'inset 0 1px 2px rgba(255,255,255,0.5)'
|
||||
}}
|
||||
>
|
||||
<i className={`${icon} text-xl text-emerald-600`} />
|
||||
<i className={`${icon} text-xl`} style={{ color: primaryMain }} />
|
||||
<Box>
|
||||
<Typography variant='caption' color='text.secondary' display='block' lineHeight={1.2}>
|
||||
{label}
|
||||
@@ -462,6 +453,10 @@ function CropCard({
|
||||
selected: boolean
|
||||
onClick: () => void
|
||||
}) {
|
||||
const theme = useTheme()
|
||||
const primaryMain = theme.palette.primary.main
|
||||
const primaryDark = theme.palette.primary.dark
|
||||
const paperBg = theme.palette.background.paper
|
||||
return (
|
||||
<Card
|
||||
component='button'
|
||||
@@ -470,18 +465,18 @@ function CropCard({
|
||||
onClick={onClick}
|
||||
className='flex items-center gap-3 px-4 py-3 rounded-2xl cursor-pointer transition-all duration-300 border-2 text-start'
|
||||
sx={{
|
||||
borderColor: selected ? '#22c55e' : 'transparent',
|
||||
borderColor: selected ? primaryMain : 'transparent',
|
||||
background: selected
|
||||
? 'linear-gradient(145deg, rgba(34, 197, 94, 0.15) 0%, rgba(124, 58, 237, 0.06) 100%)'
|
||||
: 'linear-gradient(145deg, #ffffff 0%, #faf5ff 100%)',
|
||||
? `linear-gradient(145deg, ${alpha(primaryMain, 0.15)} 0%, ${alpha(primaryMain, 0.06)} 100%)`
|
||||
: `linear-gradient(145deg, ${paperBg} 0%, ${alpha(primaryMain, 0.04)} 100%)`,
|
||||
boxShadow: selected
|
||||
? '0 4px 20px rgba(34, 197, 94, 0.2), inset 0 1px 0 rgba(255,255,255,0.8)'
|
||||
? `0 4px 20px ${alpha(primaryMain, 0.2)}, inset 0 1px 0 rgba(255,255,255,0.8)`
|
||||
: '0 2px 8px rgba(0,0,0,0.04), inset 0 1px 0 rgba(255,255,255,0.9)',
|
||||
'&:hover': {
|
||||
transform: 'translateY(-2px)',
|
||||
boxShadow: selected
|
||||
? '0 6px 24px rgba(34, 197, 94, 0.25)'
|
||||
: '0 4px 16px rgba(34, 197, 94, 0.12)'
|
||||
? `0 6px 24px ${alpha(primaryMain, 0.25)}`
|
||||
: `0 4px 16px ${alpha(primaryMain, 0.12)}`
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -489,23 +484,24 @@ function CropCard({
|
||||
className='w-11 h-11 rounded-xl flex items-center justify-center shrink-0 transition-all duration-300'
|
||||
sx={{
|
||||
background: selected
|
||||
? 'linear-gradient(135deg, #22c55e 0%, #16a34a 100%)'
|
||||
: 'linear-gradient(145deg, rgba(34, 197, 94, 0.1) 0%, rgba(124, 58, 237, 0.05) 100%)'
|
||||
? `linear-gradient(135deg, ${primaryMain} 0%, ${primaryDark} 100%)`
|
||||
: `linear-gradient(145deg, ${alpha(primaryMain, 0.1)} 0%, ${alpha(primaryMain, 0.05)} 100%)`
|
||||
}}
|
||||
>
|
||||
<i
|
||||
className={`${crop.icon} text-xl ${selected ? 'text-white' : 'text-emerald-600'}`}
|
||||
className={`${crop.icon} text-xl ${selected ? 'text-white' : ''}`}
|
||||
style={!selected ? { color: primaryMain } : undefined}
|
||||
/>
|
||||
</Box>
|
||||
<Typography
|
||||
variant='body2'
|
||||
fontWeight={600}
|
||||
color={selected ? '#16a34a' : 'text.primary'}
|
||||
color={selected ? 'primary.main' : 'text.primary'}
|
||||
>
|
||||
{label}
|
||||
</Typography>
|
||||
{selected && (
|
||||
<i className='tabler-circle-check-filled text-xl text-emerald-600 ms-auto' />
|
||||
<i className='tabler-circle-check-filled text-xl ms-auto' style={{ color: primaryMain }} />
|
||||
)}
|
||||
</Card>
|
||||
)
|
||||
@@ -520,15 +516,17 @@ function PrescriptionRow({
|
||||
label: string
|
||||
value: string
|
||||
}) {
|
||||
const theme = useTheme()
|
||||
const primaryMain = theme.palette.primary.main
|
||||
return (
|
||||
<Box
|
||||
className='flex items-center gap-4 p-3 rounded-2xl transition-colors duration-200'
|
||||
sx={{
|
||||
background: 'rgba(34, 197, 94, 0.06)',
|
||||
border: '1px solid rgba(34, 197, 94, 0.08)'
|
||||
background: alpha(primaryMain, 0.06),
|
||||
border: `1px solid ${alpha(primaryMain, 0.08)}`
|
||||
}}
|
||||
>
|
||||
<i className={`${icon} text-2xl text-emerald-600 shrink-0`} />
|
||||
<i className={`${icon} text-2xl shrink-0`} style={{ color: primaryMain }} />
|
||||
<Box className='flex-1 min-w-0'>
|
||||
<Typography variant='caption' color='text.secondary'>
|
||||
{label}
|
||||
|
||||
@@ -9,6 +9,7 @@ import Typography from '@mui/material/Typography'
|
||||
import Button from '@mui/material/Button'
|
||||
import IconButton from '@mui/material/IconButton'
|
||||
import CircularProgress from '@mui/material/CircularProgress'
|
||||
import { useTheme, alpha } from '@mui/material/styles'
|
||||
|
||||
// Types
|
||||
interface FarmInfo {
|
||||
@@ -60,10 +61,16 @@ function generateIrrigationPlan(_cropId: string, _farmInfo: FarmInfo): Irrigatio
|
||||
|
||||
export default function SmartIrrigationRecommendation() {
|
||||
const t = useTranslations('irrigation')
|
||||
const theme = useTheme()
|
||||
const [farmInfo] = useState<FarmInfo>(DEFAULT_FARM_INFO)
|
||||
const [selectedCrop, setSelectedCrop] = useState<string | null>(null)
|
||||
const [plan, setPlan] = useState<IrrigationPlan | null>(null)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const primaryMain = theme.palette.primary.main
|
||||
const primaryLight = theme.palette.primary.light
|
||||
const primaryDark = theme.palette.primary.dark
|
||||
const bgDefault = theme.palette.background.default
|
||||
const paperBg = theme.palette.background.paper
|
||||
|
||||
const handleGenerate = () => {
|
||||
if (!selectedCrop) return
|
||||
@@ -78,9 +85,10 @@ export default function SmartIrrigationRecommendation() {
|
||||
|
||||
return (
|
||||
<Box
|
||||
className='min-bs-screen pb-24'
|
||||
className='min-bs-screen'
|
||||
sx={{
|
||||
background: 'linear-gradient(165deg, #e0f2fe 0%, #f0f9ff 35%, #f8fcff 70%, var(--mui-palette-background-default) 100%)',
|
||||
background: (theme) =>
|
||||
`linear-gradient(165deg, ${alpha(theme.palette.primary.main, 0.08)} 0%, ${alpha(theme.palette.primary.main, 0.04)} 35%, ${alpha(theme.palette.primary.main, 0.02)} 70%, ${theme.palette.background.default} 100%)`,
|
||||
minHeight: '100vh'
|
||||
}}
|
||||
>
|
||||
@@ -91,7 +99,7 @@ export default function SmartIrrigationRecommendation() {
|
||||
variant='h4'
|
||||
className='font-bold tracking-tight'
|
||||
sx={{
|
||||
background: 'linear-gradient(135deg, #0ea5e9 0%, #0284c7 50%, #0369a1 100%)',
|
||||
background: `linear-gradient(135deg, ${primaryLight} 0%, ${primaryMain} 50%, ${primaryDark} 100%)`,
|
||||
backgroundClip: 'text',
|
||||
WebkitBackgroundClip: 'text',
|
||||
color: 'transparent',
|
||||
@@ -111,9 +119,9 @@ export default function SmartIrrigationRecommendation() {
|
||||
className='mb-6 overflow-hidden transition-all duration-300 hover:shadow-lg'
|
||||
sx={{
|
||||
borderRadius: '24px',
|
||||
background: 'linear-gradient(145deg, #ffffff 0%, #f8fcff 100%)',
|
||||
boxShadow: '0 4px 24px rgba(14, 165, 233, 0.08), 0 1px 3px rgba(0,0,0,0.04)',
|
||||
border: '1px solid rgba(14, 165, 233, 0.12)'
|
||||
background: `linear-gradient(145deg, ${paperBg} 0%, ${alpha(primaryMain, 0.04)} 100%)`,
|
||||
boxShadow: `0 4px 24px ${alpha(primaryMain, 0.08)}, 0 1px 3px rgba(0,0,0,0.04)`,
|
||||
border: `1px solid ${alpha(primaryMain, 0.12)}`
|
||||
}}
|
||||
>
|
||||
<CardContent className='p-5'>
|
||||
@@ -125,7 +133,7 @@ export default function SmartIrrigationRecommendation() {
|
||||
<Box
|
||||
className='px-2.5 py-1 rounded-full text-xs font-medium'
|
||||
sx={{
|
||||
background: 'linear-gradient(135deg, #22c55e 0%, #16a34a 100%)',
|
||||
background: (t) => `linear-gradient(135deg, ${t.palette.success.main} 0%, ${t.palette.success.dark} 100%)`,
|
||||
color: 'white',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
@@ -152,7 +160,7 @@ export default function SmartIrrigationRecommendation() {
|
||||
<Typography variant='subtitle2' fontWeight={600} color='text.secondary' className='mbe-3'>
|
||||
{t('plantSelection.title')}
|
||||
</Typography>
|
||||
<Box className='flex flex-wrap gap-3 mb-8'>
|
||||
<Box className='flex flex-wrap gap-3 mb-6'>
|
||||
{CROP_OPTIONS.map(crop => (
|
||||
<CropCard
|
||||
key={crop.id}
|
||||
@@ -164,6 +172,33 @@ export default function SmartIrrigationRecommendation() {
|
||||
))}
|
||||
</Box>
|
||||
|
||||
{/* 4) Primary CTA Button - End of form */}
|
||||
<Box className='mb-8'>
|
||||
<Button
|
||||
fullWidth
|
||||
variant='contained'
|
||||
disabled={!selectedCrop || loading}
|
||||
onClick={handleGenerate}
|
||||
startIcon={<i className='tabler-sparkles text-xl' />}
|
||||
className='rounded-2xl py-3.5 text-base font-semibold shadow-lg transition-all duration-300 hover:shadow-xl hover:scale-[1.01] active:scale-[0.99]'
|
||||
sx={{
|
||||
background: `linear-gradient(135deg, ${primaryLight} 0%, ${primaryMain} 50%, ${primaryDark} 100%)`,
|
||||
boxShadow: `0 4px 20px ${alpha(primaryMain, 0.4)}`,
|
||||
'&:hover': {
|
||||
background: `linear-gradient(135deg, ${primaryLight} 0%, ${primaryMain} 50%, ${primaryDark} 100%)`,
|
||||
boxShadow: `0 6px 28px ${alpha(primaryMain, 0.5)}`,
|
||||
filter: 'brightness(1.05)'
|
||||
},
|
||||
'&:disabled': {
|
||||
background: 'action.disabledBackground',
|
||||
color: 'action.disabled'
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('generateCta')}
|
||||
</Button>
|
||||
</Box>
|
||||
|
||||
{/* 5) Result Card (after click) */}
|
||||
{plan && (
|
||||
<Box className='mb-6 animate-fade-in'>
|
||||
@@ -171,9 +206,9 @@ export default function SmartIrrigationRecommendation() {
|
||||
elevation={0}
|
||||
sx={{
|
||||
borderRadius: '24px',
|
||||
background: 'linear-gradient(160deg, #ffffff 0%, #f0f9ff 100%)',
|
||||
boxShadow: '0 8px 32px rgba(14, 165, 233, 0.15), 0 2px 8px rgba(0,0,0,0.06)',
|
||||
border: '1px solid rgba(14, 165, 233, 0.18)',
|
||||
background: `linear-gradient(160deg, ${paperBg} 0%, ${alpha(primaryMain, 0.06)} 100%)`,
|
||||
boxShadow: `0 8px 32px ${alpha(primaryMain, 0.15)}, 0 2px 8px rgba(0,0,0,0.06)`,
|
||||
border: `1px solid ${alpha(primaryMain, 0.18)}`,
|
||||
overflow: 'visible'
|
||||
}}
|
||||
>
|
||||
@@ -187,7 +222,7 @@ export default function SmartIrrigationRecommendation() {
|
||||
cy={60}
|
||||
r={52}
|
||||
fill='none'
|
||||
stroke='rgba(14, 165, 233, 0.12)'
|
||||
stroke={alpha(primaryMain, 0.12)}
|
||||
strokeWidth={10}
|
||||
/>
|
||||
<circle
|
||||
@@ -203,8 +238,8 @@ export default function SmartIrrigationRecommendation() {
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient id='moistureGradient' x1='0%' y1='0%' x2='100%' y2='100%'>
|
||||
<stop offset='0%' stopColor='#0ea5e9' />
|
||||
<stop offset='100%' stopColor='#0284c7' />
|
||||
<stop offset='0%' stopColor={primaryLight} />
|
||||
<stop offset='100%' stopColor={primaryMain} />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
@@ -212,7 +247,7 @@ export default function SmartIrrigationRecommendation() {
|
||||
className='absolute inset-0 flex flex-col items-center justify-center'
|
||||
sx={{ top: 0, left: 0, right: 0, bottom: 0 }}
|
||||
>
|
||||
<i className='tabler-droplet text-3xl text-[#0ea5e9] mbe-0.5' />
|
||||
<i className='tabler-droplet text-3xl mbe-0.5' style={{ color: primaryMain }} />
|
||||
<Typography variant='h4' fontWeight={700} color='primary.main'>
|
||||
{plan.moistureLevel}%
|
||||
</Typography>
|
||||
@@ -274,13 +309,13 @@ export default function SmartIrrigationRecommendation() {
|
||||
className='mb-6'
|
||||
sx={{
|
||||
borderRadius: '24px',
|
||||
background: 'linear-gradient(160deg, #ffffff 0%, #f0f9ff 100%)',
|
||||
boxShadow: '0 8px 32px rgba(14, 165, 233, 0.1)',
|
||||
border: '1px solid rgba(14, 165, 233, 0.12)'
|
||||
background: `linear-gradient(160deg, ${paperBg} 0%, ${alpha(primaryMain, 0.06)} 100%)`,
|
||||
boxShadow: `0 8px 32px ${alpha(primaryMain, 0.1)}`,
|
||||
border: `1px solid ${alpha(primaryMain, 0.12)}`
|
||||
}}
|
||||
>
|
||||
<CardContent className='p-12 flex flex-col items-center gap-4'>
|
||||
<CircularProgress size={48} sx={{ color: '#0ea5e9' }} />
|
||||
<CircularProgress size={48} sx={{ color: 'primary.main' }} />
|
||||
<Typography variant='body2' color='text.secondary'>
|
||||
{t('generating')}
|
||||
</Typography>
|
||||
@@ -288,38 +323,6 @@ export default function SmartIrrigationRecommendation() {
|
||||
</Card>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* 4) Primary CTA Button - Sticky */}
|
||||
<Box
|
||||
className='fixed bottom-0 start-0 end-0 p-4 z-10'
|
||||
sx={{
|
||||
background: 'linear-gradient(to top, rgba(255,255,255,0.98) 0%, rgba(255,255,255,0.9) 70%, transparent 100%)',
|
||||
backdropFilter: 'blur(8px)'
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
fullWidth
|
||||
variant='contained'
|
||||
disabled={!selectedCrop || loading}
|
||||
onClick={handleGenerate}
|
||||
startIcon={<i className='tabler-sparkles text-xl' />}
|
||||
className='rounded-2xl py-3.5 text-base font-semibold shadow-lg transition-all duration-300 hover:shadow-xl hover:scale-[1.01] active:scale-[0.99]'
|
||||
sx={{
|
||||
background: 'linear-gradient(135deg, #0ea5e9 0%, #0284c7 50%, #0369a1 100%)',
|
||||
boxShadow: '0 4px 20px rgba(14, 165, 233, 0.4)',
|
||||
'&:hover': {
|
||||
background: 'linear-gradient(135deg, #38bdf8 0%, #0ea5e9 50%, #0284c7 100%)',
|
||||
boxShadow: '0 6px 28px rgba(14, 165, 233, 0.5)'
|
||||
},
|
||||
'&:disabled': {
|
||||
background: 'action.disabledBackground',
|
||||
color: 'action.disabled'
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('generateCta')}
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -335,16 +338,18 @@ function FarmBadge({
|
||||
label: string
|
||||
value: string
|
||||
}) {
|
||||
const theme = useTheme()
|
||||
const primaryMain = theme.palette.primary.main
|
||||
return (
|
||||
<Box
|
||||
className='flex items-center gap-2 px-4 py-2.5 rounded-2xl transition-transform duration-200 hover:scale-[1.02]'
|
||||
sx={{
|
||||
background: 'linear-gradient(145deg, rgba(14, 165, 233, 0.08) 0%, rgba(14, 165, 233, 0.04) 100%)',
|
||||
border: '1px solid rgba(14, 165, 233, 0.15)',
|
||||
background: `linear-gradient(145deg, ${alpha(primaryMain, 0.08)} 0%, ${alpha(primaryMain, 0.04)} 100%)`,
|
||||
border: `1px solid ${alpha(primaryMain, 0.15)}`,
|
||||
boxShadow: 'inset 0 1px 2px rgba(255,255,255,0.5)'
|
||||
}}
|
||||
>
|
||||
<i className={`${icon} text-xl text-sky-600`} />
|
||||
<i className={`${icon} text-xl`} style={{ color: primaryMain }} />
|
||||
<Box>
|
||||
<Typography variant='caption' color='text.secondary' display='block' lineHeight={1.2}>
|
||||
{label}
|
||||
@@ -368,6 +373,10 @@ function CropCard({
|
||||
selected: boolean
|
||||
onClick: () => void
|
||||
}) {
|
||||
const theme = useTheme()
|
||||
const primaryMain = theme.palette.primary.main
|
||||
const primaryDark = theme.palette.primary.dark
|
||||
const paperBg = theme.palette.background.paper
|
||||
return (
|
||||
<Card
|
||||
component='button'
|
||||
@@ -376,18 +385,18 @@ function CropCard({
|
||||
onClick={onClick}
|
||||
className='flex items-center gap-3 px-4 py-3 rounded-2xl cursor-pointer transition-all duration-300 border-2 text-start'
|
||||
sx={{
|
||||
borderColor: selected ? '#0ea5e9' : 'transparent',
|
||||
borderColor: selected ? primaryMain : 'transparent',
|
||||
background: selected
|
||||
? 'linear-gradient(145deg, rgba(14, 165, 233, 0.12) 0%, rgba(14, 165, 233, 0.06) 100%)'
|
||||
: 'linear-gradient(145deg, #ffffff 0%, #f8fcff 100%)',
|
||||
? `linear-gradient(145deg, ${alpha(primaryMain, 0.12)} 0%, ${alpha(primaryMain, 0.06)} 100%)`
|
||||
: `linear-gradient(145deg, ${paperBg} 0%, ${alpha(primaryMain, 0.04)} 100%)`,
|
||||
boxShadow: selected
|
||||
? '0 4px 20px rgba(14, 165, 233, 0.2), inset 0 1px 0 rgba(255,255,255,0.8)'
|
||||
? `0 4px 20px ${alpha(primaryMain, 0.2)}, inset 0 1px 0 rgba(255,255,255,0.8)`
|
||||
: '0 2px 8px rgba(0,0,0,0.04), inset 0 1px 0 rgba(255,255,255,0.9)',
|
||||
'&:hover': {
|
||||
transform: 'translateY(-2px)',
|
||||
boxShadow: selected
|
||||
? '0 6px 24px rgba(14, 165, 233, 0.25)'
|
||||
: '0 4px 16px rgba(14, 165, 233, 0.12)'
|
||||
? `0 6px 24px ${alpha(primaryMain, 0.25)}`
|
||||
: `0 4px 16px ${alpha(primaryMain, 0.12)}`
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -395,17 +404,17 @@ function CropCard({
|
||||
className='w-11 h-11 rounded-xl flex items-center justify-center shrink-0'
|
||||
sx={{
|
||||
background: selected
|
||||
? 'linear-gradient(135deg, #0ea5e9 0%, #0284c7 100%)'
|
||||
: 'linear-gradient(145deg, rgba(14, 165, 233, 0.1) 0%, rgba(14, 165, 233, 0.05) 100%)'
|
||||
? `linear-gradient(135deg, ${primaryMain} 0%, ${primaryDark} 100%)`
|
||||
: `linear-gradient(145deg, ${alpha(primaryMain, 0.1)} 0%, ${alpha(primaryMain, 0.05)} 100%)`
|
||||
}}
|
||||
>
|
||||
<i className={`${crop.icon} text-xl ${selected ? 'text-white' : 'text-sky-600'}`} />
|
||||
<i className={`${crop.icon} text-xl ${selected ? 'text-white' : ''}`} style={!selected ? { color: primaryMain } : undefined} />
|
||||
</Box>
|
||||
<Typography variant='body2' fontWeight={600} color={selected ? 'primary.main' : 'text.primary'}>
|
||||
{label}
|
||||
</Typography>
|
||||
{selected && (
|
||||
<i className='tabler-circle-check-filled text-xl text-sky-600 ms-auto' />
|
||||
<i className='tabler-circle-check-filled text-xl ms-auto' style={{ color: primaryMain }} />
|
||||
)}
|
||||
</Card>
|
||||
)
|
||||
@@ -420,9 +429,11 @@ function ResultRow({
|
||||
label: string
|
||||
value: string
|
||||
}) {
|
||||
const theme = useTheme()
|
||||
const primaryMain = theme.palette.primary.main
|
||||
return (
|
||||
<Box className='flex items-center gap-4 p-3 rounded-2xl' sx={{ bgcolor: 'rgba(14, 165, 233, 0.06)' }}>
|
||||
<i className={`${icon} text-2xl text-sky-600 shrink-0`} />
|
||||
<Box className='flex items-center gap-4 p-3 rounded-2xl' sx={{ bgcolor: alpha(primaryMain, 0.06) }}>
|
||||
<i className={`${icon} text-2xl shrink-0`} style={{ color: primaryMain }} />
|
||||
<Box className='flex-1 min-w-0'>
|
||||
<Typography variant='caption' color='text.secondary'>
|
||||
{label}
|
||||
|
||||
Reference in New Issue
Block a user