-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #143 from AWSary/aai-planner
Aai planner
- Loading branch information
Showing
10 changed files
with
520 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import SwiftUI | ||
import EventKit | ||
import StoreKit | ||
import RevenueCat | ||
|
||
struct AAIplannerContentView: View { | ||
@StateObject private var viewModel = CalendarViewModel() | ||
@State private var selectedEventName = "Architecting on AWS" | ||
@State private var startDate = Date() | ||
@State private var selectedTimeZone = TimeZone.current.identifier | ||
@State private var accessGranted = false | ||
@ObservedObject var userModel = UserViewModel.shared | ||
@State private var showAlert = false | ||
|
||
let eventNames = AAIEventData.eventNames | ||
let eventSequences = AAIEventData.eventSequences | ||
let timeZones = AAIEventData.timeZones | ||
|
||
var body: some View { | ||
VStack { | ||
Spacer() | ||
Text("AAI Planner") | ||
.font(.largeTitle) | ||
.fontWeight(.bold) | ||
.padding(.top) | ||
.padding(.bottom) | ||
|
||
Text("If you are an AWS Authorized Instructor (AAI), you can easily add to your calendar the plans for your training deliverability. Select the training, the start time, and the time zone, and press Add Events.\n\nRecommended Modules plans and breaks will be added to your calendar.") | ||
.font(.body) | ||
.multilineTextAlignment(.center) | ||
.padding([.leading, .trailing, .bottom]) | ||
|
||
if accessGranted { | ||
Picker("Event Name", selection: $selectedEventName) { | ||
ForEach(eventNames, id: \.self) { | ||
Text($0) | ||
} | ||
} | ||
.pickerStyle(MenuPickerStyle()) | ||
.padding() | ||
|
||
DatePicker("Start Date and Time", selection: $startDate) | ||
.padding() | ||
|
||
Picker("Time Zone", selection: $selectedTimeZone) { | ||
ForEach(timeZones) { timeZone in | ||
Text(timeZone.displayName).tag(timeZone.id) | ||
} | ||
} | ||
.pickerStyle(MenuPickerStyle()) | ||
.padding() | ||
|
||
if selectedEventName == "Architecting on AWS" || selectedEventName == "Developing on AWS" { | ||
Button("Add Events to Calendar") { | ||
if let sequence = eventSequences[selectedEventName] { | ||
if let timeZone = TimeZone(identifier: selectedTimeZone) { | ||
viewModel.addEvents(sequence: sequence, startDate: startDate, timeZone: timeZone) | ||
showAlert = true | ||
} | ||
} | ||
}.padding() | ||
.alert(isPresented: $showAlert) { | ||
Alert( | ||
title: Text("Events Added"), | ||
message: Text("All your events added scucessfully. 🥳" + | ||
"Enjoy your training day") | ||
) | ||
} | ||
} else { | ||
Text("Please install App for full access").padding() | ||
} | ||
Spacer() | ||
Label { | ||
VStack(alignment: .leading){ | ||
Text("Send Feedback") | ||
Text("Feedback emails are lovely to read!").font(.footnote).opacity(0.6) | ||
} | ||
} icon:{ | ||
Image(systemName: "envelope") | ||
}.onTapGesture { | ||
let address = "[email protected]" | ||
let subject = "Feedback on AWSary" | ||
|
||
// Example email body with useful info for bug reports | ||
let body = "\n\n--\nAWSary Version: \(Bundle.main.appVersionLong) (\(Bundle.main.appBuild))\n\nScreen: AAI Planner" | ||
|
||
// Build the URL from its components | ||
var components = URLComponents() | ||
components.scheme = "mailto" | ||
components.path = address | ||
components.queryItems = [ | ||
URLQueryItem(name: "subject", value: subject), | ||
URLQueryItem(name: "body", value: body) | ||
] | ||
|
||
guard let email_url = components.url else { | ||
NSLog("Failed to create mailto URL") | ||
return | ||
} | ||
UIApplication.shared.open(email_url) { success in | ||
// handle success or failure | ||
} | ||
} | ||
} else { | ||
Spacer() | ||
Text("Requesting Calendar Access...") | ||
.onAppear { | ||
viewModel.requestAccess { granted in | ||
accessGranted = granted | ||
} | ||
} | ||
Spacer() | ||
} | ||
} | ||
} | ||
} | ||
|
||
struct ContentView_Previews: PreviewProvider { | ||
static var previews: some View { | ||
AAIplannerContentView() | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import SwiftUI | ||
import Foundation | ||
|
||
struct AAIEventData { | ||
@ObservedObject var userModel = UserViewModel.shared | ||
|
||
static let eventNames = [ | ||
// "Debug", | ||
// "AWS Cloud Essentials for Business Leaders", | ||
// "AWS Cloud Essentials for Business Leaders – Financial Services", | ||
"AWS Cloud Practitioner Essentials", | ||
// "AWS Technical Essentials", | ||
// "AWS Security Essentials", | ||
"Architecting on AWS", | ||
// "AWS Cloud Financial Management for Builders", | ||
// "AWS Cloud for Finance Professionals", | ||
// "AWS Security Best Practices", | ||
// "AWS Security Governance at Scale", | ||
// "AWS Well-Architected Best Practices", | ||
// "Advanced AWS Well-Architected Best Practices", | ||
// "Big Data on AWS", | ||
// "Building Batch Data Analytics Solutions on AWS", | ||
// "Building Data Analytics Solutions Using Amazon Redshift", | ||
// "Building Data Lakes on AWS", | ||
// "Building Streaming Data Analytics on AWS", | ||
// "Cloud Operations on AWS", | ||
// "Data Warehousing on AWS", | ||
// "Deep Learning on AWS", | ||
// "Developing on AWS", | ||
// "Developing Serverless Solutions on AWS", | ||
// "DevOps Engineering on AWS", | ||
// "Migrating to AWS", | ||
// "MLOps Engineering on AWS", | ||
// "Planning and Designing Databases on AWS", | ||
// "Practical Data Science with Amazon SageMaker", | ||
// "Running Containers on Amazon Elastic Kubernetes Service", | ||
// "Security Engineering on AWS", | ||
// "The Machine Learning Pipeline on AWS", | ||
// "Video Streaming Essentials for AWS Media Services", | ||
// "Advanced Architecting on AWS", | ||
// "Advanced Developing on AWS", | ||
// "Architecting on AWS - Accelerator", | ||
// "Amazon SageMaker Studio for Data Scientists", | ||
// "Authoring Visual Analytics Using Amazon QuickSight", | ||
] | ||
|
||
static let eventSequences: [String: [(name: String, duration: Int)]] = [ | ||
"Debug": [("Debug - Module 0", 20), ("Debug - Module 1", 45)], | ||
"AWS Cloud Practitioner Essentials": [ | ||
("CP - Welcome", 20), | ||
("CP - Module 0: Course Introduction", 5), | ||
("CP - Module 1: Introduction to Amazon Web Services", 30), | ||
("CP - Module 2: Compute in the Cloud (Part 1)", 30), | ||
("CP - ☕️🍎 Break", 15), | ||
("CP - Module 2: Compute in the Cloud (Part 2)", 30), | ||
("CP - Module 3: Global Infrastructure and Reliability", 25), | ||
("CP - Module 4: Networking (Part 1)", 20), | ||
("CP - 🍴🍲🥝 Lunch", 60), | ||
("CP - Module 4: Networking (Part 2)", 15), | ||
("CP - Module 5: Storage and Databases", 40), | ||
("CP - Module 6: Security", 35), | ||
("CP - ☕️🍎 Break", 10), | ||
("CP - Module 7: Monitoring and Analytics", 20), | ||
("CP - Module 8: Pricing and Support", 30), | ||
("CP - Module 9: Migration and Innovation", 40), | ||
("CP - Module 10: AWS Certified Cloud Practitioner Basics", 15), | ||
("CP - 🙋♀️❓🙋♂️ Q&A", 30), | ||
], | ||
"Architecting on AWS": [ | ||
("AoA - Module 0: Course Introduction", 30), | ||
("AoA - Module 1: Architecting Fundamentals", 45), | ||
("AoA - ☕️🍎 Break", 15), | ||
("AoA - 🔬🛠️ Lab 1: Explore and interact with the AWS Management Console and AWS Command Line Interface", 35), | ||
("AoA - Module 2: Account Security", 60), | ||
("AoA - ☕️🍎 Break", 15), | ||
("AoA - Module 3: Networking 1", 60), | ||
("AoA - 🍴🍲🥝 Lunch", 60), | ||
("AoA - Module 4: Compute", 75), | ||
("AoA - ☕️🍎 Break", 15), | ||
("AoA - 🔬🛠️ Lab 2: Build your Amazon VPC infrastructure", 45), | ||
("AoA - 🙋♀️❓🙋♂️ Q&A", 15), | ||
("nextDay",1), | ||
|
||
("AoA - Day 1 review", 10), | ||
("AoA - Module 5: Storage", 70), | ||
("AoA - ☕️🍎 Break", 15), | ||
("AoA - Module 6: Database Services", 70), | ||
("AoA - 🔬🛠️ Lab 3: Create a database layer in your Amazon VPC infrastructure", 45), | ||
("AoA - 🍴🍲🥝 Lunch", 60), | ||
("AoA - Module 7: Monitoring and Scaling", 70), | ||
("AoA - 🔬🛠️ Lab 4: Configure high availability in your Amazon VPC", 45), | ||
("AoA - ☕️🍎 Break", 15), | ||
("AoA - Module 8: Automation", 30), | ||
("AoA - Module 9: Containers", 40), | ||
("AoA - 🙋♀️❓🙋♂️ Q&A", 15), | ||
("nextDay",2), | ||
|
||
("AoA - Day 2 review", 10), | ||
("AoA - Module 10: Networking 2", 45), | ||
("AoA - Module 11: Serverless", 45), | ||
("AoA - ☕️🍎 Break", 15), | ||
("AoA - 🔬🛠️ Lab 5: Build a serverless architecture", 45), | ||
("AoA - Module 12: Edge Services", 60), | ||
("AoA - 🍴🍲🥝 Lunch", 60), | ||
("AoA - 🔬🛠️ Lab 6: Configure an Amazon CloudFront distribution with an Amazon S3 origin", 60), | ||
("AoA - Module 13: Backup and Recovery", 40), | ||
("AoA - 🔬🛠️ Capstone lab: Build an AWS multi-tier architecture", 90), | ||
("AoA - Module 14: Course Summary", 10), | ||
("AoA - 🙋♀️❓🙋♂️ Q&A", 15), | ||
], | ||
// "Advanced Architecting on AWS": [("AAoA - Module 0", 20), ("AAoA - Module 1", 45), ("AAoA - Module 2", 20)], | ||
// "Developing on AWS": [("DoA - Module 0", 20), ("DoA - Module 1", 10), ("DoA - Module 2", 120)], | ||
// "Advanced Developing on AWS": [("ADoA - Module 0", 20), ("ADoA - Module 1", 10), ("ADoA - Module 2", 120)] | ||
] | ||
|
||
static let timeZones: [TimeZoneInfo] = TimeZone.knownTimeZoneIdentifiers.sorted().map { TimeZoneInfo(identifier: $0) } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
import SwiftUI | ||
import EventKit | ||
import StoreKit | ||
import RevenueCat | ||
|
||
struct AAIplannerContentView: View { | ||
@StateObject private var viewModel = CalendarViewModel() | ||
@State private var selectedEventName = "Architecting on AWS" | ||
@State private var startDate = Date() | ||
@State private var selectedTimeZone = TimeZone.current.identifier | ||
@State private var accessGranted = false | ||
@ObservedObject var userModel = UserViewModel.shared | ||
@State private var showAlert = false | ||
@State var displayPaywall: Bool = false | ||
|
||
let eventNames = AAIEventData.eventNames | ||
let eventSequences = AAIEventData.eventSequences | ||
let timeZones = AAIEventData.timeZones | ||
|
||
var body: some View { | ||
VStack { | ||
Spacer() | ||
Text("AAI Planner") | ||
.font(.largeTitle) | ||
.fontWeight(.bold) | ||
.padding(.top) | ||
.padding(.bottom) | ||
|
||
Text("If you are an AWS Authorized Instructor (AAI), you can easily add to your calendar the plans for your training deliverability. Select the training, the start time, and the time zone, and press Add Events.\n\nRecommended Modules plans and breaks will be added to your calendar.") | ||
.font(.body) | ||
.multilineTextAlignment(.center) | ||
.padding([.leading, .trailing, .bottom]) | ||
|
||
if accessGranted { | ||
Picker("Event Name", selection: $selectedEventName) { | ||
ForEach(eventNames, id: \.self) { | ||
if self.userModel.subscriptionActive { | ||
Text($0) | ||
}else{ | ||
Text((try! $0.contains(Regex("(^Architecting on AWS$)|(^.*Essentials$)") ) ) | ||
? "✅ Free - \($0)" : "🔒 Premium - \($0)") | ||
} | ||
} | ||
} | ||
.pickerStyle(MenuPickerStyle()) | ||
.padding() | ||
|
||
DatePicker("Start Date and Time", selection: $startDate) | ||
.padding() | ||
|
||
Picker("Time Zone", selection: $selectedTimeZone) { | ||
ForEach(timeZones) { timeZone in | ||
Text(timeZone.displayName).tag(timeZone.id) | ||
} | ||
} | ||
.pickerStyle(MenuPickerStyle()) | ||
.padding() | ||
|
||
let isFree = try! selectedEventName.contains(Regex("(^Architecting on AWS$)|(^.*Essentials$)")) | ||
if self.userModel.subscriptionActive || isFree { | ||
Button("Add Events to Calendar") { | ||
if let sequence = eventSequences[selectedEventName] { | ||
if let timeZone = TimeZone(identifier: selectedTimeZone) { | ||
viewModel.addEvents(sequence: sequence, startDate: startDate, timeZone: timeZone) | ||
showAlert = true | ||
} | ||
} | ||
}.padding() | ||
.alert(isPresented: $showAlert) { | ||
Alert( | ||
title: Text("Events Added"), | ||
message: Text("All your events added scucessfully. 🥳" + | ||
"Enjoy your training day") | ||
) | ||
} | ||
} else { | ||
Button("Unlock Premium to Add Event") { | ||
self.displayPaywall.toggle() | ||
}.padding() | ||
} | ||
Spacer() | ||
Label { | ||
VStack(alignment: .leading){ | ||
Text("Send Feedback") | ||
Text("Feedback emails are lovely to read!\nMissing a training? Email me and I will prioritize it.").font(.footnote).opacity(0.6) | ||
} | ||
} icon:{ | ||
Image(systemName: "envelope") | ||
}.onTapGesture { | ||
let address = "[email protected]" | ||
let subject = "Feedback on AWSary" | ||
|
||
// Example email body with useful info for bug reports | ||
let body = "\n\n--\nAWSary Version: \(Bundle.main.appVersionLong) (\(Bundle.main.appBuild))\n\nScreen: AAI Planner" | ||
|
||
// Build the URL from its components | ||
var components = URLComponents() | ||
components.scheme = "mailto" | ||
components.path = address | ||
components.queryItems = [ | ||
URLQueryItem(name: "subject", value: subject), | ||
URLQueryItem(name: "body", value: body) | ||
] | ||
|
||
guard let email_url = components.url else { | ||
NSLog("Failed to create mailto URL") | ||
return | ||
} | ||
UIApplication.shared.open(email_url) { success in | ||
// handle success or failure | ||
} | ||
} | ||
} else { | ||
Spacer() | ||
Text("Requesting Calendar Access...") | ||
.onAppear { | ||
viewModel.requestAccess { granted in | ||
accessGranted = granted | ||
} | ||
} | ||
Spacer() | ||
} | ||
} | ||
.sheet(isPresented: $displayPaywall) { | ||
PaywallView(isPresented: .constant(true)) | ||
} | ||
} | ||
} | ||
|
||
struct ContentView_Previews: PreviewProvider { | ||
static var previews: some View { | ||
AAIplannerContentView() | ||
} | ||
} | ||
|
Oops, something went wrong.