// // Created by caleb on 4/10/24. // #include #include #include #include #include #include #include #include #include #include #include "userData.h" #include "simpleMenu.h" std::tm enterDate() { std::string temp; std::tm tm; std::printf("Enter day: "); std::getline(std::cin, temp); std::sscanf(temp.c_str(), "%i", &tm.tm_mday); std::printf("Enter month: "); std::getline(std::cin, temp); std::sscanf(temp.c_str(), "%i", &tm.tm_mon); std::printf("Enter year: "); std::getline(std::cin, temp); std::sscanf(temp.c_str(), "%i", &tm.tm_year); tm.tm_mon -= 1; tm.tm_year -= 1900; tm.tm_hour = 0, tm.tm_min = 0, tm.tm_sec = 0; return tm; } userData enterRecord() { auto pUserData = new userData(); std::printf("Entering a new record...\n"); std::printf("Enter name (spaces allowed): "); std::getline(std::cin, pUserData->name); std::printf("Enter address: "); std::getline(std::cin, pUserData->address); std::printf("Enter city: "); std::getline(std::cin, pUserData->city); std::printf("Enter state: "); std::getline(std::cin, pUserData->state); std::printf("Enter ZIP code: "); std::getline(std::cin, pUserData->zip); std::printf("Enter phone #: "); std::getline(std::cin, pUserData->phone); while (true) { std::printf("Enter account balance: $"); std::string temp; std::getline(std::cin, temp); try { pUserData->accountBal = std::stod(temp); if (pUserData->accountBal < 0) { throw std::invalid_argument("less than zero"); } else { break; } } catch (const std::invalid_argument &e) { std::cerr << "Invalid input for balance. Please enter a valid number." << std::endl; } } std::printf("Entering date of last payment...\n"); std::tm tm = enterDate(); pUserData->dateOfLastPayment = std::mktime(&tm); return *pUserData; } userData *findRecord(const std::vector& data) { std::printf("Here's the list of entries: \n%s\n", displayData(data).c_str()); while (true) { std::printf("Enter the name for the entry you want to search for\n(we're searching for substrings, so you don't have to type out the full thing): "); std::string userInput; std::getline(std::cin, userInput); // attempt to find entry std::printf("Looking for %s...\n", userInput.c_str()); for (auto& entry : data) { std::string lowerName = boost::algorithm::to_lower_copy(entry.name); if (lowerName.find(boost::algorithm::to_lower_copy(userInput)) != std::string::npos) { std::printf("%s\nIs this the entry you wanted (y/n)? ", displayData(entry).c_str()); std::getline(std::cin, userInput); boost::algorithm::to_lower(userInput); if (userInput == "y") { return &entry; // Return pointer to the found entry directly } } } printf("Unable to find entry... Press enter to continue."); std::getline(std::cin, userInput); boost::algorithm::to_lower(userInput); } } void deleteEntry(std::vector& data) { std::printf("What entry would you like to delete?"); userData *entryToDelete = findRecord(data); for(int i = 0; i < data.size(); ++i) { if (entryToDelete->name == data[i].name) { data.erase(data.begin() + i); } } updateData(data); } void changeEntry(std::vector& data) { std::printf("What entry would you like to modify?"); userData* selectedEntry = findRecord(data); std::vector menuItems = structContents(selectedEntry); int selection = simpleMenu(menuItems, "What data would you like to modify?"); switch (selection) { default: break; case 9: { std::printf("Old value: %s\nEnter new name (spaces allowed): ", selectedEntry->name.c_str()); std::getline(std::cin, selectedEntry->name); break; } case 1: { std::printf("Old value: %s\nEnter new address: ", selectedEntry->address.c_str()); std::getline(std::cin, selectedEntry->address); break; } case 2: { std::printf("Old value: %s\nEnter new city: ", selectedEntry->city.c_str()); std::getline(std::cin, selectedEntry->city); break; } case 3: { std::printf("Old value: %s\nEnter new state: ", selectedEntry->state.c_str()); std::getline(std::cin, selectedEntry->state); break; } case 4: { std::printf("Old value: %s\nEnter new ZIP: ", selectedEntry->zip.c_str()); std::getline(std::cin, selectedEntry->zip); break; } case 5: { std::printf("Old value: %s\nEnter new phone #: ", selectedEntry->phone.c_str()); std::getline(std::cin, selectedEntry->phone); break; } case 6: { while (true) { std::printf("Old value: %.2lf\nEnter new balance: $", selectedEntry->accountBal); std::string temp; std::getline(std::cin, temp); try { selectedEntry->accountBal = std::stod(temp); if (selectedEntry->accountBal < 0) { throw std::invalid_argument("less than zero"); } else { break; } } catch (const std::invalid_argument &e) { std::cerr << "Invalid input for balance. Please enter a valid number." << std::endl; } } break; } case 7: { std::printf("Old value: %s\nEntering new date of last payment...\n", std::put_time(std::localtime(&selectedEntry->dateOfLastPayment), "%c")); std::tm tm = enterDate(); selectedEntry->dateOfLastPayment = std::mktime(&tm); break; } } updateData(data); } std::vector structContents(const userData *entry) { std::vector structItems; structItems.emplace_back("Name: " + entry->name); structItems.emplace_back("Address: " + entry->address); structItems.emplace_back("City: " + entry->city); structItems.emplace_back("State: " + entry->state); structItems.emplace_back("ZIP: " + entry->zip); structItems.emplace_back("Phone #: " + entry->phone); // Format account balance std::stringstream ss; ss << "Account Balance: $" << std::fixed << std::setprecision(2) << entry->accountBal; structItems.emplace_back(ss.str()); // Get gregorian calendar date of unix timestamp structItems.emplace_back("Date of Last Payment: " + std::string(ctime(&entry->dateOfLastPayment))); return structItems; } std::string displayData(const userData& entry) { std::vector data = structContents(&entry); std::stringstream ss; for(auto str: data) { ss << str << "\n"; } return ss.str(); } std::string displayData(std::vector data) { std::string returnString = ""; //std::printf("vector size: %i\n", static_cast(data.size())); for (int i = 0; i < data.size(); ++i) { userData currentUserData = data[i]; returnString.append(DASHES).append(" entry ").append(std::to_string(i + 1)).append(DASHES).append("\n"); returnString.append(displayData(currentUserData)); } return returnString; } inline bool fileExists (const std::string& name) { std::ifstream f(name.c_str()); return f.good(); } void updateData(std::vector data) { // delete the old data std::remove("data.dat"); // Serialize the vector std::stringstream ss; boost::archive::text_oarchive oa(ss); oa << data; // Save the contents std::ofstream outputFile("data.dat"); if (outputFile.is_open()) { outputFile << ss.str(); outputFile.close(); } } std::vector getData() { std::vector data; if (!fileExists("data.dat")) { std::printf("Warning! No datafile detected! Prompting for data entry...\n"); auto pUserData = enterRecord(); data.emplace_back(pUserData); updateData(data); } else { // Deserialize and read the data from the file std::ifstream inputFile("data.dat", std::ios::binary); boost::archive::text_iarchive ia(inputFile); ia >> data; } return data; }