Compare commits

..
5 Commits
Author SHA1 Message Date
riccardoandClaude Opus 5 bdaecd4d26 Convert HEIC gallery photos to JPEG on iOS
Use the compatible asset representation so the picker transcodes HEIC
to JPEG before upload, a format the backend resizes and browsers display.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 16:16:13 +02:00
riccardoandClaude Opus 5 e2ed8547c5 Set Apple team ID for iOS signing
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 16:16:13 +02:00
riccardoandClaude Opus 5 38a868a962 Add back button to dashboard header
The dashboard is a hidden tab screen with the tab bar hidden, so on iOS
there was no way to return home (no hardware back, no swipe gesture on
tabs). Match the construction-site header pattern.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 15:11:55 +02:00
riccardoandClaude Opus 5 5c66e9d5cb Upgrade to Expo SDK 57 for Xcode 27 support
- expo 57.0.23, react-native 0.86.3, react 19.2.3
- Remove @react-navigation/* direct deps (unsupported by expo-router 56+)
- Drop newArchEnabled and android.edgeToEdgeEnabled (removed from schema)
- Enable ios.enableSceneSupport (UIScene lifecycle required by iOS 27 SDK)
- Remove deprecated expo-build-properties deploymentTarget (default 16.4)
- Add withPodsDeploymentTarget plugin: raise CocoaPods resource bundle
  targets (RNCAsyncStorage, RNSVG, SDWebImage) to the app minimum
- Replace removed StyleSheet.absoluteFillObject with absoluteFill

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 12:54:53 +02:00
riccardoandClaude Opus 5 2653c7cad6 Add expo-build-properties, rename slug and bundle ids
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 12:41:32 +02:00
7 changed files with 2171 additions and 3000 deletions
+19 -6
View File
@@ -1,29 +1,28 @@
{
"expo": {
"name": "Fonsi Costruzioni",
"slug": "fonsi_app",
"slug": "fonsi-app",
"version": "1.0",
"orientation": "portrait",
"icon": "./assets/images/icon.png",
"scheme": "fonsiapp",
"userInterfaceStyle": "automatic",
"newArchEnabled": true,
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.pcrt.fonsi-app"
"bundleIdentifier": "com.pcrt.fonsi.app",
"appleTeamId": "T96LGS238W"
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/images/adaptive-icon.png",
"backgroundColor": "#ffffff"
},
"edgeToEdgeEnabled": true,
"predictiveBackGestureEnabled": false,
"permissions": [
"android.permission.CAMERA",
"android.permission.RECORD_AUDIO"
],
"package": "com.pcrt.fonsi_app"
"package": "com.pcrt.fonsi.app"
},
"web": {
"output": "static",
@@ -52,7 +51,21 @@
"recordAudioAndroid": true
}
],
"expo-font"
"expo-font",
[
"expo-build-properties",
{
"ios": {
"enableSceneSupport": true
}
}
],
"expo-image",
"expo-secure-store",
"expo-sharing",
"expo-status-bar",
"expo-web-browser",
"./plugins/withPodsDeploymentTarget"
],
"experiments": {
"typedRoutes": true,
+2
View File
@@ -117,6 +117,8 @@ export default function ActivityFormScreen() {
allowsMultipleSelection: true,
selectionLimit: limit,
quality: 0.8,
// iOS: converte HEIC in JPEG, formato gestito dal backend e visualizzabile sul web
preferredAssetRepresentationMode: ImagePicker.UIImagePickerPreferredAssetRepresentationMode.Compatible,
});
if (!result.canceled && result.assets) {
+10 -2
View File
@@ -2,7 +2,8 @@ import React, { useState, useEffect, useMemo, useRef } from 'react';
import { View, Text, ScrollView, TouchableOpacity, ActivityIndicator, Dimensions } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { StatusBar } from 'expo-status-bar';
import { ChevronDown, ChevronUp, LayoutDashboard } from 'lucide-react-native';
import { useRouter } from 'expo-router';
import { ChevronDown, ChevronLeft, ChevronUp, LayoutDashboard } from 'lucide-react-native';
import api from '@/utils/api';
import EchartWrapper from '@/components/EchartWrapper';
import {
@@ -22,6 +23,7 @@ const TOOLTIP_BASE = {
const screenWidth = Dimensions.get("window").width;
export default function DashboardScreen() {
const router = useRouter();
const [chartData, setChartData] = useState<any>({
costiRicavi: null,
@@ -206,7 +208,13 @@ export default function DashboardScreen() {
<SafeAreaView edges={['top']} className="pt-5">
<View className="pb-6 px-6 z-10 flex-row justify-between items-center">
<View>
<TouchableOpacity
onPress={() => router.navigate('/')}
className="p-2 -ml-2 rounded-full active:bg-white/20 w-12 items-center justify-center"
>
<ChevronLeft size={28} color="white" pointerEvents="none" />
</TouchableOpacity>
<View className="flex-1 px-2">
<Text className="text-white text-md font-semibold uppercase tracking-wider mb-1">Grafici e Statistiche</Text>
<Text className="text-yellow-400 text-3xl font-bold leading-tight">Dashboard</Text>
</View>
+3 -3
View File
@@ -52,7 +52,7 @@ export default function QrScanModal({ visible, onClose, onScan }: QrScanModalPro
<View className="flex-1 bg-black">
{/* Camera Full Screen */}
<CameraView
style={StyleSheet.absoluteFillObject}
style={StyleSheet.absoluteFill}
facing="back"
onBarcodeScanned={scanned ? undefined : handleBarCodeScanned}
barcodeScannerSettings={{
@@ -61,7 +61,7 @@ export default function QrScanModal({ visible, onClose, onScan }: QrScanModalPro
/>
{/* Mask Overlay with transparent cutout */}
<View style={StyleSheet.absoluteFillObject} pointerEvents="none">
<View style={StyleSheet.absoluteFill} pointerEvents="none">
<View className="flex-1 bg-black/60" />
<View style={{ height: squareSize }} className="flex-row">
<View className="flex-1 bg-black/60" />
@@ -85,7 +85,7 @@ export default function QrScanModal({ visible, onClose, onScan }: QrScanModalPro
</View>
{/* UI Overlay */}
<SafeAreaView style={StyleSheet.absoluteFillObject} pointerEvents="box-none">
<SafeAreaView style={StyleSheet.absoluteFill} pointerEvents="box-none">
<View className="flex-1 justify-between pt-8">
{/* Header */}
<View className="items-center">
+2067 -2951
View File
File diff suppressed because it is too large Load Diff
+36 -38
View File
@@ -1,5 +1,5 @@
{
"name": "fonsi_app",
"name": "fonsi-app",
"main": "expo-router/entry",
"version": "1.0",
"scripts": {
@@ -17,56 +17,54 @@
"dependencies": {
"@expo/vector-icons": "^15.0.3",
"@react-native-async-storage/async-storage": "2.2.0",
"@react-native-community/netinfo": "11.4.1",
"@react-navigation/bottom-tabs": "^7.4.0",
"@react-navigation/elements": "^2.6.3",
"@react-navigation/native": "^7.1.8",
"@react-native-community/netinfo": "12.0.1",
"@wuba/react-native-echarts": "^3.1.1",
"axios": "^1.13.2",
"babel-preset-expo": "~54.0.10",
"babel-preset-expo": "~57.0.0",
"echarts": "^5.5.0",
"expo": "~54.0.36",
"expo-camera": "~17.0.10",
"expo-constants": "~18.0.10",
"expo-dev-client": "~6.0.20",
"expo-document-picker": "~14.0.8",
"expo-file-system": "~19.0.23",
"expo-font": "~14.0.12",
"expo-haptics": "~15.0.7",
"expo-image": "~3.0.10",
"expo-image-picker": "~17.0.11",
"expo-linking": "~8.0.11",
"expo-router": "~6.0.24",
"expo-secure-store": "~15.0.8",
"expo-sharing": "~14.0.8",
"expo-splash-screen": "~31.0.11",
"expo-status-bar": "~3.0.8",
"expo-symbols": "~1.0.7",
"expo-system-ui": "~6.0.8",
"expo-web-browser": "~15.0.9",
"expo": "~57.0.23",
"expo-build-properties": "~57.0.20",
"expo-camera": "~57.0.5",
"expo-constants": "~57.0.18",
"expo-dev-client": "~57.0.19",
"expo-document-picker": "~57.0.2",
"expo-file-system": "~57.0.7",
"expo-font": "~57.0.4",
"expo-haptics": "~57.0.3",
"expo-image": "~57.0.5",
"expo-image-picker": "~57.0.18",
"expo-linking": "~57.0.10",
"expo-router": "~57.0.21",
"expo-secure-store": "~57.0.4",
"expo-sharing": "~57.0.20",
"expo-splash-screen": "~57.0.9",
"expo-status-bar": "~57.0.1",
"expo-symbols": "~57.0.3",
"expo-system-ui": "~57.0.4",
"expo-web-browser": "~57.0.3",
"lucide-react-native": "^0.563.0",
"nativewind": "^4.2.1",
"prettier-plugin-tailwindcss": "^0.5.14",
"react": "19.1.0",
"react-dom": "19.1.0",
"react-native": "0.81.5",
"react-native-gesture-handler": "~2.28.0",
"react": "19.2.3",
"react-dom": "19.2.3",
"react-native": "0.86.3",
"react-native-gesture-handler": "~2.32.0",
"react-native-image-viewing": "^0.2.2",
"react-native-keyboard-controller": "1.18.5",
"react-native-reanimated": "~4.1.1",
"react-native-safe-area-context": "~5.6.0",
"react-native-screens": "~4.16.0",
"react-native-svg": "15.12.1",
"react-native-keyboard-controller": "1.21.9",
"react-native-reanimated": "4.5.1",
"react-native-safe-area-context": "~5.7.0",
"react-native-screens": "~4.26.0",
"react-native-svg": "15.15.4",
"react-native-ui-datepicker": "^3.1.2",
"react-native-web": "~0.21.0",
"react-native-worklets": "0.5.1",
"react-native-worklets": "0.10.1",
"tailwindcss": "^3.4.18"
},
"devDependencies": {
"@types/react": "~19.1.0",
"@types/react": "~19.2.14",
"eslint": "^9.25.0",
"eslint-config-expo": "~10.0.0",
"typescript": "~5.9.2"
"eslint-config-expo": "~57.0.2",
"typescript": "~6.0.3"
},
"private": true
}
+34
View File
@@ -0,0 +1,34 @@
const { withPodfile } = require('expo/config-plugins');
const MARKER = '# @generated withPodsDeploymentTarget';
// CocoaPods resource bundle targets (e.g. RNCAsyncStorage_resources, RNSVGFilters)
// keep the deployment target declared in their podspec, ignoring `platform :ios`.
// Xcode 27 rejects targets below iOS 15, so raise every pod target to the app's minimum.
const SNIPPET = `
${MARKER}
min_ios_target = podfile_properties['ios.deploymentTarget'] || '16.4'
installer.pods_project.targets.each do |target|
target.build_configurations.each do |build_config|
current = build_config.build_settings['IPHONEOS_DEPLOYMENT_TARGET']
if current.nil? || Gem::Version.new(current) < Gem::Version.new(min_ios_target)
build_config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = min_ios_target
end
end
end
`;
module.exports = function withPodsDeploymentTarget(config) {
return withPodfile(config, (config) => {
const podfile = config.modResults;
if (podfile.contents.includes(MARKER)) {
return config;
}
const anchor = /post_install do \|installer\|\n/;
if (!anchor.test(podfile.contents)) {
throw new Error('withPodsDeploymentTarget: post_install block not found in Podfile');
}
podfile.contents = podfile.contents.replace(anchor, (match) => match + SNIPPET);
return config;
});
};