mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-24 03:56:41 +02:00
feat(chat): integrate chat project into monorepo with full app structure
- Restructure chat as apps/mobile, apps/web, apps/landing, backend - Add NestJS backend for secure Azure OpenAI API calls - Remove exposed API key from mobile app (security fix) - Add shared chat-types package - Create SvelteKit web app scaffold - Create Astro landing page scaffold - Update pnpm workspace configuration - Add project-level CLAUDE.md documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
fcf3a344b1
commit
c638a7ffee
155 changed files with 22622 additions and 348 deletions
161
chat/apps/mobile/components/ModelDropdown.tsx
Normal file
161
chat/apps/mobile/components/ModelDropdown.tsx
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { View, Text, TouchableOpacity, Modal, FlatList, StyleSheet } from 'react-native';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import { useAppTheme } from '../theme/ThemeProvider';
|
||||
import { Model } from '../types';
|
||||
import { availableModels } from '../config/azure';
|
||||
import { getModels } from '../services/modelService';
|
||||
|
||||
// Verwende Modelle aus der Konfiguration
|
||||
const FALLBACK_MODELS: Model[] = availableModels;
|
||||
|
||||
type ModelDropdownProps = {
|
||||
selectedModelId: string;
|
||||
onSelectModel: (id: string) => void;
|
||||
};
|
||||
|
||||
export default function ModelDropdown({ selectedModelId, onSelectModel }: ModelDropdownProps) {
|
||||
const { isDarkMode } = useAppTheme();
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const [models, setModels] = useState<Model[]>(FALLBACK_MODELS);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
// Lade die Modelle vom ModelService
|
||||
useEffect(() => {
|
||||
const fetchModels = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const modelsList = await getModels();
|
||||
setModels(modelsList);
|
||||
} catch (err) {
|
||||
console.error('Fehler beim Laden der Modelle:', err);
|
||||
setModels(FALLBACK_MODELS);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchModels();
|
||||
}, []);
|
||||
|
||||
const selectedModel = models.find(model => model.id === selectedModelId) || models[0];
|
||||
|
||||
return (
|
||||
<View>
|
||||
<TouchableOpacity
|
||||
onPress={() => setIsModalVisible(true)}
|
||||
className={`flex-row items-center rounded-lg px-2 py-1 ${isDarkMode ? 'bg-[#2C2C2E]' : 'bg-gray-100'}`}
|
||||
>
|
||||
<Text className={`text-sm font-medium ${isDarkMode ? 'text-white' : 'text-black'}`}>
|
||||
{selectedModel.name}
|
||||
</Text>
|
||||
<Ionicons
|
||||
name="chevron-down"
|
||||
size={16}
|
||||
color={isDarkMode ? '#FFFFFF' : '#000000'}
|
||||
style={{ marginLeft: 4 }}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
|
||||
<Modal
|
||||
visible={isModalVisible}
|
||||
transparent={true}
|
||||
animationType="fade"
|
||||
onRequestClose={() => setIsModalVisible(false)}
|
||||
>
|
||||
<TouchableOpacity
|
||||
style={styles.modalOverlay}
|
||||
activeOpacity={1}
|
||||
onPress={() => setIsModalVisible(false)}
|
||||
>
|
||||
<View
|
||||
className={`mx-4 rounded-xl p-4 ${isDarkMode ? 'bg-[#1C1C1E]' : 'bg-white'}`}
|
||||
style={styles.modalContent}
|
||||
>
|
||||
<Text className={`text-lg font-bold mb-4 ${isDarkMode ? 'text-white' : 'text-black'}`}>
|
||||
Modell auswählen
|
||||
</Text>
|
||||
|
||||
{loading ? (
|
||||
<View className="py-4 items-center">
|
||||
<Text className={`${isDarkMode ? 'text-gray-400' : 'text-gray-500'}`}>
|
||||
Modelle werden geladen...
|
||||
</Text>
|
||||
</View>
|
||||
) : (
|
||||
<FlatList
|
||||
data={models}
|
||||
keyExtractor={(item) => item.id}
|
||||
renderItem={({ item }) => (
|
||||
<TouchableOpacity
|
||||
className={`flex-row items-center p-3 mb-2 rounded-lg ${
|
||||
item.id === selectedModelId
|
||||
? isDarkMode ? 'bg-blue-900/30' : 'bg-blue-100'
|
||||
: isDarkMode ? 'bg-[#2C2C2E]' : 'bg-gray-100'
|
||||
}`}
|
||||
onPress={() => {
|
||||
onSelectModel(item.id);
|
||||
setIsModalVisible(false);
|
||||
}}
|
||||
>
|
||||
<View className="w-8 h-8 rounded-full bg-blue-500/20 items-center justify-center mr-3">
|
||||
<Ionicons
|
||||
name="chatbubble-ellipses-outline"
|
||||
size={16}
|
||||
color="#0A84FF"
|
||||
/>
|
||||
</View>
|
||||
|
||||
<View className="flex-1">
|
||||
<Text className={`font-medium ${isDarkMode ? 'text-white' : 'text-black'}`}>
|
||||
{item.name}
|
||||
</Text>
|
||||
<Text
|
||||
className={`text-xs mt-1 ${isDarkMode ? 'text-gray-400' : 'text-gray-500'}`}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{item.description}
|
||||
</Text>
|
||||
{item.parameters?.deployment && (
|
||||
<Text
|
||||
className={`text-xs mt-1 ${isDarkMode ? 'text-blue-400' : 'text-blue-500'}`}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{item.parameters.deployment}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{item.id === selectedModelId && (
|
||||
<View className="w-6 h-6 rounded-full bg-blue-500 items-center justify-center">
|
||||
<Ionicons name="checkmark" size={14} color="#FFFFFF" />
|
||||
</View>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<TouchableOpacity
|
||||
className={`mt-3 py-3 rounded-lg items-center ${isDarkMode ? 'bg-[#0A84FF]' : 'bg-[#0A84FF]'}`}
|
||||
onPress={() => setIsModalVisible(false)}
|
||||
>
|
||||
<Text className="text-white font-medium">Schließen</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</Modal>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
modalOverlay: {
|
||||
flex: 1,
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
modalContent: {
|
||||
maxHeight: '80%',
|
||||
},
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue