Changing my code to reset all IsSetButton (not only one Row) to False once (save or delete) is clicked

Since I included comment in my code I will send few post with all relevant detail here are my question for this SWfit file

  1. you place the projects = Modeldata().projectswhile I was trying to do it under modelData.swift file because when I add it into Project.swift file it's not Parse since not in my JSON. should I also add into the JSON ?
  2. to make the code cleaner I use a SetTimeKeyInView that I will share in next post
  3. creation of date and date range for my new datepickrange buttons
  4. @State var isSet: [Bool] = [false, false, false, false, false,false,false,false] is not needed
  5. @State private var hideTeco = false is not needed I have also remove it isTECO means Technically close project meaning I don't need to work on it but keep them as reference if one day I want to go and see location, customer and other info
  6. remove and change name for more clarity on code and like to point 2 I don't use it in here anymore
  7. Datepicker I will dedicate another threat POST as manyquestion I'm facing
  8. as you can see I don;t use the code
.onAppear() {
                    // <<-- Added
                    for (row, project) in filteredNonTECOProjects.enumerated() {
                            isSet[row] = !project.isTeco
                        }
                    }

so I'm not sure what is the code doing and I don't see the link with isTeco

import SwiftUI

struct TimeKeyinList: View, Observable {
    @Environment(ModelData.self) var modelData
    @Environment(TimesheetCalculation.self) var timesheetCalculation
    
    //MARK: - Variable & Constants
//    let projects = ModelData().projects // Now need a State var to be able to modify later I did not do that need to check with Claude
    var listofpossibleHourstext = TimesheetCalculation().listOfPossibleHoursText
  @State var isSet = ModelData().isSet // I think the code from claude is what I'm trying to do here
 //   @State var projects = [Project] ()        //CLAUDEADD Now need a State var to be able to modify
    @State var date = TimesheetCalculation().date
    @State var dateRange = TimesheetCalculation().dateRange
//    @State var isSet: [Bool] = [false, false, false, false, false,false,false,false] // explain to claude that I I ahve more proejct in future and I don't increare this list then the app crash ... not very sure why but i believe it's due to the number of row link with the number of project dispaly
    //CLAUDE SAY Not the correct var to keep hours for each project. @State var isSet: [Bool] = [true, false, false, true]
   //Alreadydeleted  @State private var hideTeco = false     // What's the purpose of this ? Not used in code
    
//    I remove the on appear as the code don't need it
    
//@State var filteredProjects: [Project] = []
    /* {  Init will be done in onAppear
    // removeTECO project
    projects.filter { project in
        !project.isTeco
    }
}*/
    //MARK: - Body
    var body: some View {
        @Bindable var timesheetCalculation = timesheetCalculation
        VStack {
            HStack {
                Text("TIME KEY_IN")
                    .font(.title)
                    .multilineTextAlignment(.leading)
                    .bold()
                
                Spacer()
                
                    Button {
                        print(TimesheetCalculation().date)
                    } label: {
                        Label("SAVE", systemImage: "square.and.arrow.down")
                            .foregroundStyle(.blue )
                            .scaleEffect(1.2)
                    }
                Spacer()
                //findtheocde to remove intRemaining hours keep the full string into a flooting point and tronacte teh string to only 3character
                let IntremainingHour = Int(timesheetCalculation.remainingHour)
                
                Text("\(IntremainingHour)h")
                    .font(.title)
                    .multilineTextAlignment(.leading)
                    .bold()
                
                
            }.padding()
            
            Spacer()
            
            
            VStack(spacing: 5){
                HStack(alignment: .top) {
                    
                    Text(" Project Name")
                        .multilineTextAlignment(.center)
                        .lineLimit(2)
                        .frame(width: 120.0) // <<-- To get proper alignment
                    //use the TimekeyinRow listofPossibleHour
                    ForEach((listofpossibleHourstext),id: \.self) { hour in
                        Text("\(hour)h")
                    }
                    Text("Time Left")
                        .multilineTextAlignment(.trailing)
                        .lineLimit(2)
                        .frame(width: 50)
                }.environment(\.sizeCategory, .extraExtraExtraLarge)
                    
                
              
                SetTimeKeyInView()
                
                
            }
            HStack {
                DatePickerStyle().environment(\.sizeCategory, .extraExtraExtraLarge)
                Button {
            //I need to clean the cache for
                    //clean all button
                    // remove all of date cvs
                    //remaining hour = 8
                    $timesheetCalculation.remainingHour
                } label: {
                    Label("CLEAN", systemImage: "arrow.up.trash")
                        .labelStyle(.iconOnly)
                        .foregroundStyle(.blue )
                        .scaleEffect(1.2)
                }
            }
            Spacer()
//                .onAppear() {
//                    // <<-- Added
//                    for (row, project) in filteredNonTECOProjects.enumerated() {
//                            isSet[row] = !project.isTeco
//                        }
//                    }
            
            let _ = print(modelData.isSet)
            
            Spacer()
        }
    }
}

struct TimeKeyinList_Previews2: PreviewProvider {
    static var previews: some View {
        //#Preview

        TimeKeyinList()
          .environment(ModelData())
            .environment(TimesheetCalculation())
        
    }
}

Hi Claude, few questions here or point here

  1. I have change the id from string to id for all id but I'm getting confuse on what id is actually doing in our code since we use the Id\ offset of the creation of array...
  2. You uses in TimekeyinRow @Binding var projects: [Project] which is for all project I use in in the project list when we combined all the list
  3. I change all double to float because float as less digit therefore I believe use less memory I hope
  4. your foreacharray uses 2 new property projectNum and allHoursFalse ( isHourSet) butI believe my buttonHoursbutton already collect all those information Right ?
  5. What you are doing now is the meaning of what I was trying to say when I was saying the the code as a glitch because seems like with the actual program I have I cannot collect and reset all button in one click
  6. I do not use onappear fonction at all ( I'm not sure what it's doing but my program work without it
  7. the button you add I would like to add it somewhere else on the list I will share the screenshot on the next tread which I try to make and call it ClearRow()

import SwiftUI

struct TimeKeyInRow: View,Identifiable {
    @Environment(TimesheetCalculation.self) var timesheetCalculation
    //MARK: - Variable & Constants
    var id: Int //CLAUDE use String
    var project: Project
//    @Binding var projects: [Project]     // we pass all projects as well as the projectNum to handle. That eases the update of the project properties
    @Binding var isSet: Bool
   // CLAUDE ADD var projectNum: Int                          // The project displayed in the row
//    let allHoursFalse = [false, false, false, false, false, false]     // To simplify code writing, we hard code: there are 6 hours defined
    let listOfPossibleHours = TimesheetCalculation().listOfPossibleHours// double is 15 digit decimal while Float is only 6 digit < I'm only one digit therefore I use Float
    
    //MARK: - Body
    var body: some View {
        HStack {
            Text(project.shortname.paddedToWidth(10)) // Text(projects[1].shortname)
                .font(.custom("Menlo", size: 16))
                .multilineTextAlignment(.trailing)
                .frame(width: 120.0) // <<-- To get proper alignment
            Spacer()
            ForEach(listOfPossibleHours, id: \.self) { hour in
                HourButton(id: project.id, isSet: isSet, value: hour)
            }
//            ForEach(Array(listOfPossibleHours.enumerated()), id: \.offset) { (index, hour) in
//                HourButton(id: "\(projects[projectNum].shortname)", isSet: $projects[projectNum].isHourSet[index], value: hour)       // HourButton has changed, to pass value of isSet for each hour
//            }
            Text("\(project.leftPMtime)")
//    CLAUDE CHANGE here to collect a project number        Text("\(projects[projectNum].leftPMtime)")
                .font(.custom("Menlo", size: 16))
                .multilineTextAlignment(.leading)
                .frame(width: 45) // <<-- To get proper alignment
//   CLAUDE ADDED BUTTTON TO SET ALL TO FALSE         Button {
            //                projects[projectNum].isHourSet = allHoursFalse
            //            } label: {
            //                Text("all off")
            //            }
        }
    }
}


struct TimeKeyInRow_Previews: PreviewProvider {
    static var previews: some View {
        //#Preview {
        let projects = ModelData().projects
        
        TimeKeyInRow(id: projects[2].id, project: projects[2], isSet: .constant(false))
            .environment(TimesheetCalculation())
    }
}

extension String {  // To align text
    func paddedToWidth(_ width: Int) -> String {
        let length = self.count
        guard length < width else {
            return self
        }

        let spaces = Array<Character>.init(repeating: " ", count: width - length)
        return self + spaces
    }
}

struct BlankKeyInRow: View {
    var body: some View {
        HStack {
            Text(" ") // Text(projects[1].shortname)
        }
    }
}

struct ClearRow: View {
    @Environment(TimesheetCalculation.self) var timesheetCalculation
    var project: Project
    @Binding var isSet: Bool
    let listOfPossibleHours = TimesheetCalculation().listOfPossibleHours// double is 15 digit decimal while Float is only 6 digit < I'm only one digit therefore I use Float
    var body: some View {
        HStack {
            ForEach(listOfPossibleHours, id: \.self) { hour in
                HourButton(id: project.id, isSet: false, value: hour)
            }
        }
    }
}

Here is a SetTimeKeyInView which is use to set the row, to have a comprehensible discussion with you.

  1. I'm trying to make our conversation more clear and more focus oriented by splitting my code
  2. bring filternonTECOproject here
  3. this codecompletly change but same philosophy as previous post explain you want to use the ProRow as your row to simplify the code but for what ?
ForEach($filteredProjects, id: \.id) { $project in
TimeKeyInRow(id: project.shortname, projects: $projects, projectNum: project.projectRow)`

import Foundation
import SwiftUI

//MARK: - Variable & Constants
struct SetTimeKeyInView: View {
    
    @Environment(ModelData.self) var modelData
    let projects = ModelData().projects // Now need a State var to be able to modify later I did not do that need to check with Claude
    @State var isSet = ModelData().isSet // look similar that what Claude is truing to do in Project(isSet) but not working when I wanted to put in Project Structure ( error waring was cannot Parse Thread 1: Fatal error: Couldn't parse ProjectData.json as Array<Project>:
    var filteredNonTECOProjects: [Project] {
        // removeTECO project
        modelData.projects.filter { project in
            !project.isTeco
        }}
    //MARK: - Body
    var body: some View {
        VStack(spacing: 20) {
            BlankKeyInRow()
//   Claude Code completly changed         ForEach($filteredProjects, id: \.id) { $project in
//                TimeKeyInRow(id: project.shortname, projects: $projects, projectNum: project.projectRow)
//            }
            ForEach(Array(filteredNonTECOProjects.enumerated()), id: \.offset) { (row, project) in
                if project .isSpecific {
                    Divider()
                    Divider()
                    TimeKeyInRow(id: project.id, project: project, isSet: $isSet[row])
                } else {
                    TimeKeyInRow(id: project.id, project: project, isSet: $isSet[row])}}
        }.listStyle(.inset)
            .environment(\.sizeCategory, .extraExtraExtraLarge)
    }
}
//MARK: - Preview
struct SetTimeKeyInView_Previews2: PreviewProvider {
    static var previews: some View {
        //#Preview

        SetTimeKeyInView()
            .environment(ModelData())
            .environment(TimesheetCalculation())
        
    }
}


Here is the remaining code and questions :

  1. why do you use string ( or maybe I change in between and I ws the one using String at first ^^sorry if yes haha
  2. are you trying to use @binding here to clear all the button to false regardless of the project and it;s the only way to achieve it ?
  3. this 2 line of code timesheetCalculation.checkRemainingHours(isSet: true) keep telling me that it will never be used but actually it's beeing use and working : warning from Apple is Result of call to 'checkRemainingHours(isSet:)' is unused

timesheetCalculation.checkRemainingHours(isSet: true)

//
//  HourButton.swift
//  MyPmV1
//
//  Created by Sebastien BENAVIDES on 9/2/24.
//
import SwiftUI


struct HourButton: View,Identifiable, Observable { //This Identifiable is 
    @Environment(TimesheetCalculation.self) var timesheetCalculation
    
    //MARK: - Variable & Constants
    var id: Int = 1001 //CLAUDE USE STRING
    @State var isSet: Bool
// CLAUDE IUSE BINDING   @Binding var isSet: Bool      // A Binding, as we change the value and want it updated in TimeKeyInRow
    var value: Float
    
    //MARK: - Body
    var body: some View {
        @State var timesheetCalculation = timesheetCalculation

        HStack {
            Button {
                if value <= timesheetCalculation.remainingHour || isSet == true {
                    isSet.toggle()
                    
                    print("clic on Project : \(id) an turn to : \(isSet) for  \(value)h")
                    if isSet == true {
                        timesheetCalculation.hourchoosen = value
                        timesheetCalculation.checkRemainingHours(isSet: true)
                        timesheetCalculation.hourchoosen = value
                        timesheetCalculation.cvsrecordedtrackingcodek(idofthiskeyin: id, valueOfThisKeyin: value, itemsisSet: isSet)
                    } else {
                    
                        timesheetCalculation.hourchoosen = value
                        timesheetCalculation.checkRemainingHours(isSet: false)
                        timesheetCalculation.cvsrecordedtrackingcodek(idofthiskeyin: id, valueOfThisKeyin: value, itemsisSet: isSet)
                    }
                } else {
  //      Warning message
                    print("Please understand that you cannot work more than 8h")
                }
                
            } label: {
                Label("" , systemImage: isSet ? "circle.fill" : "circle")
                    .labelStyle(.iconOnly)
                    .foregroundStyle(isSet ? .blue : .gray)
                
            }.id(id)
            
        }
        
        }
}

//MARK: - Preview
struct HourButton_Previews: PreviewProvider {
    static var previews: some View {
        //#Preview {
        let timesheetCalculation = TimesheetCalculation()
        HourButton(id: 1001, isSet: false, value: 3).environment(timesheetCalculation)
    }
}
//MARK: - Extension
extension Array where Element: Equatable {
    mutating func removeFirstOccurrence(of element: Element) {
        if let index = firstIndex(of: element) {
            remove(at: index)
        }
    }
}


here is the logic code

//
//  TimesheetCalculation.swift
//  MyPmV1
//
//  Created by Sebastien BENAVIDES on 12/2/24.
//

import Foundation
import SwiftUI

@Observable
class TimesheetCalculation {

    var date = Date()
    let dateRange: ClosedRange<Date> = {
        let calendar = Calendar.current
        let startComponents = DateComponents(year: 2024, month: 1, day: 1)
        let endComponents = DateComponents(year: 2024, month: 12, day: 31)
        return calendar.date(from:startComponents)!
            ...
            calendar.date(from:endComponents)!
    }()
    var isSet: [Bool] = [false, false, false, false, false,false,false,false]
    let listOfPossibleHours: [Float] = [0.5,1,2,3,4,8]
    let listOfPossibleHoursText: [String] = [".5","1","2","3","4","8"]
    var hourchoosen: Float = 0
    var remainingHour: Float = 8
    var cvsrecordedtrackingcodek: String = ""
    var cvsrecordedtrackingcodekall: [String] = []
    func checkRemainingHours(isSet: Bool) -> Float {
        if isSet == true {
            remainingHour = remainingHour - hourchoosen
            return remainingHour
        } else {
            remainingHour = remainingHour + hourchoosen
            return remainingHour
        }
    }
    func cvsrecordedtrackingcodek(idofthiskeyin id:Int,valueOfThisKeyin value:Float,itemsisSet isSet:Bool) {
        let newstring = String("ProjectID:\(id) Number of Hours: \(value) For Day: \(date.formatted(date: .abbreviated, time: .omitted))")
        if isSet == true {
            cvsrecordedtrackingcodekall.append(newstring)
        } else {
            cvsrecordedtrackingcodekall.removeFirstOccurrence(of: newstring)
        }
            
        print(cvsrecordedtrackingcodekall)
    }
}

here is my Modeldata

//
//  ModelData.swift
//  MyPmV1
//
//  Created by Sebastien BENAVIDES on 3/2/24.
//

import Foundation
import Observation

@Observable
class ModelData {
    var projects: [Project] = load("ProjectData.json")
    var hikes: [Hike] = load("hikeData.json")
    var profile = Profile.default
   var isSet: [Bool] = [false, false, false, false, false,false,false,false]
    
    var features: [Project] {
           projects.filter { $0.isFeatured }
       }

    
    var categories: [String: [Project]] {
           Dictionary(
               grouping: projects,
               by: { $0.category.rawValue }
           )
       }
    
    var buisness: [String: [Project]] {
           Dictionary(
               grouping: projects,
               by: { $0.buisness_model.rawValue }
           )
       }
    var customers: [String: [Project]] {
           Dictionary(
               grouping: projects,
               by: { $0.customer.rawValue }
           )
       }
}



func load<T: Decodable>(_ filename: String) -> T {
    let data: Data


    guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
    else {
        fatalError("Couldn't find \(filename) in main bundle.")
    }


    do {
        data = try Data(contentsOf: file)
    } catch {
        fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
    }


    do {
        let decoder = JSONDecoder()
        return try decoder.decode(T.self, from: data)
      
    } catch {
        fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
    }
}

here is my button change data :

import SwiftUI

struct DatePickerStyle: View, Observable {
    
    @Environment(TimesheetCalculation.self) var timesheetCalculation
    
    @State var date = TimesheetCalculation().date
    @State var dateRange = TimesheetCalculation().dateRange
    
    var body: some View {
        @Bindable var timesheetCalculation = timesheetCalculation
        
        HStack {
            Button {
                timesheetCalculation.date = timesheetCalculation.date.addingTimeInterval(-86400)
            } label: {Label("8h", systemImage: "arrowtriangle.backward.fill")
                    .labelStyle(.iconOnly)
                    .offset(x:5)
                    .scaleEffect(2)
                .foregroundStyle(Color(red: 0.937, green: 0.937, blue: 0.942) )}
            
            DatePicker(
                "Start Date",
                selection: $timesheetCalculation.date,
                in: timesheetCalculation.dateRange,
                displayedComponents: [.date]
                
            ).padding(.horizontal)
            
            Button {
                
                timesheetCalculation.date = timesheetCalculation.date.addingTimeInterval(86400)
                
            } label: {Label("8h", systemImage: "arrowtriangle.forward.fill")
                    .labelStyle(.iconOnly)
                    .offset(x:-5)
                    .scaleEffect(2)
                    .foregroundStyle(Color(red: 0.937, green: 0.937, blue: 0.942) )
                
            }
        }.labelsHidden()
        
    }
}

struct DatePickerStyle_Previews2: PreviewProvider {
    static var previews: some View {
        //#Preview

        DatePickerStyle()
          
            .environment(TimesheetCalculation())
        
    }
}

and here is my Project Struct

import Foundation
import SwiftUI
import CoreLocation

struct Project: Hashable, Codable,Identifiable {
    var id: Int //UUID = UUID()
    var name: String
    var shortname: String
    var leftPMtime: Int
//    var projectRow: Int // inside projects ( why claude add this ??
//    var city: String
//    var country: String
    var state: String
    var description: String
    var isFeatured: Bool
    var isSpecific: Bool
   var isFavorite: Bool
    var isTeco: Bool
    var coordinates: Coordinates
//  var isHourSet : [Bool] // ### To store for each hour of the project
    
    //    private enum CodingKeys : String, CodingKey {
    //        case name, shortname, leftPMtime, isTeco
    //    }
    
    var category: Category
        enum Category: String, CaseIterable, Codable {
            case foodAndBaverage = "Food & Baverage"
            case NutritionAndBioscience = "Nutrition & Bioscience"
        }
    
    var buisness_model: Buisness
        enum Buisness: String, CaseIterable, Codable {
            case BM1 = "BM1"
            case BM2 = "BM2"
            case BM3 = "BM3"
        }
    
    var customer: CustomerCat
        enum CustomerCat: String, CaseIterable, Codable {
            case Nestle = "Nestle"
            case IFF = "IFF"
            case FFI = "FFI"
            case IFFCO = "IFFCO"
            case Abbott = "Abbott"
            case Mondelez = "Mondelez"
            case Internal = "Internal"

            
        }
    
    private var imageName: String
       var image: Image {
           Image(imageName)
       }
    
    var featureImage: Image? {
           isFeatured ? Image(imageName + "_feature") : nil
       }
    
    
    
    var locationCoordinate: CLLocationCoordinate2D {
          CLLocationCoordinate2D(
              latitude: coordinates.latitude,
              longitude: coordinates.longitude)
      }


        struct Coordinates: Hashable, Codable {
            var latitude: Double
            var longitude: Double
        }
}

as you can see UUID and isSet is not working when I place them inside I believe due to JSON way

I propose you to share by mail, so that you can send the complete project. Ity will be much easier. just indicate a mail where to contact you.

See the older thread for a cleaner solution.

Changing my code to reset all IsSetButton (not only one Row) to False once (save or delete) is clicked
 
 
Q