Skip to content

Commit

Permalink
Update Calculators
Browse files Browse the repository at this point in the history
  • Loading branch information
dimitrisna committed Jan 30, 2025
1 parent de954bd commit e8074b9
Show file tree
Hide file tree
Showing 9 changed files with 809 additions and 580 deletions.
70 changes: 70 additions & 0 deletions app/components/ProgressRing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import Svg, { Circle } from 'react-native-svg';

const ProgressRing = ({ value = 0, maxValue = 100, unit = '' }) => {
const size = 150; // Diameter of the progress ring
const strokeWidth = 12; // Thickness of the ring
const radius = (size - strokeWidth) / 2; // Adjust radius based on stroke width
const circumference = 2 * Math.PI * radius; // Full circumference
const progress = Math.min(value / maxValue, 1); // Ensure the progress is between 0-1
const strokeDashoffset = circumference * (1 - progress); // Calculate offset for progress
const ringColor = value < maxValue * 0.4 ? '#4CAF50' : value < maxValue * 0.7 ? '#FFC107' : '#F44336'; // Color based on value

return (
<View style={styles.container}>
<Svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
{/* Background Circle */}
<Circle
cx={size / 2}
cy={size / 2}
r={radius}
stroke="#E0E0E0" // Light grey background
strokeWidth={strokeWidth}
fill="none"
/>
{/* Progress Circle */}
<Circle
cx={size / 2}
cy={size / 2}
r={radius}
stroke={ringColor} // Dynamic color based on value
strokeWidth={strokeWidth}
fill="none"
strokeLinecap="round"
strokeDasharray={circumference}
strokeDashoffset={strokeDashoffset}
/>
</Svg>
{/* Centered Text */}
<View style={styles.textContainer}>
<Text style={styles.valueText}>{value.toFixed(1)}</Text>
<Text style={styles.unitText}>{unit}</Text>
</View>
</View>
);
};

const styles = StyleSheet.create({
container: {
alignItems: 'center',
justifyContent: 'center',
position: 'relative',
},
textContainer: {
position: 'absolute',
alignItems: 'center',
},
valueText: {
fontSize: 24,
fontWeight: 'bold',
color: '#1E4E75',
},
unitText: {
fontSize: 14,
fontWeight: '500',
color: '#777',
},
});

export default ProgressRing;
259 changes: 259 additions & 0 deletions app/components/calculator/InputFields.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
import React, { useState } from 'react';
import { View, Text, TextInput, TouchableOpacity, StyleSheet, Dimensions } from 'react-native';
import { Picker } from '@react-native-picker/picker'; // Correct Picker import
import Slider from '@react-native-community/slider';

const screenWidth = Dimensions.get('window').width;
const screenHeight = Dimensions.get('window').height;

const InputFields = ({ selectedCalculator, inputs, setInputs, handleCalculate }) => {
const [sliderTempValues, setSliderTempValues] = useState({});
const inputFields = {
'1': [
{ label: '🌍 Transport Distance (km)', key: 'transportDistance', type: 'number', placeholder: 'Enter distance' },
{
label: '🚗 Vehicle Type', key: 'vehicleType', type: 'dropdown',
options: [{ label: 'Car', value: 'car' }, { label: 'Bus', value: 'bus' }, { label: 'Bike', value: 'bike' }],
defaultOption: 'Select Vehicle Type',
},
],
'2': [
{ label: '🚿 Weekly Showers', key: 'weeklyShowers', type: 'slider', min: 0, max: 30, step: 1 },
{ label: '🍳 Daily Cooking Sessions', key: 'dailyCooking', type: 'slider', min: 0, max: 10, step: 1 },
{ label: '👕 Weekly Laundry Loads', key: 'weeklyLaundry', type: 'slider', min: 0, max: 20, step: 1 },
],
'3': [
{ label: '🥩 Meat (portions/day)', key: 'meat', type: 'slider', min: 0, max: 5, step: 1 },
{ label: '🥦 Vegetables (portions/day)', key: 'vegetables', type: 'slider', min: 0, max: 10, step: 1 },
{ label: '🌾 Grains (portions/day)', key: 'grains', type: 'slider', min: 0, max: 10, step: 1 },
],
'4': [
{ label: '⚡ Monthly Energy Usage (kWh)', key: 'energyUsage', type: 'number', placeholder: 'Enter kWh' },
{ label: '💰 kWh Price (€)', key: 'kwhPrice', type: 'number', placeholder: 'Enter price per kWh' },
{ label: '☀️ Solar Panel Cost (€)', key: 'solarPanelCost', type: 'number', placeholder: 'Enter system cost' },
],
'5': [
{ label: '💪 Daily Protein Intake (grams)', key: 'proteinIntake', type: 'slider', min: 0, max: 2000, step: 10 },
{
label: '🍽 Protein Source', key: 'proteinSource', type: 'dropdown',
options: [
{ label: 'Beef', value: 'beef' },
{ label: 'Chicken', value: 'chicken' },
{ label: 'Fish', value: 'fish' },
{ label: 'Eggs', value: 'eggs' },
{ label: 'Dairy', value: 'dairy' },
{ label: 'Plant-based', value: 'plant' }
],
defaultOption: 'Select Protein Source',
},
],
'6': [
{ label: '🛍️ Small Bags (Grocery bag, ~0.5 kg)', key: 'smallBags', type: 'slider', min: 0, max: 20, step: 1 },
{ label: '🗑️ Medium Bags (Kitchen bag, ~2 kg)', key: 'mediumBags', type: 'slider', min: 0, max: 10, step: 1 },
{ label: '🛢️ Large Bags (Bin collection, ~5 kg)', key: 'largeBags', type: 'slider', min: 0, max: 5, step: 1 },
],
};



return (
<View style={styles.container}>
{inputFields[selectedCalculator.id]?.map((field) => (
<View key={field.key} style={styles.inputContainer}>
<Text style={styles.label}>{field.label}:</Text>
{field.type === 'number' ? (
<TextInput
style={[styles.input, inputs[field.key] === '' ? styles.inputError : null]}
keyboardType="numeric"
placeholder={field.placeholder || "Enter value"}
value={inputs[field.key] ?? ''} // Ensures empty state is handled
onChangeText={(value) => {
setInputs((prev) => ({ ...prev, [field.key]: value }));
}}
onBlur={() => {
if (inputs[field.key] === '') {
setInputs((prev) => ({ ...prev, [field.key]: '' })); // Keep it empty instead of setting a default
}
}}
/>
) : field.type === 'slider' ? (
<View style={styles.sliderWrapper}>
<Text style={styles.sliderValue}>
{sliderTempValues[field.key] ?? inputs[field.key] ?? 0}
</Text>
<Slider
style={styles.slider}
minimumValue={field.min}
maximumValue={field.max}
step={field.step}
value={inputs[field.key] ?? 0}
onValueChange={(value) => {
setSliderTempValues((prev) => ({ ...prev, [field.key]: value }));
}}
onSlidingComplete={(value) => {
setInputs((prev) => ({ ...prev, [field.key]: value }));
setSliderTempValues((prev) => ({ ...prev, [field.key]: null }));
}}
minimumTrackTintColor="#4CAF50"
maximumTrackTintColor="#ddd"
thumbTintColor="#FF9800"
thumbStyle={styles.thumbStyle}
trackStyle={styles.trackStyle}
/>
</View>
) : (
<View style={styles.pickerContainer}>
<Picker
selectedValue={inputs[field.key] ?? ""}
onValueChange={(value) => {
setInputs((prev) => ({
...prev,
[field.key]: value !== "" ? value : undefined, // Ensure default is not empty
}));
}}
style={styles.picker}
>
<Picker.Item label={field.defaultOption} value="" color="#999" />
{field.options.map((option) => (
<Picker.Item key={option.value} label={option.label} value={option.value} />
))}
</Picker>
</View>
)}
</View>
))}

<TouchableOpacity onPress={handleCalculate} style={styles.calculateButton}>
<Text style={styles.calculateButtonText}>Calculate</Text>
</TouchableOpacity>
</View>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
width: '100%',
justifyContent: 'center',
alignItems: 'center',
paddingHorizontal: '5%',
},
title: {
fontSize: 26,
fontWeight: 'bold',
textAlign: 'center',
letterSpacing: 1.2,
color: '#1E4E75',
marginBottom: 15,
},
inputContainer: {
width: '100%',
marginBottom: 15,
alignItems: 'center',
},
label: {
fontSize: 16,
fontWeight: '500',
color: '#333',
marginBottom: 5,
textAlign: 'center',
},
input: {
width: '100%',
padding: 12,
borderWidth: 2,
borderColor: '#B0C4DE',
borderRadius: 12,
backgroundColor: '#F8FAFC',
fontSize: 16,
textAlign: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 5,
elevation: 3,
},
sliderWrapper: {
width: '100%',
paddingVertical: 10,
backgroundColor: '#E3F2FD', // Light blue background for better contrast
borderRadius: 15,
alignItems: 'center',
justifyContent: 'center',
padding: 10,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.15,
shadowRadius: 5,
elevation: 4,
},
slider: {
width: '90%',
height: 45,
},
trackStyle: {
height: 14, // Thicker track for better visibility
borderRadius: 7,
backgroundColor: '#4A90E2', // Modern blue track
},
thumbStyle: {
width: 32,
height: 32,
backgroundColor: '#1E4E75', // Darker blue thumb
borderRadius: 50,
borderColor: '#fff',
borderWidth: 3,
shadowColor: '#000',
shadowOffset: { width: 0, height: 3 },
shadowOpacity: 0.25,
shadowRadius: 6,
elevation: 4,
},
inputError: {
borderColor: 'red',
borderWidth: 2,
backgroundColor: '#FFE5E5', // Light red to highlight missing input
},
sliderValue: {
fontSize: 20,
fontWeight: 'bold',
color: '#1E4E75', // Matches thumb color for consistency
marginBottom: 5,
},
calculateButton: {
width: '90%',
backgroundColor: '#1E4E75',
borderRadius: 12,
paddingVertical: 14,
alignItems: 'center',
justifyContent: 'center',
marginTop: 20,
shadowColor: '#000',
shadowOffset: { width: 0, height: 3 },
shadowOpacity: 0.15,
shadowRadius: 6,
elevation: 4,
},
calculateButtonText: {
color: '#fff',
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
pickerContainer: {
width: '100%',
borderWidth: 2,
borderColor: '#B0C4DE',
borderRadius: 12,
backgroundColor: '#F8FAFC',
overflow: 'hidden',
alignItems: 'center',
},
picker: {
width: '100%',
backgroundColor: '#F8FAFC',
},
});


export default InputFields;
Loading

0 comments on commit e8074b9

Please sign in to comment.