feature aggiunta
This commit is contained in:
88
ios/Podfile.lock
Normal file
88
ios/Podfile.lock
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
PODS:
|
||||||
|
- app_links (7.0.0):
|
||||||
|
- Flutter
|
||||||
|
- DKImagePickerController/Core (4.3.9):
|
||||||
|
- DKImagePickerController/ImageDataManager
|
||||||
|
- DKImagePickerController/Resource
|
||||||
|
- DKImagePickerController/ImageDataManager (4.3.9)
|
||||||
|
- DKImagePickerController/PhotoGallery (4.3.9):
|
||||||
|
- DKImagePickerController/Core
|
||||||
|
- DKPhotoGallery
|
||||||
|
- DKImagePickerController/Resource (4.3.9)
|
||||||
|
- DKPhotoGallery (0.0.19):
|
||||||
|
- DKPhotoGallery/Core (= 0.0.19)
|
||||||
|
- DKPhotoGallery/Model (= 0.0.19)
|
||||||
|
- DKPhotoGallery/Preview (= 0.0.19)
|
||||||
|
- DKPhotoGallery/Resource (= 0.0.19)
|
||||||
|
- SDWebImage
|
||||||
|
- SwiftyGif
|
||||||
|
- DKPhotoGallery/Core (0.0.19):
|
||||||
|
- DKPhotoGallery/Model
|
||||||
|
- DKPhotoGallery/Preview
|
||||||
|
- SDWebImage
|
||||||
|
- SwiftyGif
|
||||||
|
- DKPhotoGallery/Model (0.0.19):
|
||||||
|
- SDWebImage
|
||||||
|
- SwiftyGif
|
||||||
|
- DKPhotoGallery/Preview (0.0.19):
|
||||||
|
- DKPhotoGallery/Model
|
||||||
|
- DKPhotoGallery/Resource
|
||||||
|
- SDWebImage
|
||||||
|
- SwiftyGif
|
||||||
|
- DKPhotoGallery/Resource (0.0.19):
|
||||||
|
- SDWebImage
|
||||||
|
- SwiftyGif
|
||||||
|
- file_picker (0.0.1):
|
||||||
|
- DKImagePickerController/PhotoGallery
|
||||||
|
- Flutter
|
||||||
|
- Flutter (1.0.0)
|
||||||
|
- SDWebImage (5.21.7):
|
||||||
|
- SDWebImage/Core (= 5.21.7)
|
||||||
|
- SDWebImage/Core (5.21.7)
|
||||||
|
- shared_preferences_foundation (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
- FlutterMacOS
|
||||||
|
- SwiftyGif (5.4.5)
|
||||||
|
- url_launcher_ios (0.0.1):
|
||||||
|
- Flutter
|
||||||
|
|
||||||
|
DEPENDENCIES:
|
||||||
|
- app_links (from `.symlinks/plugins/app_links/ios`)
|
||||||
|
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||||
|
- Flutter (from `Flutter`)
|
||||||
|
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||||
|
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||||
|
|
||||||
|
SPEC REPOS:
|
||||||
|
trunk:
|
||||||
|
- DKImagePickerController
|
||||||
|
- DKPhotoGallery
|
||||||
|
- SDWebImage
|
||||||
|
- SwiftyGif
|
||||||
|
|
||||||
|
EXTERNAL SOURCES:
|
||||||
|
app_links:
|
||||||
|
:path: ".symlinks/plugins/app_links/ios"
|
||||||
|
file_picker:
|
||||||
|
:path: ".symlinks/plugins/file_picker/ios"
|
||||||
|
Flutter:
|
||||||
|
:path: Flutter
|
||||||
|
shared_preferences_foundation:
|
||||||
|
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
|
||||||
|
url_launcher_ios:
|
||||||
|
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||||
|
|
||||||
|
SPEC CHECKSUMS:
|
||||||
|
app_links: a754cbec3c255bd4bbb4d236ecc06f28cd9a7ce8
|
||||||
|
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
||||||
|
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
||||||
|
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
|
||||||
|
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||||
|
SDWebImage: e9fc87c1aab89a8ab1bbd74eba378c6f53be8abf
|
||||||
|
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
|
||||||
|
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||||
|
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
|
||||||
|
|
||||||
|
PODFILE CHECKSUM: 3c63482e143d1b91d2d2560aee9fb04ecc74ac7e
|
||||||
|
|
||||||
|
COCOAPODS: 1.16.2
|
||||||
@@ -10,6 +10,8 @@
|
|||||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||||
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
|
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
|
||||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||||
|
44490B82E7859424F77CB04B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 975E96D2C8BBF1CF6A3F5F40 /* Pods_Runner.framework */; };
|
||||||
|
449A3D64DB8C9C60EBDF7DD1 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A2C92D305DE434FC3C442B0 /* Pods_RunnerTests.framework */; };
|
||||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||||
7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */; };
|
7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */; };
|
||||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
@@ -43,27 +45,44 @@
|
|||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||||
|
1A2C92D305DE434FC3C442B0 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
|
||||||
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
35D61C73467480800D34D7BC /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||||
|
881F7F471B1559BA585653D1 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
8B6E013555080C92974ED449 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
8D4A0EA2456F02E466FCB0E1 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||||
|
975E96D2C8BBF1CF6A3F5F40 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
AB44F93458B7D70EE383A3A9 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
BDDDA09E437D9C0E7B65B3B1 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
0170592D7DFD7A1AE8221644 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
449A3D64DB8C9C60EBDF7DD1 /* Pods_RunnerTests.framework in Frameworks */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
44490B82E7859424F77CB04B /* Pods_Runner.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -78,6 +97,15 @@
|
|||||||
path = RunnerTests;
|
path = RunnerTests;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
6A991A28CCED9666CA172E00 /* Frameworks */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
975E96D2C8BBF1CF6A3F5F40 /* Pods_Runner.framework */,
|
||||||
|
1A2C92D305DE434FC3C442B0 /* Pods_RunnerTests.framework */,
|
||||||
|
);
|
||||||
|
name = Frameworks;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -96,6 +124,8 @@
|
|||||||
97C146F01CF9000F007C117D /* Runner */,
|
97C146F01CF9000F007C117D /* Runner */,
|
||||||
97C146EF1CF9000F007C117D /* Products */,
|
97C146EF1CF9000F007C117D /* Products */,
|
||||||
331C8082294A63A400263BE5 /* RunnerTests */,
|
331C8082294A63A400263BE5 /* RunnerTests */,
|
||||||
|
F5D002C3092D87755D552D32 /* Pods */,
|
||||||
|
6A991A28CCED9666CA172E00 /* Frameworks */,
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
@@ -124,6 +154,20 @@
|
|||||||
path = Runner;
|
path = Runner;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
F5D002C3092D87755D552D32 /* Pods */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
881F7F471B1559BA585653D1 /* Pods-Runner.debug.xcconfig */,
|
||||||
|
BDDDA09E437D9C0E7B65B3B1 /* Pods-Runner.release.xcconfig */,
|
||||||
|
AB44F93458B7D70EE383A3A9 /* Pods-Runner.profile.xcconfig */,
|
||||||
|
8B6E013555080C92974ED449 /* Pods-RunnerTests.debug.xcconfig */,
|
||||||
|
35D61C73467480800D34D7BC /* Pods-RunnerTests.release.xcconfig */,
|
||||||
|
8D4A0EA2456F02E466FCB0E1 /* Pods-RunnerTests.profile.xcconfig */,
|
||||||
|
);
|
||||||
|
name = Pods;
|
||||||
|
path = Pods;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
@@ -131,8 +175,10 @@
|
|||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
|
7385E42426A562D77ADB127F /* [CP] Check Pods Manifest.lock */,
|
||||||
331C807D294A63A400263BE5 /* Sources */,
|
331C807D294A63A400263BE5 /* Sources */,
|
||||||
331C807F294A63A400263BE5 /* Resources */,
|
331C807F294A63A400263BE5 /* Resources */,
|
||||||
|
0170592D7DFD7A1AE8221644 /* Frameworks */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@@ -148,12 +194,14 @@
|
|||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
|
||||||
buildPhases = (
|
buildPhases = (
|
||||||
|
55692154E5E0FA98E80084D6 /* [CP] Check Pods Manifest.lock */,
|
||||||
9740EEB61CF901F6004384FC /* Run Script */,
|
9740EEB61CF901F6004384FC /* Run Script */,
|
||||||
97C146EA1CF9000F007C117D /* Sources */,
|
97C146EA1CF9000F007C117D /* Sources */,
|
||||||
97C146EB1CF9000F007C117D /* Frameworks */,
|
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||||
97C146EC1CF9000F007C117D /* Resources */,
|
97C146EC1CF9000F007C117D /* Resources */,
|
||||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
|
6F6F1B58AD2DC9B50492B34B /* [CP] Embed Pods Frameworks */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@@ -241,6 +289,67 @@
|
|||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
||||||
};
|
};
|
||||||
|
55692154E5E0FA98E80084D6 /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
6F6F1B58AD2DC9B50492B34B /* [CP] Embed Pods Frameworks */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
||||||
|
);
|
||||||
|
name = "[CP] Embed Pods Frameworks";
|
||||||
|
outputFileListPaths = (
|
||||||
|
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
|
7385E42426A562D77ADB127F /* [CP] Check Pods Manifest.lock */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputFileListPaths = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||||
|
"${PODS_ROOT}/Manifest.lock",
|
||||||
|
);
|
||||||
|
name = "[CP] Check Pods Manifest.lock";
|
||||||
|
outputFileListPaths = (
|
||||||
|
);
|
||||||
|
outputPaths = (
|
||||||
|
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
|
showEnvVarsInLog = 0;
|
||||||
|
};
|
||||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
alwaysOutOfDate = 1;
|
alwaysOutOfDate = 1;
|
||||||
@@ -382,6 +491,7 @@
|
|||||||
};
|
};
|
||||||
331C8088294A63A400263BE5 /* Debug */ = {
|
331C8088294A63A400263BE5 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 8B6E013555080C92974ED449 /* Pods-RunnerTests.debug.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
@@ -399,6 +509,7 @@
|
|||||||
};
|
};
|
||||||
331C8089294A63A400263BE5 /* Release */ = {
|
331C8089294A63A400263BE5 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 35D61C73467480800D34D7BC /* Pods-RunnerTests.release.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
@@ -414,6 +525,7 @@
|
|||||||
};
|
};
|
||||||
331C808A294A63A400263BE5 /* Profile */ = {
|
331C808A294A63A400263BE5 /* Profile */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
|
baseConfigurationReference = 8D4A0EA2456F02E466FCB0E1 /* Pods-RunnerTests.profile.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
|||||||
3
ios/Runner.xcworkspace/contents.xcworkspacedata
generated
3
ios/Runner.xcworkspace/contents.xcworkspacedata
generated
@@ -4,4 +4,7 @@
|
|||||||
<FileRef
|
<FileRef
|
||||||
location = "group:Runner.xcodeproj">
|
location = "group:Runner.xcodeproj">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Pods/Pods.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
</Workspace>
|
</Workspace>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:flux/core/blocs/session/session_bloc.dart';
|
import 'package:flux/core/blocs/session/session_bloc.dart';
|
||||||
import 'package:flux/features/auth/ui/auth_screen.dart';
|
import 'package:flux/features/auth/ui/auth_screen.dart';
|
||||||
import 'package:flux/features/company/ui/create_company_screen.dart';
|
import 'package:flux/features/company/ui/create_company_screen.dart';
|
||||||
@@ -8,7 +7,6 @@ import 'package:flux/features/customers/ui/customer_detail_screen.dart';
|
|||||||
import 'package:flux/features/home/ui/home_screen.dart';
|
import 'package:flux/features/home/ui/home_screen.dart';
|
||||||
import 'package:flux/features/master_data/products/ui/products_screen.dart';
|
import 'package:flux/features/master_data/products/ui/products_screen.dart';
|
||||||
import 'package:flux/features/master_data/store/ui/create_store_screen.dart';
|
import 'package:flux/features/master_data/store/ui/create_store_screen.dart';
|
||||||
import 'package:flux/features/services/blocs/services_cubit.dart';
|
|
||||||
import 'package:flux/features/services/models/service_model.dart';
|
import 'package:flux/features/services/models/service_model.dart';
|
||||||
import 'package:flux/features/services/ui/service_form_screen/service_form_screen.dart';
|
import 'package:flux/features/services/ui/service_form_screen/service_form_screen.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|||||||
@@ -19,4 +19,24 @@ extension MyStringExtensions on String? {
|
|||||||
})
|
})
|
||||||
.join(' ');
|
.join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String fileExtension() {
|
||||||
|
if (this == null || this!.trim().isEmpty) return '';
|
||||||
|
|
||||||
|
final parts = this!.split('.');
|
||||||
|
if (parts.length < 2) return ''; // Nessuna estensione trovata
|
||||||
|
|
||||||
|
return parts.last.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
String fileNameWithoutExtension() {
|
||||||
|
if (this == null || this!.trim().isEmpty) return '';
|
||||||
|
|
||||||
|
final parts = this!.split('.');
|
||||||
|
if (parts.length < 2) return this!; // Nessuna estensione trovata
|
||||||
|
|
||||||
|
return parts
|
||||||
|
.sublist(0, parts.length - 1)
|
||||||
|
.join('.'); // Ritorna tutto tranne l'ultima parte
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
|
import 'package:flux/core/blocs/session/session_bloc.dart';
|
||||||
|
import 'package:flux/core/utils/string_extensions.dart';
|
||||||
import 'package:flux/features/customers/models/customer_file_model.dart';
|
import 'package:flux/features/customers/models/customer_file_model.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||||
import '../models/customer_model.dart';
|
import '../models/customer_model.dart';
|
||||||
|
|
||||||
class CustomerRepository {
|
class CustomerRepository {
|
||||||
final SupabaseClient _client = GetIt.I<SupabaseClient>();
|
final SupabaseClient _supabase = GetIt.I<SupabaseClient>();
|
||||||
|
final String companyId = GetIt.I.get<SessionBloc>().state.company!.id;
|
||||||
|
|
||||||
// Crea un nuovo cliente
|
// Crea un nuovo cliente
|
||||||
Future<CustomerModel> saveCustomer(CustomerModel customer) async {
|
Future<CustomerModel> saveCustomer(CustomerModel customer) async {
|
||||||
try {
|
try {
|
||||||
final response = await _client
|
final response = await _supabase
|
||||||
.from('customer')
|
.from('customer')
|
||||||
.upsert(customer.toJson())
|
.upsert(customer.toJson())
|
||||||
.select()
|
.select()
|
||||||
@@ -25,7 +26,7 @@ class CustomerRepository {
|
|||||||
|
|
||||||
Future<CustomerModel> updateCustomer(CustomerModel customer) async {
|
Future<CustomerModel> updateCustomer(CustomerModel customer) async {
|
||||||
try {
|
try {
|
||||||
final response = await _client
|
final response = await _supabase
|
||||||
.from('customer')
|
.from('customer')
|
||||||
.update(customer.toJson())
|
.update(customer.toJson())
|
||||||
.eq('id', customer.id!)
|
.eq('id', customer.id!)
|
||||||
@@ -40,7 +41,7 @@ class CustomerRepository {
|
|||||||
// Recupera tutti i clienti dell'azienda
|
// Recupera tutti i clienti dell'azienda
|
||||||
Future<List<CustomerModel>> getCustomers(String companyId) async {
|
Future<List<CustomerModel>> getCustomers(String companyId) async {
|
||||||
try {
|
try {
|
||||||
final response = await _client
|
final response = await _supabase
|
||||||
.from('customer')
|
.from('customer')
|
||||||
.select('*, customer_file(count)')
|
.select('*, customer_file(count)')
|
||||||
.eq('company_id', companyId)
|
.eq('company_id', companyId)
|
||||||
@@ -59,7 +60,7 @@ class CustomerRepository {
|
|||||||
String query,
|
String query,
|
||||||
) async {
|
) async {
|
||||||
try {
|
try {
|
||||||
final response = await _client
|
final response = await _supabase
|
||||||
.from('customer')
|
.from('customer')
|
||||||
.select()
|
.select()
|
||||||
.eq('company_id', companyId)
|
.eq('company_id', companyId)
|
||||||
@@ -75,13 +76,13 @@ class CustomerRepository {
|
|||||||
/// Recupera i file di un cliente specifico
|
/// Recupera i file di un cliente specifico
|
||||||
Future<List<CustomerFileModel>> getCustomerFiles(String customerId) async {
|
Future<List<CustomerFileModel>> getCustomerFiles(String customerId) async {
|
||||||
try {
|
try {
|
||||||
final response = await _client
|
final response = await _supabase
|
||||||
.from('customer_file')
|
.from('customer_file')
|
||||||
.select()
|
.select()
|
||||||
.eq('customer_id', customerId);
|
.eq('customer_id', customerId);
|
||||||
|
|
||||||
return (response as List)
|
return (response as List)
|
||||||
.map((f) => CustomerFileModel.fromJson(f))
|
.map((f) => CustomerFileModel.fromMap(f))
|
||||||
.toList();
|
.toList();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw 'Errore recupero file: $e';
|
throw 'Errore recupero file: $e';
|
||||||
@@ -90,7 +91,7 @@ class CustomerRepository {
|
|||||||
|
|
||||||
/// Salva il riferimento del file nel DB
|
/// Salva il riferimento del file nel DB
|
||||||
Future<void> saveFileReference(CustomerFileModel file) async {
|
Future<void> saveFileReference(CustomerFileModel file) async {
|
||||||
await _client.from('customer_file').insert(file.toJson());
|
await _supabase.from('customer_file').insert(file.toMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Carica un file e salva il riferimento nel database
|
/// Carica un file e salva il riferimento nel database
|
||||||
@@ -98,15 +99,24 @@ class CustomerRepository {
|
|||||||
required String customerId,
|
required String customerId,
|
||||||
required PlatformFile pickedFile,
|
required PlatformFile pickedFile,
|
||||||
}) async {
|
}) async {
|
||||||
|
final cleanFileName = pickedFile.name.replaceAll(
|
||||||
|
RegExp(r'[^a-zA-Z0-9\.\-]'),
|
||||||
|
'_',
|
||||||
|
);
|
||||||
|
final storagePath =
|
||||||
|
'$companyId/customers/${DateTime.now().millisecondsSinceEpoch}_$cleanFileName';
|
||||||
|
final int fileSize = pickedFile.size;
|
||||||
|
final fileToSave = CustomerFileModel(
|
||||||
|
customerId: customerId,
|
||||||
|
name: cleanFileName.fileNameWithoutExtension(),
|
||||||
|
extension: cleanFileName.fileExtension(),
|
||||||
|
url: '',
|
||||||
|
fileSize: fileSize,
|
||||||
|
);
|
||||||
|
final String mimeType = fileToSave.extension.toLowerCase() == 'pdf'
|
||||||
|
? 'application/pdf'
|
||||||
|
: 'image/${fileToSave.extension}';
|
||||||
try {
|
try {
|
||||||
final user = _client.auth.currentUser;
|
|
||||||
if (user == null) throw 'Utente non autenticato';
|
|
||||||
|
|
||||||
final fileName = pickedFile.name;
|
|
||||||
final extension = pickedFile.extension ?? '';
|
|
||||||
final path =
|
|
||||||
'${user.id}/$customerId/${DateTime.now().millisecondsSinceEpoch}_$fileName';
|
|
||||||
|
|
||||||
// Usiamo bytes invece del path per massima compatibilità
|
// Usiamo bytes invece del path per massima compatibilità
|
||||||
if (pickedFile.bytes == null && pickedFile.path == null) {
|
if (pickedFile.bytes == null && pickedFile.path == null) {
|
||||||
throw 'Impossibile leggere il contenuto del file';
|
throw 'Impossibile leggere il contenuto del file';
|
||||||
@@ -114,32 +124,26 @@ class CustomerRepository {
|
|||||||
|
|
||||||
// Se siamo su desktop/mobile abbiamo il path, su web abbiamo i bytes
|
// Se siamo su desktop/mobile abbiamo il path, su web abbiamo i bytes
|
||||||
if (pickedFile.bytes != null) {
|
if (pickedFile.bytes != null) {
|
||||||
await _client.storage
|
await _supabase.storage
|
||||||
.from('documents')
|
.from('documents')
|
||||||
.uploadBinary(path, pickedFile.bytes!);
|
.uploadBinary(
|
||||||
} else {
|
storagePath,
|
||||||
final file = File(pickedFile.path!);
|
pickedFile.bytes!,
|
||||||
await _client.storage.from('documents').upload(path, file);
|
fileOptions: FileOptions(contentType: mimeType, upsert: true),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final String publicUrl = _client.storage
|
final String publicUrl = _supabase.storage
|
||||||
.from('documents')
|
.from('documents')
|
||||||
.getPublicUrl(path);
|
.getPublicUrl(storagePath);
|
||||||
|
|
||||||
final fileRecord = CustomerFileModel(
|
final response = await _supabase
|
||||||
customerId: customerId,
|
|
||||||
name: fileName,
|
|
||||||
url: publicUrl,
|
|
||||||
extension: extension,
|
|
||||||
);
|
|
||||||
|
|
||||||
final response = await _client
|
|
||||||
.from('customer_file')
|
.from('customer_file')
|
||||||
.insert(fileRecord.toJson())
|
.insert(fileToSave.copyWith(url: publicUrl).toMap())
|
||||||
.select()
|
.select()
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
return CustomerFileModel.fromJson(response);
|
return CustomerFileModel.fromMap(response);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw 'Errore durante l\'upload: $e';
|
throw 'Errore durante l\'upload: $e';
|
||||||
}
|
}
|
||||||
@@ -147,13 +151,16 @@ class CustomerRepository {
|
|||||||
|
|
||||||
/// Aggiorna la lista degli URL nel database
|
/// Aggiorna la lista degli URL nel database
|
||||||
Future<void> updateCustomerDocuments(int id, List<String> urls) async {
|
Future<void> updateCustomerDocuments(int id, List<String> urls) async {
|
||||||
await _client.from('customer').update({'document_urls': urls}).eq('id', id);
|
await _supabase
|
||||||
|
.from('customer')
|
||||||
|
.update({'document_urls': urls})
|
||||||
|
.eq('id', id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Elimina un file dallo storage
|
/// Elimina un file dallo storage
|
||||||
Future<void> deleteDocument(String fullPath) async {
|
Future<void> deleteDocument(String fullPath) async {
|
||||||
// Il path dovrebbe essere ricavato dall'URL
|
// Il path dovrebbe essere ricavato dall'URL
|
||||||
final path = fullPath.split('documents/').last;
|
final path = fullPath.split('documents/').last;
|
||||||
await _client.storage.from('documents').remove([path]);
|
await _supabase.storage.from('documents').remove([path]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ class CustomerFileModel extends Equatable {
|
|||||||
final String url;
|
final String url;
|
||||||
final String extension;
|
final String extension;
|
||||||
final DateTime? createdAt;
|
final DateTime? createdAt;
|
||||||
|
final int fileSize;
|
||||||
|
|
||||||
const CustomerFileModel({
|
const CustomerFileModel({
|
||||||
this.id,
|
this.id,
|
||||||
@@ -15,31 +16,76 @@ class CustomerFileModel extends Equatable {
|
|||||||
required this.url,
|
required this.url,
|
||||||
required this.extension,
|
required this.extension,
|
||||||
this.createdAt,
|
this.createdAt,
|
||||||
|
required this.fileSize,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory CustomerFileModel.fromJson(Map<String, dynamic> json) {
|
// Trasforma i byte in qualcosa di leggibile (KB, MB, GB)
|
||||||
|
String get sizeFormatted {
|
||||||
|
if (fileSize <= 0) return "0 B";
|
||||||
|
const suffixes = ["B", "KB", "MB", "GB", "TB"];
|
||||||
|
var i = (fileSize.toString().length - 1) ~/ 3;
|
||||||
|
if (i >= suffixes.length) i = suffixes.length - 1;
|
||||||
|
double num = fileSize / (1 << (i * 10));
|
||||||
|
return "${num.toStringAsFixed(i == 0 ? 0 : 1)} ${suffixes[i]}";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get isPdf => extension.toLowerCase().replaceAll('.', '') == 'pdf';
|
||||||
|
|
||||||
|
CustomerFileModel copyWith({
|
||||||
|
String? id,
|
||||||
|
String? customerId,
|
||||||
|
String? name,
|
||||||
|
String? url,
|
||||||
|
String? extension,
|
||||||
|
DateTime? createdAt,
|
||||||
|
int? fileSize,
|
||||||
|
}) {
|
||||||
return CustomerFileModel(
|
return CustomerFileModel(
|
||||||
id: json['id'] as String,
|
id: id ?? this.id,
|
||||||
customerId: json['customer_id'],
|
customerId: customerId ?? this.customerId,
|
||||||
name: json['name'],
|
name: name ?? this.name,
|
||||||
url: json['url'],
|
url: url ?? this.url,
|
||||||
extension: json['extension'] ?? '',
|
extension: extension ?? this.extension,
|
||||||
createdAt: json['created_at'] != null
|
createdAt: createdAt ?? this.createdAt,
|
||||||
? DateTime.parse(json['created_at'])
|
fileSize: fileSize ?? this.fileSize,
|
||||||
: null,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
factory CustomerFileModel.fromMap(Map<String, dynamic> map) {
|
||||||
|
return CustomerFileModel(
|
||||||
|
id: map['id'] as String,
|
||||||
|
customerId: map['customer_id'],
|
||||||
|
name: map['name'],
|
||||||
|
url: map['url'],
|
||||||
|
extension: map['extension'] ?? '',
|
||||||
|
createdAt: map['created_at'] != null
|
||||||
|
? DateTime.parse(map['created_at'])
|
||||||
|
: null,
|
||||||
|
fileSize: map['file_size'] is int
|
||||||
|
? map['file_size']
|
||||||
|
: int.tryParse(map['file_size']?.toString() ?? '0') ?? 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
return {
|
return {
|
||||||
if (id != null) 'id': id,
|
if (id != null) 'id': id,
|
||||||
'customer_id': customerId,
|
'customer_id': customerId,
|
||||||
'name': name,
|
'name': name,
|
||||||
'url': url,
|
'url': url,
|
||||||
'extension': extension,
|
'extension': extension,
|
||||||
|
'file_size': fileSize,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Object?> get props => [id, customerId, name, url, extension, createdAt];
|
List<Object?> get props => [
|
||||||
|
id,
|
||||||
|
customerId,
|
||||||
|
name,
|
||||||
|
url,
|
||||||
|
extension,
|
||||||
|
createdAt,
|
||||||
|
fileSize,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:equatable/equatable.dart';
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flux/core/blocs/session/session_bloc.dart';
|
import 'package:flux/core/blocs/session/session_bloc.dart';
|
||||||
@@ -129,6 +130,7 @@ class ServicesCubit extends Cubit<ServicesState> {
|
|||||||
companyId: _sessionBloc.state.company!.id,
|
companyId: _sessionBloc.state.company!.id,
|
||||||
),
|
),
|
||||||
status: ServicesStatus.ready,
|
status: ServicesStatus.ready,
|
||||||
|
localAttachments: [],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -208,7 +210,7 @@ class ServicesCubit extends Cubit<ServicesState> {
|
|||||||
final serviceToSave = state.currentService!.copyWith(isBozza: isBozza);
|
final serviceToSave = state.currentService!.copyWith(isBozza: isBozza);
|
||||||
|
|
||||||
// 2. Salvataggio corazzato
|
// 2. Salvataggio corazzato
|
||||||
await _repository.saveFullService(serviceToSave);
|
await _repository.saveFullService(serviceToSave, state.localAttachments);
|
||||||
|
|
||||||
// 3. Reset e ricaricamento
|
// 3. Reset e ricaricamento
|
||||||
emit(state.copyWith(status: ServicesStatus.saved, currentService: null));
|
emit(state.copyWith(status: ServicesStatus.saved, currentService: null));
|
||||||
@@ -222,4 +224,18 @@ class ServicesCubit extends Cubit<ServicesState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- GESTIONE ALLEGATI LOCALI ---
|
||||||
|
|
||||||
|
void addAttachments(List<PlatformFile> files) {
|
||||||
|
// Aggiungiamo i nuovi file a quelli già presenti in memoria
|
||||||
|
final updatedList = [...state.localAttachments, ...files];
|
||||||
|
emit(state.copyWith(localAttachments: updatedList));
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeLocalAttachment(int index) {
|
||||||
|
final updatedList = List<PlatformFile>.from(state.localAttachments);
|
||||||
|
updatedList.removeAt(index);
|
||||||
|
emit(state.copyWith(localAttachments: updatedList));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class ServicesState extends Equatable {
|
|||||||
final String query;
|
final String query;
|
||||||
final DateTimeRange? dateRange;
|
final DateTimeRange? dateRange;
|
||||||
final bool hasReachedMax;
|
final bool hasReachedMax;
|
||||||
|
final List<PlatformFile> localAttachments;
|
||||||
|
|
||||||
const ServicesState({
|
const ServicesState({
|
||||||
required this.status,
|
required this.status,
|
||||||
@@ -19,6 +20,7 @@ class ServicesState extends Equatable {
|
|||||||
this.query = '',
|
this.query = '',
|
||||||
this.dateRange,
|
this.dateRange,
|
||||||
this.hasReachedMax = false,
|
this.hasReachedMax = false,
|
||||||
|
this.localAttachments = const [],
|
||||||
});
|
});
|
||||||
|
|
||||||
ServicesState copyWith({
|
ServicesState copyWith({
|
||||||
@@ -29,6 +31,7 @@ class ServicesState extends Equatable {
|
|||||||
String? query,
|
String? query,
|
||||||
DateTimeRange? dateRange,
|
DateTimeRange? dateRange,
|
||||||
bool? hasReachedMax,
|
bool? hasReachedMax,
|
||||||
|
List<PlatformFile>? localAttachments,
|
||||||
}) {
|
}) {
|
||||||
return ServicesState(
|
return ServicesState(
|
||||||
status: status ?? this.status,
|
status: status ?? this.status,
|
||||||
@@ -38,6 +41,7 @@ class ServicesState extends Equatable {
|
|||||||
query: query ?? this.query,
|
query: query ?? this.query,
|
||||||
dateRange: dateRange ?? this.dateRange,
|
dateRange: dateRange ?? this.dateRange,
|
||||||
hasReachedMax: hasReachedMax ?? this.hasReachedMax,
|
hasReachedMax: hasReachedMax ?? this.hasReachedMax,
|
||||||
|
localAttachments: localAttachments ?? this.localAttachments,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,5 +54,6 @@ class ServicesState extends Equatable {
|
|||||||
query,
|
query,
|
||||||
dateRange,
|
dateRange,
|
||||||
hasReachedMax,
|
hasReachedMax,
|
||||||
|
localAttachments,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flux/core/blocs/session/session_bloc.dart';
|
||||||
|
import 'package:flux/core/utils/string_extensions.dart';
|
||||||
|
import 'package:flux/features/services/models/service_file_model.dart';
|
||||||
|
import 'package:get_it/get_it.dart';
|
||||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||||
import '../models/service_model.dart';
|
import '../models/service_model.dart';
|
||||||
|
|
||||||
class ServicesRepository {
|
class ServicesRepository {
|
||||||
final _supabase = Supabase.instance.client;
|
final _supabase = Supabase.instance.client;
|
||||||
|
final companyId = GetIt.I.get<SessionBloc>().state.company!.id;
|
||||||
|
|
||||||
// --- RECUPERO SINGOLO SERVIZIO CON JOIN COMPLETO ---
|
// --- RECUPERO SINGOLO SERVIZIO CON JOIN COMPLETO ---
|
||||||
Future<ServiceModel> fetchServiceById(String id) async {
|
Future<ServiceModel> fetchServiceById(String id) async {
|
||||||
@@ -16,7 +21,8 @@ class ServicesRepository {
|
|||||||
customer(nome),
|
customer(nome),
|
||||||
energy_service(*),
|
energy_service(*),
|
||||||
fin_service(*),
|
fin_service(*),
|
||||||
entertainment_service(*)
|
entertainment_service(*),
|
||||||
|
service_file(*)
|
||||||
''')
|
''')
|
||||||
.eq('id', id)
|
.eq('id', id)
|
||||||
.single();
|
.single();
|
||||||
@@ -44,7 +50,8 @@ class ServicesRepository {
|
|||||||
customer(nome),
|
customer(nome),
|
||||||
energy_service(*),
|
energy_service(*),
|
||||||
fin_service(*),
|
fin_service(*),
|
||||||
entertainment_service(*)
|
entertainment_service(*),
|
||||||
|
service_file(*)
|
||||||
''')
|
''')
|
||||||
.eq('company_id', companyId);
|
.eq('company_id', companyId);
|
||||||
|
|
||||||
@@ -75,7 +82,10 @@ class ServicesRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- SALVATAGGIO COMPLETO (PRIMA PADRE, POI FIGLI) ---
|
// --- SALVATAGGIO COMPLETO (PRIMA PADRE, POI FIGLI) ---
|
||||||
Future<void> saveFullService(ServiceModel service) async {
|
Future<void> saveFullService(
|
||||||
|
ServiceModel service,
|
||||||
|
List<PlatformFile> localFiles,
|
||||||
|
) async {
|
||||||
try {
|
try {
|
||||||
// 1. Upsert del record principale
|
// 1. Upsert del record principale
|
||||||
final serviceData = await _supabase
|
final serviceData = await _supabase
|
||||||
@@ -142,6 +152,63 @@ class ServicesRepository {
|
|||||||
if (insertTasks.isNotEmpty) {
|
if (insertTasks.isNotEmpty) {
|
||||||
await Future.wait(insertTasks);
|
await Future.wait(insertTasks);
|
||||||
}
|
}
|
||||||
|
if (localFiles.isNotEmpty) {
|
||||||
|
final List<Future> uploadTasks = [];
|
||||||
|
|
||||||
|
for (var file in localFiles) {
|
||||||
|
// Puliamo il nome del file per evitare problemi con spazi o caratteri strani
|
||||||
|
final cleanFileName = file.name.replaceAll(
|
||||||
|
RegExp(r'[^a-zA-Z0-9\.\-]'),
|
||||||
|
'_',
|
||||||
|
);
|
||||||
|
final storagePath =
|
||||||
|
'$companyId/services/$newId/${DateTime.now().millisecondsSinceEpoch}_$cleanFileName';
|
||||||
|
|
||||||
|
final int fileSize = file.size;
|
||||||
|
|
||||||
|
final fileToSave = ServiceFileModel(
|
||||||
|
name: cleanFileName.fileNameWithoutExtension(),
|
||||||
|
extension: cleanFileName.fileExtension(),
|
||||||
|
url: '',
|
||||||
|
serviceId: newId,
|
||||||
|
fileSize: fileSize,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Creiamo una funzione asincrona per caricare file e scrivere nel DB
|
||||||
|
Future<void> uploadAndLink() async {
|
||||||
|
// Determiniamo il MIME type corretto in base all'estensione
|
||||||
|
final String mimeType = fileToSave.extension.toLowerCase() == 'pdf'
|
||||||
|
? 'application/pdf'
|
||||||
|
: 'image/${fileToSave.extension}';
|
||||||
|
// A. Upload nel Bucket Storage (usiamo i bytes così funziona anche su Web!)
|
||||||
|
await _supabase.storage
|
||||||
|
.from('documents')
|
||||||
|
.uploadBinary(
|
||||||
|
storagePath,
|
||||||
|
file.bytes!,
|
||||||
|
fileOptions: FileOptions(
|
||||||
|
contentType:
|
||||||
|
mimeType, // Diciamo a Supabase esattamente cos'è!
|
||||||
|
upsert:
|
||||||
|
true, // Opzionale: sovrascrive se esiste già un file con lo stesso nome
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// B. Otteniamo l'URL pubblico e scriviamo il record del file nel DB
|
||||||
|
final String publicUrl = _supabase.storage
|
||||||
|
.from('documents')
|
||||||
|
.getPublicUrl(storagePath);
|
||||||
|
await _supabase
|
||||||
|
.from('service_file')
|
||||||
|
.insert(fileToSave.copyWith(url: publicUrl).toMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadTasks.add(uploadAndLink());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eseguiamo tutti gli upload in parallelo per la massima velocità
|
||||||
|
await Future.wait(uploadTasks);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Qui potresti aggiungere una logica di "rollback manuale" se necessario
|
// Qui potresti aggiungere una logica di "rollback manuale" se necessario
|
||||||
throw Exception('Errore durante il salvataggio corazzato: $e');
|
throw Exception('Errore durante il salvataggio corazzato: $e');
|
||||||
@@ -188,28 +255,4 @@ class ServicesRepository {
|
|||||||
]; // Fallback se non c'è ancora storia
|
]; // Fallback se non c'è ancora storia
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> uploadAttachment({
|
|
||||||
required String serviceId,
|
|
||||||
required String fileName,
|
|
||||||
required Uint8List fileBytes,
|
|
||||||
}) async {
|
|
||||||
try {
|
|
||||||
// 1. Upload fisico nel bucket 'service_documents'
|
|
||||||
final path = '$serviceId/$fileName';
|
|
||||||
await _supabase.storage
|
|
||||||
.from('service_documents')
|
|
||||||
.uploadBinary(path, fileBytes);
|
|
||||||
|
|
||||||
// 2. Registriamo l'esistenza del file nel database
|
|
||||||
await _supabase.from('service_attachment').insert({
|
|
||||||
'service_id': serviceId,
|
|
||||||
'file_path': path,
|
|
||||||
'file_name': fileName,
|
|
||||||
'created_at': DateTime.now().toIso8601String(),
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
throw "Errore upload: $e";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
91
lib/features/services/models/service_file_model.dart
Normal file
91
lib/features/services/models/service_file_model.dart
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
|
||||||
|
class ServiceFileModel extends Equatable {
|
||||||
|
final String? id;
|
||||||
|
final DateTime? createdAt;
|
||||||
|
final String name;
|
||||||
|
final String extension;
|
||||||
|
final String url;
|
||||||
|
final String serviceId;
|
||||||
|
final int fileSize; // <--- Aggiunto
|
||||||
|
|
||||||
|
const ServiceFileModel({
|
||||||
|
this.id,
|
||||||
|
this.createdAt,
|
||||||
|
required this.name,
|
||||||
|
required this.extension,
|
||||||
|
required this.url,
|
||||||
|
required this.serviceId,
|
||||||
|
required this.fileSize,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Trasforma i byte in qualcosa di leggibile (KB, MB, GB)
|
||||||
|
String get sizeFormatted {
|
||||||
|
if (fileSize <= 0) return "0 B";
|
||||||
|
const suffixes = ["B", "KB", "MB", "GB", "TB"];
|
||||||
|
var i = (fileSize.toString().length - 1) ~/ 3;
|
||||||
|
if (i >= suffixes.length) i = suffixes.length - 1;
|
||||||
|
double num = fileSize / (1 << (i * 10));
|
||||||
|
return "${num.toStringAsFixed(i == 0 ? 0 : 1)} ${suffixes[i]}";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get isPdf => extension.toLowerCase().replaceAll('.', '') == 'pdf';
|
||||||
|
|
||||||
|
ServiceFileModel copyWith({
|
||||||
|
String? id,
|
||||||
|
DateTime? createdAt,
|
||||||
|
String? name,
|
||||||
|
String? extension,
|
||||||
|
String? url,
|
||||||
|
String? serviceId,
|
||||||
|
int? fileSize,
|
||||||
|
}) {
|
||||||
|
return ServiceFileModel(
|
||||||
|
id: id ?? this.id,
|
||||||
|
createdAt: createdAt ?? this.createdAt,
|
||||||
|
name: name ?? this.name,
|
||||||
|
extension: extension ?? this.extension,
|
||||||
|
url: url ?? this.url,
|
||||||
|
serviceId: serviceId ?? this.serviceId,
|
||||||
|
fileSize: fileSize ?? this.fileSize,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
factory ServiceFileModel.fromMap(Map<String, dynamic> map) {
|
||||||
|
return ServiceFileModel(
|
||||||
|
id: map['id'] as String,
|
||||||
|
createdAt: map['created_at'] != null
|
||||||
|
? DateTime.parse(map['created_at'])
|
||||||
|
: null,
|
||||||
|
name: map['name'] ?? '',
|
||||||
|
extension: map['extension'] ?? '',
|
||||||
|
url: map['url'] ?? '',
|
||||||
|
serviceId: map['service_id']?.toString() ?? '',
|
||||||
|
fileSize: map['file_size'] is int
|
||||||
|
? map['file_size']
|
||||||
|
: int.tryParse(map['file_size']?.toString() ?? '0') ?? 0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {
|
||||||
|
if (id != null) 'id': id,
|
||||||
|
'name': name,
|
||||||
|
'extension': extension,
|
||||||
|
'url': url,
|
||||||
|
'service_id': serviceId,
|
||||||
|
'file_size': fileSize,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [
|
||||||
|
id,
|
||||||
|
createdAt,
|
||||||
|
name,
|
||||||
|
extension,
|
||||||
|
url,
|
||||||
|
serviceId,
|
||||||
|
fileSize,
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
import 'package:file_picker/file_picker.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flux/features/services/blocs/services_cubit.dart';
|
||||||
|
|
||||||
|
class AttachmentsSection extends StatelessWidget {
|
||||||
|
const AttachmentsSection({super.key});
|
||||||
|
|
||||||
|
Future<void> _pickFiles(BuildContext context) async {
|
||||||
|
// Usiamo withData: true fondamentale per avere i bytes e caricare su Supabase Storage
|
||||||
|
FilePickerResult? result = await FilePicker.pickFiles(
|
||||||
|
allowMultiple: true,
|
||||||
|
type: FileType.custom,
|
||||||
|
allowedExtensions: ['pdf', 'jpg', 'jpeg', 'png'],
|
||||||
|
withData: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result != null && context.mounted) {
|
||||||
|
context.read<ServicesCubit>().addAttachments(result.files);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return BlocBuilder<ServicesCubit, ServicesState>(
|
||||||
|
builder: (context, state) {
|
||||||
|
final localFiles = state.localAttachments;
|
||||||
|
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"DOCUMENTI ALLEGATI",
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Theme.of(context).colorScheme.primary,
|
||||||
|
letterSpacing: 1.2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
OutlinedButton.icon(
|
||||||
|
icon: const Icon(Icons.attach_file),
|
||||||
|
label: const Text("Aggiungi File"),
|
||||||
|
onPressed: () => _pickFiles(context),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
|
||||||
|
if (localFiles.isEmpty)
|
||||||
|
Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
color: Colors.grey.shade300,
|
||||||
|
style: BorderStyle.solid,
|
||||||
|
),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
color: Colors.grey.shade50,
|
||||||
|
),
|
||||||
|
child: const Text(
|
||||||
|
"Nessun documento allegato alla bozza.",
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(color: Colors.grey),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
itemCount: localFiles.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final file = localFiles[index];
|
||||||
|
// Calcoliamo la dimensione in MB
|
||||||
|
final sizeMb = (file.size / (1024 * 1024)).toStringAsFixed(2);
|
||||||
|
|
||||||
|
// Scegliamo un'icona in base al tipo di file
|
||||||
|
final isPdf = file.extension?.toLowerCase() == 'pdf';
|
||||||
|
|
||||||
|
return Card(
|
||||||
|
margin: const EdgeInsets.only(bottom: 8),
|
||||||
|
elevation: 0,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
side: BorderSide(color: Colors.grey.shade300),
|
||||||
|
),
|
||||||
|
child: ListTile(
|
||||||
|
leading: Icon(
|
||||||
|
isPdf ? Icons.picture_as_pdf : Icons.image,
|
||||||
|
color: isPdf ? Colors.red : Colors.blue,
|
||||||
|
size: 32,
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
file.name,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
subtitle: Text("$sizeMb MB"),
|
||||||
|
trailing: IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.delete_outline,
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
onPressed: () => context
|
||||||
|
.read<ServicesCubit>()
|
||||||
|
.removeLocalAttachment(index),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flux/features/services/blocs/services_cubit.dart';
|
import 'package:flux/features/services/blocs/services_cubit.dart';
|
||||||
import 'package:flux/features/services/models/service_model.dart';
|
import 'package:flux/features/services/models/service_model.dart';
|
||||||
|
import 'package:flux/features/services/ui/service_form_screen/attachment_section.dart';
|
||||||
import 'package:flux/features/services/ui/service_form_screen/customer_section.dart';
|
import 'package:flux/features/services/ui/service_form_screen/customer_section.dart';
|
||||||
import 'package:flux/features/services/ui/service_form_screen/general_info_section.dart';
|
import 'package:flux/features/services/ui/service_form_screen/general_info_section.dart';
|
||||||
import 'package:flux/features/services/ui/service_form_screen/services_grid.dart';
|
import 'package:flux/features/services/ui/service_form_screen/services_grid.dart';
|
||||||
@@ -113,7 +114,8 @@ class _ServiceFormScreenState extends State<ServiceFormScreen> {
|
|||||||
ServicesGrid(service: service),
|
ServicesGrid(service: service),
|
||||||
const SizedBox(height: 32),
|
const SizedBox(height: 32),
|
||||||
|
|
||||||
// TODO: _AttachmentsSection(),
|
AttachmentsSection(),
|
||||||
|
const SizedBox(height: 32),
|
||||||
_buildBottomActionButtons(context, isSaving: isSaving),
|
_buildBottomActionButtons(context, isSaving: isSaving),
|
||||||
const SizedBox(height: 32),
|
const SizedBox(height: 32),
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user