File size: 6,650 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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170






import React, { useState, useCallback, useEffect } from 'react';
import ImageUploader from '../components/ImageUploader';
import AnalysisDisplay from '../components/AnalysisDisplay';
import Spinner from '../components/Spinner';
import { SparklesIcon, AlertTriangleIcon } from '../components/icons';
import { analyzeBonsai, getProtectionProfile, isAIConfigured } from '../services/geminiService';
import type { BonsaiAnalysis, BonsaiTree, View } from '../types';
import { AppStatus } from '../types';

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

const AiStewardView: React.FC<AiStewardViewProps> = ({ setActiveView }) => {
  const [status, setStatus] = useState<AppStatus>(AppStatus.IDLE);
  const [analysisResult, setAnalysisResult] = useState<BonsaiAnalysis | null>(null);
  const [error, setError] = useState<string>('');
  const [currentImage, setCurrentImage] = useState<string>('');
  const [currentLocation, setCurrentLocation] = useState('');
  const [prefilledSpecies, setPrefilledSpecies] = useState('');
  const aiConfigured = isAIConfigured();

  useEffect(() => {
    const species = window.sessionStorage.getItem('prefilled-species');
    if (species) {
      setPrefilledSpecies(species);
      window.sessionStorage.removeItem('prefilled-species');
    }
  }, []);

  const handleAnalyze = useCallback(async (imageBase64: string, species: string, location: string) => {
    setStatus(AppStatus.ANALYZING);
    setError('');
    setAnalysisResult(null);
    setCurrentImage(imageBase64);
    setCurrentLocation(location);

    try {
      const result = await analyzeBonsai(imageBase64, species, location);
      if (result) {
        setAnalysisResult(result);
        setStatus(AppStatus.SUCCESS);
      } else {
        throw new Error('Failed to get analysis. The AI may be busy, or there was an issue with the request. Please try again.');
      }
    } catch (e: any) {
      setError(e.message);
      setStatus(AppStatus.ERROR);
    }
  }, []);

  const handleSaveToDiary = async () => {
    try {
      if (!analysisResult || !currentImage || !currentLocation) {
        alert("Cannot save. Analysis data is missing.");
        return;
      }

      const treeName = window.prompt("What would you like to name this tree in your garden?", analysisResult.species);
      if (!treeName) return; // User cancelled

      // Fetch the protection profile for the new tree
      const protectionProfileData = await getProtectionProfile(analysisResult.species);

      const newTree: BonsaiTree = {
        id: `tree-${Date.now()}`,
        name: treeName,
        species: analysisResult.species,
        acquiredDate: new Date().toISOString(),
        source: "AI Steward Analysis",
        location: currentLocation,
        initialPhoto: currentImage,
        logs: [],
        analysisHistory: [{ date: new Date().toISOString(), analysis: analysisResult }],
        protectionProfile: protectionProfileData ? { ...protectionProfileData, alertsEnabled: true } : undefined,
      };

      const storageKey = `yuki-app-bonsai-diary-trees`;
      const existingTreesJSON = window.localStorage.getItem(storageKey);
      const existingTrees: BonsaiTree[] = existingTreesJSON ? JSON.parse(existingTreesJSON) : [];
      const updatedTrees = [...existingTrees, newTree];
      window.localStorage.setItem(storageKey, JSON.stringify(updatedTrees));
      window.localStorage.setItem('yuki-bonsai-diary-newly-added-tree-id', newTree.id);
      setActiveView('garden');

    } catch (error) {
        console.error("Failed to save tree to garden:", error);
        alert("Could not save the tree to your garden. An unexpected error occurred. Please check browser permissions for storage and try again.");
    }
  };


  const handleReset = () => {
    setStatus(AppStatus.IDLE);
    setAnalysisResult(null);
    setError('');
    setCurrentImage('');
    setCurrentLocation('');
    setPrefilledSpecies('');
  };

  const renderContent = () => {
    switch (status) {
      case AppStatus.ANALYZING:
        return <div className="flex justify-center items-center h-full"><Spinner /></div>;
      case AppStatus.SUCCESS:
        return analysisResult ? <AnalysisDisplay analysis={analysisResult} onReset={handleReset} onSaveToDiary={handleSaveToDiary} treeImageBase64={currentImage} /> : null;
      case AppStatus.ERROR:
         return (
          <div className="text-center p-8 bg-white rounded-lg shadow-lg border border-red-200 max-w-md mx-auto">
            <h3 className="text-xl font-semibold text-red-700">An Error Occurred</h3>
            <p className="text-stone-600 mt-2">{error}</p>
            <button
              onClick={handleReset}
              className="mt-6 bg-green-700 text-white font-semibold py-2 px-6 rounded-lg hover:bg-green-600 transition-colors"
            >
              Try Again
            </button>
          </div>
        );
      case AppStatus.IDLE:
      default:
        return (
          <>
            <ImageUploader onAnalyze={handleAnalyze} isAnalyzing={false} defaultSpecies={prefilledSpecies} disabled={!aiConfigured}/>
            {!aiConfigured && (
              <div className="mt-6 p-4 bg-yellow-50 text-yellow-800 rounded-lg border border-yellow-200 text-center max-w-2xl mx-auto">
                <div className="flex items-center justify-center gap-2">
                  <AlertTriangleIcon className="w-5 h-5"/>
                  <h3 className="font-semibold">AI Features Disabled</h3>
                </div>
                <p className="text-sm mt-1">
                  Please set your Gemini API key in the{' '}
                  <button onClick={() => setActiveView('settings')} className="font-bold underline hover:text-yellow-900">
                    Settings page
                  </button>
                  {' '}to enable this feature.
                </p>
              </div>
            )}
          </>
        );
    }
  };

  return (
    <div className="space-y-8">
        <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">
                <SparklesIcon className="w-8 h-8 text-green-600" />
                New Tree Analysis
            </h2>
            <p className="mt-4 text-lg leading-8 text-stone-600 max-w-2xl mx-auto">
                Welcome a new tree to your collection. Get an instant, expert analysis from Yuki, our AI Bonsai Sensei.
            </p>
        </header>
        <div className="w-full">
            {renderContent()}
        </div>
    </div>
  );
};

export default AiStewardView;