managarten/nutriphi/apps/mobile/components/meals/AnalysisStatusIndicator.tsx
Till-JS 6537863696 feat(nutriphi): migrate from Supabase to PostgreSQL + Hetzner S3
- Add nutriphi-database package with Drizzle ORM
  - meals and nutrition_goals schemas
  - PostgreSQL 16 Docker setup
  - Drizzle Kit configuration

- Migrate backend from Supabase to Drizzle
  - Add DatabaseModule with connection pooling
  - Add StorageService for Hetzner Object Storage (S3-compatible)
  - Update MealsService with Drizzle queries
  - Add /api/meals/upload endpoint for image upload + analysis

- Update web app to use backend for uploads
  - Remove Supabase Storage direct upload
  - Update uploadService to send images to backend
  - Remove Supabase dependencies from package.json
  - Simplify hooks.server.ts

- Add Coolify deployment configuration
  - Dockerfile for production build
  - docker-compose.coolify.yml

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 17:52:14 +01:00

103 lines
2.9 KiB
TypeScript

import React from 'react';
import { View, Text } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { LoadingSpinner } from '../ui/LoadingSpinner';
interface AnalysisStatusIndicatorProps {
status: 'pending' | 'completed' | 'failed' | 'manual';
mini?: boolean;
}
export const AnalysisStatusIndicator: React.FC<AnalysisStatusIndicatorProps> = ({
status,
mini = false,
}) => {
const getStatusConfig = () => {
switch (status) {
case 'pending':
return {
bgColor: 'bg-yellow-100',
textColor: 'text-yellow-800',
icon: null,
text: 'Wird analysiert...',
showSpinner: true,
};
case 'completed':
return {
bgColor: 'bg-green-100',
textColor: 'text-green-800',
icon: 'checkmark-circle' as const,
text: 'Analysiert',
showSpinner: false,
};
case 'failed':
return {
bgColor: 'bg-red-100',
textColor: 'text-red-800',
icon: 'alert-circle' as const,
text: 'Analyse fehlgeschlagen',
showSpinner: false,
};
case 'manual':
return {
bgColor: 'bg-gray-100',
textColor: 'text-gray-800',
icon: 'create-outline' as const,
text: 'Manuell',
showSpinner: false,
};
default:
return {
bgColor: 'bg-gray-100',
textColor: 'text-gray-800',
icon: 'help-circle-outline' as const,
text: 'Unbekannt',
showSpinner: false,
};
}
};
const config = getStatusConfig();
if (mini) {
return (
<View className={`rounded-full px-2 py-1 ${config.bgColor}`}>
<View className="flex-row items-center">
{config.showSpinner ? (
<LoadingSpinner size={12} color="#ca8a04" />
) : (
config.icon && <Ionicons name={config.icon} size={12} color="#ca8a04" />
)}
<Text className={`ml-1 text-xs font-medium ${config.textColor}`}>{config.text}</Text>
</View>
</View>
);
}
return (
<View className={`rounded-lg p-3 ${config.bgColor}`}>
<View className="flex-row items-center">
{config.showSpinner ? (
<LoadingSpinner size={20} color="#ca8a04" />
) : (
config.icon && (
<Ionicons
name={config.icon}
size={20}
color={
config.textColor === 'text-green-800'
? '#166534'
: config.textColor === 'text-red-800'
? '#991b1b'
: config.textColor === 'text-yellow-800'
? '#854d0e'
: '#1f2937'
}
/>
)
)}
<Text className={`ml-2 text-sm font-medium ${config.textColor}`}>{config.text}</Text>
</View>
</View>
);
};