File size: 6,695 Bytes
be02369
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import React, { useState, useCallback } from 'react';
import { LeafIcon, SparklesIcon, SunIcon, WindIcon, SnowflakeIcon, SunriseIcon, AlertTriangleIcon } from '../components/icons';
import Spinner from '../components/Spinner';
import { generateSeasonalGuide, isAIConfigured } from '../services/geminiService';
import type { SeasonalGuide, View } from '../types';
import { AppStatus } from '../types';

const seasonIcons: { [key: string]: React.FC<React.SVGProps<SVGSVGElement>> } = {
    Spring: SunriseIcon,
    Summer: SunIcon,
    Autumn: WindIcon,
    Winter: SnowflakeIcon,
};

interface SeasonalGuideViewProps {
  setActiveView: (view: View) => void;
}

const SeasonalGuideView: React.FC<SeasonalGuideViewProps> = ({ setActiveView }) => {
  const [status, setStatus] = useState<AppStatus>(AppStatus.IDLE);
  const [species, setSpecies] = useState<string>('');
  const [location, setLocation] = useState<string>('');
  const [guideData, setGuideData] = useState<SeasonalGuide[] | null>(null);
  const [error, setError] = useState<string>('');
  const aiConfigured = isAIConfigured();

  const handleGenerate = useCallback(async () => {
    if (!species.trim()) {
      setError('Please enter a bonsai species.');
      return;
    }
    if (!location.trim()) {
      setError('Please enter your city or region.');
      return;
    }
    setStatus(AppStatus.ANALYZING);
    setError('');
    setGuideData(null);

    try {
      const result = await generateSeasonalGuide(species, location);
      if (result) {
        setGuideData(result);
        setStatus(AppStatus.SUCCESS);
      } else {
        throw new Error('Failed to generate the seasonal guide. The AI may be busy. Please try again.');
      }
    } catch(e: any) {
      setError(e.message);
      setStatus(AppStatus.ERROR);
    }
  }, [species, location]);

  return (
    <div className="space-y-8 max-w-4xl mx-auto">
      <header className="text-center">
        <h2 className="text-3xl font-bold tracking-tight text-stone-900 sm:text-4xl flex items-center justify-center gap-3">
          <LeafIcon className="w-8 h-8 text-green-600" />
          Seasonal Care Guides
        </h2>
        <p className="mt-4 text-lg leading-8 text-stone-600">
          Get a year-round plan for any bonsai species, tailored to your local climate.
        </p>
      </header>

      <div className="bg-white p-6 rounded-xl shadow-lg border border-stone-200 space-y-4">
        <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
          <input
            type="text"
            value={species}
            onChange={(e) => setSpecies(e.target.value)}
            className="block w-full rounded-md border-0 py-2 px-3 text-stone-900 shadow-sm ring-1 ring-inset ring-stone-300 placeholder:text-stone-400 focus:ring-2 focus:ring-inset focus:ring-green-600"
            placeholder="e.g., Ficus Retusa"
            disabled={status === AppStatus.ANALYZING}
          />
          <input
            type="text"
            value={location}
            onChange={(e) => setLocation(e.target.value)}
            className="block w-full rounded-md border-0 py-2 px-3 text-stone-900 shadow-sm ring-1 ring-inset ring-stone-300 placeholder:text-stone-400 focus:ring-2 focus:ring-inset focus:ring-green-600"
            placeholder="e.g., Miami, Florida"
            disabled={status === AppStatus.ANALYZING}
          />
        </div>
        <button
          onClick={handleGenerate}
          disabled={status === AppStatus.ANALYZING || !aiConfigured}
          className="w-full flex items-center justify-center gap-2 rounded-md bg-green-700 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-green-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-700 disabled:bg-stone-400 disabled:cursor-not-allowed"
        >
          <SparklesIcon className="w-5 h-5" />
          {status === AppStatus.ANALYZING ? 'Generating...' : 'Generate Guide'}
        </button>
        {error && <p className="text-sm text-red-600 mt-2">{error}</p>}
        {!aiConfigured && (
            <div className="mt-4 p-3 bg-yellow-50 text-yellow-800 rounded-lg border border-yellow-200 text-center">
                <p className="text-sm">
                  AI features are disabled. Please set your Gemini API key in the{' '}
                  <button onClick={() => setActiveView('settings')} className="font-bold underline hover:text-yellow-900">
                    Settings page
                  </button>.
                </p>
            </div>
        )}
      </div>

      {status === AppStatus.ANALYZING && <Spinner text="Yuki is reading the almanac..." />}
      {status === AppStatus.SUCCESS && guideData && (
        <div className="space-y-6">
           <h3 className="text-2xl font-bold text-stone-800 text-center">Seasonal Guide for a {species} in {location}</h3>
           <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
              {guideData.sort((a,b) => ['Spring', 'Summer', 'Autumn', 'Winter'].indexOf(a.season) - ['Spring', 'Summer', 'Autumn', 'Winter'].indexOf(b.season)).map(season => {
                  const Icon = seasonIcons[season.season] || LeafIcon;
                  return (
                      <div key={season.season} className="bg-white rounded-xl shadow-md border border-stone-200 p-6">
                          <div className="flex items-center gap-3 mb-4">
                              <Icon className="w-7 h-7 text-green-700" />
                              <h3 className="text-xl font-semibold text-stone-800">{season.season}</h3>
                          </div>
                          <div className="space-y-3 text-stone-600">
                              <p className="italic text-stone-600 mb-4">{season.summary}</p>
                              <ul className="space-y-2">
                                  {season.tasks.map(task => (
                                      <li key={task.task} className="flex items-center justify-between text-sm">
                                          <span>{task.task}</span>
                                          <span className={`px-2 py-0.5 text-xs font-medium rounded-full ${task.importance === 'High' ? 'bg-red-100 text-red-800' : task.importance === 'Medium' ? 'bg-yellow-100 text-yellow-800' : 'bg-blue-100 text-blue-800'}`}>{task.importance}</span>
                                      </li>
                                  ))}
                              </ul>
                          </div>
                      </div>
                  )
              })}
          </div>
        </div>
      )}
    </div>
  );
};

export default SeasonalGuideView;