diff --git a/01_Requirements/REQ0119/index.md b/01_Requirements/REQ0119/index.md index 57044bb..276e086 100644 --- a/01_Requirements/REQ0119/index.md +++ b/01_Requirements/REQ0119/index.md @@ -1 +1,21 @@ -# Demo2FaExample +--- +tags: mobile +--- + +# REQ0119-Demo2FaExample + +## description + +A demonstration of two-factor authentication implementation examples + +## schema + +```dbml +{ + // To be defined +} +``` + +## related + +- diff --git a/01_Requirements/REQ0120/index.md b/01_Requirements/REQ0120/index.md index 94df1c3..2a74080 100644 --- a/01_Requirements/REQ0120/index.md +++ b/01_Requirements/REQ0120/index.md @@ -1 +1,21 @@ -# DemoAccordionTutorial +--- +tags: +--- + +# REQ0120-PlaceholderFeature + +## description + +T.B.A. + +## schema + +```dbml +{ + // To be defined +} +``` + +## related + +- diff --git a/01_Requirements/REQ0121/index.md b/01_Requirements/REQ0121/index.md index 9751524..569d049 100644 --- a/01_Requirements/REQ0121/index.md +++ b/01_Requirements/REQ0121/index.md @@ -1 +1,21 @@ -# DemoBankingUi +--- +tags: +--- + +# REQ0121-UserProfileFeature + +## description + +T.B.A. + +## schema + +```dbml +{ + // To be defined +} +``` + +## related + +- diff --git a/01_Requirements/REQ0122/index.md b/01_Requirements/REQ0122/index.md index 7fa2f38..c6a7b5a 100644 --- a/01_Requirements/REQ0122/index.md +++ b/01_Requirements/REQ0122/index.md @@ -1 +1,21 @@ -# DemoBlogPostUi +--- +tags: +--- + +# REQ0122-NotificationSystem + +## description + +T.B.A. + +## schema + +```dbml +{ + // To be defined +} +``` + +## related + +- diff --git a/01_Requirements/REQ0123/index.md b/01_Requirements/REQ0123/index.md index 941bf56..6967b78 100644 --- a/01_Requirements/REQ0123/index.md +++ b/01_Requirements/REQ0123/index.md @@ -1 +1,21 @@ -# DemoCapacitorGoogleMapsTutorial +--- +tags: +--- + +# REQ0123-AnalyticsDashboard + +## description + +T.B.A. + +## schema + +```dbml +{ + // To be defined +} +``` + +## related + +- diff --git a/01_Requirements/REQ0124/index.md b/01_Requirements/REQ0124/index.md index 8312b4e..cd6c614 100644 --- a/01_Requirements/REQ0124/index.md +++ b/01_Requirements/REQ0124/index.md @@ -1 +1,22 @@ -# DemoClubHouse +--- +tags: [placeholder-tag1, placeholder-tag2] +--- + +# REQ0124-PlaceholderTitle + +## description + +![alt text](placeholder-image.png) + +## schema + +```dbml +{ + placeholderField1: Type; + placeholderField2: Type; +} +``` + +## related + +[[REQXXXX]] diff --git a/01_Requirements/REQ0125/index.md b/01_Requirements/REQ0125/index.md index 2ea23a1..6c77941 100644 --- a/01_Requirements/REQ0125/index.md +++ b/01_Requirements/REQ0125/index.md @@ -1 +1,22 @@ -# DemoColorTutorial +--- +tags: [placeholder-tag1, placeholder-tag2] +--- + +# REQ0125-PlaceholderTitle + +## description + +![alt text](placeholder-image.png) + +## schema + +```dbml +{ + placeholderField1: Type; + placeholderField2: Type; +} +``` + +## related + +[[REQXXXX]] diff --git a/01_Requirements/REQ0126/index.md b/01_Requirements/REQ0126/index.md index 78a822c..86d0957 100644 --- a/01_Requirements/REQ0126/index.md +++ b/01_Requirements/REQ0126/index.md @@ -1 +1,22 @@ -# DemoDictionaryApp +--- +tags: [placeholder-tag1, placeholder-tag2] +--- + +# REQ0126-PlaceholderTitle + +## description + +![alt text](placeholder-image.png) + +## schema + +```dbml +{ + placeholderField1: Type; + placeholderField2: Type; +} +``` + +## related + +[[REQXXXX]] diff --git a/01_Requirements/REQ0127/index.md b/01_Requirements/REQ0127/index.md index 1400d9c..18617ef 100644 --- a/01_Requirements/REQ0127/index.md +++ b/01_Requirements/REQ0127/index.md @@ -1 +1,22 @@ -# DemoEcommerceExample +--- +tags: [placeholder-tag1, placeholder-tag2] +--- + +# REQ0127-PlaceholderTitle + +## description + +![alt text](placeholder-image.png) + +## schema + +```dbml +{ + placeholderField1: Type; + placeholderField2: Type; +} +``` + +## related + +[[REQXXXX]] diff --git a/01_Requirements/REQ0128/index.md b/01_Requirements/REQ0128/index.md index 6e5f3da..911b11a 100644 --- a/01_Requirements/REQ0128/index.md +++ b/01_Requirements/REQ0128/index.md @@ -1 +1,22 @@ -# DemoFacebookClone +--- +tags: [placeholder-tag1, placeholder-tag2] +--- + +# REQ0128-PlaceholderTitle + +## description + +![alt text](placeholder-image.png) + +## schema + +```dbml +{ + placeholderField1: Type; + placeholderField2: Type; +} +``` + +## related + +[[REQXXXX]] diff --git a/01_Requirements/REQ0129/index.md b/01_Requirements/REQ0129/index.md index 21244a2..1f179b2 100644 --- a/01_Requirements/REQ0129/index.md +++ b/01_Requirements/REQ0129/index.md @@ -1 +1,22 @@ -# DemoFastFoodApp +--- +tags: [placeholder-tag1, placeholder-tag2] +--- + +# REQ0129-PlaceholderTitle + +## description + +![alt text](placeholder-image.png) + +## schema + +```dbml +{ + placeholderField1: Type; + placeholderField2: Type; +} +``` + +## related + +[[REQXXXX]] diff --git a/01_Requirements/REQ0130/index.md b/01_Requirements/REQ0130/index.md index 16ba394..58a7a52 100644 --- a/01_Requirements/REQ0130/index.md +++ b/01_Requirements/REQ0130/index.md @@ -1 +1,22 @@ -# DemoFloatingTabs +--- +tags: [placeholder-tag1, placeholder-tag2] +--- + +# REQ0130-PlaceholderTitle + +## description + +![alt text](placeholder-image.png) + +## schema + +```dbml +{ + placeholderField1: Type; + placeholderField2: Type; +} +``` + +## related + +[[REQXXXX]] diff --git a/01_Requirements/REQ0131/index.md b/01_Requirements/REQ0131/index.md index e55af93..32a46b0 100644 --- a/01_Requirements/REQ0131/index.md +++ b/01_Requirements/REQ0131/index.md @@ -1 +1,22 @@ -# DemoInstagramClone +--- +tags: requirements +--- + +# REQ0131-DemoInstagramClone + +## description + + + +## schema + +```dbml +// Add your DBML here +Table users { + id integer +} +``` + +## related + +[[REQ0110-PartyPage]] diff --git a/01_Requirements/REQ0132/index.md b/01_Requirements/REQ0132/index.md index 949d833..68034b7 100644 --- a/01_Requirements/REQ0132/index.md +++ b/01_Requirements/REQ0132/index.md @@ -1 +1,23 @@ -# DemoKanbanBoard +--- +tags: requirements +--- + +# REQ0132-DemoKanbanBoard + +## description + + + +## schema + +```dbml +// Add your DBML here +Table tasks { + id integer + status string +} +``` + +## related + +[[REQ0131-DemoInstagramClone]] diff --git a/01_Requirements/REQ0133/index.md b/01_Requirements/REQ0133/index.md index 22a7f73..061fe9a 100644 --- a/01_Requirements/REQ0133/index.md +++ b/01_Requirements/REQ0133/index.md @@ -1 +1,23 @@ -# DemoList +--- +tags: requirements +--- + +# REQ0133-DemoList + +## description + + + +## schema + +```dbml +// Add your DBML here +Table lists { + id integer + name string +} +``` + +## related + +[[REQ0132-DemoKanbanBoard]] diff --git a/01_Requirements/REQ0134/index.md b/01_Requirements/REQ0134/index.md index 2bd258a..9345c4b 100644 --- a/01_Requirements/REQ0134/index.md +++ b/01_Requirements/REQ0134/index.md @@ -1 +1,23 @@ -# DemoOrderingApp +--- +tags: requirements +--- + +# REQ0134-DemoOrderingApp + +## description + + + +## schema + +```dbml +// Add your DBML here +Table orders { + id integer + item string +} +``` + +## related + +[] diff --git a/01_Requirements/REQ0135/index.md b/01_Requirements/REQ0135/index.md index 257af0d..a58e7ea 100644 --- a/01_Requirements/REQ0135/index.md +++ b/01_Requirements/REQ0135/index.md @@ -1 +1,24 @@ -# DemoPinterestFloatingTabBar +--- +tags: requirements +--- + +# REQ0135-DemoPinterestFloatingTabBar + +## description + + + +## schema + +```dbml +// Add your DBML here +Table floating_tab_bars { + id integer + position string + items json +} +``` + +## related + +[[REQ0134-DemoList]] diff --git a/01_Requirements/REQ0136/index.md b/01_Requirements/REQ0136/index.md index dfa168b..b9c733e 100644 --- a/01_Requirements/REQ0136/index.md +++ b/01_Requirements/REQ0136/index.md @@ -1 +1,24 @@ -# DemoProfileExample +--- +tags: requirements +--- + +# REQ0136-DemoProfileExample + +## description + + + +## schema + +```dbml +// Add your DBML here +Table profiles { + id integer + name string + avatar_url string +} +``` + +## related + +[[REQ0135-DemoPinterestFloatingTabBar]] diff --git a/01_Requirements/REQ0137/index.md b/01_Requirements/REQ0137/index.md index 21001e3..6e375cc 100644 --- a/01_Requirements/REQ0137/index.md +++ b/01_Requirements/REQ0137/index.md @@ -1 +1,24 @@ -# DemoPullstateTutorial +--- +tags: [requirements] +--- + +# REQ0137-DemoPullstateTutorial + +## Description + +![Pullstate Tutorial](Pullstate%20Tutorial.png) + +## Schema + +```dbml +// Add your DBML here +Table pullstates { + id integer + state json + timestamp datetime +} +``` + +## Related + +- [REQ0136-DemoProfileExample](#) diff --git a/01_Requirements/REQ0138/index.md b/01_Requirements/REQ0138/index.md index 1f6339c..dc6b2a3 100644 --- a/01_Requirements/REQ0138/index.md +++ b/01_Requirements/REQ0138/index.md @@ -1 +1,24 @@ -# DemoQrScanner +--- +tags: [requirements] +--- + +# REQ0138-DemoQrScanner + +## Description + +![QR Scanner](QR%20Scanner.png) + +## Schema + +```dbml +// Add your DBML here +Table qr_scanners { + id integer + scan_data string + scan_time datetime +} +``` + +## Related + +- [REQ0137-DemoPullstateTutorial](#) diff --git a/01_Requirements/REQ0139/index.md b/01_Requirements/REQ0139/index.md index 3b0f25a..0d9a22c 100644 --- a/01_Requirements/REQ0139/index.md +++ b/01_Requirements/REQ0139/index.md @@ -1 +1,24 @@ -# DemoQuizApp +--- +tags: [requirements] +--- + +# REQ0139-DemoQuizApp + +## Description + +![Quiz App](Quiz%20App.png) + +## Schema + +```dbml +// Add your DBML here +Table quizzes { + id integer + questions json + results json +} +``` + +## Related + +- [REQ0138-DemoQrScanner](#) diff --git a/01_Requirements/REQ0140/index.md b/01_Requirements/REQ0140/index.md index 48ce230..4b45604 100644 --- a/01_Requirements/REQ0140/index.md +++ b/01_Requirements/REQ0140/index.md @@ -1 +1,25 @@ -# DemoQuoteApp +--- +tags: [requirements] +--- + +# REQ0140-DemoQuoteApp + +## Description + +![Quote App](Quote%20App.png) + +## Schema + +```dbml +// Add your DBML here +Table quotes { + id integer + content string + author string + category string +} +``` + +## Related + +- [REQ0139-DemoQuizApp](#) diff --git a/01_Requirements/REQ0141/index.md b/01_Requirements/REQ0141/index.md index da16c4b..d091b56 100644 --- a/01_Requirements/REQ0141/index.md +++ b/01_Requirements/REQ0141/index.md @@ -1 +1,25 @@ -# DemoReactAddToCart +--- +tags: [requirements] +--- + +# REQ0141-DemoReactAddToCart + +## Description + +![React Add To Cart](React%20Add%20To%20Cart.png) + +## Schema + +```dbml +// Add your DBML here +Table cart_items { + id integer + product_id integer + quantity integer + user_id integer +} +``` + +## Related + +- [REQ0140-DemoQuoteApp](#) diff --git a/01_Requirements/REQ0142/index.md b/01_Requirements/REQ0142/index.md index 0298815..2b27656 100644 --- a/01_Requirements/REQ0142/index.md +++ b/01_Requirements/REQ0142/index.md @@ -1 +1,25 @@ -# DemoReactCalculator +--- +tags: [requirements] +--- + +# REQ0142-DemoReactCalculator + +## Description + +![React Calculator](React%20Calculator.png) + +## Schema + +```dbml +// Add your DBML here +Table calculations { + id integer + expression string + result float + timestamp datetime +} +``` + +## Related + +- [REQ0141-DemoReactAddToCart](#) diff --git a/01_Requirements/REQ0143/index.md b/01_Requirements/REQ0143/index.md index 6d4a09e..7296da8 100644 --- a/01_Requirements/REQ0143/index.md +++ b/01_Requirements/REQ0143/index.md @@ -1 +1,25 @@ -# DemoReactDrawingCanvas +--- +tags: [requirements] +--- + +# REQ0143-DemoReactDrawingCanvas + +## Description + +![React Drawing Canvas](React%20Drawing%20Canvas.png) + +## Schema + +```dbml +// Add your DBML here +Table drawings { + id integer + canvas_data json + created_at datetime + updated_at datetime +} +``` + +## Related + +- [REQ0142-DemoReactCalculator](#) diff --git a/01_Requirements/REQ0144/index.md b/01_Requirements/REQ0144/index.md index 70ac74c..bd893c4 100644 --- a/01_Requirements/REQ0144/index.md +++ b/01_Requirements/REQ0144/index.md @@ -1 +1,25 @@ -# DemoReactHookFormExample +--- +tags: [requirements] +--- + +# REQ0144-DemoReactHookFormExample + +## Description + +![React Hook Form Example](React%20Hook%20Form%20Example.png) + +## Schema + +```dbml +// Add your DBML here +Table forms { + id integer + form_data json + submitted_at datetime + user_id integer +} +``` + +## Related + +- [REQ0143-DemoReactDrawingCanvas](#) diff --git a/01_Requirements/REQ0145/index.md b/01_Requirements/REQ0145/index.md index dca260a..3c1127e 100644 --- a/01_Requirements/REQ0145/index.md +++ b/01_Requirements/REQ0145/index.md @@ -1 +1,26 @@ -# DemoReactItemList +--- +tags: [requirements] +--- + +# REQ0145-DemoReactItemList + +## Description + +![React Item List](React%20Item%20List.png) + +## Schema + +```dbml +// Add your DBML here +Table items { + id integer + name string + description text + price decimal + created_at datetime +} +``` + +## Related + +- [REQ0144-DemoReactHookFormExample](#) diff --git a/01_Requirements/REQ0146/index.md b/01_Requirements/REQ0146/index.md index c5f9c29..2161f87 100644 --- a/01_Requirements/REQ0146/index.md +++ b/01_Requirements/REQ0146/index.md @@ -1 +1,26 @@ -# DemoReactLifecycles +--- +tags: [requirements] +--- + +# REQ0146-DemoReactLifecycles + +## Description + +![React Lifecycles](React%20Lifecycles.png) + +## Schema + +```dbml +// Add your DBML here +Table components { + id integer + name string + lifecycle_stage string + mounted_at datetime + updated_at datetime +} +``` + +## Related + +- [REQ0145-DemoReactItemList](#) diff --git a/01_Requirements/REQ0147/index.md b/01_Requirements/REQ0147/index.md index fb107a2..cc89710 100644 --- a/01_Requirements/REQ0147/index.md +++ b/01_Requirements/REQ0147/index.md @@ -1 +1,26 @@ -# DemoReactLogin +--- +tags: [requirements] +--- + +# REQ0147-DemoReactLogin + +## Description + +![React Login](React%20Login.png) + +## Schema + +```dbml +// Add your DBML here +Table users { + id integer + username string + password_hash string + last_login_at datetime + login_count integer +} +``` + +## Related + +- [REQ0146-DemoReactLifecycles](#) diff --git a/01_Requirements/REQ0148/index.md b/01_Requirements/REQ0148/index.md index ff6f2df..e8413af 100644 --- a/01_Requirements/REQ0148/index.md +++ b/01_Requirements/REQ0148/index.md @@ -1 +1,26 @@ -# DemoReactMarvelApp +--- +tags: [requirements] +--- + +# REQ0148-DemoReactMarvelApp + +## Description + +![React Marvel App](React%20Marvel%20App.png) + +## Schema + +```dbml +// Add your DBML here +Table characters { + id integer + name string + description text + thumbnail_url string + comics_available integer +} +``` + +## Related + +- [REQ0147-DemoReactLogin](#) diff --git a/01_Requirements/REQ0149/index.md b/01_Requirements/REQ0149/index.md index 4232c84..b22281e 100644 --- a/01_Requirements/REQ0149/index.md +++ b/01_Requirements/REQ0149/index.md @@ -1 +1,27 @@ -# DemoReactMovieAppWithAlgolia +--- +tags: [requirements] +--- + +# REQ0149-DemoReactMovieAppWithAlgolia + +## Description + +![React Movie App with Algolia](React%20Movie%20App%20with%20Algolia.png) + +## Schema + +```dbml +// Add your DBML here +Table movies { + id integer + title string + year integer + rating decimal + search_index string + last_updated datetime +} +``` + +## Related + +- [REQ0148-DemoReactMarvelApp](#) diff --git a/01_Requirements/REQ0150/index.md b/01_Requirements/REQ0150/index.md index fc77041..acfff91 100644 --- a/01_Requirements/REQ0150/index.md +++ b/01_Requirements/REQ0150/index.md @@ -1 +1,27 @@ -# DemoReactNotes +--- +tags: [requirements] +--- + +# REQ0150-DemoReactNotes + +## Description + +![React Notes App](React%20Notes%20App.png) + +## Schema + +```dbml +// Add your DBML here +Table notes { + id integer + title string + content text + created_at datetime + updated_at datetime + is_pinned boolean +} +``` + +## Related + +- [REQ0149-DemoReactMovieAppWithAlgolia](#) diff --git a/01_Requirements/REQ0151/index.md b/01_Requirements/REQ0151/index.md index ff5a8b9..5079615 100644 --- a/01_Requirements/REQ0151/index.md +++ b/01_Requirements/REQ0151/index.md @@ -1 +1,27 @@ -# DemoReactOnboardingUi +--- +tags: [requirements] +--- + +# REQ0151-DemoReactOnboardingUi + +## Description + +![React Onboarding UI](React%20Onboarding%20UI.png) + +## Schema + +```dbml +// Add your DBML here +Table onboarding_steps { + id integer + title string + description text + is_completed boolean + order_number integer + component_name string +} +``` + +## Related + +- [REQ0150-DemoReactNotes](#) diff --git a/01_Requirements/REQ0152/index.md b/01_Requirements/REQ0152/index.md index 3133d82..716075f 100644 --- a/01_Requirements/REQ0152/index.md +++ b/01_Requirements/REQ0152/index.md @@ -1 +1,26 @@ -# DemoReactOverlayHooks +--- +tags: [requirements] +--- + +# REQ0152-DemoReactOverlayHooks + +## Description + +![React Overlay Hooks](React%20Overlay%20Hooks.png) + +## Schema + +```dbml +// Add your DBML here +Table overlays { + id integer + component_name string + is_visible boolean + z_index integer + animation_type string +} +``` + +## Related + +- [REQ0151-DemoReactOnboardingUi](#) diff --git a/01_Requirements/REQ0153/index.md b/01_Requirements/REQ0153/index.md index 880847d..aabf470 100644 --- a/01_Requirements/REQ0153/index.md +++ b/01_Requirements/REQ0153/index.md @@ -1 +1,10 @@ -# DemoReactPollApp +--- +tags: [requirements] +--- + +# REQ0153-DemoReactPollApp + +## Description +![React Poll App](React%20Poll%20App.png) + +## Schema diff --git a/03_source/cms_backend/src/app/api/helloworld/route.ts b/03_source/cms_backend/src/app/api/helloworld/route.ts index 6b46568..8b04af0 100644 --- a/03_source/cms_backend/src/app/api/helloworld/route.ts +++ b/03_source/cms_backend/src/app/api/helloworld/route.ts @@ -17,7 +17,7 @@ export async function GET(req: NextRequest, res: NextResponse) { return response(result, STATUS.OK); } catch (error) { - return handleError('Post - Get latest', error); + return handleError('Helloworld - Get all', error); } } diff --git a/03_source/ionic-react-conference-app b/03_source/ionic-react-conference-app new file mode 160000 index 0000000..616068e --- /dev/null +++ b/03_source/ionic-react-conference-app @@ -0,0 +1 @@ +Subproject commit 616068e5ebce8f2a72b15bc331d8e63e5bda7eb3 diff --git a/03_source/mobile.compile_ok b/03_source/mobile.compile_ok new file mode 160000 index 0000000..69f0997 --- /dev/null +++ b/03_source/mobile.compile_ok @@ -0,0 +1 @@ +Subproject commit 69f0997a2768594b18bf5fc8054bf5e5159957ce diff --git a/03_source/mobile.react_17 b/03_source/mobile.react_17 new file mode 160000 index 0000000..e5d9508 --- /dev/null +++ b/03_source/mobile.react_17 @@ -0,0 +1 @@ +Subproject commit e5d9508eaeaeca2199386524a3d0c46b69a5f8b4 diff --git a/03_source/mobile.react_18 b/03_source/mobile.react_18 new file mode 160000 index 0000000..995871d --- /dev/null +++ b/03_source/mobile.react_18 @@ -0,0 +1 @@ +Subproject commit 995871d7c0a193367e24af01b8add4d2f437aa92 diff --git a/03_source/mobile.react_18.test_jsx b/03_source/mobile.react_18.test_jsx new file mode 160000 index 0000000..1e663d3 --- /dev/null +++ b/03_source/mobile.react_18.test_jsx @@ -0,0 +1 @@ +Subproject commit 1e663d30243369b1f4d6488ad7b3a9443cd6a896 diff --git a/03_source/mobile.react_18.thread1 b/03_source/mobile.react_18.thread1 new file mode 160000 index 0000000..6307eb4 --- /dev/null +++ b/03_source/mobile.react_18.thread1 @@ -0,0 +1 @@ +Subproject commit 6307eb427e5ec18204c2bc1b4fa48b1d7ae4724e diff --git a/03_source/mobile.react_18.thread2 b/03_source/mobile.react_18.thread2 new file mode 160000 index 0000000..6307eb4 --- /dev/null +++ b/03_source/mobile.react_18.thread2 @@ -0,0 +1 @@ +Subproject commit 6307eb427e5ec18204c2bc1b4fa48b1d7ae4724e diff --git a/03_source/mobile.react_18.thread3 b/03_source/mobile.react_18.thread3 new file mode 160000 index 0000000..6307eb4 --- /dev/null +++ b/03_source/mobile.react_18.thread3 @@ -0,0 +1 @@ +Subproject commit 6307eb427e5ec18204c2bc1b4fa48b1d7ae4724e diff --git a/03_source/mobile.react_18_baseline b/03_source/mobile.react_18_baseline new file mode 160000 index 0000000..48b471d --- /dev/null +++ b/03_source/mobile.react_18_baseline @@ -0,0 +1 @@ +Subproject commit 48b471d880ea5db8859155dc923e9ee4b3ad81aa diff --git a/03_source/mobile.react_18_test_pullstate b/03_source/mobile.react_18_test_pullstate new file mode 160000 index 0000000..02b0920 --- /dev/null +++ b/03_source/mobile.react_18_test_pullstate @@ -0,0 +1 @@ +Subproject commit 02b09209d562d6d98e115154ece7031273b9258e diff --git a/03_source/mobile.react_19 b/03_source/mobile.react_19 new file mode 160000 index 0000000..616068e --- /dev/null +++ b/03_source/mobile.react_19 @@ -0,0 +1 @@ +Subproject commit 616068e5ebce8f2a72b15bc331d8e63e5bda7eb3 diff --git a/03_source/mobile.test.pullstate b/03_source/mobile.test.pullstate new file mode 160000 index 0000000..69f0997 --- /dev/null +++ b/03_source/mobile.test.pullstate @@ -0,0 +1 @@ +Subproject commit 69f0997a2768594b18bf5fc8054bf5e5159957ce diff --git a/03_source/mobile.trunk.1/.editorconfig b/03_source/mobile.trunk.1/.editorconfig new file mode 100644 index 0000000..f1cc3ad --- /dev/null +++ b/03_source/mobile.trunk.1/.editorconfig @@ -0,0 +1,15 @@ +# http://editorconfig.org + +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/03_source/mobile.trunk.1/.firebaserc b/03_source/mobile.trunk.1/.firebaserc new file mode 100644 index 0000000..711741d --- /dev/null +++ b/03_source/mobile.trunk.1/.firebaserc @@ -0,0 +1,5 @@ +{ + "projects": { + "default": "ionic-react-conference-app" + } +} diff --git a/03_source/mobile/.github/CONTRIBUTING.md b/03_source/mobile.trunk.1/.github/CONTRIBUTING.md similarity index 100% rename from 03_source/mobile/.github/CONTRIBUTING.md rename to 03_source/mobile.trunk.1/.github/CONTRIBUTING.md diff --git a/03_source/mobile.trunk.1/.gitignore b/03_source/mobile.trunk.1/.gitignore new file mode 100644 index 0000000..9b9e15d --- /dev/null +++ b/03_source/mobile.trunk.1/.gitignore @@ -0,0 +1,89 @@ +**/*Zone.Identifier +**/*bak +**/*del +**/*log +**/*tmp + +# Logs +.firebase +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Typescript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# See https://help.github.com/ignore-files/ for more about ignoring files. + +# dependencies +/node_modules + +# testing +/coverage + +# production +/dist + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +.stencil/ diff --git a/03_source/mobile.trunk.1/.netlify/_redirects b/03_source/mobile.trunk.1/.netlify/_redirects new file mode 100644 index 0000000..50a4633 --- /dev/null +++ b/03_source/mobile.trunk.1/.netlify/_redirects @@ -0,0 +1 @@ +/* /index.html 200 \ No newline at end of file diff --git a/03_source/mobile.trunk.1/.netlify/state.json b/03_source/mobile.trunk.1/.netlify/state.json new file mode 100644 index 0000000..f97268e --- /dev/null +++ b/03_source/mobile.trunk.1/.netlify/state.json @@ -0,0 +1,3 @@ +{ + "siteId": "86675615-6271-4145-8ffe-9c78dc4d34a3" +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/.prettierrc b/03_source/mobile.trunk.1/.prettierrc new file mode 100644 index 0000000..ecf89ac --- /dev/null +++ b/03_source/mobile.trunk.1/.prettierrc @@ -0,0 +1,30 @@ +{ + "tabWidth": 2, + "semi": true, + "singleQuote": true, + "trailingComma": "es5", + "printWidth": 100, + "overrides": [ + { + "files": "src/App.tsx", + "options": { + "printWidth": 160 + } + }, + { + "files": "src/routes/*", + "options": { + "printWidth": 160 + } + }, + { + "files": [ + "*.html", + "legacy/**/*.js" + ], + "options": { + "tabWidth": 4 + } + } + ] +} diff --git a/03_source/mobile.trunk.1/.vscode/settings.json b/03_source/mobile.trunk.1/.vscode/settings.json new file mode 100644 index 0000000..3662b37 --- /dev/null +++ b/03_source/mobile.trunk.1/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib" +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/LICENSE b/03_source/mobile.trunk.1/LICENSE new file mode 100644 index 0000000..623c70a --- /dev/null +++ b/03_source/mobile.trunk.1/LICENSE @@ -0,0 +1,23 @@ +Copyright 2015-present Drifty Co. +http://drifty.com/ + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/03_source/mobile.trunk.1/README.md b/03_source/mobile.trunk.1/README.md new file mode 100644 index 0000000..7a24529 --- /dev/null +++ b/03_source/mobile.trunk.1/README.md @@ -0,0 +1,71 @@ +# Ionic React Conference App + +[![Built with Ionic](https://img.shields.io/badge/-Built%20with%20Ionic-3880FF?style=flat&logo=ionic&logoColor=white)](https://ionicframework.com) +[![React](https://img.shields.io/badge/-React-61DAFB?style=flat&logo=react&logoColor=black)](https://reactjs.org) +[![MIT License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) + + +This is a **kitchen-sink demo application** built with **Ionic Framework** and **React**. It showcases a wide range of Ionic components and features in the context of a fictional tech conference app. + +**Note**: There is no actual Ionic Conference. This project is purely for demonstration purposes. + +👉 [Try the Live Demo](https://ionic-react-conference-app-git-main-ionic1.vercel.app/tutorial) + +## 🧱 Framework Variants + +This app is also available in other frameworks: + +- 🔗 [Ionic Angular Conference App](https://github.com/ionic-team/ionic-conference-app) +- 🔗 [Ionic Vue Conference App](https://github.com/ionic-team/ionic-vue-conference-app) +- ✅ **You're viewing the React version** + +## ✨ Features + +- Browse conference schedule with filtering +- View speaker bios and session details +- User authentication and profile management +- Interactive maps for venue navigation +- Push notifications support +- Dark/Light mode toggling +- Cross-platform support: iOS, Android, and Web + +## ⚙️ Getting Started + +### Prerequisites + +- Node.js (LTS version recommended) → [Download](https://nodejs.org/) +- npm (included with Node.js) +- Ionic CLI → Install globally: + ```bash + npm install -g ionic + ``` + +### Installation +1. Clone the repository: + ```bash + git clone https://github.com/ionic-team/ionic-react-conference-app.git + ``` +2. Navigate into the project: + ```bash + cd ionic-react-conference-app + ``` +3. Install dependencies: + ```bash + npm install + ``` +4. Start the dev server: + ```bash + ionic serve + ``` +5. Open your browser to: + ``` + http://localhost:3000 + ``` + +## 🤝 Contributing + +We welcome contributions! Please see our [Contributing Guide](.github/CONTRIBUTING.md) for details on how to submit pull requests, report issues, and contribute to the project. + +## 📄 License + +This project is licensed under the MIT [License](./LICENSE). diff --git a/03_source/mobile/android/.gitignore b/03_source/mobile.trunk.1/android/.gitignore similarity index 100% rename from 03_source/mobile/android/.gitignore rename to 03_source/mobile.trunk.1/android/.gitignore diff --git a/03_source/mobile/android/app/.gitignore b/03_source/mobile.trunk.1/android/app/.gitignore similarity index 100% rename from 03_source/mobile/android/app/.gitignore rename to 03_source/mobile.trunk.1/android/app/.gitignore diff --git a/03_source/mobile/android/app/build.gradle b/03_source/mobile.trunk.1/android/app/build.gradle similarity index 100% rename from 03_source/mobile/android/app/build.gradle rename to 03_source/mobile.trunk.1/android/app/build.gradle diff --git a/03_source/mobile/android/app/capacitor.build.gradle b/03_source/mobile.trunk.1/android/app/capacitor.build.gradle similarity index 100% rename from 03_source/mobile/android/app/capacitor.build.gradle rename to 03_source/mobile.trunk.1/android/app/capacitor.build.gradle diff --git a/03_source/mobile/android/app/proguard-rules.pro b/03_source/mobile.trunk.1/android/app/proguard-rules.pro similarity index 100% rename from 03_source/mobile/android/app/proguard-rules.pro rename to 03_source/mobile.trunk.1/android/app/proguard-rules.pro diff --git a/03_source/mobile/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java b/03_source/mobile.trunk.1/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java similarity index 100% rename from 03_source/mobile/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java rename to 03_source/mobile.trunk.1/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java diff --git a/03_source/mobile/android/app/src/main/AndroidManifest.xml b/03_source/mobile.trunk.1/android/app/src/main/AndroidManifest.xml similarity index 100% rename from 03_source/mobile/android/app/src/main/AndroidManifest.xml rename to 03_source/mobile.trunk.1/android/app/src/main/AndroidManifest.xml diff --git a/03_source/mobile/android/app/src/main/java/io/ionic/starter/MainActivity.java b/03_source/mobile.trunk.1/android/app/src/main/java/io/ionic/starter/MainActivity.java similarity index 100% rename from 03_source/mobile/android/app/src/main/java/io/ionic/starter/MainActivity.java rename to 03_source/mobile.trunk.1/android/app/src/main/java/io/ionic/starter/MainActivity.java diff --git a/03_source/mobile/android/app/src/main/res/drawable-land-hdpi/splash.png b/03_source/mobile.trunk.1/android/app/src/main/res/drawable-land-hdpi/splash.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/drawable-land-hdpi/splash.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/drawable-land-hdpi/splash.png diff --git a/03_source/mobile/android/app/src/main/res/drawable-land-mdpi/splash.png b/03_source/mobile.trunk.1/android/app/src/main/res/drawable-land-mdpi/splash.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/drawable-land-mdpi/splash.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/drawable-land-mdpi/splash.png diff --git a/03_source/mobile/android/app/src/main/res/drawable-land-xhdpi/splash.png b/03_source/mobile.trunk.1/android/app/src/main/res/drawable-land-xhdpi/splash.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/drawable-land-xhdpi/splash.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/drawable-land-xhdpi/splash.png diff --git a/03_source/mobile/android/app/src/main/res/drawable-land-xxhdpi/splash.png b/03_source/mobile.trunk.1/android/app/src/main/res/drawable-land-xxhdpi/splash.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/drawable-land-xxhdpi/splash.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/drawable-land-xxhdpi/splash.png diff --git a/03_source/mobile/android/app/src/main/res/drawable-land-xxxhdpi/splash.png b/03_source/mobile.trunk.1/android/app/src/main/res/drawable-land-xxxhdpi/splash.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/drawable-land-xxxhdpi/splash.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/drawable-land-xxxhdpi/splash.png diff --git a/03_source/mobile/android/app/src/main/res/drawable-port-hdpi/splash.png b/03_source/mobile.trunk.1/android/app/src/main/res/drawable-port-hdpi/splash.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/drawable-port-hdpi/splash.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/drawable-port-hdpi/splash.png diff --git a/03_source/mobile/android/app/src/main/res/drawable-port-mdpi/splash.png b/03_source/mobile.trunk.1/android/app/src/main/res/drawable-port-mdpi/splash.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/drawable-port-mdpi/splash.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/drawable-port-mdpi/splash.png diff --git a/03_source/mobile/android/app/src/main/res/drawable-port-xhdpi/splash.png b/03_source/mobile.trunk.1/android/app/src/main/res/drawable-port-xhdpi/splash.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/drawable-port-xhdpi/splash.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/drawable-port-xhdpi/splash.png diff --git a/03_source/mobile/android/app/src/main/res/drawable-port-xxhdpi/splash.png b/03_source/mobile.trunk.1/android/app/src/main/res/drawable-port-xxhdpi/splash.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/drawable-port-xxhdpi/splash.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/drawable-port-xxhdpi/splash.png diff --git a/03_source/mobile/android/app/src/main/res/drawable-port-xxxhdpi/splash.png b/03_source/mobile.trunk.1/android/app/src/main/res/drawable-port-xxxhdpi/splash.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/drawable-port-xxxhdpi/splash.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/drawable-port-xxxhdpi/splash.png diff --git a/03_source/mobile/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/03_source/mobile.trunk.1/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml similarity index 100% rename from 03_source/mobile/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml rename to 03_source/mobile.trunk.1/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml diff --git a/03_source/mobile/android/app/src/main/res/drawable/ic_launcher_background.xml b/03_source/mobile.trunk.1/android/app/src/main/res/drawable/ic_launcher_background.xml similarity index 100% rename from 03_source/mobile/android/app/src/main/res/drawable/ic_launcher_background.xml rename to 03_source/mobile.trunk.1/android/app/src/main/res/drawable/ic_launcher_background.xml diff --git a/03_source/mobile/android/app/src/main/res/drawable/splash.png b/03_source/mobile.trunk.1/android/app/src/main/res/drawable/splash.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/drawable/splash.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/drawable/splash.png diff --git a/03_source/mobile/android/app/src/main/res/layout/activity_main.xml b/03_source/mobile.trunk.1/android/app/src/main/res/layout/activity_main.xml similarity index 100% rename from 03_source/mobile/android/app/src/main/res/layout/activity_main.xml rename to 03_source/mobile.trunk.1/android/app/src/main/res/layout/activity_main.xml diff --git a/03_source/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/03_source/mobile.trunk.1/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml similarity index 100% rename from 03_source/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml rename to 03_source/mobile.trunk.1/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml diff --git a/03_source/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/03_source/mobile.trunk.1/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml similarity index 100% rename from 03_source/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml rename to 03_source/mobile.trunk.1/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml diff --git a/03_source/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/03_source/mobile.trunk.1/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/03_source/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/03_source/mobile.trunk.1/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png diff --git a/03_source/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/03_source/mobile.trunk.1/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png diff --git a/03_source/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/03_source/mobile.trunk.1/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/03_source/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/03_source/mobile.trunk.1/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png diff --git a/03_source/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/03_source/mobile.trunk.1/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png diff --git a/03_source/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/03_source/mobile.trunk.1/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/03_source/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/03_source/mobile.trunk.1/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png diff --git a/03_source/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/03_source/mobile.trunk.1/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png diff --git a/03_source/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/03_source/mobile.trunk.1/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/03_source/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/03_source/mobile.trunk.1/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png diff --git a/03_source/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/03_source/mobile.trunk.1/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png diff --git a/03_source/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/03_source/mobile.trunk.1/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/03_source/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/03_source/mobile.trunk.1/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png diff --git a/03_source/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/03_source/mobile.trunk.1/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png similarity index 100% rename from 03_source/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png rename to 03_source/mobile.trunk.1/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/03_source/mobile/android/app/src/main/res/values/ic_launcher_background.xml b/03_source/mobile.trunk.1/android/app/src/main/res/values/ic_launcher_background.xml similarity index 100% rename from 03_source/mobile/android/app/src/main/res/values/ic_launcher_background.xml rename to 03_source/mobile.trunk.1/android/app/src/main/res/values/ic_launcher_background.xml diff --git a/03_source/mobile/android/app/src/main/res/values/strings.xml b/03_source/mobile.trunk.1/android/app/src/main/res/values/strings.xml similarity index 100% rename from 03_source/mobile/android/app/src/main/res/values/strings.xml rename to 03_source/mobile.trunk.1/android/app/src/main/res/values/strings.xml diff --git a/03_source/mobile/android/app/src/main/res/values/styles.xml b/03_source/mobile.trunk.1/android/app/src/main/res/values/styles.xml similarity index 100% rename from 03_source/mobile/android/app/src/main/res/values/styles.xml rename to 03_source/mobile.trunk.1/android/app/src/main/res/values/styles.xml diff --git a/03_source/mobile/android/app/src/main/res/xml/file_paths.xml b/03_source/mobile.trunk.1/android/app/src/main/res/xml/file_paths.xml similarity index 100% rename from 03_source/mobile/android/app/src/main/res/xml/file_paths.xml rename to 03_source/mobile.trunk.1/android/app/src/main/res/xml/file_paths.xml diff --git a/03_source/mobile/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java b/03_source/mobile.trunk.1/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java similarity index 100% rename from 03_source/mobile/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java rename to 03_source/mobile.trunk.1/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java diff --git a/03_source/mobile/android/build.gradle b/03_source/mobile.trunk.1/android/build.gradle similarity index 100% rename from 03_source/mobile/android/build.gradle rename to 03_source/mobile.trunk.1/android/build.gradle diff --git a/03_source/mobile/android/capacitor.settings.gradle b/03_source/mobile.trunk.1/android/capacitor.settings.gradle similarity index 100% rename from 03_source/mobile/android/capacitor.settings.gradle rename to 03_source/mobile.trunk.1/android/capacitor.settings.gradle diff --git a/03_source/mobile/android/gradle.properties b/03_source/mobile.trunk.1/android/gradle.properties similarity index 100% rename from 03_source/mobile/android/gradle.properties rename to 03_source/mobile.trunk.1/android/gradle.properties diff --git a/03_source/mobile/android/gradle/wrapper/gradle-wrapper.jar b/03_source/mobile.trunk.1/android/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from 03_source/mobile/android/gradle/wrapper/gradle-wrapper.jar rename to 03_source/mobile.trunk.1/android/gradle/wrapper/gradle-wrapper.jar diff --git a/03_source/mobile/android/gradle/wrapper/gradle-wrapper.properties b/03_source/mobile.trunk.1/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from 03_source/mobile/android/gradle/wrapper/gradle-wrapper.properties rename to 03_source/mobile.trunk.1/android/gradle/wrapper/gradle-wrapper.properties diff --git a/03_source/mobile/android/gradlew b/03_source/mobile.trunk.1/android/gradlew similarity index 100% rename from 03_source/mobile/android/gradlew rename to 03_source/mobile.trunk.1/android/gradlew diff --git a/03_source/mobile/android/gradlew.bat b/03_source/mobile.trunk.1/android/gradlew.bat similarity index 100% rename from 03_source/mobile/android/gradlew.bat rename to 03_source/mobile.trunk.1/android/gradlew.bat diff --git a/03_source/mobile/android/settings.gradle b/03_source/mobile.trunk.1/android/settings.gradle similarity index 100% rename from 03_source/mobile/android/settings.gradle rename to 03_source/mobile.trunk.1/android/settings.gradle diff --git a/03_source/mobile/android/variables.gradle b/03_source/mobile.trunk.1/android/variables.gradle similarity index 100% rename from 03_source/mobile/android/variables.gradle rename to 03_source/mobile.trunk.1/android/variables.gradle diff --git a/03_source/mobile.trunk.1/build.sh b/03_source/mobile.trunk.1/build.sh new file mode 100755 index 0000000..f976783 --- /dev/null +++ b/03_source/mobile.trunk.1/build.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -ex + +# npm i -D + +rm -rf **/*Zone.Identifier + +# npm run format + +# npm run build + +# git add . +# git commit -m"build ok," + +echo "done" diff --git a/03_source/mobile.trunk.1/capacitor.config.json b/03_source/mobile.trunk.1/capacitor.config.json new file mode 100644 index 0000000..0f2dd5f --- /dev/null +++ b/03_source/mobile.trunk.1/capacitor.config.json @@ -0,0 +1,8 @@ +{ + "appId": "io.ionic.starter", + "appName": "ionic-react-conference-app", + "bundledWebRuntime": false, + "npmClient": "npm", + "webDir": "dist", + "cordova": {} +} diff --git a/03_source/mobile.trunk.1/dev.sh b/03_source/mobile.trunk.1/dev.sh new file mode 100755 index 0000000..ab73a35 --- /dev/null +++ b/03_source/mobile.trunk.1/dev.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +while true; do + yarn -D + + yarn run dev + + echo "restarting..." + sleep 1 +done diff --git a/03_source/mobile/dockerfile b/03_source/mobile.trunk.1/dockerfile similarity index 100% rename from 03_source/mobile/dockerfile rename to 03_source/mobile.trunk.1/dockerfile diff --git a/03_source/mobile.trunk.1/firebase.json b/03_source/mobile.trunk.1/firebase.json new file mode 100644 index 0000000..49a74df --- /dev/null +++ b/03_source/mobile.trunk.1/firebase.json @@ -0,0 +1,31 @@ +{ + "hosting": { + "public": "build", + "rewrites": [ + { + "source": "**", + "destination": "/index.html" + } + ], + "headers": [ + { + "source": "**/static/**", + "headers": [ + { + "key": "Cache-Control", + "value": "max-age=31536000" + } + ] + }, + { + "source": "**/*.@(jpg|jpeg|gif|png|svg)", + "headers": [ + { + "key": "Cache-Control", + "value": "max-age=31536000" + } + ] + } + ] + } +} diff --git a/03_source/mobile.trunk.1/index.html b/03_source/mobile.trunk.1/index.html new file mode 100644 index 0000000..805b105 --- /dev/null +++ b/03_source/mobile.trunk.1/index.html @@ -0,0 +1,27 @@ + + + + + + Ionic Conference App + + + + + + + + + + + + + + + +
+ + + + diff --git a/03_source/mobile.trunk.1/ionic.config.json b/03_source/mobile.trunk.1/ionic.config.json new file mode 100644 index 0000000..f610198 --- /dev/null +++ b/03_source/mobile.trunk.1/ionic.config.json @@ -0,0 +1,7 @@ +{ + "name": "ionic-react-conference-app", + "integrations": { + "capacitor": {} + }, + "type": "react" +} diff --git a/03_source/mobile/ios/.gitignore b/03_source/mobile.trunk.1/ios/.gitignore similarity index 100% rename from 03_source/mobile/ios/.gitignore rename to 03_source/mobile.trunk.1/ios/.gitignore diff --git a/03_source/mobile/ios/App/App.xcodeproj/project.pbxproj b/03_source/mobile.trunk.1/ios/App/App.xcodeproj/project.pbxproj similarity index 100% rename from 03_source/mobile/ios/App/App.xcodeproj/project.pbxproj rename to 03_source/mobile.trunk.1/ios/App/App.xcodeproj/project.pbxproj diff --git a/03_source/mobile/ios/App/App.xcworkspace/contents.xcworkspacedata b/03_source/mobile.trunk.1/ios/App/App.xcworkspace/contents.xcworkspacedata similarity index 100% rename from 03_source/mobile/ios/App/App.xcworkspace/contents.xcworkspacedata rename to 03_source/mobile.trunk.1/ios/App/App.xcworkspace/contents.xcworkspacedata diff --git a/03_source/mobile/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/03_source/mobile.trunk.1/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from 03_source/mobile/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to 03_source/mobile.trunk.1/ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/03_source/mobile/ios/App/App/AppDelegate.swift b/03_source/mobile.trunk.1/ios/App/App/AppDelegate.swift similarity index 100% rename from 03_source/mobile/ios/App/App/AppDelegate.swift rename to 03_source/mobile.trunk.1/ios/App/App/AppDelegate.swift diff --git a/03_source/mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png b/03_source/mobile.trunk.1/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png similarity index 100% rename from 03_source/mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png rename to 03_source/mobile.trunk.1/ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png diff --git a/03_source/mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json b/03_source/mobile.trunk.1/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from 03_source/mobile/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json rename to 03_source/mobile.trunk.1/ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/03_source/mobile/ios/App/App/Assets.xcassets/Contents.json b/03_source/mobile.trunk.1/ios/App/App/Assets.xcassets/Contents.json similarity index 100% rename from 03_source/mobile/ios/App/App/Assets.xcassets/Contents.json rename to 03_source/mobile.trunk.1/ios/App/App/Assets.xcassets/Contents.json diff --git a/03_source/mobile/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json b/03_source/mobile.trunk.1/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json similarity index 100% rename from 03_source/mobile/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json rename to 03_source/mobile.trunk.1/ios/App/App/Assets.xcassets/Splash.imageset/Contents.json diff --git a/03_source/mobile/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png b/03_source/mobile.trunk.1/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png similarity index 100% rename from 03_source/mobile/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png rename to 03_source/mobile.trunk.1/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png diff --git a/03_source/mobile/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png b/03_source/mobile.trunk.1/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png similarity index 100% rename from 03_source/mobile/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png rename to 03_source/mobile.trunk.1/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png diff --git a/03_source/mobile/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png b/03_source/mobile.trunk.1/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png similarity index 100% rename from 03_source/mobile/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png rename to 03_source/mobile.trunk.1/ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png diff --git a/03_source/mobile/ios/App/App/Base.lproj/LaunchScreen.storyboard b/03_source/mobile.trunk.1/ios/App/App/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from 03_source/mobile/ios/App/App/Base.lproj/LaunchScreen.storyboard rename to 03_source/mobile.trunk.1/ios/App/App/Base.lproj/LaunchScreen.storyboard diff --git a/03_source/mobile/ios/App/App/Base.lproj/Main.storyboard b/03_source/mobile.trunk.1/ios/App/App/Base.lproj/Main.storyboard similarity index 100% rename from 03_source/mobile/ios/App/App/Base.lproj/Main.storyboard rename to 03_source/mobile.trunk.1/ios/App/App/Base.lproj/Main.storyboard diff --git a/03_source/mobile/ios/App/App/Info.plist b/03_source/mobile.trunk.1/ios/App/App/Info.plist similarity index 100% rename from 03_source/mobile/ios/App/App/Info.plist rename to 03_source/mobile.trunk.1/ios/App/App/Info.plist diff --git a/03_source/mobile/ios/App/Podfile b/03_source/mobile.trunk.1/ios/App/Podfile similarity index 100% rename from 03_source/mobile/ios/App/Podfile rename to 03_source/mobile.trunk.1/ios/App/Podfile diff --git a/03_source/mobile/ios/App/Podfile.lock b/03_source/mobile.trunk.1/ios/App/Podfile.lock similarity index 100% rename from 03_source/mobile/ios/App/Podfile.lock rename to 03_source/mobile.trunk.1/ios/App/Podfile.lock diff --git a/03_source/mobile.trunk.1/package.json b/03_source/mobile.trunk.1/package.json new file mode 100644 index 0000000..fd2dd7b --- /dev/null +++ b/03_source/mobile.trunk.1/package.json @@ -0,0 +1,93 @@ +{ + "name": "ionic-react-conference-app", + "version": "0.0.0", + "description": "Ionic Conference App", + "author": "Ionic Team ", + "license": "MIT", + "private": true, + "dependencies": { + "@capacitor/android": "7.0.1", + "@capacitor/barcode-scanner": "^2.0.3", + "@capacitor/clipboard": "^7.0.1", + "@capacitor/core": "^7.0.0", + "@capacitor/geolocation": "^7.1.2", + "@capacitor/google-maps": "^7.0.2", + "@capacitor/ios": "7.0.1", + "@capacitor/preferences": "^7.0.0", + "@capacitor/share": "^7.0.1", + "@hookform/resolvers": "^4.1.3", + "@ionic/react": "^8.5.0", + "@ionic/react-router": "^8.5.0", + "@ionic/storage": "^4.0.0", + "@mdx-js/react": "^3.1.0", + "@react-hook/window-size": "^3.1.1", + "@types/leaflet": "^1.9.17", + "@types/react-redux": "^7.1.34", + "axios": "^1.9.0", + "date-fns": "^2.25.0", + "ionicons": "^7.1.2", + "leaflet": "^1.9.4", + "pigeon-maps": "^0.22.1", + "pullstate": "^1", + "react": "19.0.0", + "react-canvas-draw": "^1.2.1", + "react-color": "^2.19.3", + "react-confetti": "^6.4.0", + "react-dom": "19.0.0", + "react-hook-form": "^7.55.0", + "react-iconly": "^2.2.10", + "react-leaflet": "^5.0.0", + "react-markdown": "^10.1.0", + "react-qr-code": "^2.0.15", + "react-qr-reader": "^3.0.0-beta-1", + "react-redux": "^9.2.0", + "react-router": "^5.3.4", + "react-router-dom": "^5.3.4", + "react-spinners": "^0.17.0", + "react-use": "^17.6.0", + "reselect": "^4.0.0", + "sass": "^1.85.1", + "swiper": "^11.2.8", + "use-sound": "^5.0.0", + "zod": "^3.24.2" + }, + "scripts": { + "start": "npm run dev", + "dev": "vite --force --host 0.0.0.0 --cors", + "ionic:serve": "vite", + "ionic:build": "tsc && vite build", + "build": "tsc && vite build", + "preview": "vite preview", + "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,md}\"", + "precommit": "npm run format && lint-staged" + }, + "eslintConfig": { + "extends": "react-app" + }, + "browserslist": [ + ">0.2%", + "not dead", + "not ie <= 11", + "not op_mini all" + ], + "devDependencies": { + "@capacitor/cli": "^7.0.0", + "@testing-library/react": "^9.3.1", + "@types/jest": "24.0.18", + "@types/react": "19.0.10", + "@types/react-dom": "19.0.4", + "@types/react-router": "^5.1.20", + "@types/react-router-dom": "^5.3.3", + "@vitejs/plugin-react": "^4.3.4", + "lint-staged": "^13.2.0", + "prettier": "^3.5.3", + "typescript": "^5.8.2", + "vite": "^6.2.0" + }, + "lint-staged": { + "src/**/*.{js,jsx,ts,tsx,json,md}": [ + "prettier --write", + "git add" + ] + } +} diff --git a/03_source/mobile.trunk.1/public/assets/DemoBankingUi/alan.jpg b/03_source/mobile.trunk.1/public/assets/DemoBankingUi/alan.jpg new file mode 100644 index 0000000..98093f9 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoBankingUi/alan.jpg differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoBankingUi/chip.png b/03_source/mobile.trunk.1/public/assets/DemoBankingUi/chip.png new file mode 100644 index 0000000..7861be2 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoBankingUi/chip.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoBankingUi/icon/favicon.png b/03_source/mobile.trunk.1/public/assets/DemoBankingUi/icon/favicon.png new file mode 100644 index 0000000..51888a7 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoBankingUi/icon/favicon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoBankingUi/icon/icon.png b/03_source/mobile.trunk.1/public/assets/DemoBankingUi/icon/icon.png new file mode 100644 index 0000000..a7f6374 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoBankingUi/icon/icon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoBankingUi/mastercard.png b/03_source/mobile.trunk.1/public/assets/DemoBankingUi/mastercard.png new file mode 100644 index 0000000..b4f6667 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoBankingUi/mastercard.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoBankingUi/shapes.svg b/03_source/mobile.trunk.1/public/assets/DemoBankingUi/shapes.svg new file mode 100644 index 0000000..d370b4d --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoBankingUi/shapes.svg @@ -0,0 +1 @@ + diff --git a/03_source/mobile.trunk.1/public/assets/DemoBankingUi/visa.png b/03_source/mobile.trunk.1/public/assets/DemoBankingUi/visa.png new file mode 100644 index 0000000..011d6f9 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoBankingUi/visa.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoDictionaryApp/icon/favicon.png b/03_source/mobile.trunk.1/public/assets/DemoDictionaryApp/icon/favicon.png new file mode 100644 index 0000000..51888a7 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoDictionaryApp/icon/favicon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoDictionaryApp/icon/icon.png b/03_source/mobile.trunk.1/public/assets/DemoDictionaryApp/icon/icon.png new file mode 100644 index 0000000..a7f6374 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoDictionaryApp/icon/icon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoDictionaryApp/shapes.svg b/03_source/mobile.trunk.1/public/assets/DemoDictionaryApp/shapes.svg new file mode 100644 index 0000000..d370b4d --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoDictionaryApp/shapes.svg @@ -0,0 +1 @@ + diff --git a/03_source/mobile/src/components/NoSavedEvents/alert-svgrepo-com.svg:Zone.Identifier b/03_source/mobile.trunk.1/public/assets/DemoProfileExample/.gitkeep similarity index 100% rename from 03_source/mobile/src/components/NoSavedEvents/alert-svgrepo-com.svg:Zone.Identifier rename to 03_source/mobile.trunk.1/public/assets/DemoProfileExample/.gitkeep diff --git a/03_source/mobile.trunk.1/public/assets/DemoQuizApp/icon/favicon.png b/03_source/mobile.trunk.1/public/assets/DemoQuizApp/icon/favicon.png new file mode 100644 index 0000000..51888a7 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoQuizApp/icon/favicon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoQuizApp/icon/icon.png b/03_source/mobile.trunk.1/public/assets/DemoQuizApp/icon/icon.png new file mode 100644 index 0000000..a7f6374 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoQuizApp/icon/icon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoQuizApp/main.png b/03_source/mobile.trunk.1/public/assets/DemoQuizApp/main.png new file mode 100644 index 0000000..42f8f63 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoQuizApp/main.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoQuizApp/shapes.svg b/03_source/mobile.trunk.1/public/assets/DemoQuizApp/shapes.svg new file mode 100644 index 0000000..d370b4d --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoQuizApp/shapes.svg @@ -0,0 +1 @@ + diff --git a/03_source/mobile/src/pages/DemoClubHouse/AppPages/Tab2.css b/03_source/mobile.trunk.1/public/assets/DemoReactAddToCart/.gitkeep similarity index 100% rename from 03_source/mobile/src/pages/DemoClubHouse/AppPages/Tab2.css rename to 03_source/mobile.trunk.1/public/assets/DemoReactAddToCart/.gitkeep diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactAddToCart/camera.jpeg b/03_source/mobile.trunk.1/public/assets/DemoReactAddToCart/camera.jpeg new file mode 100644 index 0000000..d212e39 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoReactAddToCart/camera.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactAddToCart/icon/favicon.png b/03_source/mobile.trunk.1/public/assets/DemoReactAddToCart/icon/favicon.png new file mode 100644 index 0000000..51888a7 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoReactAddToCart/icon/favicon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactAddToCart/icon/icon.png b/03_source/mobile.trunk.1/public/assets/DemoReactAddToCart/icon/icon.png new file mode 100644 index 0000000..a7f6374 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoReactAddToCart/icon/icon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactAddToCart/macbook.jpeg b/03_source/mobile.trunk.1/public/assets/DemoReactAddToCart/macbook.jpeg new file mode 100644 index 0000000..19930e3 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoReactAddToCart/macbook.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactAddToCart/shapes.svg b/03_source/mobile.trunk.1/public/assets/DemoReactAddToCart/shapes.svg new file mode 100644 index 0000000..d370b4d --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoReactAddToCart/shapes.svg @@ -0,0 +1 @@ + diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactAddToCart/tv.jpeg b/03_source/mobile.trunk.1/public/assets/DemoReactAddToCart/tv.jpeg new file mode 100644 index 0000000..5d1f0d0 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoReactAddToCart/tv.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactLogin/icon/favicon.png b/03_source/mobile.trunk.1/public/assets/DemoReactLogin/icon/favicon.png new file mode 100644 index 0000000..51888a7 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoReactLogin/icon/favicon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactLogin/icon/icon.png b/03_source/mobile.trunk.1/public/assets/DemoReactLogin/icon/icon.png new file mode 100644 index 0000000..a7f6374 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoReactLogin/icon/icon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactLogin/login2.jpeg b/03_source/mobile.trunk.1/public/assets/DemoReactLogin/login2.jpeg new file mode 100644 index 0000000..366dcf8 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoReactLogin/login2.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactLogin/shapes.svg b/03_source/mobile.trunk.1/public/assets/DemoReactLogin/shapes.svg new file mode 100644 index 0000000..d370b4d --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoReactLogin/shapes.svg @@ -0,0 +1 @@ + diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/alan.jpg b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/alan.jpg new file mode 100644 index 0000000..98093f9 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/alan.jpg differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/icon/favicon.png b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/icon/favicon.png new file mode 100644 index 0000000..51888a7 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/icon/favicon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/icon/icon.png b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/icon/icon.png new file mode 100644 index 0000000..a7f6374 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/icon/icon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/shapes.svg b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/shapes.svg new file mode 100644 index 0000000..d370b4d --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/shapes.svg @@ -0,0 +1 @@ + diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/blue.json b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/blue.json new file mode 100644 index 0000000..ec69043 --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/blue.json @@ -0,0 +1,16 @@ +{ + "toolbar_background_color": "blue", + "tab_bar_background_color": "blue", + "toolbar_color": "", + "tab_bar_color": "", + "tab_bar_activated_color": "", + + "main_color": "", + "light_color": "", + + "main_color_tint": "", + "main_color_shade": "", + + "light_color_tint": "", + "light_color_shade": "" +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/coffeebrown.json b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/coffeebrown.json new file mode 100644 index 0000000..8d15f25 --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/coffeebrown.json @@ -0,0 +1,16 @@ +{ + "toolbar_background_color": "#5f361e", + "tab_bar_background_color": "#5f361e", + "toolbar_color": "#d1bfb5", + "tab_bar_color": "#886551", + "tab_bar_activated_color": "", + + "main_color": "#5f361e", + "light_color": "#886551", + + "main_color_tint": "#855a41", + "main_color_shade": "#57331e", + + "light_color_tint": "#9c7d6b", + "light_color_shade": "#61412f" +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/covers/coffeebrown.png b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/covers/coffeebrown.png new file mode 100644 index 0000000..7e4df1d Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/covers/coffeebrown.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/covers/earthytones.png b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/covers/earthytones.png new file mode 100644 index 0000000..69de1b0 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/covers/earthytones.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/covers/fireyred.png b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/covers/fireyred.png new file mode 100644 index 0000000..ce87056 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/covers/fireyred.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/covers/leafygreen.png b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/covers/leafygreen.png new file mode 100644 index 0000000..206247c Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/covers/leafygreen.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/covers/moodyblue.png b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/covers/moodyblue.png new file mode 100644 index 0000000..6b3aaa8 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/covers/moodyblue.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/covers/peelyorange.png b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/covers/peelyorange.png new file mode 100644 index 0000000..a2f5685 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/covers/peelyorange.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/earthytones.json b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/earthytones.json new file mode 100644 index 0000000..262c9c1 --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/earthytones.json @@ -0,0 +1,16 @@ +{ + "toolbar_background_color": "#c4b0c4", + "tab_bar_background_color": "#c4b0c4", + "toolbar_color": "#5c4153", + "tab_bar_color": "#917788", + "tab_bar_activated_color": "#5c4153", + + "main_color": "#5c4153", + "light_color": "#917788", + + "main_color_tint": "#6b5463", + "main_color_shade": "#4d3545", + + "light_color_tint": "#a893a1", + "light_color_shade": "#74606d" +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/fireyred.json b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/fireyred.json new file mode 100644 index 0000000..5d5348f --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/fireyred.json @@ -0,0 +1,16 @@ +{ + "toolbar_background_color": "#c93608", + "tab_bar_background_color": "#c93608", + "toolbar_color": "", + "tab_bar_color": "#852506", + "tab_bar_activated_color": "", + + "main_color": "#c93608", + "light_color": "#c95834", + + "main_color_tint": "#aa614a", + "main_color_shade": "#692611", + + "light_color_tint": "#d67151", + "light_color_shade": "#a85439" +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/green.json b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/green.json new file mode 100644 index 0000000..cb41fcf --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/green.json @@ -0,0 +1,16 @@ +{ + "toolbar_background_color": "green", + "tab_bar_background_color": "green", + "toolbar_color": "", + "tab_bar_color": "", + "tab_bar_activated_color": "", + + "main_color": "", + "light_color": "", + + "main_color_tint": "", + "main_color_shade": "", + + "light_color_tint": "", + "light_color_shade": "" +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/leafygreen.json b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/leafygreen.json new file mode 100644 index 0000000..f3e86ce --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/leafygreen.json @@ -0,0 +1,16 @@ +{ + "toolbar_background_color": "#336b36", + "tab_bar_background_color": "#336b36", + "toolbar_color": "white", + "tab_bar_color": "#1c421d", + "tab_bar_activated_color": "white", + + "main_color": "#336b36", + "light_color": "#517953", + + "main_color_tint": "#497a4b", + "main_color_shade": "#305832", + + "light_color_tint": "#738574", + "light_color_shade": "#485849" +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/moodyblue.json b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/moodyblue.json new file mode 100644 index 0000000..db1ba2f --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/moodyblue.json @@ -0,0 +1,16 @@ +{ + "toolbar_background_color": "#7baec7", + "tab_bar_background_color": "#7baec7", + "toolbar_color": "", + "tab_bar_color": "#49758b", + "tab_bar_activated_color": "", + + "main_color": "#7baec7", + "light_color": "#90bbcf", + + "main_color_tint": "#96c0d5", + "main_color_shade": "#6b93a7", + + "light_color_tint": "#c3e2f1", + "light_color_shade": "#89aec0" +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/peelyorange.json b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/peelyorange.json new file mode 100644 index 0000000..f7f04f2 --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/peelyorange.json @@ -0,0 +1,16 @@ +{ + "toolbar_background_color": "#fd8f38", + "tab_bar_background_color": "#fd8f38", + "toolbar_color": "#815208", + "tab_bar_color": "#ffb67c", + "tab_bar_activated_color": "#815208", + + "main_color": "#e49200", + "light_color": "#ffb67c", + + "main_color_tint": "#e6ae4b", + "main_color_shade": "#b4780c", + + "light_color_tint": "#fad4b6", + "light_color_shade": "#ff9c4e" +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/red.json b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/red.json new file mode 100644 index 0000000..be9c737 --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoReactProfileDashboardUi/themes/red.json @@ -0,0 +1,16 @@ +{ + "toolbar_background_color": "red", + "tab_bar_background_color": "red", + "toolbar_color": "", + "tab_bar_color": "", + "tab_bar_activated_color": "", + + "main_color": "", + "light_color": "", + + "main_color_tint": "", + "main_color_shade": "", + + "light_color_tint": "", + "light_color_shade": "" +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/blue.json b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/blue.json new file mode 100644 index 0000000..ec69043 --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/blue.json @@ -0,0 +1,16 @@ +{ + "toolbar_background_color": "blue", + "tab_bar_background_color": "blue", + "toolbar_color": "", + "tab_bar_color": "", + "tab_bar_activated_color": "", + + "main_color": "", + "light_color": "", + + "main_color_tint": "", + "main_color_shade": "", + + "light_color_tint": "", + "light_color_shade": "" +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/coffeebrown.json b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/coffeebrown.json new file mode 100644 index 0000000..8d15f25 --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/coffeebrown.json @@ -0,0 +1,16 @@ +{ + "toolbar_background_color": "#5f361e", + "tab_bar_background_color": "#5f361e", + "toolbar_color": "#d1bfb5", + "tab_bar_color": "#886551", + "tab_bar_activated_color": "", + + "main_color": "#5f361e", + "light_color": "#886551", + + "main_color_tint": "#855a41", + "main_color_shade": "#57331e", + + "light_color_tint": "#9c7d6b", + "light_color_shade": "#61412f" +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/covers/coffeebrown.png b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/covers/coffeebrown.png new file mode 100644 index 0000000..7e4df1d Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/covers/coffeebrown.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/covers/earthytones.png b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/covers/earthytones.png new file mode 100644 index 0000000..69de1b0 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/covers/earthytones.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/covers/fireyred.png b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/covers/fireyred.png new file mode 100644 index 0000000..ce87056 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/covers/fireyred.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/covers/leafygreen.png b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/covers/leafygreen.png new file mode 100644 index 0000000..206247c Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/covers/leafygreen.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/covers/moodyblue.png b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/covers/moodyblue.png new file mode 100644 index 0000000..6b3aaa8 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/covers/moodyblue.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/covers/peelyorange.png b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/covers/peelyorange.png new file mode 100644 index 0000000..a2f5685 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/covers/peelyorange.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/earthytones.json b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/earthytones.json new file mode 100644 index 0000000..262c9c1 --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/earthytones.json @@ -0,0 +1,16 @@ +{ + "toolbar_background_color": "#c4b0c4", + "tab_bar_background_color": "#c4b0c4", + "toolbar_color": "#5c4153", + "tab_bar_color": "#917788", + "tab_bar_activated_color": "#5c4153", + + "main_color": "#5c4153", + "light_color": "#917788", + + "main_color_tint": "#6b5463", + "main_color_shade": "#4d3545", + + "light_color_tint": "#a893a1", + "light_color_shade": "#74606d" +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/fireyred.json b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/fireyred.json new file mode 100644 index 0000000..5d5348f --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/fireyred.json @@ -0,0 +1,16 @@ +{ + "toolbar_background_color": "#c93608", + "tab_bar_background_color": "#c93608", + "toolbar_color": "", + "tab_bar_color": "#852506", + "tab_bar_activated_color": "", + + "main_color": "#c93608", + "light_color": "#c95834", + + "main_color_tint": "#aa614a", + "main_color_shade": "#692611", + + "light_color_tint": "#d67151", + "light_color_shade": "#a85439" +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/green.json b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/green.json new file mode 100644 index 0000000..cb41fcf --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/green.json @@ -0,0 +1,16 @@ +{ + "toolbar_background_color": "green", + "tab_bar_background_color": "green", + "toolbar_color": "", + "tab_bar_color": "", + "tab_bar_activated_color": "", + + "main_color": "", + "light_color": "", + + "main_color_tint": "", + "main_color_shade": "", + + "light_color_tint": "", + "light_color_shade": "" +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/leafygreen.json b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/leafygreen.json new file mode 100644 index 0000000..f3e86ce --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/leafygreen.json @@ -0,0 +1,16 @@ +{ + "toolbar_background_color": "#336b36", + "tab_bar_background_color": "#336b36", + "toolbar_color": "white", + "tab_bar_color": "#1c421d", + "tab_bar_activated_color": "white", + + "main_color": "#336b36", + "light_color": "#517953", + + "main_color_tint": "#497a4b", + "main_color_shade": "#305832", + + "light_color_tint": "#738574", + "light_color_shade": "#485849" +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/moodyblue.json b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/moodyblue.json new file mode 100644 index 0000000..db1ba2f --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/moodyblue.json @@ -0,0 +1,16 @@ +{ + "toolbar_background_color": "#7baec7", + "tab_bar_background_color": "#7baec7", + "toolbar_color": "", + "tab_bar_color": "#49758b", + "tab_bar_activated_color": "", + + "main_color": "#7baec7", + "light_color": "#90bbcf", + + "main_color_tint": "#96c0d5", + "main_color_shade": "#6b93a7", + + "light_color_tint": "#c3e2f1", + "light_color_shade": "#89aec0" +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/peelyorange.json b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/peelyorange.json new file mode 100644 index 0000000..f7f04f2 --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/peelyorange.json @@ -0,0 +1,16 @@ +{ + "toolbar_background_color": "#fd8f38", + "tab_bar_background_color": "#fd8f38", + "toolbar_color": "#815208", + "tab_bar_color": "#ffb67c", + "tab_bar_activated_color": "#815208", + + "main_color": "#e49200", + "light_color": "#ffb67c", + + "main_color_tint": "#e6ae4b", + "main_color_shade": "#b4780c", + + "light_color_tint": "#fad4b6", + "light_color_shade": "#ff9c4e" +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/red.json b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/red.json new file mode 100644 index 0000000..be9c737 --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoReactThemeSwitcher/themes/red.json @@ -0,0 +1,16 @@ +{ + "toolbar_background_color": "red", + "tab_bar_background_color": "red", + "toolbar_color": "", + "tab_bar_color": "", + "tab_bar_activated_color": "", + + "main_color": "", + "light_color": "", + + "main_color_tint": "", + "main_color_shade": "", + + "light_color_tint": "", + "light_color_shade": "" +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/public/assets/DemoRecipeApp/bookmark.png b/03_source/mobile.trunk.1/public/assets/DemoRecipeApp/bookmark.png new file mode 100644 index 0000000..4077d82 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoRecipeApp/bookmark.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoRecipeApp/icon/favicon.png b/03_source/mobile.trunk.1/public/assets/DemoRecipeApp/icon/favicon.png new file mode 100644 index 0000000..51888a7 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoRecipeApp/icon/favicon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoRecipeApp/icon/icon.png b/03_source/mobile.trunk.1/public/assets/DemoRecipeApp/icon/icon.png new file mode 100644 index 0000000..a7f6374 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoRecipeApp/icon/icon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoRecipeApp/placeholder.png b/03_source/mobile.trunk.1/public/assets/DemoRecipeApp/placeholder.png new file mode 100644 index 0000000..1c2791a Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoRecipeApp/placeholder.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoRecipeApp/shapes.svg b/03_source/mobile.trunk.1/public/assets/DemoRecipeApp/shapes.svg new file mode 100644 index 0000000..d370b4d --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoRecipeApp/shapes.svg @@ -0,0 +1 @@ + diff --git a/03_source/mobile.trunk.1/public/assets/DemoShopAppUi/cart.png b/03_source/mobile.trunk.1/public/assets/DemoShopAppUi/cart.png new file mode 100644 index 0000000..dd0f595 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoShopAppUi/cart.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoShopAppUi/icon/favicon.png b/03_source/mobile.trunk.1/public/assets/DemoShopAppUi/icon/favicon.png new file mode 100644 index 0000000..51888a7 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoShopAppUi/icon/favicon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoShopAppUi/icon/icon.png b/03_source/mobile.trunk.1/public/assets/DemoShopAppUi/icon/icon.png new file mode 100644 index 0000000..a7f6374 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoShopAppUi/icon/icon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoShopAppUi/shapes.svg b/03_source/mobile.trunk.1/public/assets/DemoShopAppUi/shapes.svg new file mode 100644 index 0000000..d370b4d --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoShopAppUi/shapes.svg @@ -0,0 +1 @@ + diff --git a/03_source/mobile.trunk.1/public/assets/DemoShopAppUi/shop.png b/03_source/mobile.trunk.1/public/assets/DemoShopAppUi/shop.png new file mode 100644 index 0000000..a3a8bba Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoShopAppUi/shop.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/icon/favicon.png b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/icon/favicon.png new file mode 100644 index 0000000..51888a7 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/icon/favicon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/icon/icon.png b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/icon/icon.png new file mode 100644 index 0000000..a7f6374 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/icon/icon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/1.png b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/1.png new file mode 100644 index 0000000..8a5fa94 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/1.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/10.png b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/10.png new file mode 100644 index 0000000..b68cff1 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/10.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/2.png b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/2.png new file mode 100644 index 0000000..79a2bc2 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/2.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/3.png b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/3.png new file mode 100644 index 0000000..52a6d66 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/3.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/4.png b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/4.png new file mode 100644 index 0000000..f3cbf68 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/4.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/5.png b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/5.png new file mode 100644 index 0000000..7957e0b Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/5.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/6.png b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/6.png new file mode 100644 index 0000000..938c186 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/6.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/7.png b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/7.png new file mode 100644 index 0000000..777438b Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/7.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/8.png b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/8.png new file mode 100644 index 0000000..e74c488 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/8.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/9.png b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/9.png new file mode 100644 index 0000000..88bdf32 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/scenery/9.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/shapes.svg b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/shapes.svg new file mode 100644 index 0000000..d370b4d --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoSkeletonText/shapes.svg @@ -0,0 +1 @@ + diff --git a/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/autumn.png b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/autumn.png new file mode 100644 index 0000000..3725b34 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/autumn.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/avatar.jpeg b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/avatar.jpeg new file mode 100644 index 0000000..9a30b74 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/avatar.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/avatar1.png b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/avatar1.png new file mode 100644 index 0000000..094634d Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/avatar1.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/avatar2.png b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/avatar2.png new file mode 100644 index 0000000..a787854 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/avatar2.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/avatar3.png b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/avatar3.png new file mode 100644 index 0000000..084d895 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/avatar3.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/avatar4.png b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/avatar4.png new file mode 100644 index 0000000..c0c8738 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/avatar4.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/avatar5.png b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/avatar5.png new file mode 100644 index 0000000..050e908 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/avatar5.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/avatar6.png b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/avatar6.png new file mode 100644 index 0000000..8114df3 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/avatar6.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/cover1.jpeg b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/cover1.jpeg new file mode 100644 index 0000000..eeb888c Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/cover1.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/cover2.jpeg b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/cover2.jpeg new file mode 100644 index 0000000..2e5074e Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/cover2.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/cover4.jpeg b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/cover4.jpeg new file mode 100644 index 0000000..f31d1f9 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/cover4.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/cover5.jpeg b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/cover5.jpeg new file mode 100644 index 0000000..1e1fea9 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/cover5.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/cover6.jpeg b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/cover6.jpeg new file mode 100644 index 0000000..e28dd31 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/cover6.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/flower.jpeg b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/flower.jpeg new file mode 100644 index 0000000..3f56d8c Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/flower.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/h.jpeg b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/h.jpeg new file mode 100644 index 0000000..a5be47e Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/h.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/icon/favicon.png b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/icon/favicon.png new file mode 100644 index 0000000..51888a7 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/icon/favicon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/icon/icon.png b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/icon/icon.png new file mode 100644 index 0000000..a7f6374 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/icon/icon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/ocean.jpeg b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/ocean.jpeg new file mode 100644 index 0000000..22892f1 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/ocean.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/shapes.svg b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/shapes.svg new file mode 100644 index 0000000..d370b4d --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/shapes.svg @@ -0,0 +1 @@ + diff --git a/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/spring.png b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/spring.png new file mode 100644 index 0000000..2726d11 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/spring.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/summer.png b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/summer.png new file mode 100644 index 0000000..ae4def9 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/summer.png differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/van.jpeg b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/van.jpeg new file mode 100644 index 0000000..f73f338 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/van.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/winter.png b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/winter.png new file mode 100644 index 0000000..81694c9 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/DemoSlidingProfile/winter.png differ diff --git a/03_source/mobile.trunk.1/public/assets/ScoreBoard/icon/favicon.png b/03_source/mobile.trunk.1/public/assets/ScoreBoard/icon/favicon.png new file mode 100644 index 0000000..51888a7 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/ScoreBoard/icon/favicon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/ScoreBoard/icon/icon.png b/03_source/mobile.trunk.1/public/assets/ScoreBoard/icon/icon.png new file mode 100644 index 0000000..a7f6374 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/ScoreBoard/icon/icon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/ScoreBoard/icons/crown_ionicreacthub.png b/03_source/mobile.trunk.1/public/assets/ScoreBoard/icons/crown_ionicreacthub.png new file mode 100644 index 0000000..f5e4faf Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/ScoreBoard/icons/crown_ionicreacthub.png differ diff --git a/03_source/mobile.trunk.1/public/assets/ScoreBoard/icons/dashboard_ionicreacthub.png b/03_source/mobile.trunk.1/public/assets/ScoreBoard/icons/dashboard_ionicreacthub.png new file mode 100644 index 0000000..856b78d Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/ScoreBoard/icons/dashboard_ionicreacthub.png differ diff --git a/03_source/mobile.trunk.1/public/assets/ScoreBoard/scoreboardheader.jpeg b/03_source/mobile.trunk.1/public/assets/ScoreBoard/scoreboardheader.jpeg new file mode 100644 index 0000000..fe4d17c Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/ScoreBoard/scoreboardheader.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/ScoreBoard/shapes.svg b/03_source/mobile.trunk.1/public/assets/ScoreBoard/shapes.svg new file mode 100644 index 0000000..d370b4d --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/ScoreBoard/shapes.svg @@ -0,0 +1 @@ + diff --git a/03_source/mobile.trunk.1/public/assets/WeatherDemo/icon/favicon.png b/03_source/mobile.trunk.1/public/assets/WeatherDemo/icon/favicon.png new file mode 100644 index 0000000..51888a7 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/WeatherDemo/icon/favicon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/WeatherDemo/icon/icon.png b/03_source/mobile.trunk.1/public/assets/WeatherDemo/icon/icon.png new file mode 100644 index 0000000..a7f6374 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/WeatherDemo/icon/icon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/WeatherDemo/map.png b/03_source/mobile.trunk.1/public/assets/WeatherDemo/map.png new file mode 100644 index 0000000..585e1b7 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/WeatherDemo/map.png differ diff --git a/03_source/mobile.trunk.1/public/assets/WeatherDemo/shapes.svg b/03_source/mobile.trunk.1/public/assets/WeatherDemo/shapes.svg new file mode 100644 index 0000000..d370b4d --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/WeatherDemo/shapes.svg @@ -0,0 +1 @@ + diff --git a/03_source/mobile.trunk.1/public/assets/WeatherDemo/temp.png b/03_source/mobile.trunk.1/public/assets/WeatherDemo/temp.png new file mode 100644 index 0000000..981b728 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/WeatherDemo/temp.png differ diff --git a/03_source/mobile.trunk.1/public/assets/WeatherDemo/temp2.png b/03_source/mobile.trunk.1/public/assets/WeatherDemo/temp2.png new file mode 100644 index 0000000..47a19c0 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/WeatherDemo/temp2.png differ diff --git a/03_source/mobile.trunk.1/public/assets/WeatherDemo/wind.png b/03_source/mobile.trunk.1/public/assets/WeatherDemo/wind.png new file mode 100644 index 0000000..9bfa6ee Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/WeatherDemo/wind.png differ diff --git a/03_source/mobile.trunk.1/public/assets/data/data.json b/03_source/mobile.trunk.1/public/assets/data/data.json new file mode 100644 index 0000000..44f6125 --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/data/data.json @@ -0,0 +1,467 @@ +{ + "schedule": [ + { + "date": "2047-05-17", + "groups": [ + { + "time": "8:00 am", + "sessions": [ + { + "name": "Breakfast", + "timeStart": "8:00 am", + "timeEnd": "9:00 am", + "location": "Dining Hall", + "tracks": ["Food"], + "id": "1" + } + ] + }, + { + "time": "9:15 am", + "sessions": [ + { + "name": "Getting Started with Ionic", + "location": "Hall 2", + "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with React, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and React.", + "speakerNames": ["Ted Turtle"], + "timeStart": "9:30 am", + "timeEnd": "9:45 am", + "tracks": ["Ionic"], + "id": "2" + }, + { + "name": "Ionic Tooling", + "location": "Executive Ballroom", + "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with React, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and React.", + "speakerNames": ["Rachel Rabbit"], + "timeStart": "9:45 am", + "timeEnd": "10:00 am", + "tracks": ["Tooling"], + "id": "3" + }, + { + "name": "University of Ionic", + "location": "Hall 3", + "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with React, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and React.", + "speakerNames": ["Ellie Elephant"], + "timeStart": "9:15 am", + "timeEnd": "9:30 am", + "tracks": ["Ionic"], + "id": "4" + } + ] + }, + { + "time": "10:00 am", + "sessions": [ + { + "name": "Migrating to Ionic", + "location": "Hall 1", + "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with React, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and React.", + "speakerNames": ["Eva Eagle", "Lionel Lion"], + "timeStart": "10:00 am", + "timeEnd": "10:15 am", + "tracks": ["Ionic"], + "id": "5" + }, + { + "name": "What's New in React", + "location": "Hall 3", + "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with React, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and React.", + "speakerNames": ["Rachel Rabbit"], + "timeStart": "10:15 am", + "timeEnd": "10:30 am", + "tracks": ["React"], + "id": "6" + }, + { + "name": "The Evolution of Ionicons", + "location": "Hall 2", + "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with React, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and React.", + "speakerNames": ["Isabella Iguana", "Eva Eagle"], + "timeStart": "10:15 am", + "timeEnd": "10:30 am", + "tracks": ["Design"], + "id": "7" + }, + { + "name": "Ionic Pro", + "location": "Grand Ballroom A", + "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with React, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and React.", + "speakerNames": ["Charlie Cheetah"], + "timeStart": "10:45 am", + "timeEnd": "11:00 am", + "tracks": ["Services"], + "id": "8" + } + ] + }, + { + "time": "11:00 am", + "sessions": [ + { + "name": "Ionic Workshop", + "location": "Hall 1", + "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with React, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and React.", + "speakerNames": ["Karl Kitten", "Lionel Lion"], + "timeStart": "11:00 am", + "timeEnd": "11:45 am", + "tracks": ["Workshop"], + "id": "9" + }, + { + "name": "Community Interaction", + "location": "Hall 3", + "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with React, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and React.", + "speakerNames": ["Lionel Lion", "Gino Giraffe"], + "timeStart": "11:30 am", + "timeEnd": "11:50 am", + "tracks": ["Communication"], + "id": "10" + }, + { + "name": "Navigation in Ionic", + "location": "Grand Ballroom A", + "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with React, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and React.", + "speakerNames": ["Rachel Rabbit", "Eva Eagle"], + "timeStart": "11:30 am", + "timeEnd": "12:00 pm", + "tracks": ["Navigation"], + "id": "11" + } + ] + }, + { + "time": "12:00 pm", + "sessions": [ + { + "name": "Lunch", + "location": "Dining Hall", + "description": "Come grab lunch with all the Ionic fanatics and talk all things Ionic", + "timeStart": "12:00 pm", + "timeEnd": "1:00 pm", + "tracks": ["Food"], + "id": "12" + } + ] + }, + { + "time": "1:00 pm", + "sessions": [ + { + "name": "Ionic in the Enterprise", + "location": "Hall 1", + "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with React, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and React.", + "speakerNames": ["Paul Puppy"], + "timeStart": "1:00 pm", + "timeEnd": "1:15 pm", + "tracks": ["Communication"], + "id": "13" + }, + { + "name": "Ionic Worldwide", + "location": "Hall 1", + "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with React, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and React.", + "speakerNames": ["Gino Giraffe"], + "timeStart": "1:15 pm", + "timeEnd": "1:30 pm", + "tracks": ["Communication"], + "id": "14" + }, + { + "name": "The Ionic Package", + "location": "Grand Ballroom B", + "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with React, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and React.", + "speakerNames": ["Molly Mouse", "Burt Bear"], + "timeStart": "1:30 pm", + "timeEnd": "2:00 pm", + "tracks": ["Services"], + "id": "15" + } + ] + }, + { + "time": "2:00 pm", + "sessions": [ + { + "name": "Push Notifications in Ionic", + "location": "Hall 2", + "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with React, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and React.", + "speakerNames": ["Burt Bear", "Charlie Cheetah"], + "timeStart": "2:00 pm", + "timeEnd": "2:30 pm", + "tracks": ["Services"], + "id": "16" + }, + { + "name": "Ionic Documentation", + "location": "Grand Ballroom B", + "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with React, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and React.", + "speakerNames": ["Donald Duck"], + "timeStart": "2:30 pm", + "timeEnd": "2:45 pm", + "tracks": ["Documentation"], + "id": "17" + }, + { + "name": "UX in Ionic", + "location": "Hall 3", + "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with React, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and React.", + "speakerNames": ["Isabella Iguana", "Ellie Elephant"], + "timeStart": "2:45 pm", + "timeEnd": "3:00 pm", + "tracks": ["Design"], + "id": "18" + } + ] + }, + { + "time": "3:00 pm", + "sessions": [ + { + "name": "Building with React and Ionic", + "location": "Hall 1", + "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with React, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and React.", + "speakerNames": ["Ted Turtle"], + "timeStart": "3:00 pm", + "timeEnd": "3:30 pm", + "tracks": ["React"], + "id": "19" + }, + { + "name": "Mobile States", + "location": "Hall 2", + "description": "Mobile devices and browsers are now advanced enough that developers can build native-quality mobile apps using open web technologies like HTML5, Javascript, and CSS. In this talk, we’ll provide background on why and how we created Ionic, the design decisions made as we integrated Ionic with React, and the performance considerations for mobile platforms that our team had to overcome. We’ll also review new and upcoming Ionic features, and talk about the hidden powers and benefits of combining mobile app development and React.", + "speakerNames": ["Rachel Rabbit"], + "timeStart": "3:30 pm", + "timeEnd": "3:45 pm", + "tracks": ["Navigation"], + "id": "20" + } + ] + } + ] + } + ], + + "speakers": [ + { + "name": "Burt Bear", + "profilePic": "/assets/img/speakers/bear.jpg", + "instagram": "ionicframework", + "twitter": "ionicframework", + "about": "Burt is a Bear. Burt's interests include poetry, dashing space heroes, and lions.", + "title": "Software Engineer", + "location": "Everywhere", + "email": "burt@example.com", + "phone": "+1-541-754-3010", + "id": "1" + }, + { + "name": "Charlie Cheetah", + "profilePic": "/assets/img/speakers/cheetah.jpg", + "instagram": "ionicframework", + "twitter": "ionicframework", + "about": "Charlie is a Cheetah. Charlie's interests include country music, plush animals, pyrotechnics, and skeletons.", + "title": "Software Engineer", + "location": "Everywhere", + "email": "charlie@example.com", + "phone": "+1-541-754-3010", + "id": "2" + }, + { + "name": "Donald Duck", + "profilePic": "/assets/img/speakers/duck.jpg", + "instagram": "ionicframework", + "twitter": "ionicframework", + "about": "Donald is a Duck. Donald's interests include carpentry, superheroes, merpeople, and glam rock.", + "title": "Software Engineer", + "location": "Everywhere", + "email": "donald@example.com", + "phone": "+1-541-754-3010", + "id": "3" + }, + { + "name": "Eva Eagle", + "profilePic": "/assets/img/speakers/eagle.jpg", + "instagram": "ionicframework", + "twitter": "ionicframework", + "about": "Eva is an Eagle. Eva's interests include ants, seashells, and cupcakes.", + "title": "Developer Advocate", + "location": "Everywhere", + "email": "eva@example.com", + "phone": "+1-541-754-3010", + "id": "4" + }, + { + "name": "Ellie Elephant", + "profilePic": "/assets/img/speakers/elephant.jpg", + "instagram": "ionicframework", + "twitter": "ionicframework", + "about": "Ellie is an Elephant. Ellie's interests include pocket watches, pool, hand fans, and ninjas.", + "title": "Software Engineer", + "location": "Everywhere", + "email": "ellie@example.com", + "phone": "+1-541-754-3010", + "id": "5" + }, + { + "name": "Gino Giraffe", + "profilePic": "/assets/img/speakers/giraffe.jpg", + "instagram": "ionicframework", + "twitter": "ionicframework", + "about": "Gino is a Giraffe. Gino's interests include candy-making, unicorns, and birdhouses.", + "title": "Software Engineer", + "location": "Everywhere", + "email": "gino@example.com", + "phone": "+1-541-754-3010", + "id": "6" + }, + { + "name": "Isabella Iguana", + "profilePic": "/assets/img/speakers/iguana.jpg", + "instagram": "ionicframework", + "twitter": "ionicframework", + "about": "Isabella is an Iguana. Isabella's interests include crystals, architecture, and candle-making.", + "title": "Software Engineer", + "location": "Everywhere", + "email": "isabella@example.com", + "phone": "+1-541-754-3010", + "id": "7" + }, + { + "name": "Karl Kitten", + "profilePic": "/assets/img/speakers/kitten.jpg", + "instagram": "ionicframework", + "twitter": "ionicframework", + "about": "Karl is a Kitten. Karl's interests include skiing, jewelry, and needlepoint.", + "title": "Developer Advocate", + "location": "Everywhere", + "email": "karl@example.com", + "phone": "+1-541-754-3010", + "id": "8" + }, + { + "name": "Lionel Lion", + "profilePic": "/assets/img/speakers/lion.jpg", + "instagram": "ionicframework", + "twitter": "ionicframework", + "about": "Lionel is a Lion. Lionel's interests include lizards and mathematics.", + "title": "Developer Advocate", + "location": "Everywhere", + "email": "lionel@example.com", + "phone": "+1-541-754-3010", + "id": "9" + }, + { + "name": "Molly Mouse", + "profilePic": "/assets/img/speakers/mouse.jpg", + "instagram": "ionicframework", + "twitter": "ionicframework", + "about": "Molly is a Mouse. Molly's interests include werewolves and magic.", + "title": "Software Engineer", + "location": "Everywhere", + "email": "molly@example.com", + "phone": "+1-541-754-3010", + "id": "10" + }, + { + "name": "Paul Puppy", + "profilePic": "/assets/img/speakers/puppy.jpg", + "instagram": "ionicframework", + "twitter": "ionicframework", + "about": "Paul is a Puppy. Paul's interests include maps, whales, and dragons.", + "title": "Software Engineer", + "location": "Everywhere", + "email": "paul@example.com", + "phone": "+1-541-754-3010", + "id": "11" + }, + { + "name": "Rachel Rabbit", + "profilePic": "/assets/img/speakers/rabbit.jpg", + "instagram": "ionicframework", + "twitter": "ionicframework", + "about": "Rachel is a Rabbit. Rachel's interests include clowns, skeletons, and yo-yos.", + "title": "Senior Software Engineer", + "location": "Everywhere", + "email": "rachel@example.com", + "phone": "+1-541-754-3010", + "id": "12" + }, + { + "name": "Ted Turtle", + "profilePic": "/assets/img/speakers/turtle.jpg", + "instagram": "ionicframework", + "twitter": "ionicframework", + "about": "Ted is a Turtle. Ted's interests include butterflies, skiing, and cupcakes.", + "title": "Software Engineer", + "location": "Everywhere", + "email": "ted@example.com", + "phone": "+1-541-754-3010", + "id": "13" + } + ], + + "map": [ + { + "name": "Monona Terrace Convention Center", + "lat": 43.071584, + "lng": -89.38012, + "center": true + }, + { + "name": "Ionic HQ", + "lat": 43.074395, + "lng": -89.381056 + }, + { + "name": "Afterparty - Brocach Irish Pub", + "lat": 43.07336, + "lng": -89.38335 + } + ], + + "tracks": [ + { + "name": "React", + "icon": "logo-react" + }, + { + "name": "Documentation", + "icon": "document" + }, + { + "name": "Food", + "icon": "restaurant" + }, + { + "name": "Ionic", + "icon": "logo-ionic" + }, + { + "name": "Tooling", + "icon": "hammer" + }, + { + "name": "Design", + "icon": "color-palette" + }, + { + "name": "Services", + "icon": "cog" + }, + { + "name": "Workshop", + "icon": "construct" + }, + { + "name": "Communication", + "icon": "call" + }, + { + "name": "Navigation", + "icon": "compass" + } + ] +} diff --git a/03_source/mobile.trunk.1/public/assets/data/locations.json b/03_source/mobile.trunk.1/public/assets/data/locations.json new file mode 100644 index 0000000..7743cc9 --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/data/locations.json @@ -0,0 +1,23 @@ +[ + { + "id": 1, + "name": "Map Center", + "lat": 43.071584, + "lng": -89.380120 + }, { + "id": 2, + "name": "Monona Terrace Convention Center", + "lat": 43.071584, + "lng": -89.380120 + }, { + "id": 3, + "name": "Ionic HQ", + "lat": 43.074395, + "lng": -89.381056 + }, { + "id": 4, + "name": "Afterparty - Brocach Irish Pub", + "lat": 43.07336, + "lng": -89.38335 + } +] diff --git a/03_source/mobile.trunk.1/public/assets/icon/favicon.png b/03_source/mobile.trunk.1/public/assets/icon/favicon.png new file mode 100644 index 0000000..51888a7 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/icon/favicon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/img/about/austin.jpg b/03_source/mobile.trunk.1/public/assets/img/about/austin.jpg new file mode 100644 index 0000000..b4eb329 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/img/about/austin.jpg differ diff --git a/03_source/mobile.trunk.1/public/assets/img/about/chicago.jpg b/03_source/mobile.trunk.1/public/assets/img/about/chicago.jpg new file mode 100644 index 0000000..b982b65 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/img/about/chicago.jpg differ diff --git a/03_source/mobile.trunk.1/public/assets/img/about/madison.jpg b/03_source/mobile.trunk.1/public/assets/img/about/madison.jpg new file mode 100644 index 0000000..c46fbf0 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/img/about/madison.jpg differ diff --git a/03_source/mobile.trunk.1/public/assets/img/about/seattle.jpg b/03_source/mobile.trunk.1/public/assets/img/about/seattle.jpg new file mode 100644 index 0000000..4c5946a Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/img/about/seattle.jpg differ diff --git a/03_source/mobile.trunk.1/public/assets/img/appicon.png b/03_source/mobile.trunk.1/public/assets/img/appicon.png new file mode 100644 index 0000000..3aa6ee2 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/img/appicon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/img/appicon.svg b/03_source/mobile.trunk.1/public/assets/img/appicon.svg new file mode 100644 index 0000000..3d2a03b --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/img/appicon.svg @@ -0,0 +1,2 @@ + + diff --git a/03_source/mobile.trunk.1/public/assets/img/ica-slidebox-img-1.png b/03_source/mobile.trunk.1/public/assets/img/ica-slidebox-img-1.png new file mode 100644 index 0000000..9233602 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/img/ica-slidebox-img-1.png differ diff --git a/03_source/mobile.trunk.1/public/assets/img/ica-slidebox-img-2.png b/03_source/mobile.trunk.1/public/assets/img/ica-slidebox-img-2.png new file mode 100644 index 0000000..2fdce7d Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/img/ica-slidebox-img-2.png differ diff --git a/03_source/mobile.trunk.1/public/assets/img/ica-slidebox-img-3.png b/03_source/mobile.trunk.1/public/assets/img/ica-slidebox-img-3.png new file mode 100644 index 0000000..a304e88 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/img/ica-slidebox-img-3.png differ diff --git a/03_source/mobile.trunk.1/public/assets/img/ica-slidebox-img-4.png b/03_source/mobile.trunk.1/public/assets/img/ica-slidebox-img-4.png new file mode 100644 index 0000000..2f263d6 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/img/ica-slidebox-img-4.png differ diff --git a/03_source/mobile.trunk.1/public/assets/img/ionic-logo-white.svg b/03_source/mobile.trunk.1/public/assets/img/ionic-logo-white.svg new file mode 100644 index 0000000..7ebf0cd --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/img/ionic-logo-white.svg @@ -0,0 +1 @@ + diff --git a/03_source/mobile.trunk.1/public/assets/img/speaker-background.png b/03_source/mobile.trunk.1/public/assets/img/speaker-background.png new file mode 100644 index 0000000..220634e Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/img/speaker-background.png differ diff --git a/03_source/mobile.trunk.1/public/assets/img/speakers/bear.jpg b/03_source/mobile.trunk.1/public/assets/img/speakers/bear.jpg new file mode 100755 index 0000000..a968db7 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/img/speakers/bear.jpg differ diff --git a/03_source/mobile.trunk.1/public/assets/img/speakers/cheetah.jpg b/03_source/mobile.trunk.1/public/assets/img/speakers/cheetah.jpg new file mode 100755 index 0000000..f188a81 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/img/speakers/cheetah.jpg differ diff --git a/03_source/mobile.trunk.1/public/assets/img/speakers/duck.jpg b/03_source/mobile.trunk.1/public/assets/img/speakers/duck.jpg new file mode 100755 index 0000000..b8b7c52 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/img/speakers/duck.jpg differ diff --git a/03_source/mobile.trunk.1/public/assets/img/speakers/eagle.jpg b/03_source/mobile.trunk.1/public/assets/img/speakers/eagle.jpg new file mode 100755 index 0000000..ac839a5 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/img/speakers/eagle.jpg differ diff --git a/03_source/mobile.trunk.1/public/assets/img/speakers/elephant.jpg b/03_source/mobile.trunk.1/public/assets/img/speakers/elephant.jpg new file mode 100755 index 0000000..0727847 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/img/speakers/elephant.jpg differ diff --git a/03_source/mobile.trunk.1/public/assets/img/speakers/giraffe.jpg b/03_source/mobile.trunk.1/public/assets/img/speakers/giraffe.jpg new file mode 100755 index 0000000..30a854c Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/img/speakers/giraffe.jpg differ diff --git a/03_source/mobile.trunk.1/public/assets/img/speakers/iguana.jpg b/03_source/mobile.trunk.1/public/assets/img/speakers/iguana.jpg new file mode 100755 index 0000000..75d528f Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/img/speakers/iguana.jpg differ diff --git a/03_source/mobile.trunk.1/public/assets/img/speakers/kitten.jpg b/03_source/mobile.trunk.1/public/assets/img/speakers/kitten.jpg new file mode 100755 index 0000000..3cc1359 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/img/speakers/kitten.jpg differ diff --git a/03_source/mobile.trunk.1/public/assets/img/speakers/lion.jpg b/03_source/mobile.trunk.1/public/assets/img/speakers/lion.jpg new file mode 100755 index 0000000..f5eb632 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/img/speakers/lion.jpg differ diff --git a/03_source/mobile.trunk.1/public/assets/img/speakers/mouse.jpg b/03_source/mobile.trunk.1/public/assets/img/speakers/mouse.jpg new file mode 100755 index 0000000..ca30177 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/img/speakers/mouse.jpg differ diff --git a/03_source/mobile.trunk.1/public/assets/img/speakers/puppy.jpg b/03_source/mobile.trunk.1/public/assets/img/speakers/puppy.jpg new file mode 100755 index 0000000..d635a8a Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/img/speakers/puppy.jpg differ diff --git a/03_source/mobile.trunk.1/public/assets/img/speakers/rabbit.jpg b/03_source/mobile.trunk.1/public/assets/img/speakers/rabbit.jpg new file mode 100755 index 0000000..475d62c Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/img/speakers/rabbit.jpg differ diff --git a/03_source/mobile.trunk.1/public/assets/img/speakers/turtle.jpg b/03_source/mobile.trunk.1/public/assets/img/speakers/turtle.jpg new file mode 100755 index 0000000..60c3496 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/img/speakers/turtle.jpg differ diff --git a/03_source/mobile.trunk.1/public/assets/react-shop/beds.jpeg b/03_source/mobile.trunk.1/public/assets/react-shop/beds.jpeg new file mode 100644 index 0000000..8582d80 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/react-shop/beds.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/react-shop/coats3.jpeg b/03_source/mobile.trunk.1/public/assets/react-shop/coats3.jpeg new file mode 100644 index 0000000..edff4bf Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/react-shop/coats3.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/react-shop/coffee_table.jpeg b/03_source/mobile.trunk.1/public/assets/react-shop/coffee_table.jpeg new file mode 100644 index 0000000..21e9601 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/react-shop/coffee_table.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/react-shop/dresses3.jpeg b/03_source/mobile.trunk.1/public/assets/react-shop/dresses3.jpeg new file mode 100644 index 0000000..7a34676 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/react-shop/dresses3.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/react-shop/formal_shirts2.jpeg b/03_source/mobile.trunk.1/public/assets/react-shop/formal_shirts2.jpeg new file mode 100644 index 0000000..59cd44f Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/react-shop/formal_shirts2.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/react-shop/home.jpeg b/03_source/mobile.trunk.1/public/assets/react-shop/home.jpeg new file mode 100644 index 0000000..75f42a3 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/react-shop/home.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/react-shop/icon/favicon.png b/03_source/mobile.trunk.1/public/assets/react-shop/icon/favicon.png new file mode 100644 index 0000000..51888a7 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/react-shop/icon/favicon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/react-shop/icon/icon.png b/03_source/mobile.trunk.1/public/assets/react-shop/icon/icon.png new file mode 100644 index 0000000..a7f6374 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/react-shop/icon/icon.png differ diff --git a/03_source/mobile.trunk.1/public/assets/react-shop/jeans.jpeg b/03_source/mobile.trunk.1/public/assets/react-shop/jeans.jpeg new file mode 100644 index 0000000..b90bfd1 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/react-shop/jeans.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/react-shop/makeup2.jpeg b/03_source/mobile.trunk.1/public/assets/react-shop/makeup2.jpeg new file mode 100644 index 0000000..e291108 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/react-shop/makeup2.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/react-shop/men.jpeg b/03_source/mobile.trunk.1/public/assets/react-shop/men.jpeg new file mode 100644 index 0000000..23bf901 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/react-shop/men.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/react-shop/office.jpeg b/03_source/mobile.trunk.1/public/assets/react-shop/office.jpeg new file mode 100644 index 0000000..486ec39 Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/react-shop/office.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/react-shop/shapes.svg b/03_source/mobile.trunk.1/public/assets/react-shop/shapes.svg new file mode 100644 index 0000000..d370b4d --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/react-shop/shapes.svg @@ -0,0 +1 @@ + diff --git a/03_source/mobile.trunk.1/public/assets/react-shop/sportswear2.jpeg b/03_source/mobile.trunk.1/public/assets/react-shop/sportswear2.jpeg new file mode 100644 index 0000000..285614c Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/react-shop/sportswear2.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/react-shop/women.jpeg b/03_source/mobile.trunk.1/public/assets/react-shop/women.jpeg new file mode 100644 index 0000000..0ad70ef Binary files /dev/null and b/03_source/mobile.trunk.1/public/assets/react-shop/women.jpeg differ diff --git a/03_source/mobile.trunk.1/public/assets/shapes.svg b/03_source/mobile.trunk.1/public/assets/shapes.svg new file mode 100644 index 0000000..d370b4d --- /dev/null +++ b/03_source/mobile.trunk.1/public/assets/shapes.svg @@ -0,0 +1 @@ + diff --git a/03_source/mobile.trunk.1/public/favicon.ico b/03_source/mobile.trunk.1/public/favicon.ico new file mode 100644 index 0000000..a11777c Binary files /dev/null and b/03_source/mobile.trunk.1/public/favicon.ico differ diff --git a/03_source/mobile.trunk.1/public/manifest.json b/03_source/mobile.trunk.1/public/manifest.json new file mode 100644 index 0000000..ee4307c --- /dev/null +++ b/03_source/mobile.trunk.1/public/manifest.json @@ -0,0 +1,21 @@ +{ + "short_name": "Ionic React Conf", + "name": "Ionic React Conf", + "icons": [ + { + "src": "assets/icon/favicon.png", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "assets/icon/icon.png", + "type": "image/png", + "sizes": "512x512", + "purpose": "maskable" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#ffffff", + "background_color": "#ffffff" +} diff --git a/03_source/mobile.trunk.1/src/App.scss b/03_source/mobile.trunk.1/src/App.scss new file mode 100644 index 0000000..b0e2713 --- /dev/null +++ b/03_source/mobile.trunk.1/src/App.scss @@ -0,0 +1,29 @@ +/* + * App Global CSS + * ---------------------------------------------------------------------------- + * Put style rules here that you want to apply globally. These styles are for + * the entire app and not just one component. + */ + +@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+HK:wght@100..900&family=Noto+Sans+JP:wght@100..900&family=Noto+Sans+SC:wght@100..900&display=swap'); + +.noto-sans-hk-body { + font-family: 'Noto Sans HK', sans-serif; + font-optical-sizing: auto; + font-weight: unset; + font-style: normal; +} + +.noto-sans-sc-body { + font-family: 'Noto Sans SC', sans-serif; + font-optical-sizing: auto; + font-weight: unset; + font-style: normal; +} + +.noto-sans-jp-body { + font-family: 'Noto Sans JP', sans-serif; + font-optical-sizing: auto; + font-weight: unset; + font-style: normal; +} diff --git a/03_source/mobile.trunk.1/src/App.test.tsx b/03_source/mobile.trunk.1/src/App.test.tsx new file mode 100644 index 0000000..2ae5593 --- /dev/null +++ b/03_source/mobile.trunk.1/src/App.test.tsx @@ -0,0 +1,7 @@ +import App from './App'; +import { render } from '@testing-library/react'; + +it('renders without crashing', () => { + const { asFragment, container } = render(); + expect(asFragment()).toMatchSnapshot(); +}); diff --git a/03_source/mobile.trunk.1/src/App.tsx b/03_source/mobile.trunk.1/src/App.tsx new file mode 100644 index 0000000..98fc660 --- /dev/null +++ b/03_source/mobile.trunk.1/src/App.tsx @@ -0,0 +1,149 @@ +import React, { useEffect } from 'react'; +import { Route } from 'react-router-dom'; +import { IonApp, IonRouterOutlet, IonSplitPane, setupIonicReact } from '@ionic/react'; +import { IonReactRouter } from '@ionic/react-router'; + +import Menu from './components/Menu'; + +/* Core CSS required for Ionic components to work properly */ +import '@ionic/react/css/core.css'; + +/* Basic CSS for apps built with Ionic */ +import '@ionic/react/css/normalize.css'; +import '@ionic/react/css/structure.css'; +import '@ionic/react/css/typography.css'; + +/* Optional CSS utils that can be commented out */ +import '@ionic/react/css/padding.css'; +import '@ionic/react/css/float-elements.css'; +import '@ionic/react/css/text-alignment.css'; +import '@ionic/react/css/text-transformation.css'; +import '@ionic/react/css/flex-utils.css'; +import '@ionic/react/css/display.css'; + +/** + * Ionic Dark Mode + * ----------------------------------------------------- + * For more info, please see: + * https://ionicframework.com/docs/theming/dark-mode + */ + +// import "@ionic/react/css/palettes/dark.always.css"; +// import "@ionic/react/css/palettes/dark.system.css"; +import '@ionic/react/css/palettes/dark.class.css'; + +/* Theme variables */ +import './theme/variables.css'; + +/* Leaflet CSS */ +import 'leaflet/dist/leaflet.css'; + +/* Global styles */ +import './App.scss'; +import MainTabs from './pages/MainTabs'; +import { connect } from './data/connect'; +import { AppContextProvider } from './data/AppContext'; +import { loadConfData } from './data/sessions/sessions.actions'; +import { setIsLoggedIn, setUsername, loadUserData } from './data/user/user.actions'; +import Account from './pages/Account'; +import Login from './pages/Login'; +import MyLogin from './pages/MyLogin'; +import Signup from './pages/Signup'; +import Support from './pages/Support'; +import Tutorial from './pages/Tutorial'; +import HomeOrTutorial from './components/HomeOrTutorial'; +import { Schedule } from './models/Schedule'; +import RedirectToLogin from './components/RedirectToLogin'; +import AppRoute from './AppRoute'; + +import AppDemoRoute from './routes/DemoRoute'; +import Settings from './pages/Settings'; + +setupIonicReact(); + +const App: React.FC = () => { + return ( + + + + ); +}; + +interface StateProps { + darkMode: boolean; + schedule: Schedule; +} + +interface DispatchProps { + loadConfData: typeof loadConfData; + loadUserData: typeof loadUserData; + setIsLoggedIn: typeof setIsLoggedIn; + setUsername: typeof setUsername; +} + +interface IonicAppProps extends StateProps, DispatchProps {} + +const IonicApp: React.FC = ({ darkMode, schedule, setIsLoggedIn, setUsername, loadConfData, loadUserData }) => { + useEffect(() => { + loadUserData(); + loadConfData(); + + // eslint-disable-next-line + }, []); + + return schedule.groups.length === 0 ? ( +
+ ) : ( + + + + + + {/* + We use IonRoute here to keep the tabs state intact, + which makes transitions between tabs and non tab pages smooth + */} + + + + + } /> + + + + + + + + + + + { + return ; + }} + /> + + + + + + ); +}; + +export default App; + +const IonicAppConnected = connect<{}, StateProps, DispatchProps>({ + mapStateToProps: (state) => ({ + darkMode: state.user.darkMode, + schedule: state.data.schedule, + }), + mapDispatchToProps: { + loadConfData, + loadUserData, + setIsLoggedIn, + setUsername, + }, + component: IonicApp, +}); diff --git a/03_source/mobile.trunk.1/src/AppRoute.tsx b/03_source/mobile.trunk.1/src/AppRoute.tsx new file mode 100644 index 0000000..070aafc --- /dev/null +++ b/03_source/mobile.trunk.1/src/AppRoute.tsx @@ -0,0 +1,39 @@ +// +// pages without bottom tab bar +// + +import { Route } from 'react-router'; +import NotImplemented from './pages/NotImplemented'; +import EventDetail from './pages/EventDetail'; +import MemberProfile from './pages/MemberProfile'; +import PATHS from './PATHS'; +import Settings from './pages/Settings'; +import ChangeLanguage from './pages/ChangeLanguage'; +import ServiceAgreement from './pages/ServiceAgreement'; +import PrivacyAgreement from './pages/PrivacyAgreement'; +// import OrderDetails from './pages/OrderDetail'; +import OrderDetail from './pages/OrderDetail'; + +const AppRoute: React.FC = () => { + return ( + <> + + + {/* */} + + + + {/* component make the ":id" available in the "OrderDetail" */} + + {/* */} + + {/* */} + + + + + + ); +}; + +export default AppRoute; diff --git a/03_source/mobile.trunk.1/src/PATHS.ts b/03_source/mobile.trunk.1/src/PATHS.ts new file mode 100644 index 0000000..032b23e --- /dev/null +++ b/03_source/mobile.trunk.1/src/PATHS.ts @@ -0,0 +1,81 @@ +const PATHS = { + NOT_IMPLEMENTED: '/not_implemented', + SETTINGS: '/settings', + CHANGE_LANGUAGE: '/change_language', + SERVICE_AGREEMENT: '/service_agreement', + PRIVACY_AGREEMENT: '/privacy_agreement', + SIGN_IN: '/mylogin', + // + ORDER_DETAIL: '/order_detail/:id', + getOrderDetail: (id: string) => `/order_detail/${id}`, + // + TAB_NOT_IMPLEMENTED: '/tabs/not_implemented', + EVENT_LIST: `/tabs/events`, + MESSAGE_LIST: `/tabs/messages`, + NEARBY_LIST: '/tabs/nearby', + ORDERS_LIST: '/tabs/orders', + FAVOURITES_LIST: `/tabs/favourites`, + PROFILE: '/tabs/my_profile', + + // + // DEMO_WEATHER_APP: '/demo-weather-app', + DEMO_WEATHER_APP_UI: '/demo-weather-app-ui', + // + + DEMO_2FA_EXAMPLE: '/demo-2fa-example', + DEMO_ACCORDION_TUTORIAL: '/demo-accordion-tutorial', + DEMO_BANKING_UI: '/demo-banking-ui', + DEMO_BLOG_POST_UI: '/demo-blog-post-ui', + DEMO_CAPACITOR_GOOGLE_MAPS_TUTORIAL: '/demo-capacitor-google-maps-tutorial', + DEMO_CLUB_HOUSE: '/demo-club-house', + DEMO_COLOR_TUTORIAL: '/demo-color-tutorial', + DEMO_DICTIONARY_APP: '/demo-dictionary-app', + DEMO_ECOMMERCE_EXAMPLE: '/demo-ecommerce-example', + DEMO_FACEBOOK_CLONE: '/demo-facebook-clone', + DEMO_FAST_FOOD_APP: '/demo-fast-food-app', + DEMO_FLOATING_TABS: '/demo-floating-tabs', + DEMO_INSTAGRAM_CLONE: '/demo-instagram-clone', + DEMO_KANBAN_BOARD: '/demo-kanban-board', + DEMO_ORDERING_APP: '/demo-ordering-app', + DEMO_PAGE: '/tabs/demo-list', + DEMO_PINTEREST_FLOATING_TAB_BAR: '/demo-pinterest-floating-tab-bar', + DEMO_PROFILE_EXAMPLE: '/demo-profile-example', + DEMO_PULLSTATE_TUTORIAL: '/demo-pullstate-tutorial', + DEMO_QUIZ_APP: '/demo-quiz-app', + DEMO_QUOTE_APP: '/demo-quote-app', + DEMO_REACT_ADD_TO_CART: '/demo-react-add-to-cart', + DEMO_REACT_CALCULATOR: '/demo-react-calculator', + DEMO_REACT_DRAWING_CANVAS: '/demo-react-drawing-canvas', + DEMO_REACT_HOOK_FORM_EXAMPLE: '/demo-react-hook-form-example', + DEMO_REACT_ITEM_LIST: '/demo-react-item-list', + DEMO_REACT_LIFECYCLES: '/demo-react-lifecycles', + DEMO_REACT_LOGIN: '/demo-react-login', + DEMO_REACT_MARVEL_APP: '/demo-react-marvel-app', + DEMO_REACT_MOVIE_APP_WITH_ALGOLIA: '/demo-react-movie-app-with-algolia', + DEMO_REACT_NOTES: '/demo-react-notes', + DEMO_REACT_ONBOARDING_UI: '/demo-react-onboarding-ui', + DEMO_REACT_OVERLAY_HOOKS: '/demo-react-overlay-hooks', + DEMO_REACT_POLL_APP: '/demo-react-poll-app', + DEMO_REACT_PROFILE_DASHBOARD_UI: '/demo-react-profile-dashboard-ui', + + DEMO_REACT_QR_CODE: '/demo-react-qr-code', + DEMO_QR_SCANNER: '/demo-qr-scanner', + + DEMO_REACT_QUOTES: '/demo-react-quotes', + DEMO_REACT_SHOP_UI: '/demo-react-shop-ui', + DEMO_REACT_SHOP: '/demo-react-shop', + DEMO_REACT_SWITCH_TABS: '/demo-react-switch-tabs', + DEMO_REACT_TABS_MENUS_CUSTOM: '/demo-react-tabs-menus-custom', + DEMO_REACT_THEME_SWITCHER: '/demo-react-theme-switcher', + DEMO_REACT_TRAVEL_APP: '/demo-react-travel-app', + DEMO_REACT_WHATSAPP_CLONE: '/demo-react-whatsapp-clone', + DEMO_RECIPE_APP: '/demo-recipe-app', + DEMO_RESTAURANT_FINDER: '/demo-restaurant-finder', + DEMO_SCORE_BOARD: '/demo-score-board', + DEMO_SHOP_APP_UI: '/demo-shop-app-ui', + DEMO_SKELETON_TEXT: '/demo-skeleton-text', + DEMO_SLIDING_PROFILE: '/demo-sliding-profile', + DEMO_STICKY_BOTTOM_SHEET_EXAMPLE: '/demo-sticky-bottom-sheet-example', + DEMO_STORAGE_EXAMPLE: '/demo-storage-example', +}; +export default PATHS; diff --git a/03_source/mobile.trunk.1/src/TabAppRoute.tsx b/03_source/mobile.trunk.1/src/TabAppRoute.tsx new file mode 100644 index 0000000..7f64525 --- /dev/null +++ b/03_source/mobile.trunk.1/src/TabAppRoute.tsx @@ -0,0 +1,49 @@ +import { Route } from 'react-router'; +import NotImplemented from './pages/NotImplemented'; +import EventDetail from './pages/EventDetail'; +import MemberProfile from './pages/MemberProfile'; +import PATHS from './PATHS'; +import MembersNearByList from './pages/MembersNearByList'; +import OrderList from './pages/OrderList'; +import MessageList from './pages/MessageList'; +import Favourites from './pages/Favourites'; +import MyProfile from './pages/MyProfile'; +import EventList from './pages/EventList'; +import Helloworld from './pages/Helloworld'; +// import WeatherDemo from './pages/WeatherDemo/Tab1'; +import DemoList from './pages/DemoList'; +// import DemoReactShop from './pages/DemoReactShop'; + +const TabAppRoute: React.FC = () => { + return ( + <> + + + {/* */} + } exact={true} /> + + {/* */} + } exact={true} /> + + {/* */} + } exact={true} /> + + {/* */} + } exact={true} /> + + {/* */} + } exact={true} /> + + {/* */} + } exact={true} /> + + {/* */} + } exact={true} /> + + {/* */} + } exact={true} /> + + ); +}; + +export default TabAppRoute; diff --git a/03_source/mobile.trunk.1/src/__snapshots__/App.test.tsx.snap b/03_source/mobile.trunk.1/src/__snapshots__/App.test.tsx.snap new file mode 100644 index 0000000..444f038 --- /dev/null +++ b/03_source/mobile.trunk.1/src/__snapshots__/App.test.tsx.snap @@ -0,0 +1,280 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders without crashing 1`] = ` + + + + + + + + Menu + + + + + + + Navigate + + + + + + Schedule + + + + + + + + Speakers + + + + + + + + Map + + + + + + + + About + + + + + + + Account + + + + + + Account + + + + + + + + Support + + + + + + + + Logout + + + + + + + Tutorial + + + + Show Tutorial + + + + + +
+
+ +
+ + + + + + + + All + + + Favorites + + + + + + + + + + + + + + + + + + + No Sessions Found + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + + + Schedule + + + + + + Speakers + + + + + + Map + + + + + + About + + + +
+
+
+
+
+`; diff --git a/03_source/mobile.trunk.1/src/api/Helloworld.ts b/03_source/mobile.trunk.1/src/api/Helloworld.ts new file mode 100644 index 0000000..5c5216f --- /dev/null +++ b/03_source/mobile.trunk.1/src/api/Helloworld.ts @@ -0,0 +1,5 @@ +function Helloworld() { + console.log('helloworld'); +} + +export { Helloworld }; diff --git a/03_source/mobile.trunk.1/src/api/getEventById.ts b/03_source/mobile.trunk.1/src/api/getEventById.ts new file mode 100644 index 0000000..f75b2b2 --- /dev/null +++ b/03_source/mobile.trunk.1/src/api/getEventById.ts @@ -0,0 +1,17 @@ +// REQ0042/event-detail +// +// PURPOSE: +// - provide api access to backend db +// +// RULES: +// - T.B.A. +// + +import axios from 'axios'; +import constants from '../constants'; + +function getEventById(id: string) { + return axios.get(`${constants.API_ENDPOINT}/v1/events/1`); +} + +export { getEventById }; diff --git a/03_source/mobile.trunk.1/src/api/getEvents.ts b/03_source/mobile.trunk.1/src/api/getEvents.ts new file mode 100644 index 0000000..14b9e74 --- /dev/null +++ b/03_source/mobile.trunk.1/src/api/getEvents.ts @@ -0,0 +1,17 @@ +// REQ0042/event-detail +// +// PURPOSE: +// - provide api access to backend db +// +// RULES: +// - T.B.A. +// + +import axios from 'axios'; +import constants from '../constants'; + +function getEvents() { + return axios.get(`${constants.API_ENDPOINT}/v1/events`); +} + +export { getEvents }; diff --git a/03_source/mobile.trunk.1/src/api/getMemberById.ts b/03_source/mobile.trunk.1/src/api/getMemberById.ts new file mode 100644 index 0000000..dfee0ed --- /dev/null +++ b/03_source/mobile.trunk.1/src/api/getMemberById.ts @@ -0,0 +1,17 @@ +// REQ0042/event-detail +// +// PURPOSE: +// - provide api access to backend db +// +// RULES: +// - T.B.A. +// + +import axios from 'axios'; +import constants from '../constants'; + +function getMemberById(id: string) { + return axios.get(`${constants.API_ENDPOINT}/v1/members/2`); +} + +export { getMemberById }; diff --git a/03_source/mobile.trunk.1/src/api/getMembers.ts b/03_source/mobile.trunk.1/src/api/getMembers.ts new file mode 100644 index 0000000..b65bab8 --- /dev/null +++ b/03_source/mobile.trunk.1/src/api/getMembers.ts @@ -0,0 +1,17 @@ +// REQ0042/event-detail +// +// PURPOSE: +// - provide api access to backend db +// +// RULES: +// - T.B.A. +// + +import axios from 'axios'; +import constants from '../constants'; + +function getMembers() { + return axios.get(`${constants.API_ENDPOINT}/v1/members`); +} + +export { getMembers }; diff --git a/03_source/mobile.trunk.1/src/api/getOrders.ts b/03_source/mobile.trunk.1/src/api/getOrders.ts new file mode 100644 index 0000000..50bb6fd --- /dev/null +++ b/03_source/mobile.trunk.1/src/api/getOrders.ts @@ -0,0 +1,17 @@ +// REQ0047/order-page +// +// PURPOSE: +// - provide api access to backend db for orders +// +// RULES: +// - T.B.A. +// + +import axios from 'axios'; +import constants from '../constants'; + +function getOrders() { + return axios.get(`${constants.API_ENDPOINT}/v1/orders`); +} + +export { getOrders }; diff --git a/03_source/mobile.trunk.1/src/api/getProfileById.ts b/03_source/mobile.trunk.1/src/api/getProfileById.ts new file mode 100644 index 0000000..9b09f93 --- /dev/null +++ b/03_source/mobile.trunk.1/src/api/getProfileById.ts @@ -0,0 +1,17 @@ +// REQ0053/profile-page +// +// PURPOSE: +// - Provides functionality get user profile by id +// +// RULES: +// - T.B.A. +// + +import axios from 'axios'; +import constants from '../constants'; + +function getProfileById(id: string) { + return axios.get(`${constants.API_ENDPOINT}/v1/profile/${id}`); +} + +export { getProfileById }; diff --git a/03_source/mobile.trunk.1/src/components/AboutPopover.tsx b/03_source/mobile.trunk.1/src/components/AboutPopover.tsx new file mode 100644 index 0000000..030106e --- /dev/null +++ b/03_source/mobile.trunk.1/src/components/AboutPopover.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { IonList, IonItem, IonLabel } from '@ionic/react'; + +interface AboutPopoverProps { + dismiss: () => void; +} + +const AboutPopover: React.FC = ({ dismiss }) => { + const close = (url: string) => { + window.open(url, '_blank'); + dismiss(); + }; + + return ( + + close('https://ionicframework.com/docs')}> + Learn Ionic + + close('https://ionicframework.com/docs/react')}> + Documentation + + close('https://showcase.ionicframework.com')}> + Showcase + + close('https://github.com/ionic-team/ionic-framework')}> + GitHub Repo + + + Support + + + ); +}; + +export default AboutPopover; diff --git a/03_source/mobile.trunk.1/src/components/HomeOrTutorial.tsx b/03_source/mobile.trunk.1/src/components/HomeOrTutorial.tsx new file mode 100644 index 0000000..8d4f61e --- /dev/null +++ b/03_source/mobile.trunk.1/src/components/HomeOrTutorial.tsx @@ -0,0 +1,18 @@ +import React from 'react'; +import { connect } from '../data/connect'; +import { Redirect } from 'react-router'; + +interface StateProps { + hasSeenTutorial: boolean; +} + +const HomeOrTutorial: React.FC = ({ hasSeenTutorial }) => { + return hasSeenTutorial ? : ; +}; + +export default connect<{}, StateProps, {}>({ + mapStateToProps: (state) => ({ + hasSeenTutorial: state.user.hasSeenTutorial, + }), + component: HomeOrTutorial, +}); diff --git a/03_source/mobile.trunk.1/src/components/Loading/index.tsx b/03_source/mobile.trunk.1/src/components/Loading/index.tsx new file mode 100644 index 0000000..f306eaf --- /dev/null +++ b/03_source/mobile.trunk.1/src/components/Loading/index.tsx @@ -0,0 +1,58 @@ +import React, { CSSProperties, useState } from 'react'; +import { connect } from '../../data/connect'; +import { IonLoading } from '@ionic/react'; +import { GridLoader } from 'react-spinners'; + +interface OwnProps {} + +interface StateProps {} + +interface DispatchProps {} + +const override: CSSProperties = { + display: 'block', + margin: '0 auto', + borderColor: 'red', +}; + +function Loading() { + let [loading, setLoading] = useState(true); + let [color, setColor] = useState('#333333'); + + return ( + <> +
+
+ +
Loading...
+
+
+ + ); +} + +export default connect({ + mapStateToProps: (state) => ({}), + component: React.memo(Loading), +}); diff --git a/03_source/mobile.trunk.1/src/components/Menu.css b/03_source/mobile.trunk.1/src/components/Menu.css new file mode 100644 index 0000000..cee89be --- /dev/null +++ b/03_source/mobile.trunk.1/src/components/Menu.css @@ -0,0 +1,89 @@ +ion-menu ion-content { + --padding-top: 20px; + --padding-bottom: 20px; + + --background: var(--ion-item-background, var(--ion-background-color, #fff)); +} + +/* Remove background transitions for switching themes */ +ion-menu ion-item { + --transition: none; +} + +ion-item.selected { + --color: var(--ion-color-primary); +} + +/* + * Material Design Menu +*/ +ion-menu.md ion-list { + padding: 20px 0; +} + +ion-menu.md ion-list-header { + padding-left: 18px; + padding-right: 18px; + + text-transform: uppercase; + letter-spacing: 0.1em; + font-size: min(0.875rem, 32px); + font-weight: 450; +} + +ion-menu.md ion-item { + --padding-start: 18px; + + margin-right: 10px; + + border-radius: 0 50px 50px 0; + + font-weight: 500; +} + +ion-menu.md ion-item.selected { + --background: rgba(var(--ion-color-primary-rgb), 0.14); +} + +ion-menu.md ion-item.selected ion-icon { + color: var(--ion-color-primary); +} + +ion-menu.md ion-list-header, +ion-menu.md ion-item ion-icon { + color: var(--ion-color-step-650, #5f6368); +} + +ion-menu.md ion-list:not(:last-of-type) { + border-bottom: 1px solid var(--ion-color-step-150, #d7d8da); +} + + +/* + * iOS Menu +*/ +ion-menu.ios ion-list-header { + padding-left: 16px; + padding-right: 16px; + + margin-bottom: 8px; + font-size: clamp(22px, 1.375rem, 40px); +} + +ion-menu.ios ion-list { + padding: 20px 0 0; +} + +ion-menu.ios ion-item { + --padding-start: 16px; + --min-height: 50px; +} + +ion-menu.ios ion-item ion-icon { + font-size: 24px; + color: #73849a; +} + +ion-menu.ios ion-item.selected ion-icon { + color: var(--ion-color-primary); +} diff --git a/03_source/mobile.trunk.1/src/components/Menu.tsx b/03_source/mobile.trunk.1/src/components/Menu.tsx new file mode 100644 index 0000000..717b85e --- /dev/null +++ b/03_source/mobile.trunk.1/src/components/Menu.tsx @@ -0,0 +1,145 @@ +import React from 'react'; +import { RouteComponentProps, withRouter, useLocation } from 'react-router'; + +import { + IonContent, + IonIcon, + IonItem, + IonLabel, + IonList, + IonListHeader, + IonMenu, + IonMenuToggle, + IonToggle, +} from '@ionic/react'; +import { + calendarOutline, + hammer, + moonOutline, + help, + informationCircleOutline, + logIn, + logOut, + mapOutline, + peopleOutline, + person, + personAdd, +} from 'ionicons/icons'; + +import { connect } from '../data/connect'; +import { setDarkMode } from '../data/user/user.actions'; + +import './Menu.css'; + +const routes = { + appPages: [ + { title: 'Schedule', path: '/tabs/schedule', icon: calendarOutline }, + { title: 'Speakers', path: '/tabs/speakers', icon: peopleOutline }, + { title: 'Map', path: '/tabs/map', icon: mapOutline }, + { title: 'About', path: '/tabs/about', icon: informationCircleOutline }, + ], + loggedInPages: [ + { title: 'Account', path: '/account', icon: person }, + { title: 'Support', path: '/support', icon: help }, + { title: 'Logout', path: '/logout', icon: logOut }, + ], + loggedOutPages: [ + { title: 'Login', path: '/login', icon: logIn }, + { title: 'Support', path: '/support', icon: help }, + { title: 'Signup', path: '/signup', icon: personAdd }, + ], +}; + +interface Pages { + title: string; + path: string; + icon: string; + routerDirection?: string; +} +interface StateProps { + darkMode: boolean; + isAuthenticated: boolean; + menuEnabled: boolean; +} + +interface DispatchProps { + setDarkMode: typeof setDarkMode; +} + +interface MenuProps extends RouteComponentProps, StateProps, DispatchProps {} + +const Menu: React.FC = ({ + darkMode, + history, + isAuthenticated, + setDarkMode, + menuEnabled, +}) => { + const location = useLocation(); + + function renderlistItems(list: Pages[]) { + return list + .filter((route) => !!route.path) + .map((p) => ( + + + + {p.title} + + + )); + } + + return ( + + + + Conference + {renderlistItems(routes.appPages)} + + + Account + {isAuthenticated + ? renderlistItems(routes.loggedInPages) + : renderlistItems(routes.loggedOutPages)} + + + setDarkMode(!darkMode)}> + Dark Mode + + + + + Tutorial + { + history.push('/tutorial'); + }} + > + + Show Tutorial + + + + + ); +}; + +export default connect<{}, StateProps, {}>({ + mapStateToProps: (state) => ({ + darkMode: state.user.darkMode, + isAuthenticated: state.user.isLoggedin, + menuEnabled: state.data.menuEnabled, + }), + mapDispatchToProps: { + setDarkMode, + }, + component: withRouter(Menu), +}); diff --git a/03_source/mobile.trunk.1/src/components/NoSavedEvents/alert-svgrepo-com.svg b/03_source/mobile.trunk.1/src/components/NoSavedEvents/alert-svgrepo-com.svg new file mode 100644 index 0000000..4a975fe --- /dev/null +++ b/03_source/mobile.trunk.1/src/components/NoSavedEvents/alert-svgrepo-com.svg @@ -0,0 +1,17 @@ + + + + + alert + Created with Sketch Beta. + + + + + + + + + + + \ No newline at end of file diff --git a/03_source/mobile.trunk.1/src/components/NoSavedEvents/index.tsx b/03_source/mobile.trunk.1/src/components/NoSavedEvents/index.tsx new file mode 100644 index 0000000..2b8a86e --- /dev/null +++ b/03_source/mobile.trunk.1/src/components/NoSavedEvents/index.tsx @@ -0,0 +1,78 @@ +// REQ0088/favorite-event-empty + +import React, { CSSProperties, useState } from 'react'; +import { connect } from '../../data/connect'; +import { IonIcon, IonLoading } from '@ionic/react'; +import { GridLoader } from 'react-spinners'; +import { alertCircleOutline, alertOutline } from 'ionicons/icons'; +// import AlertSvg from './alert-svgrepo-com.svg'; + +interface OwnProps {} + +interface StateProps {} + +interface DispatchProps {} + +const override: CSSProperties = { + display: 'block', + margin: '0 auto', + borderColor: 'red', +}; + +function NoSavedEvents() { + let [loading, setLoading] = useState(true); + let [color, setColor] = useState('#333333'); + + return ( + <> +
+
+
+ +
+
You have not saved yet
+
+ To add your favourites,
+ simply take a look here +
+
+
+
+ + ); +} + +export default connect({ + mapStateToProps: (state) => ({}), + component: React.memo(NoSavedEvents), +}); diff --git a/03_source/mobile.trunk.1/src/components/RedirectToLogin.tsx b/03_source/mobile.trunk.1/src/components/RedirectToLogin.tsx new file mode 100644 index 0000000..ca86a65 --- /dev/null +++ b/03_source/mobile.trunk.1/src/components/RedirectToLogin.tsx @@ -0,0 +1,19 @@ +import React, { useEffect, useContext } from 'react'; +import { IonRouterContext } from '@ionic/react'; + +interface RedirectToLoginProps { + setIsLoggedIn: Function; + setUsername: Function; +} + +const RedirectToLogin: React.FC = ({ setIsLoggedIn, setUsername }) => { + const ionRouterContext = useContext(IonRouterContext); + useEffect(() => { + setIsLoggedIn(false); + setUsername(undefined); + ionRouterContext.push('/tabs/events'); + }, [setIsLoggedIn, setUsername]); + return null; +}; + +export default RedirectToLogin; diff --git a/03_source/mobile.trunk.1/src/components/SessionList.tsx b/03_source/mobile.trunk.1/src/components/SessionList.tsx new file mode 100644 index 0000000..ac8f9fd --- /dev/null +++ b/03_source/mobile.trunk.1/src/components/SessionList.tsx @@ -0,0 +1,110 @@ +import { + IonItemDivider, + IonItemGroup, + IonLabel, + IonList, + IonListHeader, + IonAlert, + AlertButton, +} from '@ionic/react'; +import React, { useState, useCallback, useRef, useEffect } from 'react'; +import { Schedule, Session } from '../models/Schedule'; +import SessionListItem from './SessionListItem'; +import { connect } from '../data/connect'; +import { addFavorite, removeFavorite } from '../data/sessions/sessions.actions'; + +interface OwnProps { + schedule: Schedule; + listType: 'all' | 'favorites'; + hide: boolean; +} + +interface StateProps { + favoriteSessions: number[]; +} + +interface DispatchProps { + addFavorite: typeof addFavorite; + removeFavorite: typeof removeFavorite; +} + +interface SessionListProps extends OwnProps, StateProps, DispatchProps {} + +const SessionList: React.FC = ({ + addFavorite, + removeFavorite, + favoriteSessions, + hide, + schedule, + listType, +}) => { + const scheduleListRef = useRef(null); + const [showAlert, setShowAlert] = useState(false); + const [alertHeader, setAlertHeader] = useState(''); + const [alertMessage, setAlertMessage] = useState(''); + const [alertButtons, setAlertButtons] = useState<(AlertButton | string)[]>([]); + + const handleShowAlert = useCallback((header: string, message: string, buttons: AlertButton[]) => { + setAlertHeader(header); + setAlertMessage(message); + setAlertButtons(buttons); + setShowAlert(true); + }, []); + + useEffect(() => { + if (scheduleListRef.current) { + scheduleListRef.current.closeSlidingItems(); + } + }, [hide]); + + if (schedule.groups.length === 0 && !hide) { + return ( + + No Sessions Found + + ); + } + + return ( + <> + + {schedule.groups.map((group, index: number) => ( + + + {group.time} + + {group.sessions.map((session: Session, sessionIndex: number) => ( + -1} + onAddFavorite={addFavorite} + onRemoveFavorite={removeFavorite} + key={`group-${index}-${sessionIndex}`} + session={session} + listType={listType} + /> + ))} + + ))} + + setShowAlert(false)} + > + + ); +}; + +export default connect({ + mapStateToProps: (state) => ({ + favoriteSessions: state.data.favorites, + }), + mapDispatchToProps: { + addFavorite, + removeFavorite, + }, + component: SessionList, +}); diff --git a/03_source/mobile.trunk.1/src/components/SessionListFilter.css b/03_source/mobile.trunk.1/src/components/SessionListFilter.css new file mode 100644 index 0000000..4516c7a --- /dev/null +++ b/03_source/mobile.trunk.1/src/components/SessionListFilter.css @@ -0,0 +1,31 @@ +/* + * Material Design + */ + +.md .session-list-filter ion-toolbar ion-button { + text-transform: capitalize; + letter-spacing: 0; +} + +.md .session-list-filter ion-checkbox { + --checkbox-background-checked: transparent; + --border-color: transparent; + --border-color-checked: transparent; + --checkmark-color: var(--ion-color-primary); +} + +.md .session-list-filter ion-list { + background: inherit; +} + +/* + * iOS + */ + +.ios .session-list-filter ion-list-header { + margin-top: 10px; +} + +.ios .session-list-filter ion-checkbox { + color: var(--ion-color-primary); +} diff --git a/03_source/mobile.trunk.1/src/components/SessionListFilter.tsx b/03_source/mobile.trunk.1/src/components/SessionListFilter.tsx new file mode 100644 index 0000000..b6345c1 --- /dev/null +++ b/03_source/mobile.trunk.1/src/components/SessionListFilter.tsx @@ -0,0 +1,155 @@ +import React from 'react'; + +import { getMode } from '@ionic/core'; +import { + IonHeader, + IonToolbar, + IonButtons, + IonButton, + IonTitle, + IonContent, + IonList, + IonListHeader, + IonItem, + IonLabel, + IonCheckbox, + IonFooter, + IonIcon, +} from '@ionic/react'; +import { + logoReact, + call, + document, + logoIonic, + hammer, + restaurant, + cog, + colorPalette, + construct, + compass, +} from 'ionicons/icons'; + +import './SessionListFilter.css'; + +import { connect } from '../data/connect'; +import { updateFilteredTracks } from '../data/sessions/sessions.actions'; + +interface OwnProps { + onDismissModal: () => void; +} + +interface StateProps { + allTracks: string[]; + filteredTracks: string[]; +} + +interface DispatchProps { + updateFilteredTracks: typeof updateFilteredTracks; +} + +type SessionListFilterProps = OwnProps & StateProps & DispatchProps; + +const SessionListFilter: React.FC = ({ + allTracks, + filteredTracks, + onDismissModal, + updateFilteredTracks, +}) => { + const ios = getMode() === 'ios'; + + const toggleTrackFilter = (track: string) => { + if (filteredTracks.indexOf(track) > -1) { + updateFilteredTracks(filteredTracks.filter((x) => x !== track)); + } else { + updateFilteredTracks([...filteredTracks, track]); + } + }; + + const handleDeselectAll = () => { + updateFilteredTracks([]); + }; + + const handleSelectAll = () => { + updateFilteredTracks([...allTracks]); + }; + + const iconMap: { [key: string]: any } = { + React: logoReact, + Documentation: document, + Food: restaurant, + Ionic: logoIonic, + Tooling: hammer, + Design: colorPalette, + Services: cog, + Workshop: construct, + Navigation: compass, + Communication: call, + }; + + return ( + <> + + + + {ios && Cancel} + {!ios && Reset} + + + Filter Sessions + + + + Done + + + + + + + + Tracks + + {allTracks.map((track) => ( + + {ios && ( + + ))} + + + + {ios && ( + + + + Deselect All + + + Select All + + + + )} + + ); +}; + +export default connect({ + mapStateToProps: (state) => ({ + allTracks: state.data.allTracks, + filteredTracks: state.data.filteredTracks, + }), + mapDispatchToProps: { + updateFilteredTracks, + }, + component: SessionListFilter, +}); diff --git a/03_source/mobile.trunk.1/src/components/SessionListItem.tsx b/03_source/mobile.trunk.1/src/components/SessionListItem.tsx new file mode 100644 index 0000000..32b3b25 --- /dev/null +++ b/03_source/mobile.trunk.1/src/components/SessionListItem.tsx @@ -0,0 +1,106 @@ +import React, { useRef } from 'react'; +import { + IonItemSliding, + IonItem, + IonLabel, + IonItemOptions, + IonItemOption, + AlertButton, + useIonToast, +} from '@ionic/react'; +import { Session } from '../models/Schedule'; + +interface SessionListItemProps { + session: Session; + listType: 'all' | 'favorites'; + onAddFavorite: (id: number) => void; + onRemoveFavorite: (id: number) => void; + onShowAlert: (header: string, message: string, buttons: AlertButton[]) => void; + isFavorite: boolean; +} + +const SessionListItem: React.FC = ({ + isFavorite, + onAddFavorite, + onRemoveFavorite, + onShowAlert, + session, + listType, +}) => { + const [presentToast] = useIonToast(); + const ionItemSlidingRef = useRef(null); + + const dismissAlert = () => { + ionItemSlidingRef.current && ionItemSlidingRef.current.close(); + }; + + const removeFavoriteSession = (title: string) => { + onAddFavorite(session.id); + onShowAlert(title, 'Would you like to remove this session from your favorites?', [ + { + text: 'Cancel', + handler: dismissAlert, + }, + { + text: 'Remove', + handler: () => { + onRemoveFavorite(session.id); + dismissAlert(); + }, + }, + ]); + }; + + const addFavoriteSession = async () => { + if (isFavorite) { + // Prompt to remove favorite + removeFavoriteSession('Favorite already added'); + } else { + // Add as a favorite + onAddFavorite(session.id); + + // Close the open item + ionItemSlidingRef.current && ionItemSlidingRef.current.close(); + + // Create a toast + presentToast({ + message: `${session.name} was successfully added as a favorite.`, + duration: 3000, + buttons: [ + { + text: 'Close', + role: 'cancel', + }, + ], + }); + } + }; + + return ( + + + +

{session.name}

+

+ {session.timeStart} —  + {session.timeEnd}:  + {session.location} +

+
+
+ + {listType === 'favorites' ? ( + removeFavoriteSession('Remove Favorite')}> + Remove + + ) : ( + + Favorite + + )} + +
+ ); +}; + +export default React.memo(SessionListItem); diff --git a/03_source/mobile.trunk.1/src/components/ShareSocialFab.tsx b/03_source/mobile.trunk.1/src/components/ShareSocialFab.tsx new file mode 100644 index 0000000..210c66e --- /dev/null +++ b/03_source/mobile.trunk.1/src/components/ShareSocialFab.tsx @@ -0,0 +1,46 @@ +import { IonLoading, IonFab, IonFabButton, IonIcon, IonFabList } from '@ionic/react'; +import { shareSocial, logoVimeo, logoInstagram, logoTwitter, logoFacebook } from 'ionicons/icons'; +import React, { useState } from 'react'; + +const ShareSocialFab: React.FC = () => { + const [loadingMessage, setLoadingMessage] = useState(''); + const [showLoading, setShowLoading] = useState(false); + + const openSocial = (network: string) => { + setLoadingMessage(`Posting to ${network}`); + setShowLoading(true); + }; + + return ( + <> + setShowLoading(false)} + /> + + + + + + openSocial('Vimeo')}> + + + openSocial('Instagram')}> + + + openSocial('Twitter')}> + + + openSocial('Facebook')}> + + + + + + ); +}; + +export default ShareSocialFab; diff --git a/03_source/mobile.trunk.1/src/components/SpeakerItem.tsx b/03_source/mobile.trunk.1/src/components/SpeakerItem.tsx new file mode 100644 index 0000000..242a9df --- /dev/null +++ b/03_source/mobile.trunk.1/src/components/SpeakerItem.tsx @@ -0,0 +1,66 @@ +import React from 'react'; +import { Session } from '../models/Schedule'; +import { Speaker } from '../models/Speaker'; +import { + IonCard, + IonCardHeader, + IonItem, + IonLabel, + IonAvatar, + IonCardContent, + IonList, +} from '@ionic/react'; + +interface SpeakerItemProps { + speaker: Speaker; + sessions: Session[]; +} + +const SpeakerItem: React.FC = ({ speaker, sessions }) => { + return ( + <> + + + + + Speaker profile pic + + +

{speaker.name}

+

{speaker.title}

+
+
+
+ + + + {sessions.map((session) => ( + + +

{session.name}

+
+
+ ))} + + +

About {speaker.name}

+
+
+
+
+
+ + ); +}; + +export default SpeakerItem; diff --git a/03_source/mobile.trunk.1/src/constants.ts b/03_source/mobile.trunk.1/src/constants.ts new file mode 100644 index 0000000..b6f26ac --- /dev/null +++ b/03_source/mobile.trunk.1/src/constants.ts @@ -0,0 +1,3 @@ +const constants = { API_ENDPOINT: import.meta.env.VITE_API_ENDPOINT }; + +export default constants; diff --git a/03_source/mobile/src/pages/DemoClubHouse/AppPages/Tab3.css b/03_source/mobile.trunk.1/src/context/action.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoClubHouse/AppPages/Tab3.css rename to 03_source/mobile.trunk.1/src/context/action.tsx diff --git a/03_source/mobile/src/pages/DemoReactDrawingCanvas/theme/variables.scss b/03_source/mobile.trunk.1/src/context/index.ts similarity index 100% rename from 03_source/mobile/src/pages/DemoReactDrawingCanvas/theme/variables.scss rename to 03_source/mobile.trunk.1/src/context/index.ts diff --git a/03_source/mobile.trunk.1/src/context/jwt/action.tsx b/03_source/mobile.trunk.1/src/context/jwt/action.tsx new file mode 100644 index 0000000..e091237 --- /dev/null +++ b/03_source/mobile.trunk.1/src/context/jwt/action.tsx @@ -0,0 +1,92 @@ +import axios, { endpoints } from '../../lib/axios'; + +import { setSession } from './utils'; +import { JWT_STORAGE_KEY } from './constant'; + +// ---------------------------------------------------------------------- + +export type SignInParams = { + email: string; + password: string; +}; + +export type SignUpParams = { + email: string; + password: string; + firstName: string; + lastName: string; +}; + +const ERR_ACCESS_TOKEN_NOT_FOUND = `Access token not found in response`; + +/** ************************************** + * Sign in + *************************************** */ +export const signInWithPassword = async ({ + email, + password, +}: SignInParams): Promise => { + try { + const params = { email, password }; + + const res = await axios.post(endpoints.auth.signIn, params); + + const { accessToken } = res.data; + + console.log({ t: res.data }); + + if (!accessToken) { + throw new Error(ERR_ACCESS_TOKEN_NOT_FOUND); + } + + // setSession(accessToken); + return accessToken; + } catch (error) { + console.error('Error during sign in:', error); + throw error; + } +}; + +/** ************************************** + * Sign up + *************************************** */ +export const signUp = async ({ + email, + password, + firstName, + lastName, +}: SignUpParams): Promise => { + const params = { + email, + password, + firstName, + lastName, + }; + + try { + const res = await axios.post(endpoints.auth.signUp, params); + + const { accessToken } = res.data; + + if (!accessToken) { + throw new Error('Access token not found in response'); + } + + sessionStorage.setItem(JWT_STORAGE_KEY, accessToken); + } catch (error) { + console.error('Error during sign up:', error); + throw error; + } +}; + +/** ************************************** + * Sign out + *************************************** */ +export const signOut = async (): Promise => { + try { + await setSession(null); + } catch (error) { + console.error('Error during sign out:', error); + throw error; + } +}; diff --git a/03_source/mobile.trunk.1/src/context/jwt/constant.ts b/03_source/mobile.trunk.1/src/context/jwt/constant.ts new file mode 100644 index 0000000..c9cb827 --- /dev/null +++ b/03_source/mobile.trunk.1/src/context/jwt/constant.ts @@ -0,0 +1 @@ +export const JWT_STORAGE_KEY = 'jwt_access_token'; diff --git a/03_source/mobile.trunk.1/src/context/jwt/utils.tsx b/03_source/mobile.trunk.1/src/context/jwt/utils.tsx new file mode 100644 index 0000000..6bdb963 --- /dev/null +++ b/03_source/mobile.trunk.1/src/context/jwt/utils.tsx @@ -0,0 +1,97 @@ +// import { paths } from 'src/routes/paths'; + +import axios from '../../lib/axios'; + +import { JWT_STORAGE_KEY } from './constant.js'; +import PATHS from '../../PATHS.js'; + +// ---------------------------------------------------------------------- + +export function jwtDecode(token: string) { + try { + if (!token) return null; + + const parts = token.split('.'); + if (parts.length < 2) { + throw new Error('Invalid token!'); + } + + const base64Url = parts[1]; + const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); + const decoded = JSON.parse(atob(base64)); + + return decoded; + } catch (error) { + console.error('Error decoding token:', error); + throw error; + } +} + +// ---------------------------------------------------------------------- + +export function isValidToken(accessToken: string) { + if (!accessToken) { + return false; + } + + try { + const decoded = jwtDecode(accessToken); + + if (!decoded || !('exp' in decoded)) { + return false; + } + + const currentTime = Date.now() / 1000; + + return decoded.exp > currentTime; + } catch (error) { + console.error('Error during token validation:', error); + return false; + } +} + +// ---------------------------------------------------------------------- + +export function tokenExpired(exp: number) { + const currentTime = Date.now(); + const timeLeft = exp * 1000 - currentTime; + + setTimeout(() => { + try { + alert('Token expired!'); + sessionStorage.removeItem(JWT_STORAGE_KEY); + window.location.href = PATHS.SIGN_IN; + } catch (error) { + console.error('Error during token expiration:', error); + throw error; + } + }, timeLeft); +} + +// ---------------------------------------------------------------------- + +const INVALID_ACCESS_TOKEN = 'Invalid access token!'; + +export async function setSession(accessToken: string | null) { + try { + if (accessToken) { + sessionStorage.setItem(JWT_STORAGE_KEY, accessToken); + + axios.defaults.headers.common.Authorization = `Bearer ${accessToken}`; + + const decodedToken = jwtDecode(accessToken); // ~3 days by minimals server + + if (decodedToken && 'exp' in decodedToken) { + tokenExpired(decodedToken.exp); + } else { + throw new Error(INVALID_ACCESS_TOKEN); + } + } else { + sessionStorage.removeItem(JWT_STORAGE_KEY); + delete axios.defaults.headers.common.Authorization; + } + } catch (error) { + console.error('Error during set session:', error); + throw error; + } +} diff --git a/03_source/mobile.trunk.1/src/data/AppContext.tsx b/03_source/mobile.trunk.1/src/data/AppContext.tsx new file mode 100644 index 0000000..d1375ad --- /dev/null +++ b/03_source/mobile.trunk.1/src/data/AppContext.tsx @@ -0,0 +1,27 @@ +import React, { createContext, PropsWithChildren, useReducer } from 'react'; +import { initialState, AppState, reducers } from './state'; + +export interface AppContextState { + state: AppState; + dispatch: React.Dispatch; +} + +export const AppContext = createContext({ + state: initialState, + dispatch: () => undefined, +}); + +export const AppContextProvider: React.FC = ({ children }) => { + const [store, dispatch] = useReducer(reducers, initialState); + + return ( + + {children} + + ); +}; diff --git a/03_source/mobile.trunk.1/src/data/combineReducers.ts b/03_source/mobile.trunk.1/src/data/combineReducers.ts new file mode 100644 index 0000000..44769b0 --- /dev/null +++ b/03_source/mobile.trunk.1/src/data/combineReducers.ts @@ -0,0 +1,18 @@ +interface R { + [key: string]: (...args: any) => any; +} + +export function combineReducers(reducers: R) { + type keys = keyof typeof reducers; + type returnType = { [K in keys]: ReturnType<(typeof reducers)[K]> }; + const combinedReducer = (state: any, action: any) => { + const newState: returnType = {} as any; + const keys = Object.keys(reducers); + keys.forEach((key) => { + const result = reducers[key](state[key], action); + newState[key as keys] = result || state[key]; + }); + return newState; + }; + return combinedReducer; +} diff --git a/03_source/mobile.trunk.1/src/data/connect.tsx b/03_source/mobile.trunk.1/src/data/connect.tsx new file mode 100644 index 0000000..8d6a24a --- /dev/null +++ b/03_source/mobile.trunk.1/src/data/connect.tsx @@ -0,0 +1,55 @@ +import React, { useContext, useMemo } from 'react'; +import { AppContext } from './AppContext'; +import { DispatchObject } from '../util/types'; +import { AppState } from './state'; + +interface ConnectParams { + mapStateToProps?: (state: AppState, props: TOwnProps) => TStateProps; + mapDispatchToProps?: TDispatchProps; + component: React.ComponentType; +} + +export function connect({ + mapStateToProps = () => ({}) as TStateProps, + mapDispatchToProps = {} as TDispatchProps, + component, +}: ConnectParams): React.FunctionComponent { + const Connect = (ownProps: TOwnProps) => { + const context = useContext(AppContext); + + const dispatchFuncs = useMemo(() => { + const dispatchFuncs: { [key: string]: any } = {}; + if (mapDispatchToProps) { + Object.keys(mapDispatchToProps).forEach((key) => { + const oldFunc = (mapDispatchToProps as any)[key]; + const newFunc = (...args: any) => { + const dispatchFunc = oldFunc(...args); + if (typeof dispatchFunc === 'object') { + context.dispatch(dispatchFunc); + } else { + const result = dispatchFunc(context.dispatch); + if (typeof result === 'object' && result.then) { + result.then((dispatchObject?: DispatchObject) => { + if (dispatchObject && dispatchObject.type) { + context.dispatch(dispatchObject); + } + }); + } + } + }; + dispatchFuncs[key] = newFunc; + }); + } + return dispatchFuncs; + // eslint-disable-next-line + }, [mapDispatchToProps]); + + const props = useMemo(() => { + return Object.assign({}, ownProps, mapStateToProps(context.state, ownProps), dispatchFuncs); + // eslint-disable-next-line + }, [ownProps, context.state]); + + return React.createElement(component, props); + }; + return React.memo(Connect as any); +} diff --git a/03_source/mobile.trunk.1/src/data/dataApi.ts b/03_source/mobile.trunk.1/src/data/dataApi.ts new file mode 100644 index 0000000..e08558d --- /dev/null +++ b/03_source/mobile.trunk.1/src/data/dataApi.ts @@ -0,0 +1,151 @@ +import { GetResult, Preferences as Storage } from '@capacitor/preferences'; +import { Schedule, Session } from '../models/Schedule'; +import { Speaker } from '../models/Speaker'; +import { Location } from '../models/Location'; +import axios from 'axios'; +import constants from '../constants'; +import { IOrderItem } from '../models/Order'; +import { Event } from '../models/Event'; + +const dataUrl = '/assets/data/data.json'; +const locationsUrl = '/assets/data/locations.json'; + +const HAS_LOGGED_IN = 'hasLoggedIn'; +const HAS_SEEN_TUTORIAL = 'hasSeenTutorial'; +const USERNAME = 'username'; +const ACCESS_TOKEN = 'a_token'; +const ACTIVE_SESSION = 'a_session'; + +export const getConfData = async () => { + const response = await Promise.all([ + fetch(dataUrl), + fetch(locationsUrl), + fetch(`${constants.API_ENDPOINT}/api/order/list`), + fetch(`${constants.API_ENDPOINT}/api/event/list`), + // axios.get(`${constants.API_ENDPOINT}/v1/events`), + // axios.get(`${constants.API_ENDPOINT}/v1/members`), + // + ]); + + const responseData = await response[0].json(); + const schedule = responseData.schedule[0] as Schedule; + const sessions = parseSessions(schedule); + const speakers = responseData.speakers as Speaker[]; + const locations = (await response[1].json()) as Location[]; + const allTracks = sessions + .reduce((all, session) => all.concat(session.tracks), [] as string[]) + .filter((trackName, index, array) => array.indexOf(trackName) === index) + .sort(); + + // const events = response[2].data; + // const nearByMembers = response[3].data; + + // TODO: update this due to not use axios anymore + // the data object is not available + // const orders = response[2].data.orders as IOrderItem[]; + // const events = response[3].data.events as Event[]; + const orderResponse = response[2]; + let orders = { + result: { status: orderResponse.status, ok: orderResponse.ok }, + data: [], + }; + if (orderResponse.status == 200) { + orders = { ...orders, data: await orderResponse.json() }; + } + + const eventResponse = response[3]; + let events = { + result: { status: eventResponse.status, ok: eventResponse.ok }, + data: [], + }; + if (eventResponse.status == 200) { + events = { ...events, data: await eventResponse.json() }; + } + + const nearByMembers = []; + + const data = { + schedule, + sessions, + locations, + speakers, + allTracks, + filteredTracks: [...allTracks], + // + events, + // nearByMembers, + orders, + hello: 'world', + // + }; + + return data; +}; + +export const getUserData = async () => { + const response = await Promise.all([ + Storage.get({ key: HAS_LOGGED_IN }), + Storage.get({ key: HAS_SEEN_TUTORIAL }), + Storage.get({ key: USERNAME }), + ]); + const isLoggedin = (await response[0].value) === 'true'; + const hasSeenTutorial = (await response[1].value) === 'true'; + const username = (await response[2].value) || undefined; + const data = { + isLoggedin, + hasSeenTutorial, + username, + }; + return data; +}; + +export const setIsLoggedInData = async (isLoggedIn: boolean) => { + await Storage.set({ key: HAS_LOGGED_IN, value: JSON.stringify(isLoggedIn) }); +}; + +export const setHasSeenTutorialData = async (hasSeenTutorial: boolean) => { + await Storage.set({ + key: HAS_SEEN_TUTORIAL, + value: JSON.stringify(hasSeenTutorial), + }); +}; + +export const setUsernameData = async (username?: string) => { + if (!username) { + await Storage.remove({ key: USERNAME }); + } else { + await Storage.set({ key: USERNAME, value: username }); + } +}; + +export const setAccessTokenData = async (accessToken?: string) => { + if (!accessToken) { + await Storage.remove({ key: ACCESS_TOKEN }); + } else { + await Storage.set({ key: ACCESS_TOKEN, value: accessToken }); + } +}; + +export const getAccessTokenData = async (): Promise => { + return Storage.get({ key: ACCESS_TOKEN }); +}; + +function parseSessions(schedule: Schedule) { + const sessions: Session[] = []; + schedule.groups.forEach((g) => { + g.sessions.forEach((s) => sessions.push(s)); + }); + return sessions; +} + +export const setActiveSessionData = async (activeSession: any) => { + if (!activeSession) { + await Storage.remove({ key: ACTIVE_SESSION }); + } else { + await Storage.set({ key: ACTIVE_SESSION, value: JSON.stringify(activeSession) }); + } +}; + +export const getActiveSessionData = async (): Promise => { + return Storage.get({ key: JSON.parse(ACTIVE_SESSION) }); +}; diff --git a/03_source/mobile.trunk.1/src/data/locations/locations.actions.ts b/03_source/mobile.trunk.1/src/data/locations/locations.actions.ts new file mode 100644 index 0000000..62071b4 --- /dev/null +++ b/03_source/mobile.trunk.1/src/data/locations/locations.actions.ts @@ -0,0 +1,31 @@ +import { DispatchObject } from '../../util/types'; +import { Location } from '../../models/Location'; + +export const SET_LOCATIONS = 'SET_LOCATIONS'; + +export const setLocations = (locations: Location[]): DispatchObject => ({ + type: SET_LOCATIONS, + payload: locations, +}); + +export const loadLocations = () => async (dispatch: (action: DispatchObject) => void) => { + try { + const response = await fetch('/assets/data/locations.json'); + const locations = await response.json(); + dispatch(setLocations(locations)); + } catch (error) { + console.error('Error loading locations:', error); + // Set default locations if data cannot be loaded + dispatch( + setLocations([ + { + id: 1, + name: 'Monona Terrace Convention Center', + lat: 43.071584, + lng: -89.38012, + center: true, + }, + ]) + ); + } +}; diff --git a/03_source/mobile.trunk.1/src/data/locations/locations.reducer.ts b/03_source/mobile.trunk.1/src/data/locations/locations.reducer.ts new file mode 100644 index 0000000..87cbf78 --- /dev/null +++ b/03_source/mobile.trunk.1/src/data/locations/locations.reducer.ts @@ -0,0 +1,17 @@ +import { LocationState, initialState } from '../../models/Location'; +import { SET_LOCATIONS } from './locations.actions'; + +export const locationsReducer = ( + state: LocationState = initialState, + action: { type: string; payload: any } +): LocationState => { + switch (action.type) { + case SET_LOCATIONS: + return { + ...state, + locations: action.payload, + }; + default: + return state; + } +}; diff --git a/03_source/mobile.trunk.1/src/data/selectors.ts b/03_source/mobile.trunk.1/src/data/selectors.ts new file mode 100644 index 0000000..1b06acc --- /dev/null +++ b/03_source/mobile.trunk.1/src/data/selectors.ts @@ -0,0 +1,197 @@ +import { createSelector } from 'reselect'; +import { Schedule, Session, ScheduleGroup } from '../models/Schedule'; +import { Speaker } from '../models/Speaker'; +import { Location } from '../models/Location'; + +import { AppState } from './state'; +import { IOrderItem } from '../models/Order'; +import { Event } from '../models/Event'; + +const getSchedule = (state: AppState) => { + return state.data.schedule; +}; + +export const getSpeakers = (state: AppState) => state.data.speakers; + +const getSessions = (state: AppState) => state.data.sessions; +const getFilteredTracks = (state: AppState) => state.data.filteredTracks; +const getFavoriteIds = (state: AppState) => state.data.favorites; +const getSearchText = (state: AppState) => state.data.searchText; + +export const getEvents = (state: AppState) => { + return state.data.events; +}; + +export const getNearbyMembers = (state: AppState) => state.data.nearByMembers; + +export const getOrders = (state: AppState) => { + return state.data.orders; +}; + +export const getFilteredSchedule = createSelector( + getSchedule, + getFilteredTracks, + (schedule, filteredTracks) => { + const groups: ScheduleGroup[] = []; + + // Helper function to convert 12-hour time to 24-hour time for proper sorting + const convertTo24Hour = (timeStr: string) => { + const [time, period] = timeStr.toLowerCase().split(' '); + let [hours, minutes] = time.split(':').map(Number); + + if (period === 'pm' && hours !== 12) { + hours += 12; + } else if (period === 'am' && hours === 12) { + hours = 0; + } + + return `${hours.toString().padStart(2, '0')}:${minutes || '00'}`; + }; + + // Sort the groups by time + const sortedGroups = [...schedule.groups].sort((a, b) => { + const timeA = convertTo24Hour(a.time); + const timeB = convertTo24Hour(b.time); + return timeA.localeCompare(timeB); + }); + + sortedGroups.forEach((group: ScheduleGroup) => { + const sessions: Session[] = []; + group.sessions.forEach((session) => { + session.tracks.forEach((track) => { + if (filteredTracks.indexOf(track) > -1) { + sessions.push(session); + } + }); + }); + + if (sessions.length) { + // Sort sessions within each group by start time + const sortedSessions = sessions.sort((a, b) => { + const timeA = convertTo24Hour(a.timeStart); + const timeB = convertTo24Hour(b.timeStart); + return timeA.localeCompare(timeB); + }); + + const groupToAdd: ScheduleGroup = { + time: group.time, + sessions: sortedSessions, + }; + groups.push(groupToAdd); + } + }); + + return { + date: schedule.date, + groups, + } as Schedule; + } +); + +export const getSearchedSchedule = createSelector( + getFilteredSchedule, + getSearchText, + (schedule, searchText) => { + if (!searchText) { + return schedule; + } + const groups: ScheduleGroup[] = []; + schedule.groups.forEach((group) => { + const sessions = group.sessions.filter( + (s) => s.name.toLowerCase().indexOf(searchText.toLowerCase()) > -1 + ); + if (sessions.length) { + const groupToAdd: ScheduleGroup = { + time: group.time, + sessions, + }; + groups.push(groupToAdd); + } + }); + return { + date: schedule.date, + groups, + } as Schedule; + } +); + +export const getScheduleList = createSelector(getSearchedSchedule, (schedule) => schedule); + +export const getGroupedFavorites = createSelector( + getScheduleList, + getFavoriteIds, + (schedule, favoriteIds) => { + const groups: ScheduleGroup[] = []; + schedule.groups.forEach((group) => { + const sessions = group.sessions.filter((s) => favoriteIds.indexOf(s.id) > -1); + if (sessions.length) { + const groupToAdd: ScheduleGroup = { + time: group.time, + sessions, + }; + groups.push(groupToAdd); + } + }); + return { + date: schedule.date, + groups, + } as Schedule; + } +); + +const getIdParam = (_state: AppState, props: any) => { + return props.match.params['id']; +}; + +export const getSession = createSelector(getSessions, getIdParam, (sessions, id) => { + return sessions.find((s: Session) => s.id === id); +}); + +export const getSpeaker = createSelector(getSpeakers, getIdParam, (speakers, id) => + speakers.find((x: Speaker) => x.id === id) +); + +export const getEvent = createSelector(getEvents, getIdParam, (data_events, id) => { + const { + data: { events }, + } = data_events; + + return events.find((x: Event) => x.id === id); +}); + +export const getOrder = createSelector(getOrders, getIdParam, (data_orders, id) => { + const { + data: { orders }, + } = data_orders; + + return orders.find((x: IOrderItem) => x.id === id); +}); + +export const getSpeakerSessions = createSelector(getSessions, (sessions) => { + const speakerSessions: { [key: string]: Session[] } = {}; + + sessions.forEach((session: Session) => { + session.speakerNames && + session.speakerNames.forEach((name) => { + if (speakerSessions[name]) { + speakerSessions[name].push(session); + } else { + speakerSessions[name] = [session]; + } + }); + }); + return speakerSessions; +}); + +export const mapCenter = (state: AppState) => { + const item = state.data.locations.find((l: Location) => l.id === state.data.mapCenterId); + if (item == null) { + return { + id: 1, + name: 'Map Center', + lat: 43.071584, + lng: -89.38012, + }; + } + return item; +}; diff --git a/03_source/mobile.trunk.1/src/data/sessions/conf.state.ts b/03_source/mobile.trunk.1/src/data/sessions/conf.state.ts new file mode 100644 index 0000000..94d5c80 --- /dev/null +++ b/03_source/mobile.trunk.1/src/data/sessions/conf.state.ts @@ -0,0 +1,23 @@ +import { Location } from '../../models/Location'; +import { Speaker } from '../../models/Speaker'; +import { Schedule, Session } from '../../models/Schedule'; +// +import { Event } from '../../models/Event'; +import { IOrderItem } from '../../models/Order'; + +export interface ConfState { + schedule: Schedule; + sessions: Session[]; + speakers: Speaker[]; + favorites: number[]; + locations: Location[]; + filteredTracks: string[]; + searchText?: string; + mapCenterId?: number; + loading?: boolean; + allTracks: string[]; + menuEnabled: boolean; + // + events: Event[]; + orders: IOrderItem[]; +} diff --git a/03_source/mobile.trunk.1/src/data/sessions/orders.actions.ts b/03_source/mobile.trunk.1/src/data/sessions/orders.actions.ts new file mode 100644 index 0000000..38fec72 --- /dev/null +++ b/03_source/mobile.trunk.1/src/data/sessions/orders.actions.ts @@ -0,0 +1,61 @@ +import { getConfData } from '../dataApi'; +import { ActionType } from '../../util/types'; +import { ConfState } from './conf.state'; + +export const loadConfData = () => async (dispatch: React.Dispatch) => { + dispatch(setLoading(true)); + const data = await getConfData(); + dispatch(setData(data)); + dispatch(setLoading(false)); +}; + +export const setLoading = (isLoading: boolean) => + ({ + type: 'set-conf-loading', + isLoading, + }) as const; + +export const setData = (data: Partial) => + ({ + type: 'set-conf-data', + data, + }) as const; + +export const addFavorite = (sessionId: number) => + ({ + type: 'add-favorite', + sessionId, + }) as const; + +export const removeFavorite = (sessionId: number) => + ({ + type: 'remove-favorite', + sessionId, + }) as const; + +export const updateFilteredTracks = (filteredTracks: string[]) => + ({ + type: 'update-filtered-tracks', + filteredTracks, + }) as const; + +export const setSearchText = (searchText?: string) => + ({ + type: 'set-search-text', + searchText, + }) as const; + +export const setMenuEnabled = (menuEnabled: boolean) => + ({ + type: 'set-menu-enabled', + menuEnabled, + }) as const; + +export type OrdersActions = + | ActionType + | ActionType + | ActionType + | ActionType + | ActionType + | ActionType + | ActionType; diff --git a/03_source/mobile.trunk.1/src/data/sessions/orders.reducer.ts b/03_source/mobile.trunk.1/src/data/sessions/orders.reducer.ts new file mode 100644 index 0000000..3687bc0 --- /dev/null +++ b/03_source/mobile.trunk.1/src/data/sessions/orders.reducer.ts @@ -0,0 +1,31 @@ +import { OrdersActions } from './orders.actions'; +import { ConfState } from './conf.state'; + +export const orderReducer = (state: ConfState, action: OrdersActions): ConfState => { + switch (action.type) { + case 'set-conf-loading': { + return { ...state, loading: action.isLoading }; + } + case 'set-conf-data': { + return { ...state, ...action.data }; + } + case 'add-favorite': { + return { ...state, favorites: [...state.favorites, action.sessionId] }; + } + case 'remove-favorite': { + return { + ...state, + favorites: [...state.favorites.filter((x) => x !== action.sessionId)], + }; + } + case 'update-filtered-tracks': { + return { ...state, filteredTracks: action.filteredTracks }; + } + case 'set-search-text': { + return { ...state, searchText: action.searchText }; + } + case 'set-menu-enabled': { + return { ...state, menuEnabled: action.menuEnabled }; + } + } +}; diff --git a/03_source/mobile.trunk.1/src/data/sessions/sessions.actions.ts b/03_source/mobile.trunk.1/src/data/sessions/sessions.actions.ts new file mode 100644 index 0000000..b4badfb --- /dev/null +++ b/03_source/mobile.trunk.1/src/data/sessions/sessions.actions.ts @@ -0,0 +1,61 @@ +import { getConfData } from '../dataApi'; +import { ActionType } from '../../util/types'; +import { ConfState } from './conf.state'; + +export const loadConfData = () => async (dispatch: React.Dispatch) => { + dispatch(setLoading(true)); + const data = await getConfData(); + dispatch(setData(data)); + dispatch(setLoading(false)); +}; + +export const setLoading = (isLoading: boolean) => + ({ + type: 'set-conf-loading', + isLoading, + }) as const; + +export const setData = (data: Partial) => + ({ + type: 'set-conf-data', + data, + }) as const; + +export const addFavorite = (sessionId: number) => + ({ + type: 'add-favorite', + sessionId, + }) as const; + +export const removeFavorite = (sessionId: number) => + ({ + type: 'remove-favorite', + sessionId, + }) as const; + +export const updateFilteredTracks = (filteredTracks: string[]) => + ({ + type: 'update-filtered-tracks', + filteredTracks, + }) as const; + +export const setSearchText = (searchText?: string) => + ({ + type: 'set-search-text', + searchText, + }) as const; + +export const setMenuEnabled = (menuEnabled: boolean) => + ({ + type: 'set-menu-enabled', + menuEnabled, + }) as const; + +export type SessionsActions = + | ActionType + | ActionType + | ActionType + | ActionType + | ActionType + | ActionType + | ActionType; diff --git a/03_source/mobile.trunk.1/src/data/sessions/sessions.reducer.ts b/03_source/mobile.trunk.1/src/data/sessions/sessions.reducer.ts new file mode 100644 index 0000000..3540821 --- /dev/null +++ b/03_source/mobile.trunk.1/src/data/sessions/sessions.reducer.ts @@ -0,0 +1,31 @@ +import { SessionsActions } from './sessions.actions'; +import { ConfState } from './conf.state'; + +export const sessionsReducer = (state: ConfState, action: SessionsActions): ConfState => { + switch (action.type) { + case 'set-conf-loading': { + return { ...state, loading: action.isLoading }; + } + case 'set-conf-data': { + return { ...state, ...action.data }; + } + case 'add-favorite': { + return { ...state, favorites: [...state.favorites, action.sessionId] }; + } + case 'remove-favorite': { + return { + ...state, + favorites: [...state.favorites.filter((x) => x !== action.sessionId)], + }; + } + case 'update-filtered-tracks': { + return { ...state, filteredTracks: action.filteredTracks }; + } + case 'set-search-text': { + return { ...state, searchText: action.searchText }; + } + case 'set-menu-enabled': { + return { ...state, menuEnabled: action.menuEnabled }; + } + } +}; diff --git a/03_source/mobile.trunk.1/src/data/state.ts b/03_source/mobile.trunk.1/src/data/state.ts new file mode 100644 index 0000000..0680912 --- /dev/null +++ b/03_source/mobile.trunk.1/src/data/state.ts @@ -0,0 +1,47 @@ +import { combineReducers } from './combineReducers'; +// +import { sessionsReducer } from './sessions/sessions.reducer'; +import { userReducer } from './user/user.reducer'; +import { locationsReducer } from './locations/locations.reducer'; +// +import { orderReducer } from './sessions/orders.reducer'; + +export const initialState: AppState = { + data: { + schedule: { groups: [] } as any, + sessions: [], + speakers: [], + favorites: [], + locations: [], + allTracks: [], + filteredTracks: [], + mapCenterId: 0, + loading: false, + menuEnabled: true, + // + events: [], + nearbyMembers: [], + orders: [], + }, + user: { + hasSeenTutorial: false, + darkMode: false, + isLoggedin: false, + loading: false, + // + isSessionValid: false, + }, + locations: { + locations: [], + }, +}; + +export const reducers = combineReducers({ + data: sessionsReducer, + user: userReducer, + locations: locationsReducer, + // + order: orderReducer, +}); + +export type AppState = ReturnType; diff --git a/03_source/mobile.trunk.1/src/data/user/user.actions.ts b/03_source/mobile.trunk.1/src/data/user/user.actions.ts new file mode 100644 index 0000000..fd303fb --- /dev/null +++ b/03_source/mobile.trunk.1/src/data/user/user.actions.ts @@ -0,0 +1,132 @@ +import { + getUserData, + setIsLoggedInData, + setUsernameData, + setHasSeenTutorialData, + setAccessTokenData, + getAccessTokenData, + setActiveSessionData, +} from '../dataApi'; +import { ActionType } from '../../util/types'; +import { UserState } from './user.state'; +import { isValidToken } from '../../context/jwt/utils'; +import axios from 'axios'; +import { endpoints } from '../../pages/MyLogin/endpoints'; + +export const loadUserData = () => async (dispatch: React.Dispatch) => { + dispatch(setLoading(true)); + + const data = await getUserData(); + dispatch(setData(data)); + + dispatch(setLoading(false)); +}; + +export const setLoading = (isLoading: boolean) => + ({ + type: 'set-user-loading', + isLoading, + }) as const; + +export const setData = (data: Partial) => + ({ + type: 'set-user-data', + data, + }) as const; + +export const logoutUser = () => async (dispatch: React.Dispatch) => { + // + await setIsLoggedInData(false); + dispatch(setUsername()); +}; + +export const setIsLoggedIn = (loggedIn: boolean) => async (dispatch: React.Dispatch) => { + await setIsLoggedInData(loggedIn); + return { + type: 'set-is-loggedin', + loggedIn, + } as const; +}; + +export const setUsername = (username?: string) => async (dispatch: React.Dispatch) => { + await setUsernameData(username); + console.log('setUsername triggered'); + + return { + type: 'set-username', + username, + } as const; +}; + +export const setAccessToken = (token?: string) => async (dispatch: React.Dispatch) => { + await setAccessTokenData(token); + + return { + type: 'set-access-token', + token, + } as const; +}; + +export const setActiveSession = (session: any) => async (dispatch: React.Dispatch) => { + await setActiveSessionData(session); + return { + type: 'set-active-session', + session, + } as const; +}; + +export const checkUserSession = () => async (dispatch: React.Dispatch) => { + let accessToken = (await getAccessTokenData()).value; + console.log('check user session'); + let sessionValid = false; + + try { + if (accessToken && isValidToken(accessToken)) { + const res = await axios.get(endpoints.auth.me, { + headers: { Authorization: `Bearer ${accessToken}` }, + }); + + const { user } = res.data; + + setActiveSession({ user: { ...user, accessToken }, loading: false }); + sessionValid = true; + console.log('session valid'); + } else { + setActiveSession({ user: null, loading: false }); + console.log('session not valid'); + } + } catch (error) { + console.error(error); + setActiveSession({ user: null, loading: false }); + } + + return { + type: 'check-user-session', + sessionValid, + } as const; +}; + +export const setHasSeenTutorial = + (hasSeenTutorial: boolean) => async (dispatch: React.Dispatch) => { + await setHasSeenTutorialData(hasSeenTutorial); + return { + type: 'set-has-seen-tutorial', + hasSeenTutorial, + } as const; + }; + +export const setDarkMode = (darkMode: boolean) => + ({ + type: 'set-dark-mode', + darkMode, + }) as const; + +export type UserActions = + | ActionType + | ActionType + | ActionType + | ActionType + | ActionType + | ActionType + | ActionType + | ActionType; diff --git a/03_source/mobile.trunk.1/src/data/user/user.reducer.ts b/03_source/mobile.trunk.1/src/data/user/user.reducer.ts new file mode 100644 index 0000000..f6e6acb --- /dev/null +++ b/03_source/mobile.trunk.1/src/data/user/user.reducer.ts @@ -0,0 +1,23 @@ +import { UserActions } from './user.actions'; +import { UserState } from './user.state'; + +export function userReducer(state: UserState, action: UserActions): UserState { + switch (action.type) { + case 'set-user-loading': + return { ...state, loading: action.isLoading }; + case 'set-user-data': + return { ...state, ...action.data }; + case 'set-username': + return { ...state, username: action.username }; + case 'set-has-seen-tutorial': + return { ...state, hasSeenTutorial: action.hasSeenTutorial }; + case 'set-dark-mode': + return { ...state, darkMode: action.darkMode }; + case 'set-is-loggedin': + return { ...state, isLoggedin: action.loggedIn }; + case 'set-access-token': + return { ...state, token: action.token }; + case 'check-user-session': + return { ...state, isSessionValid: action.sessionValid }; + } +} diff --git a/03_source/mobile.trunk.1/src/data/user/user.state.ts b/03_source/mobile.trunk.1/src/data/user/user.state.ts new file mode 100644 index 0000000..56e5666 --- /dev/null +++ b/03_source/mobile.trunk.1/src/data/user/user.state.ts @@ -0,0 +1,10 @@ +export interface UserState { + loading: boolean; + username?: string; + hasSeenTutorial: boolean; + darkMode: boolean; + isLoggedin: boolean; + isSessionValid: boolean; + session?: any; + token?: string; +} diff --git a/03_source/mobile.trunk.1/src/declarations.ts b/03_source/mobile.trunk.1/src/declarations.ts new file mode 100644 index 0000000..dbe667a --- /dev/null +++ b/03_source/mobile.trunk.1/src/declarations.ts @@ -0,0 +1,5 @@ +export interface AppPage { + url: string; + icon: object; + title: string; +} diff --git a/03_source/mobile.trunk.1/src/global-config.ts b/03_source/mobile.trunk.1/src/global-config.ts new file mode 100644 index 0000000..71b4f35 --- /dev/null +++ b/03_source/mobile.trunk.1/src/global-config.ts @@ -0,0 +1,3 @@ +export const CONFIG = { + serverUrl: '', +}; diff --git a/03_source/mobile.trunk.1/src/hooks/use-set-state.ts b/03_source/mobile.trunk.1/src/hooks/use-set-state.ts new file mode 100644 index 0000000..f820bd2 --- /dev/null +++ b/03_source/mobile.trunk.1/src/hooks/use-set-state.ts @@ -0,0 +1,32 @@ +/** + * Custom hook to manage state with utility functions to set state, set a specific field, and reset state. + * + * @param {T} initialState - The initial state value. + * + * @returns {UseSetStateReturn} - An object containing: + * - `state`: The current state. + * - `resetState`: A function to reset the state to the initial value. + * - `setState`: A function to update the state. + * - `setField`: A function to update a specific field in the state. + * + * @example + * const { state, setState, setField, resetState } = useSetState({ name: '', age: 0 }); + * + * return ( + *
+ *

Name: {state.name}

+ *

Age: {state.age}

+ * + * + *
+ * ); + */ +type UseSetStateReturn = { + state: T; + resetState: (defaultState?: T) => void; + setState: (updateState: T | Partial) => void; + setField: (name: keyof T, updateValue: T[keyof T]) => void; +}; +declare function useSetState(initialState?: T): UseSetStateReturn; + +export { type UseSetStateReturn, useSetState }; diff --git a/03_source/mobile.trunk.1/src/main.tsx b/03_source/mobile.trunk.1/src/main.tsx new file mode 100644 index 0000000..964b804 --- /dev/null +++ b/03_source/mobile.trunk.1/src/main.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; +import * as serviceWorker from './serviceWorker'; + +ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( + + + +); +// If you want your app to work offline and load faster, you can change +// unregister() to register() below. Note this comes with some pitfalls. +// Learn more about service workers: https://bit.ly/CRA-PWA +serviceWorker.register(); diff --git a/03_source/mobile.trunk.1/src/models/Event.ts b/03_source/mobile.trunk.1/src/models/Event.ts new file mode 100644 index 0000000..31ac548 --- /dev/null +++ b/03_source/mobile.trunk.1/src/models/Event.ts @@ -0,0 +1,24 @@ +// 03_source/mobile/src/models/Event.ts + +export type IDateValue = string | number | null; + +export interface Event { + id: string; + createdAt: IDateValue; + updatedAt: IDateValue; + // + name: string; + code: string; + price: number; + // + eventDate: Date; + joinMembers: { email: string; avatar: string; sex: string }[]; + title: string; + currency: string; + duration_m: number; + ageBottom: number; + ageTop: number; + location: string; + avatar: string; + // +} diff --git a/03_source/mobile.trunk.1/src/models/Location.ts b/03_source/mobile.trunk.1/src/models/Location.ts new file mode 100644 index 0000000..9bc6d77 --- /dev/null +++ b/03_source/mobile.trunk.1/src/models/Location.ts @@ -0,0 +1,15 @@ +export interface Location { + id: number; + name: string; + lat: number; + lng: number; + center?: boolean; +} + +export interface LocationState { + locations: Location[]; +} + +export const initialState: LocationState = { + locations: [], +}; diff --git a/03_source/mobile.trunk.1/src/models/Order.ts b/03_source/mobile.trunk.1/src/models/Order.ts new file mode 100644 index 0000000..6d4b184 --- /dev/null +++ b/03_source/mobile.trunk.1/src/models/Order.ts @@ -0,0 +1,55 @@ +export type IDateValue = string | number | null; + +export type IOrderProductItem = { + id: string; + sku: string; + name: string; + price: number; + coverUrl: string; + quantity: number; +}; + +export type IOrderHistory = { + orderTime: IDateValue; + paymentTime: IDateValue; + deliveryTime: IDateValue; + completionTime: IDateValue; + timeline: { title: string; time: IDateValue }[]; +}; + +export type IOrderDelivery = { + shipBy: string; + speedy: string; + trackingNumber: string; +}; + +export type IOrderShippingAddress = { + fullAddress: string; + phoneNumber: string; +}; + +export type IOrderPayment = { + cardType: string; + cardNumber: string; +}; + +export interface IOrderItem { + id: string; + createdAt: IDateValue; + updatedAt: IDateValue; + // + taxes: number; + status: string; + shipping: number; + discount: number; + subtotal: number; + orderNumber: string; + totalAmount: number; + totalQuantity: number; + // + items: IOrderProductItem[]; + history: IOrderHistory | undefined; + delivery: IOrderDelivery; + shippingAddress: IOrderShippingAddress; + payment: IOrderPayment; +} diff --git a/03_source/mobile.trunk.1/src/models/Schedule.ts b/03_source/mobile.trunk.1/src/models/Schedule.ts new file mode 100644 index 0000000..f210ff8 --- /dev/null +++ b/03_source/mobile.trunk.1/src/models/Schedule.ts @@ -0,0 +1,20 @@ +export interface Schedule { + date: string; + groups: ScheduleGroup[]; +} + +export interface ScheduleGroup { + time: string; + sessions: Session[]; +} + +export interface Session { + id: number; + timeStart: string; + timeEnd: string; + name: string; + location: string; + description: string; + speakerNames: string[]; + tracks: string[]; +} diff --git a/03_source/mobile.trunk.1/src/models/SessionGroup.ts b/03_source/mobile.trunk.1/src/models/SessionGroup.ts new file mode 100644 index 0000000..93d46ad --- /dev/null +++ b/03_source/mobile.trunk.1/src/models/SessionGroup.ts @@ -0,0 +1,5 @@ +import { Session } from './Schedule'; +export interface SessionGroup { + startTime: string; + sessions: Session[]; +} diff --git a/03_source/mobile.trunk.1/src/models/Speaker.ts b/03_source/mobile.trunk.1/src/models/Speaker.ts new file mode 100644 index 0000000..35d8c78 --- /dev/null +++ b/03_source/mobile.trunk.1/src/models/Speaker.ts @@ -0,0 +1,12 @@ +export interface Speaker { + id: number; + name: string; + profilePic: string; + twitter: string; + instagram: string; + about: string; + title: string; + location: string; + email: string; + phone: string; +} diff --git a/03_source/mobile.trunk.1/src/pages/About.scss b/03_source/mobile.trunk.1/src/pages/About.scss new file mode 100644 index 0000000..f7d1ac0 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/About.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url("/assets/img/about/madison.jpg"); + } + + .about-header .austin { + background-image: url("/assets/img/about/austin.jpg"); + } + + .about-header .chicago { + background-image: url("/assets/img/about/chicago.jpg"); + } + + .about-header .seattle { + background-image: url("/assets/img/about/seattle.jpg"); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile.trunk.1/src/pages/About.tsx b/03_source/mobile.trunk.1/src/pages/About.tsx new file mode 100644 index 0000000..f7416ad --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/About.tsx @@ -0,0 +1,156 @@ +import React, { useState } from 'react'; +import { + IonHeader, + IonToolbar, + IonContent, + IonPage, + IonButtons, + IonMenuButton, + IonButton, + IonIcon, + IonDatetime, + IonSelectOption, + IonList, + IonItem, + IonLabel, + IonSelect, + IonPopover, + IonText, +} from '@ionic/react'; +import './About.scss'; +import { ellipsisHorizontal, ellipsisVertical } from 'ionicons/icons'; +import AboutPopover from '../components/AboutPopover'; +import { format, parseISO } from 'date-fns'; + +interface AboutProps {} + +const About: React.FC = () => { + const [showPopover, setShowPopover] = useState(false); + const [popoverEvent, setPopoverEvent] = useState(); + const [location, setLocation] = useState<'madison' | 'austin' | 'chicago' | 'seattle'>('madison'); + const [conferenceDate, setConferenceDate] = useState('2047-05-17T00:00:00-05:00'); + + const selectOptions = { + header: 'Select a Location', + }; + + const presentPopover = (e: React.MouseEvent) => { + setPopoverEvent(e.nativeEvent); + setShowPopover(true); + }; + + function displayDate(date: string, dateFormat: string) { + return format(parseISO(date), dateFormat); + } + + return ( + + + + + + + + + + + + + + + +
+ {/* Instead of loading an image each time the select changes, use opacity to transition them */} +
+
+
+
+
+
+

About

+ +

+ The Ionic Conference is a one-day event happening on{' '} + {displayDate(conferenceDate, 'MMM dd, yyyy')}, featuring talks from the Ionic team. The + conference focuses on building applications with Ionic Framework, including topics such + as app migration to the latest version, React best practices, Webpack, Sass, and other + technologies commonly used in the Ionic ecosystem. Tickets are completely sold out, and + we're expecting over 1,000 developers — making this the largest Ionic conference to + date! +

+ +

Details

+ + + + setLocation(e.detail.value as any)} + > + Madison, WI + Austin, TX + Chicago, IL + Seattle, WA + + + + Date + {displayDate(conferenceDate, 'MMM dd, yyyy')} + + setConferenceDate(e.detail.value! as string)} + presentation="date" + > + + + + +

Internet

+ + + + Wifi network + ica{displayDate(conferenceDate, 'y')} + + + Password + makegoodthings + + +
+
+ + setShowPopover(false)} + > + setShowPopover(false)} /> + +
+ ); +}; + +export default React.memo(About); diff --git a/03_source/mobile.trunk.1/src/pages/Account.scss b/03_source/mobile.trunk.1/src/pages/Account.scss new file mode 100644 index 0000000..e3c2761 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/Account.scss @@ -0,0 +1,6 @@ +#account-page { + img { + max-width: 140px; + border-radius: 50%; + } +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/src/pages/Account.tsx b/03_source/mobile.trunk.1/src/pages/Account.tsx new file mode 100644 index 0000000..7c74df4 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/Account.tsx @@ -0,0 +1,101 @@ +import React, { useState } from 'react'; +import { + IonHeader, + IonToolbar, + IonTitle, + IonContent, + IonPage, + IonButtons, + IonMenuButton, + IonList, + IonItem, + IonAlert, +} from '@ionic/react'; +import './Account.scss'; +import { setUsername } from '../data/user/user.actions'; +import { connect } from '../data/connect'; +import { RouteComponentProps } from 'react-router'; + +interface OwnProps extends RouteComponentProps {} + +interface StateProps { + username?: string; +} + +interface DispatchProps { + setUsername: typeof setUsername; +} + +interface AccountProps extends OwnProps, StateProps, DispatchProps {} + +const Account: React.FC = ({ setUsername, username }) => { + const [showAlert, setShowAlert] = useState(false); + + const clicked = (text: string) => { + console.log(`Clicked ${text}`); + }; + + return ( + + + + + + + Account + + + + {username && ( +
+ avatar +

{username}

+ + clicked('Update Picture')}>Update Picture + setShowAlert(true)}>Change Username + clicked('Change Password')}>Change Password + + Support + + + Logout + + +
+ )} +
+ { + setUsername(data.username); + }, + }, + ]} + inputs={[ + { + type: 'text', + name: 'username', + value: username, + placeholder: 'username', + }, + ]} + onDidDismiss={() => setShowAlert(false)} + /> +
+ ); +}; + +export default connect({ + mapStateToProps: (state) => ({ + username: state.user.username, + }), + mapDispatchToProps: { + setUsername, + }, + component: Account, +}); diff --git a/03_source/mobile.trunk.1/src/pages/ChangeLanguage/index.tsx b/03_source/mobile.trunk.1/src/pages/ChangeLanguage/index.tsx new file mode 100644 index 0000000..6fd288a --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/ChangeLanguage/index.tsx @@ -0,0 +1,246 @@ +// REQ0041/home_discover_event_tab + +import React, { useEffect, useRef, useState } from 'react'; +import { + IonHeader, + IonToolbar, + IonTitle, + IonContent, + IonPage, + IonButtons, + IonMenuButton, + IonGrid, + IonRow, + IonCol, + useIonRouter, + IonButton, + IonIcon, + IonPopover, + IonAvatar, + IonImg, + IonItem, + IonLabel, + IonList, + IonModal, + IonSearchbar, + useIonModal, + IonInput, + IonRefresher, + IonRefresherContent, + RefresherEventDetail, + IonToast, + useIonToast, +} from '@ionic/react'; +import SpeakerItem from '../../components/SpeakerItem'; +import { Speaker } from '../../models/Speaker'; +import { Session } from '../../models/Schedule'; +import { connect } from '../../data/connect'; +import * as selectors from '../../data/selectors'; +import '../SpeakerList.scss'; +import { getEvents } from '../../api/getEvents'; +import { format } from 'date-fns'; + +// import { Event } from './types'; +interface Event { + eventDate: Date; + joinMembers: undefined; + title: string; + price: number; + currency: string; + duration_m: number; + ageBottom: number; + ageTop: number; + location: string; + avatar: string; + // + id: string; +} + +import { + checkmarkOutline, + chevronBackOutline, + chevronDownCircleOutline, + closeOutline, + heart, + languageOutline, + menuOutline, +} from 'ionicons/icons'; +import AboutPopover from '../../components/AboutPopover'; +import Loading from '../../components/Loading'; + +interface OwnProps {} + +interface StateProps { + events: Event[]; +} + +interface DispatchProps {} + +interface SpeakerListProps extends OwnProps, StateProps, DispatchProps {} + +const EventList: React.FC = ({ events }) => { + const modal = useRef(null); + + const router = useIonRouter(); + + function handleShowPartyEventDetail(event_id: string) { + router.push(`/event_detail/${event_id}`); + } + + function handleRefresh(event: CustomEvent) { + setTimeout(() => { + // Any calls to load data go here + event.detail.complete(); + }, 2000); + } + + const [confirmChangeLanguage, setConfirmChangeLanguage] = useState(false); + + function handleChangeToChinese() { + setConfirmChangeLanguage(true); + } + + function handleChangeToEnglish() { + setConfirmChangeLanguage(true); + } + + function handleChangeToJapanese() { + setConfirmChangeLanguage(true); + } + + function handleApplyChangeLanguage() { + setConfirmChangeLanguage(false); + } + + function handleBackClick() { + router.goBack(); + } + + function handleCancelChangeLanguage() { + setConfirmChangeLanguage(false); + } + + const [present] = useIonToast(); + + const presentToast = () => { + present({ + message: 'sorry but the function not yet implemented!', + duration: 1500, + position: 'bottom', + }); + + setConfirmChangeLanguage(false); + }; + + if (!events || events.length == 0) return ; + + return ( + + + + + {/* */} + + + + +
+ + Change Language +
+
+
+ + + + + Change Language + + + + + + Chinese + + + English + + + Japanese + + + + + {/* REQ0079/event-filter */} + setConfirmChangeLanguage(false)} + > + +
+
+ Confirm change language +
+
+ Change language to Chinese +
+
+ + + No + + + + Yes + +
+
+
+
+
+ ); +}; + +export default connect({ + mapStateToProps: (state) => ({ + events: selectors.getEvents(state), + }), + component: React.memo(EventList), +}); diff --git a/03_source/mobile/src/pages/DemoReactShop/style.scss b/03_source/mobile.trunk.1/src/pages/ChangeLanguage/style.scss similarity index 100% rename from 03_source/mobile/src/pages/DemoReactShop/style.scss rename to 03_source/mobile.trunk.1/src/pages/ChangeLanguage/style.scss diff --git a/03_source/mobile/src/pages/DemoColorTutorial/AppPages/Tab1.jsx b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/AppPages/Tab1.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoColorTutorial/AppPages/Tab1.jsx rename to 03_source/mobile.trunk.1/src/pages/Demo2FaExample/AppPages/Tab1.jsx diff --git a/03_source/mobile/src/pages/DemoColorTutorial/AppPages/Tab2.jsx b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/AppPages/Tab2.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoColorTutorial/AppPages/Tab2.jsx rename to 03_source/mobile.trunk.1/src/pages/Demo2FaExample/AppPages/Tab2.jsx diff --git a/03_source/mobile.trunk.1/src/pages/Demo2FaExample/NOTES.md b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/NOTES.md new file mode 100644 index 0000000..a1700c9 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/NOTES.md @@ -0,0 +1,13 @@ +--- +tags: mobile +--- + +# REQ0119/demo-2fa-example + +## description + +This is the Ionic implementation example of two-factor authentication (2FA) demonstrating various authentication flows and UI components. + +## relation + +- diff --git a/03_source/mobile/src/pages/DemoClubHouse/components1/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/TestComponents/CurrentWeather/WeatherProperty.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoClubHouse/components1/CurrentWeather/WeatherProperty.tsx rename to 03_source/mobile.trunk.1/src/pages/Demo2FaExample/TestComponents/CurrentWeather/WeatherProperty.tsx diff --git a/03_source/mobile/src/pages/DemoClubHouse/components1/CurrentWeather/index.tsx b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/TestComponents/CurrentWeather/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoClubHouse/components1/CurrentWeather/index.tsx rename to 03_source/mobile.trunk.1/src/pages/Demo2FaExample/TestComponents/CurrentWeather/index.tsx diff --git a/03_source/mobile/src/pages/DemoClubHouse/components1/SkeletonDashboard/index.tsx b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/TestComponents/SkeletonDashboard/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoClubHouse/components1/SkeletonDashboard/index.tsx rename to 03_source/mobile.trunk.1/src/pages/Demo2FaExample/TestComponents/SkeletonDashboard/index.tsx diff --git a/03_source/mobile.trunk.1/src/pages/Demo2FaExample/components/Keypad.module.scss b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/components/Keypad.module.scss new file mode 100644 index 0000000..38c85a2 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/components/Keypad.module.scss @@ -0,0 +1,5 @@ +.keypad { + bottom: 0; + position: absolute; + width: 100%; +} diff --git a/03_source/mobile.trunk.1/src/pages/Demo2FaExample/components/Keypad.tsx b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/components/Keypad.tsx new file mode 100644 index 0000000..f7927f0 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/components/Keypad.tsx @@ -0,0 +1,118 @@ +import { IonRow } from '@ionic/react'; +import styles from './Keypad.module.scss'; +import KeypadButton from './KeypadButton'; + +const Keypad = (props: any): JSX.Element => { + const { activeIndex, handleClick, handleRemove, amount, correct } = props; + + const keypadButtons = [ + [ + { + value: '1', + handleClick: () => handleClick(activeIndex, 1), + small: false, + remove: false, + }, + { + value: '2', + handleClick: () => handleClick(activeIndex, 2), + small: false, + remove: false, + }, + { + value: '3', + handleClick: () => handleClick(activeIndex, 3), + small: false, + remove: false, + }, + ], + [ + { + value: '4', + handleClick: () => handleClick(activeIndex, 4), + small: false, + remove: false, + }, + { + value: '5', + handleClick: () => handleClick(activeIndex, 5), + small: false, + remove: false, + }, + { + value: '6', + handleClick: () => handleClick(activeIndex, 6), + small: false, + remove: false, + }, + ], + [ + { + value: '7', + handleClick: () => handleClick(activeIndex, 7), + small: false, + remove: false, + }, + { + value: '8', + handleClick: () => handleClick(activeIndex, 8), + small: false, + remove: false, + }, + { + value: '9', + handleClick: () => handleClick(activeIndex, 9), + small: false, + remove: false, + }, + ], + [ + { + value: 'Resend', + handleClick: () => handleClick(activeIndex, 1), + small: true, + remove: false, + }, + { + value: '0', + handleClick: () => handleClick(activeIndex, 2), + small: false, + remove: false, + }, + { + value: '', + handleClick: () => handleRemove(), + small: true, + remove: true, + }, + ], + ]; + + return ( +
+ {keypadButtons.map((keypadRow, index) => { + const isDisabled = parseInt(activeIndex) === parseInt(amount); + + return ( + + {keypadRow.map((button, index2) => { + return ( + + ); + })} + + ); + })} +
+ ); +}; + +export default Keypad; diff --git a/03_source/mobile.trunk.1/src/pages/Demo2FaExample/components/KeypadButton.module.scss b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/components/KeypadButton.module.scss new file mode 100644 index 0000000..400a722 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/components/KeypadButton.module.scss @@ -0,0 +1,30 @@ +.logo { + height: 4rem; + width: auto; +} + +.keypadButton { + --background: none; + --color: black; + font-size: 2rem; + font-weight: 700; + --outline: none; + --border: none; + --box-shadow: none; + padding: none; + margin: none; + --background-hover: rgb(245, 245, 245) !important; + --background-focused: rgb(245, 245, 245) !important; + --background-activated: rgb(245, 245, 245) !important; +} + +.smallKeypadButton { + font-size: 1.4rem; + margin-top: 1rem; +} + +.keypad { + bottom: 0; + position: absolute; + width: 100%; +} diff --git a/03_source/mobile.trunk.1/src/pages/Demo2FaExample/components/KeypadButton.tsx b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/components/KeypadButton.tsx new file mode 100644 index 0000000..1188e6f --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/components/KeypadButton.tsx @@ -0,0 +1,22 @@ +import { IonButton, IonCol, IonIcon } from '@ionic/react'; +import { backspaceOutline } from 'ionicons/icons'; +import styles from './KeypadButton.module.scss'; + +const KeypadButton = (props: any): JSX.Element => { + const { small, value, remove, handleClick, isDisabled = false, correct } = props; + + return ( + + + {!remove && value} + {remove && } + + + ); +}; + +export default KeypadButton; diff --git a/03_source/mobile.trunk.1/src/pages/Demo2FaExample/components/KeypadInput.module.scss b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/components/KeypadInput.module.scss new file mode 100644 index 0000000..c54a1b8 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/components/KeypadInput.module.scss @@ -0,0 +1,38 @@ +.keypadInput { + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + align-items: center; + font-size: 2.5rem; + width: 100%; + min-height: 2.5rem; + background-color: rgb(245, 245, 245); + border-radius: 4px; + color: rgb(207, 207, 207); + transition: 0.2s linear; +} + +.active { + background-color: rgba(26, 150, 251, 0.2); + border: 0 !important; + color: white !important; + transition: 0.2s linear; +} + +.filled { + color: rgb(151, 151, 151); + transition: 0.2s linear; +} + +.incorrect { + background-color: rgba(251, 26, 26, 0.2); + color: rgb(218, 67, 67); + transition: 0.2s linear; +} + +.correct { + background-color: rgba(26, 251, 120, 0.2); + color: rgb(67, 218, 112); + transition: 0.2s linear; +} diff --git a/03_source/mobile.trunk.1/src/pages/Demo2FaExample/components/KeypadInput.tsx b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/components/KeypadInput.tsx new file mode 100644 index 0000000..7d6f476 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/components/KeypadInput.tsx @@ -0,0 +1,22 @@ +import { IonCol } from '@ionic/react'; +import styles from './KeypadInput.module.scss'; + +const KeypadInput = (props: any): JSX.Element => { + const { value, isActive = false, isFilled = false, incorrect, correct } = props; + + return ( + +
+ {value} + {!isFilled && '0'} +
+
+ ); +}; + +export default KeypadInput; diff --git a/03_source/mobile.trunk.1/src/pages/Demo2FaExample/components/KeypadInputs.tsx b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/components/KeypadInputs.tsx new file mode 100644 index 0000000..d0c1788 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/components/KeypadInputs.tsx @@ -0,0 +1,50 @@ +import { IonRow } from '@ionic/react'; +import { useEffect, useRef } from 'react'; +import KeypadInput from './KeypadInput'; + +const KeypadInputs = (props: any): JSX.Element => { + const { values, activeIndex, incorrect, correct } = props; + const keypadRef = useRef(null); + + useEffect(() => { + if (incorrect && keypadRef.current) { + keypadRef.current.classList.add('incorrect'); + + setTimeout(() => { + if (keypadRef.current) { + keypadRef.current.classList.remove('incorrect'); + } + }, 1000); + } + }, [incorrect]); + + useEffect(() => { + if (correct) { + if (keypadRef.current) { + keypadRef.current.classList.add('correct'); + } + } + }, [correct]); + + return ( + + {values.map((value: string, index: number) => { + const isActive = parseInt(index.toString()) === parseInt(activeIndex); + const isFilled = value !== '' ? true : false; + + return ( + + ); + })} + + ); +}; + +export default KeypadInputs; diff --git a/03_source/mobile.trunk.1/src/pages/Demo2FaExample/index.tsx b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/index.tsx new file mode 100644 index 0000000..d91e0db --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/index.tsx @@ -0,0 +1,26 @@ +// REQ0119/demo-2fa-example +// +// RULES: +// T.B.A. +// +import { IonRouterOutlet, IonTabs } from '@ionic/react'; + +import { Route, Redirect } from 'react-router'; + +import Home from './pages/Home'; + +function Demo2FaExample() { + return ( + + + + + + + + + + ); +} + +export default Demo2FaExample; diff --git a/03_source/mobile.trunk.1/src/pages/Demo2FaExample/pages/Home.module.scss b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/pages/Home.module.scss new file mode 100644 index 0000000..3d0a7fe --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/pages/Home.module.scss @@ -0,0 +1,30 @@ +.logo { + height: 4rem; + width: auto; +} + +.incorrect { + color: rgb(218, 67, 67); +} + +.successContainer { +} + +.successText { + background-color: rgba(26, 251, 120, 0.2); + color: rgb(67, 218, 112); + padding: 1rem; +} + +.successContinue { + font-weight: 700; + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + align-items: center; +} + +.successContinue ion-icon { + margin-top: 0.2rem; +} diff --git a/03_source/mobile.trunk.1/src/pages/Demo2FaExample/pages/Home.tsx b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/pages/Home.tsx new file mode 100644 index 0000000..1cd7885 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/pages/Home.tsx @@ -0,0 +1,135 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonImg, + IonPage, + IonRow, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import { arrowForwardOutline, chevronBackOutline } from 'ionicons/icons'; +import styles from './Home.module.scss'; +import KeypadInputs from '../components/KeypadInputs'; +import Keypad from '../components/Keypad'; +import { JSX, useEffect, useRef, useState } from 'react'; + +const Home = (): JSX.Element => { + const correctCode = [5, 9, 2, 5]; + const [keypadValues, setKeypadValues] = useState(['', '', '', '']); + const [activeIndex, setActiveIndex] = useState(0); + const successRef = useRef(null); + + const [incorrect, setIncorrect] = useState(false); + const [correct, setCorrect] = useState(false); + + const tempValues: { [key: string]: any } = {}; + + const handleClick = (index: number, value: any) => { + const stringKey = index.toString(); + tempValues[stringKey] = value; + + setKeypadValues(value); + setActiveIndex((activeIndex) => activeIndex + 1); + }; + + const handleRemove = () => { + const tempValues = [...keypadValues]; + tempValues[activeIndex - 1] = ''; + + setKeypadValues(tempValues); + activeIndex > 0 && setActiveIndex((activeIndex) => activeIndex - 1); + setIncorrect(false); + setCorrect(false); + }; + + const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + useEffect(() => { + if (parseInt(activeIndex.toString()) === parseInt(keypadValues.length.toString())) { + var error = false; + + keypadValues.forEach((value, index) => { + if (parseInt(value) !== parseInt(correctCode[index].toString())) { + error = true; + return false; + } + }); + + if (error) { + setIncorrect(true); + } else { + setCorrect(true); + + setTimeout(() => { + if (successRef.current) { + successRef.current.classList.remove('hidden'); + successRef.current.classList.add('success'); + } + }, 900); + } + } + }, [activeIndex]); + + return ( + + + + + + handleBackClick()}> + + + + + + + + + +

Verification required

+

Enter your 4 digit verification code

+
+
+ + + +

+ Awesome! You may continue. +
+ + Continue   + + +

+
+
+ + + {incorrect &&

Wrong code entered

} + +
+
+
+ ); +}; + +export default Home; diff --git a/03_source/mobile.trunk.1/src/pages/Demo2FaExample/theme/variables.scss b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/theme/variables.scss new file mode 100644 index 0000000..dc5200e --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/Demo2FaExample/theme/variables.scss @@ -0,0 +1,249 @@ +.demo-2fa-example { + /* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + + * { + font-family: 'Lato', sans-serif; + } + + .hidden { + display: none; + transition: 0.2s linear; + } + + /** Ionic CSS Variables **/ + :root { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + } + + .incorrect { + -webkit-animation: incorrect-animation 0.9s both; + animation: incorrect-animation 0.9s both; + } + + @-webkit-keyframes incorrect-animation { + 0% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 30% { + -webkit-transform: scale3d(1.25, 0.75, 1); + transform: scale3d(1.25, 0.75, 1); + } + + 40% { + -webkit-transform: scale3d(0.75, 1.25, 1); + transform: scale3d(0.75, 1.25, 1); + } + + 50% { + -webkit-transform: scale3d(1.15, 0.85, 1); + transform: scale3d(1.15, 0.85, 1); + } + + 65% { + -webkit-transform: scale3d(0.95, 1.05, 1); + transform: scale3d(0.95, 1.05, 1); + } + + 75% { + -webkit-transform: scale3d(1.05, 0.95, 1); + transform: scale3d(1.05, 0.95, 1); + } + + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + } + + @keyframes incorrect-animation { + 0% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 30% { + -webkit-transform: scale3d(1.25, 0.75, 1); + transform: scale3d(1.25, 0.75, 1); + } + + 40% { + -webkit-transform: scale3d(0.75, 1.25, 1); + transform: scale3d(0.75, 1.25, 1); + } + + 50% { + -webkit-transform: scale3d(1.15, 0.85, 1); + transform: scale3d(1.15, 0.85, 1); + } + + 65% { + -webkit-transform: scale3d(0.95, 1.05, 1); + transform: scale3d(0.95, 1.05, 1); + } + + 75% { + -webkit-transform: scale3d(1.05, 0.95, 1); + transform: scale3d(1.05, 0.95, 1); + } + + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + } + + .correct { + -webkit-animation: correct-animation 1s ease-in both; + animation: correct-animation 1s ease-in both; + } + + @-webkit-keyframes correct-animation { + 0% { + -webkit-transform: translateY(0) rotateX(0) scale(1); + transform: translateY(0) rotateX(0) scale(1); + -webkit-transform-origin: 50% 1400px; + transform-origin: 50% 1400px; + opacity: 1; + } + + 100% { + -webkit-transform: translateY(-600px) rotateX(-30deg) scale(0); + transform: translateY(-600px) rotateX(-30deg) scale(0); + -webkit-transform-origin: 50% 100%; + transform-origin: 50% 100%; + opacity: 1; + } + } + + @keyframes correct-animation { + 0% { + -webkit-transform: translateY(0) rotateX(0) scale(1); + transform: translateY(0) rotateX(0) scale(1); + -webkit-transform-origin: 50% 1400px; + transform-origin: 50% 1400px; + opacity: 1; + } + + 100% { + -webkit-transform: translateY(-600px) rotateX(-30deg) scale(0); + transform: translateY(-600px) rotateX(-30deg) scale(0); + -webkit-transform-origin: 50% 100%; + transform-origin: 50% 100%; + opacity: 1; + } + } + + .success { + -webkit-animation: success-animation 0.7s cubic-bezier(0.25, 0.46, 0.45, 0.94) both; + animation: success-animation 0.7s cubic-bezier(0.25, 0.46, 0.45, 0.94) both; + } + + @-webkit-keyframes success-animation { + 0% { + -webkit-transform: translateY(-600px) rotateX(-30deg) scale(0); + transform: translateY(-600px) rotateX(-30deg) scale(0); + -webkit-transform-origin: 50% 100%; + transform-origin: 50% 100%; + opacity: 0; + } + + 100% { + -webkit-transform: translateY(0) rotateX(0) scale(1); + transform: translateY(0) rotateX(0) scale(1); + -webkit-transform-origin: 50% 1400px; + transform-origin: 50% 1400px; + opacity: 1; + } + } + + @keyframes success-animation { + 0% { + -webkit-transform: translateY(-600px) rotateX(-30deg) scale(0); + transform: translateY(-600px) rotateX(-30deg) scale(0); + -webkit-transform-origin: 50% 100%; + transform-origin: 50% 100%; + opacity: 0; + } + + 100% { + -webkit-transform: translateY(0) rotateX(0) scale(1); + transform: translateY(0) rotateX(0) scale(1); + -webkit-transform-origin: 50% 1400px; + transform-origin: 50% 1400px; + opacity: 1; + } + } +} diff --git a/03_source/mobile/src/pages/DemoReactPollApp/AppPages/Tab1.jsx b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/AppPages/Tab1.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactPollApp/AppPages/Tab1.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/AppPages/Tab1.jsx diff --git a/03_source/mobile/src/pages/DemoQrScanner/AppPages/Tab2.jsx b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/AppPages/Tab2.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoQrScanner/AppPages/Tab2.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/AppPages/Tab2.jsx diff --git a/03_source/mobile/src/pages/DemoAccordionTutorial/notes.md b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/NOTES.md similarity index 100% rename from 03_source/mobile/src/pages/DemoAccordionTutorial/notes.md rename to 03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/NOTES.md diff --git a/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/components/Accordion.jsx b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/components/Accordion.jsx new file mode 100644 index 0000000..c679c72 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/components/Accordion.jsx @@ -0,0 +1,29 @@ +import { IonAccordion, IonAccordionGroup, IonIcon, IonItem, IonLabel, IonList } from '@ionic/react'; +import { topics } from '../data'; + +export const Accordion = () => { + return ( + + {topics.map((topic, index) => { + return ( + + + + {topic.header} + + + + {topic.options.map((option, index2) => { + return ( + + {option.label} + + ); + })} + + + ); + })} + + ); +}; diff --git a/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/components/Accordion.tsx b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/components/Accordion.tsx new file mode 100644 index 0000000..7e495fa --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/components/Accordion.tsx @@ -0,0 +1,30 @@ +import { IonAccordion, IonAccordionGroup, IonIcon, IonItem, IonLabel, IonList } from '@ionic/react'; +import { topics } from '../data'; +import React from 'react'; + +export const Accordion: React.FC = () => { + return ( + + {topics.map((topic: any, index: number) => { + return ( + + + + {topic.header} + + + + {topic.options.map((option: any, index2: number) => { + return ( + + {option.label} + + ); + })} + + + ); + })} + + ); +}; diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/components/CurrentWeather/WeatherProperty.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/TestComponents/CurrentWeather/WeatherProperty.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/components/CurrentWeather/WeatherProperty.tsx diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/components/CurrentWeather/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/TestComponents/CurrentWeather/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/components/CurrentWeather/index.tsx diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/components/SkeletonDashboard/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/TestComponents/SkeletonDashboard/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/components/SkeletonDashboard/index.tsx diff --git a/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/data.js b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/data.js new file mode 100644 index 0000000..30389eb --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/data.js @@ -0,0 +1,126 @@ +import { + bicycleOutline, + fastFoodOutline, + filmOutline, + gameControllerOutline, + libraryOutline, +} from 'ionicons/icons'; + +export const topics = [ + { + header: 'Attractions', + color: 'primary', + icon: filmOutline, + options: [ + { + label: 'Cinema', + }, + { + label: 'Bowling Alley', + }, + { + label: 'Crazy Golf', + }, + { + label: 'Theme Park', + }, + ], + }, + { + header: 'Dining', + color: 'success', + icon: fastFoodOutline, + options: [ + { + label: 'Breakfast & Brunch', + }, + { + label: 'New American', + }, + { + label: 'Sushi Bars', + }, + { + label: 'Filipino Food', + }, + { + label: 'Asian Fusion', + }, + { + label: 'Ramen Houses', + }, + { + label: 'Dinner Venues', + }, + ], + }, + { + header: 'Gaming', + color: 'warning', + icon: gameControllerOutline, + options: [ + { + label: 'Xbox', + }, + { + label: 'Playstation', + }, + { + label: 'Nintendo Switch', + }, + { + label: 'PC', + }, + { + label: 'Mobile', + }, + { + label: 'Dreamcast', + }, + ], + }, + { + header: 'Exercise', + color: 'secondary', + icon: bicycleOutline, + options: [ + { + label: 'Yoga', + }, + { + label: 'Pilates', + }, + { + label: 'Weight Training', + }, + { + label: 'Cardio', + }, + { + label: 'Zumba', + }, + ], + }, + { + header: 'Education', + color: 'danger', + icon: libraryOutline, + options: [ + { + label: 'School', + }, + { + label: 'High School', + }, + { + label: 'University Bachelors', + }, + { + label: 'University Masters', + }, + { + label: 'University pHD', + }, + ], + }, +]; diff --git a/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/data.ts b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/data.ts new file mode 100644 index 0000000..10fb915 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/data.ts @@ -0,0 +1,126 @@ +import { + bicycleOutline, + fastFoodOutline, + filmOutline, + gameControllerOutline, + libraryOutline, +} from 'ionicons/icons'; + +export const topics: any = [ + { + header: 'Attractions', + color: 'primary', + icon: filmOutline, + options: [ + { + label: 'Cinema', + }, + { + label: 'Bowling Alley', + }, + { + label: 'Crazy Golf', + }, + { + label: 'Theme Park', + }, + ], + }, + { + header: 'Dining', + color: 'success', + icon: fastFoodOutline, + options: [ + { + label: 'Breakfast & Brunch', + }, + { + label: 'New American', + }, + { + label: 'Sushi Bars', + }, + { + label: 'Filipino Food', + }, + { + label: 'Asian Fusion', + }, + { + label: 'Ramen Houses', + }, + { + label: 'Dinner Venues', + }, + ], + }, + { + header: 'Gaming', + color: 'warning', + icon: gameControllerOutline, + options: [ + { + label: 'Xbox', + }, + { + label: 'Playstation', + }, + { + label: 'Nintendo Switch', + }, + { + label: 'PC', + }, + { + label: 'Mobile', + }, + { + label: 'Dreamcast', + }, + ], + }, + { + header: 'Exercise', + color: 'secondary', + icon: bicycleOutline, + options: [ + { + label: 'Yoga', + }, + { + label: 'Pilates', + }, + { + label: 'Weight Training', + }, + { + label: 'Cardio', + }, + { + label: 'Zumba', + }, + ], + }, + { + header: 'Education', + color: 'danger', + icon: libraryOutline, + options: [ + { + label: 'School', + }, + { + label: 'High School', + }, + { + label: 'University Bachelors', + }, + { + label: 'University Masters', + }, + { + label: 'University pHD', + }, + ], + }, +]; diff --git a/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/index.tsx new file mode 100644 index 0000000..1cbb174 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/index.tsx @@ -0,0 +1,62 @@ +// REQ0119/demo-accordion-tutorial +// +// RULES: +// T.B.A. +// + +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +// import Tab1 from './AppPages/Tab1'; +// import Tab2 from './AppPages/Tab2'; + +import Topic from './pages/Topic'; +import Home from './pages/Home'; + +import './style.scss'; + +function DemoAccordionTutorial() { + return ( + + + {/* + + + + + + + */} + + + + + + + + + + + + {/* */} + + + {/* + + + + Dashboard + + + + Search + + + */} + + ); +} + +export default DemoAccordionTutorial; diff --git a/03_source/mobile/src/pages/DemoReactItemList/theme/variables.scss b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/pages/Home.css similarity index 100% rename from 03_source/mobile/src/pages/DemoReactItemList/theme/variables.scss rename to 03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/pages/Home.css diff --git a/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/pages/Home.jsx b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/pages/Home.jsx new file mode 100644 index 0000000..a0add98 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/pages/Home.jsx @@ -0,0 +1,97 @@ +import { + IonAccordion, + IonAccordionGroup, + IonButton, + IonButtons, + IonContent, + IonHeader, + IonIcon, + IonItem, + IonLabel, + IonList, + IonPage, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import { Accordion } from '../components/Accordion'; +import { chevronBackOutline } from 'ionicons/icons'; + +const Home = () => { + const router = useIonRouter(); + + function handleBackClick() { + router.goBack(); + } + + return ( + + + + Accordion + + + handleBackClick()}> + + + + + + + + + Accordion + + + handleBackClick()}> + + + + + + + + + {/* + + + Languages + + + + + English + + + Spanish + + + Italian + + + + + + + Languages 2 + + + + + English + + + Spanish + + + Italian + + + + */} + + + ); +}; + +export default Home; diff --git a/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/pages/Home.tsx b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/pages/Home.tsx new file mode 100644 index 0000000..65a2e4a --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/pages/Home.tsx @@ -0,0 +1,97 @@ +import { + IonAccordion, + IonAccordionGroup, + IonButton, + IonButtons, + IonContent, + IonHeader, + IonIcon, + IonItem, + IonLabel, + IonList, + IonPage, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import { Accordion } from '../components/Accordion'; +import { chevronBackOutline } from 'ionicons/icons'; + +const Home: React.FC = () => { + const router = useIonRouter(); + + function handleBackClick() { + router.goBack(); + } + + return ( + + + + Accordion + + + handleBackClick()}> + + + + + + + + + Accordion + + + handleBackClick()}> + + + + + + + + + {/* + + + Languages + + + + + English + + + Spanish + + + Italian + + + + + + + Languages 2 + + + + + English + + + Spanish + + + Italian + + + + */} + + + ); +}; + +export default Home; diff --git a/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/pages/Topic.jsx b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/pages/Topic.jsx new file mode 100644 index 0000000..0e82926 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/pages/Topic.jsx @@ -0,0 +1,49 @@ +import { + IonBackButton, + IonButtons, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonLabel, + IonPage, + IonRow, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useParams } from 'react-router'; + +const Topic = () => { + const { topic } = useParams(); + + return ( + + + + + + + + {topic} + + + + + + {topic} + + + + + + + This is the page for the topic: {topic}. + + + + + + ); +}; + +export default Topic; diff --git a/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/pages/Topic.tsx b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/pages/Topic.tsx new file mode 100644 index 0000000..54c48e7 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/pages/Topic.tsx @@ -0,0 +1,49 @@ +import { + IonBackButton, + IonButtons, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonLabel, + IonPage, + IonRow, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useParams } from 'react-router'; + +const Topic = () => { + const { topic } = useParams<{ topic: string }>(); + + return ( + + + + + + + + {topic} + + + + + + {topic} + + + + + + + This is the page for the topic: {topic}. + + + + + + ); +}; + +export default Topic; diff --git a/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/style.scss b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/style.scss new file mode 100644 index 0000000..d12d506 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoAccordionTutorial/style.scss @@ -0,0 +1,237 @@ +.demo-accordion-tutorial { + /* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + + /** Ionic CSS Variables **/ + * { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + } + + @media (prefers-color-scheme: dark) { + /* + * Dark Colors + * ------------------------------------------- + */ + + body { + --ion-color-primary: #428cff; + --ion-color-primary-rgb: 66, 140, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3a7be0; + --ion-color-primary-tint: #5598ff; + + --ion-color-secondary: #50c8ff; + --ion-color-secondary-rgb: 80, 200, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #46b0e0; + --ion-color-secondary-tint: #62ceff; + + --ion-color-tertiary: #6a64ff; + --ion-color-tertiary-rgb: 106, 100, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #5d58e0; + --ion-color-tertiary-tint: #7974ff; + + --ion-color-success: #2fdf75; + --ion-color-success-rgb: 47, 223, 117; + --ion-color-success-contrast: #000000; + --ion-color-success-contrast-rgb: 0, 0, 0; + --ion-color-success-shade: #29c467; + --ion-color-success-tint: #44e283; + + --ion-color-warning: #ffd534; + --ion-color-warning-rgb: 255, 213, 52; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0bb2e; + --ion-color-warning-tint: #ffd948; + + --ion-color-danger: #ff4961; + --ion-color-danger-rgb: 255, 73, 97; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #e04055; + --ion-color-danger-tint: #ff5b71; + + --ion-color-dark: #f4f5f8; + --ion-color-dark-rgb: 244, 245, 248; + --ion-color-dark-contrast: #000000; + --ion-color-dark-contrast-rgb: 0, 0, 0; + --ion-color-dark-shade: #d7d8da; + --ion-color-dark-tint: #f5f6f9; + + --ion-color-medium: #989aa2; + --ion-color-medium-rgb: 152, 154, 162; + --ion-color-medium-contrast: #000000; + --ion-color-medium-contrast-rgb: 0, 0, 0; + --ion-color-medium-shade: #86888f; + --ion-color-medium-tint: #a2a4ab; + + --ion-color-light: #222428; + --ion-color-light-rgb: 34, 36, 40; + --ion-color-light-contrast: #ffffff; + --ion-color-light-contrast-rgb: 255, 255, 255; + --ion-color-light-shade: #1e2023; + --ion-color-light-tint: #383a3e; + } + + /* + * iOS Dark Theme + * ------------------------------------------- + */ + + .ios body { + --ion-background-color: #000000; + --ion-background-color-rgb: 0, 0, 0; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-color-step-50: #0d0d0d; + --ion-color-step-100: #1a1a1a; + --ion-color-step-150: #262626; + --ion-color-step-200: #333333; + --ion-color-step-250: #404040; + --ion-color-step-300: #4d4d4d; + --ion-color-step-350: #595959; + --ion-color-step-400: #666666; + --ion-color-step-450: #737373; + --ion-color-step-500: #808080; + --ion-color-step-550: #8c8c8c; + --ion-color-step-600: #999999; + --ion-color-step-650: #a6a6a6; + --ion-color-step-700: #b3b3b3; + --ion-color-step-750: #bfbfbf; + --ion-color-step-800: #cccccc; + --ion-color-step-850: #d9d9d9; + --ion-color-step-900: #e6e6e6; + --ion-color-step-950: #f2f2f2; + + --ion-item-background: #000000; + + --ion-card-background: #1c1c1d; + } + + .ios ion-modal { + --ion-background-color: var(--ion-color-step-100); + --ion-toolbar-background: var(--ion-color-step-150); + --ion-toolbar-border-color: var(--ion-color-step-250); + } + + /* + * Material Design Dark Theme + * ------------------------------------------- + */ + + .md body { + --ion-background-color: #121212; + --ion-background-color-rgb: 18, 18, 18; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-border-color: #222222; + + --ion-color-step-50: #1e1e1e; + --ion-color-step-100: #2a2a2a; + --ion-color-step-150: #363636; + --ion-color-step-200: #414141; + --ion-color-step-250: #4d4d4d; + --ion-color-step-300: #595959; + --ion-color-step-350: #656565; + --ion-color-step-400: #717171; + --ion-color-step-450: #7d7d7d; + --ion-color-step-500: #898989; + --ion-color-step-550: #949494; + --ion-color-step-600: #a0a0a0; + --ion-color-step-650: #acacac; + --ion-color-step-700: #b8b8b8; + --ion-color-step-750: #c4c4c4; + --ion-color-step-800: #d0d0d0; + --ion-color-step-850: #dbdbdb; + --ion-color-step-900: #e7e7e7; + --ion-color-step-950: #f3f3f3; + + --ion-item-background: #1e1e1e; + + --ion-toolbar-background: #1f1f1f; + + --ion-tab-bar-background: #1f1f1f; + + --ion-card-background: #1e1e1e; + } + } +} diff --git a/03_source/mobile/src/pages/DemoStickyBottomSheetExample/AppPages/Tab1.jsx b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/AppPages/Tab1.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoStickyBottomSheetExample/AppPages/Tab1.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoBankingUi/AppPages/Tab1.jsx diff --git a/03_source/mobile/src/pages/DemoReactPollApp/AppPages/Tab2.jsx b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/AppPages/Tab2.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactPollApp/AppPages/Tab2.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoBankingUi/AppPages/Tab2.jsx diff --git a/03_source/mobile/src/pages/DemoBankingUi/notes.md b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/NOTES.md similarity index 100% rename from 03_source/mobile/src/pages/DemoBankingUi/notes.md rename to 03_source/mobile.trunk.1/src/pages/DemoBankingUi/NOTES.md diff --git a/03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/CardSlide.jsx b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/CardSlide.jsx new file mode 100644 index 0000000..40f29e0 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/CardSlide.jsx @@ -0,0 +1,97 @@ +import { IonButton, IonCardSubtitle, IonCol, IonIcon, IonList, IonRow } from '@ionic/react'; +import DebitCard from './DebitCard'; + +import styles from './CardSlide.module.css'; +import TransactionItem from './TransactionItem'; +import { addOutline, arrowRedoOutline } from 'ionicons/icons'; +import { formatBalance } from '../data/Utils'; + +const CardSlide = (props) => { + const { index, card, profile } = props; + + return ( + <> + + + balance + + £ +  {formatBalance(card.balance)} + + + + + + + + + + + + + + +
Transactions
+
+
+ + {card.transactions.length > 0 && ( + + + + {card.transactions.length > 0 && + card.transactions + .slice(0) + .reverse() + .map((transaction, index) => ( + + ))} + + + + )} + + {card.transactions.length === 0 && ( + + +
No transactions found
+ + +  Transfer funds + +
+
+ )} + + ); +}; + +export default CardSlide; diff --git a/03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/CardSlide.module.css b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/CardSlide.module.css new file mode 100644 index 0000000..7ef5587 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/CardSlide.module.css @@ -0,0 +1,51 @@ +.customSlide { + display: flex; + flex-direction: column; +} + +.transactionList { + overflow: scroll; + width: 100vw; +} + +.balance { + font-weight: 300; + font-size: 1.5rem; + color: black; + + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + align-items: center; +} + +.poundSign { + font-weight: 800; + font-size: 1.2rem; +} + +.heading h6 { + padding: 0; + margin: 0; + text-align: left !important; + float: left !important; + text-align: left !important; + color: rgb(124, 124, 124); + font-weight: 400; +} + +.heading { + width: 83%; + padding: 0; + margin: 0; + margin-top: 0.75rem; +} + +.addButton { + --border-radius: 500px !important; + width: fit-content !important; + margin-top: 0.45rem; + margin-left: 1rem; + opacity: 0.6; +} diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/CurrentWeather/WeatherProperty.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/TestComponents/CurrentWeather/WeatherProperty.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/CurrentWeather/WeatherProperty.tsx diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/CurrentWeather/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/TestComponents/CurrentWeather/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/CurrentWeather/index.tsx diff --git a/03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/DebitCard.jsx b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/DebitCard.jsx new file mode 100644 index 0000000..e552f68 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/DebitCard.jsx @@ -0,0 +1,68 @@ +import { useEffect, useState } from 'react'; +import styles from './DebitCard.module.css'; + +const DebitCard = (props) => { + const { type, number, profile, expiry, secret, color } = props; + const [lastFourCardNumbers, setLastFourCardNumbers] = useState('****'); + + const cardClass = `card_${color}`; + const cardTypeLogo = type === 'visa' ? '/visa.png' : '/mastercard.png'; + + useEffect(() => { + var lastFourNumbers = number ? number.substr(number.length - 4) : '1234'; + setLastFourCardNumbers(lastFourNumbers); + }, [number]); + + return ( +
+
+ 1 + 1 + 2 +

**** **** **** {lastFourCardNumbers}

+
+ Card holder +

{`${profile.firstname} ${profile.surname}`}

+
+
+ Expires +

{expiry}

+
+
+ +
+
+
+
+

{secret}

+
+ + 3 + 5 +
+
+
+ ); +}; + +export default DebitCard; diff --git a/03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/DebitCard.module.css b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/DebitCard.module.css new file mode 100644 index 0000000..b2064a5 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/DebitCard.module.css @@ -0,0 +1,188 @@ +@import url('https://fonts.googleapis.com/css?family=Space+Mono:400,400i,700,700i'); + +.card { + box-sizing: border-box; + font-family: 'Space Mono', monospace; + margin: 0 auto; +} + +.title { + margin-bottom: 30px; + color: #162969; +} + +.card { + width: 320px; + height: 190px; + -webkit-perspective: 600px; + -moz-perspective: 600px; + perspective: 600px; +} + +.card__part { + box-shadow: 1px 1px #aaa3a3; + top: 0; + position: absolute; + z-index: 1000; + left: 0; + display: inline-block; + width: 320px; + height: 190px; + background-repeat: no-repeat; + background-position: center; + background-size: cover; + border-radius: 8px; + + -webkit-transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); + -moz-transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); + -ms-transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); + -o-transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); + transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); + -webkit-transform-style: preserve-3d; + -moz-transform-style: preserve-3d; + -webkit-backface-visibility: hidden; + -moz-backface-visibility: hidden; +} + +.card_orange { + background: linear-gradient(to right bottom, #fd696b, #fa616e, #f65871, #c74261, #d62158); +} + +.card_blue { + background: linear-gradient(to right bottom, #699dfd, #61b5fa, #58aff6, #4b86c9, #2151d6); +} + +.card_black { + background: linear-gradient(to right bottom, #292929, #363636, #555555, #444444, #0f0f0f); +} + +.card_purple { + background: linear-gradient(to right bottom, #7a43df, #644897, #8964cf, #633cac, #512c96); +} + +.card__front { + padding: 18px; + -webkit-transform: rotateY(0); + -moz-transform: rotateY(0); +} + +.card__back { + padding: 18px 0; + -webkit-transform: rotateY(-180deg); + -moz-transform: rotateY(-180deg); +} + +.card__black_line { + margin-top: 5px; + height: 38px; + background-color: #303030; +} + +.card__logo { + height: 16px !important; +} + +.card__front_chip { + left: 1.2rem; + height: 1.5rem !important; + position: absolute; +} + +.card__front_logo { + position: absolute; + top: 18px; + right: 18px; +} + +.card__square { + border-radius: 5px; + height: 30px !important; +} + +.card_number { + display: block; + width: 100%; + word-spacing: 4px; + font-size: 20px; + letter-spacing: 2px; + color: #fff; + text-align: center; + margin-bottom: 20px; + margin-top: 20px; +} + +.card__space_75 { + width: 75%; + float: left; +} + +.card__space_25 { + width: 25%; + float: left; +} + +.card__label { + font-size: 10px; + text-transform: uppercase; + color: rgba(255, 255, 255, 0.8); + letter-spacing: 1px; +} + +.card__info { + margin-bottom: 0; + margin-top: 5px; + font-size: 16px; + line-height: 18px; + color: #fff; + letter-spacing: 1px; + text-transform: uppercase; +} + +.card__back_content { + padding: 15px 15px 0; +} +.card__secret__last { + color: #303030; + text-align: right; + margin: 0; + font-size: 14px; +} + +.card__secret { + padding: 5px 12px; + background-color: #fff; + position: relative; +} + +.card__secret:before { + content: ''; + position: absolute; + top: -3px; + left: -3px; + height: calc(100% + 6px); + width: calc(100% - 42px); + border-radius: 4px; + background: repeating-linear-gradient(45deg, #ededed, #ededed 5px, #f9f9f9 5px, #f9f9f9 10px); +} + +.card__back_logo { + position: absolute; + bottom: 15px; + right: 15px; +} + +.card__back_square { + position: absolute; + bottom: 15px; + left: 15px; +} + +.card:hover .card__front { + -webkit-transform: rotateY(180deg); + -moz-transform: rotateY(180deg); +} + +.card:hover .card__back { + -webkit-transform: rotateY(0deg); + -moz-transform: rotateY(0deg); +} diff --git a/03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/SkeletonDashboard/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..ae39e44 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/SkeletonDashboard/index.tsx @@ -0,0 +1,118 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import React from 'react'; + +export const SkeletonDashboard = (): React.JSX.Element => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/TransactionItem.jsx b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/TransactionItem.jsx new file mode 100644 index 0000000..b36f7c0 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/TransactionItem.jsx @@ -0,0 +1,60 @@ +import { IonAvatar, IonItem, IonLabel } from '@ionic/react'; +import { formatBalance } from '../data/Utils'; +import styles from './TransactionItem.module.css'; + +const TransactionItem = (props) => { + const { name, amount, deposit, color } = props; + + const getContactNameInitials = (contactName) => { + var nameInitials = ''; + + if (contactName && contactName !== '' && contactName !== undefined) { + const nameParts = contactName && contactName.split(' '); + + if (nameParts) { + if (nameParts[0].charAt(0).match(/^[a-z]+$/i)) { + nameInitials += nameParts[0].charAt(0).toUpperCase(); + } + + if (nameParts[1]) { + if (nameParts[1].charAt(0).match(/^[a-z]+$/i)) { + nameInitials += nameParts[1].charAt(0).toUpperCase(); + } + } else { + nameInitials += nameParts[0].charAt(1).toUpperCase(); + } + } + } + + return nameInitials; + }; + + return ( + +
+ +
+ {getContactNameInitials(name)} +
+
+ + +

{name}

+
+ + +

+ {deposit ? '+' : '-'} + £{formatBalance(amount)} +

+
+
+
+ ); +}; + +export default TransactionItem; diff --git a/03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/TransactionItem.module.css b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/TransactionItem.module.css new file mode 100644 index 0000000..68649fd --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/components/TransactionItem.module.css @@ -0,0 +1,48 @@ +.avatarImage { + /* background-color: var(--ion-color); */ + width: 2.5rem; + height: 2.5rem; + border-radius: 500px; + color: black; + font-size: 1.3rem; + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + align-items: center; + padding: 0.5rem !important; + border: 2px solid rgb(44, 44, 44); + margin-top: 0.2rem; +} + +.transactionItem { + flex-direction: row; + padding: 0; + margin: 0; +} + +.transactionItemContent { + padding-left: 3rem; + padding-right: 2rem; + display: flex !important; + flex-direction: row !important; + justify-content: space-between; + width: 100%; + align-content: center; + align-items: center; + margin-top: -0.2rem; + margin-bottom: -0.2rem; +} + +.transactionContent { + padding: 1rem; + text-align: left !important; +} + +.green { + color: rgb(0, 165, 0); +} + +.red { + color: red; +} diff --git a/03_source/mobile.trunk.1/src/pages/DemoBankingUi/data/AccountStore.js b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/data/AccountStore.js new file mode 100644 index 0000000..d8f7c7d --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/data/AccountStore.js @@ -0,0 +1,127 @@ +import { Store } from 'pullstate'; + +export const AccountStore = new Store({ + profile: { + firstname: 'Alan', + surname: 'Montgomery', + avatar: '/assets/DemoBankingUi/alan.jpg', + }, + cards: [ + { + id: 1, + type: 'visa', + description: 'Current Account', + number: '4859 2390 5635 7347', + expiry: '11/22', + secret: '483', + color: 'orange', + balance: '38.21', + transactions: [ + { + name: 'Joe Bloggs', + amount: '2.50', + deposit: true, + }, + { + name: 'Ocean Pratt', + amount: '12.99', + deposit: true, + }, + { + name: 'Eugene Piper', + amount: '74.99', + deposit: false, + }, + { + name: 'Emeli Potts', + amount: '4.20', + deposit: false, + }, + { + name: 'Asia Wells', + amount: '12.73', + deposit: true, + }, + { + name: 'Awais Brook', + amount: '17.10', + deposit: false, + }, + { + name: 'Coen Haas', + amount: '9.99', + deposit: true, + }, + ], + }, + { + id: 2, + type: 'visa', + description: 'Savings', + number: '7349 1284 6790 4587', + expiry: '05/23', + secret: '590', + color: 'blue', + balance: '120.90', + transactions: [ + { + name: 'Joe Bloggs', + amount: '120.90', + deposit: true, + }, + ], + }, + { + id: 3, + type: 'visa', + description: 'House Fund', + number: '6783 5692 4475 6682', + expiry: '01/24', + secret: '321', + color: 'purple', + balance: '0', + transactions: [], + }, + ], +}); + +export const addCardToAccount = (newCard) => { + AccountStore.update((s) => { + s.cards = [...s.cards, newCard]; + }); +}; + +export const addTransactionToCard = (newTransaction, cardID) => { + AccountStore.update((s) => { + s.cards.find((c, index) => + parseInt(c.id) === parseInt(cardID) + ? (s.cards[index].transactions = [...s.cards[index].transactions, newTransaction]) + : false + ); + }); + + if (newTransaction.deposit) { + AccountStore.update((s) => { + s.cards.find((c, index) => + parseInt(c.id) === parseInt(cardID) + ? (s.cards[index].balance = + parseFloat(s.cards[index].balance) + parseFloat(newTransaction.amount)) + : false + ); + }); + } else { + AccountStore.update((s) => { + s.cards.find((c, index) => + parseInt(c.id) === parseInt(cardID) + ? (s.cards[index].balance = + parseFloat(s.cards[index].balance) - parseFloat(newTransaction.amount)) + : false + ); + }); + } +}; + +// export const removeFromCart = productIndex => { + +// AccountStore.update(s => { s.product_ids.splice(productIndex, 1) }); +// } diff --git a/03_source/mobile/src/pages/DemoBankingUi/data/CardStore.js b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/data/CardStore.js similarity index 100% rename from 03_source/mobile/src/pages/DemoBankingUi/data/CardStore.js rename to 03_source/mobile.trunk.1/src/pages/DemoBankingUi/data/CardStore.js diff --git a/03_source/mobile/src/pages/DemoBankingUi/data/Utils.js b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/data/Utils.js similarity index 100% rename from 03_source/mobile/src/pages/DemoBankingUi/data/Utils.js rename to 03_source/mobile.trunk.1/src/pages/DemoBankingUi/data/Utils.js diff --git a/03_source/mobile.trunk.1/src/pages/DemoBankingUi/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/index.tsx new file mode 100644 index 0000000..ca65ad4 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/index.tsx @@ -0,0 +1,48 @@ +// REQ0119/demo-banking-ui +// +// RULES: +// T.B.A. +// + +import { IonRouterOutlet, IonTabs } from '@ionic/react'; + +import { Route, Redirect } from 'react-router'; + +// import Tab1 from './AppPages/Tab1'; +// import Tab2 from './AppPages/Tab2'; + +import Home from './pages/Home'; +import Account from './pages/Account'; +import AddCard from './pages/AddCard'; +import AddTransaction from './pages/AddTransaction'; + +import './style.scss'; +import React from 'react'; + +function DemoBankingUi(): React.JSX.Element { + return ( + + + + + + + + + + + + + + + + + + + + + + ); +} + +export default DemoBankingUi; diff --git a/03_source/mobile.trunk.1/src/pages/DemoBankingUi/pages/Account.jsx b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/pages/Account.jsx new file mode 100644 index 0000000..5145b51 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/pages/Account.jsx @@ -0,0 +1,103 @@ +import { + IonBackButton, + IonButton, + IonButtons, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonItem, + IonLabel, + IonPage, + IonRow, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import styles from './Account.module.css'; +import { AccountStore } from '../data/AccountStore'; +import { addOutline, logOutOutline } from 'ionicons/icons'; +import { formatBalance } from '../data/Utils'; + +const Account = () => { + const cards = AccountStore.useState((s) => s.cards); + const profile = AccountStore.useState((s) => s.profile); + + return ( + + + + + + + + Account + + + + + + + + + + + + + + account avatar + + + + + +
{`${profile.firstname} ${profile.surname}`}
+
{cards.length} current cards
+
+
+ + + + + + Add Card + + + + +
+ {cards.map((card, index) => { + return ( + + + +
+ + +

{card.description}

+
+ + +

£{formatBalance(card.balance)}

+
+
+
+
+ ); + })} +
+
+
+
+ ); +}; + +export default Account; diff --git a/03_source/mobile.trunk.1/src/pages/DemoBankingUi/pages/Account.module.css b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/pages/Account.module.css new file mode 100644 index 0000000..8e59143 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/pages/Account.module.css @@ -0,0 +1,54 @@ +.accountPage ion-toolbar { + --border-style: none; + --padding-top: 1rem; + --padding-bottom: 1rem; + padding-left: 1rem; + padding-right: 1rem; +} + +.avatar { + border-radius: 500px; + border: 3px solid var(--ion-color-primary); + padding: 0.2rem; +} + +.profileDetails { +} + +.profileDetails h6, +.profileDetails h5 { + margin: 0; + padding: 0; +} + +.profileDetails h6 { + color: var(--ion-color-medium); +} + +.cards { + /* margin-top: */ +} + +.smallCard { + width: 15%; + height: 80%; + border-radius: 5px; + opacity: 0.7; +} + +.cardDescription { + padding-left: 1.5rem; +} + +.cardDescription h4 { + font-weight: 400; +} + +.cardItem { + --background: rgb(246, 246, 246); + --border-radius: 5px; + --padding-start: 1rem; + --padding-end: 1rem; + --padding-top: 0.5rem; + --padding-bottom: 0.5rem; +} diff --git a/03_source/mobile.trunk.1/src/pages/DemoBankingUi/pages/AddCard.jsx b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/pages/AddCard.jsx new file mode 100644 index 0000000..41dd2f2 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/pages/AddCard.jsx @@ -0,0 +1,247 @@ +import { useState } from 'react'; +import { + IonBackButton, + IonButton, + IonButtons, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonInput, + IonItem, + IonLabel, + IonPage, + IonRow, + IonSelect, + IonSelectOption, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import styles from './Account.module.css'; +import DebitCard from '../components/DebitCard'; +import { AccountStore, addCardToAccount } from '../data/AccountStore'; +import { CardStore } from '../data/CardStore'; +import { addOutline, timerOutline } from 'ionicons/icons'; +import { useHistory } from 'react-router'; + +const AddCard = () => { + const cards = AccountStore.useState((s) => s.cards); + const cardTypes = CardStore.useState((s) => s.card_types); + const cardColors = CardStore.useState((s) => s.card_colors); + const profile = AccountStore.useState((s) => s.profile); + + const [cardType, setCardType] = useState(cardTypes[0]); + const [cardColor, setCardColor] = useState(cardColors[0]); + const [cardDescription, setCardDescription] = useState(''); + const [cardNumber, setCardNumber] = useState('1234 1234 1234 1234'); + const [cardSecret, setCardSecret] = useState('123'); + const [cardExpiry, setCardExpiry] = useState('01/22'); + const [cardBalance, setCardBalance] = useState(0); + + const history = useHistory(); + const [adding, setAdding] = useState(false); + + const addCard = async () => { + setAdding(true); + + const newCard = { + id: cards.length + 1, + type: cardType, + color: cardColor, + description: cardDescription, + number: cardNumber, + secret: cardSecret, + expiry: cardExpiry, + balance: cardBalance, + transactions: [ + { + name: 'Starting Balance', + amount: cardBalance, + deposit: true, + }, + ], + }; + + await addCardToAccount(newCard); + + setTimeout(() => { + setAdding(false); + history.goBack(); + }, 500); + }; + + return ( + + + + + + + + Add Card + + + + + + + + + + + + + + + Card Type + setCardType(e.currentTarget.value)} + > + {cardTypes.map((option, index) => { + return ( + + {option.toUpperCase()} + + ); + })} + + + + + + + Card Color + setCardColor(e.currentTarget.value)} + > + {cardColors.map((option, index) => { + return ( + + {option.toUpperCase()} + + ); + })} + + + + + + + + + Card Name + setCardDescription(e.currentTarget.value)} + /> + + + + + + Starting Balance + setCardBalance(e.currentTarget.value)} + /> + + + + + + + + Card Number + setCardNumber(e.currentTarget.value)} + /> + + + + + + + + Card Expiry + setCardExpiry(e.currentTarget.value)} + /> + + + + + + Card Secret + setCardSecret(e.currentTarget.value)} + /> + + + + + + + + {!adding && ( + <> + +   Add Card + + )} + + {adding && ( + <> + +   Adding... + + )} + + + + + + + ); +}; + +export default AddCard; diff --git a/03_source/mobile/src/pages/DemoBankingUi/pages/AddTransaction.jsx b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/pages/AddTransaction.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoBankingUi/pages/AddTransaction.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoBankingUi/pages/AddTransaction.jsx diff --git a/03_source/mobile/src/pages/DemoBankingUi/pages/Home.jsx b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/pages/Home.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoBankingUi/pages/Home.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoBankingUi/pages/Home.jsx diff --git a/03_source/mobile.trunk.1/src/pages/DemoBankingUi/pages/Home.module.css b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/pages/Home.module.css new file mode 100644 index 0000000..5d5719c --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/pages/Home.module.css @@ -0,0 +1,38 @@ +.homePage ion-toolbar { + --border-style: none; + --padding-top: 1rem; + --padding-bottom: 1rem; + padding-left: 1rem; + padding-right: 1rem; +} + +.customSlide { + display: flex; + flex-direction: column; +} + +.transactionList { + overflow: scroll; + width: 100vw; +} + +.balance { + font-weight: 300; + font-size: 1.5rem; + color: black; +} + +.poundSign { + font-weight: 800; + font-size: 1.2rem; +} + +.toolbarAvatarImage { + border-radius: 500px; + height: 100%; + width: auto; +} + +.helloworld { + background-color: gold; +} diff --git a/03_source/mobile.trunk.1/src/pages/DemoBankingUi/pages/Home.module.scss b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/pages/Home.module.scss new file mode 100644 index 0000000..0065249 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/pages/Home.module.scss @@ -0,0 +1,38 @@ +.homePage ion-toolbar { + --border-style: none; + --padding-top: 1rem; + --padding-bottom: 1rem; + padding-left: 1rem; + padding-right: 1rem; +} + +.customSlide { + display: flex; + flex-direction: column; +} + +.transactionList { + overflow: scroll; + width: 100vw; +} + +.balance { + font-weight: 300; + font-size: 1.5rem; + color: black; +} + +.poundSign { + font-weight: 800; + font-size: 1.2rem; +} + +.toolbarAvatarImage { + border-radius: 500px; + height: 32px; + width: auto; +} + +.helloworld { + background-color: cyan; +} diff --git a/03_source/mobile.trunk.1/src/pages/DemoBankingUi/style.scss b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/style.scss new file mode 100644 index 0000000..378cae3 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoBankingUi/style.scss @@ -0,0 +1,139 @@ +.demo-banking-ui { + /* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + + /** Ionic CSS Variables **/ + * { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + } + + ion-item { + --padding-start: 0; + --inner-padding-end: 0; + } + + ion-label { + margin-top: 12px; + margin-bottom: 12px; + } + + ion-item h2 { + font-weight: 600; + margin: 0; + } + + ion-item p { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + width: 95%; + } + + ion-item .date { + float: right; + align-items: center; + display: flex; + } + + ion-item ion-icon { + color: #c9c9ca; + } + + ion-item ion-note { + font-size: 15px; + margin-right: 8px; + font-weight: normal; + } + + ion-item ion-note.md { + margin-right: 14px; + } + + .dot { + display: block; + height: 12px; + width: 12px; + border-radius: 50%; + align-self: start; + margin: 16px 10px 16px 16px; + } + + .dot-unread { + background: var(--ion-color-primary); + } + + ion-footer ion-title { + font-size: 11px; + font-weight: normal; + } +} diff --git a/03_source/mobile.trunk.1/src/pages/DemoBlogPostUi/AppPages/BlogPost.css b/03_source/mobile.trunk.1/src/pages/DemoBlogPostUi/AppPages/BlogPost.css new file mode 100644 index 0000000..e754dfd --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoBlogPostUi/AppPages/BlogPost.css @@ -0,0 +1,7 @@ +.view-post-footer { + + background-color: white; + padding-left: 1rem; + padding-right: 1rem; + padding-bottom: 1rem; +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoBlogPostUi/AppPages/BlogPost.jsx b/03_source/mobile.trunk.1/src/pages/DemoBlogPostUi/AppPages/BlogPost.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoBlogPostUi/AppPages/BlogPost.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoBlogPostUi/AppPages/BlogPost.jsx diff --git a/03_source/mobile/src/pages/DemoBlogPostUi/AppPages/Home.jsx b/03_source/mobile.trunk.1/src/pages/DemoBlogPostUi/AppPages/Home.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoBlogPostUi/AppPages/Home.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoBlogPostUi/AppPages/Home.jsx diff --git a/03_source/mobile/src/pages/DemoBlogPostUi/notes.md b/03_source/mobile.trunk.1/src/pages/DemoBlogPostUi/NOTES.md similarity index 100% rename from 03_source/mobile/src/pages/DemoBlogPostUi/notes.md rename to 03_source/mobile.trunk.1/src/pages/DemoBlogPostUi/NOTES.md diff --git a/03_source/mobile.trunk.1/src/pages/DemoBlogPostUi/components/Post.css b/03_source/mobile.trunk.1/src/pages/DemoBlogPostUi/components/Post.css new file mode 100644 index 0000000..4b6c13d --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoBlogPostUi/components/Post.css @@ -0,0 +1,37 @@ +.post-author-avatar { + height: 2rem; + width: 2rem; + border-radius: 500px; +} + +.post-title { + font-size: 1.4rem; + margin-top: 0.75rem; +} + +.post-content { + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; +} + +.post-footer { + display: flex; + flex-direction: row; + justify-content: space-between; + align-content: center; + width: 100%; + border-top: 2px solid rgb(245, 245, 245); + margin-top: 2rem; + padding-top: 1rem; +} + +.post-category { + margin-top: 1.1rem; +} + +.post-image { + width: 100%; +} diff --git a/03_source/mobile/src/pages/DemoBlogPostUi/components/Post.jsx b/03_source/mobile.trunk.1/src/pages/DemoBlogPostUi/components/Post.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoBlogPostUi/components/Post.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoBlogPostUi/components/Post.jsx diff --git a/03_source/mobile.trunk.1/src/pages/DemoBlogPostUi/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoBlogPostUi/index.tsx new file mode 100644 index 0000000..0b67601 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoBlogPostUi/index.tsx @@ -0,0 +1,33 @@ +// REQ0119/demo-blog-post-ui +// +// RULES: +// T.B.A. +// + +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Home from './AppPages/Home'; +import BlogPost from './AppPages/BlogPost'; + +import './style.scss'; + +function DemoBlogPostUi() { + return ( + + + + + + + + + + + + ); +} + +export default DemoBlogPostUi; diff --git a/03_source/mobile.trunk.1/src/pages/DemoBlogPostUi/localData/index.js b/03_source/mobile.trunk.1/src/pages/DemoBlogPostUi/localData/index.js new file mode 100644 index 0000000..a9b37fc --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoBlogPostUi/localData/index.js @@ -0,0 +1,117 @@ +export const blogPosts = [ + { + id: 1, + title: 'How to Convince Your Boss to Choose Ionic', + title_link: 'https://ionicframework.com/blog/convince-boss-choose-ionic-app-development/', + date: 'August 3, 2021', + author: 'By Kim Maida', + authorImage: 'https://ionicframework.com/blog/wp-content/uploads/2021/07/kim-maida-150x150.jpg', + category: 'ANNOUNCEMENTS', + category_link: 'https://ionicframework.com/blog//blog/category/announcements', + image: + 'https://ionicframework.com/blog/wp-content/uploads/2021/07/how-to-convince-your-boss_image_1aug2021.png', + content: + 'Greetings, friend! You’re a web developer, team lead, or engineering manager who has discovered that Ionic products are awesome. They have helped you build cross-platform applications quickly, made the app development process enjoyable, and solved important mobile development problems. You can see that Ionic would be extremely beneficial in your daily job, but are wondering how to convince your boss to endorse the adoption of new software. In a nutshell:', + }, + { + id: 2, + title: 'Ioniconf 2021 Conference Recap', + title_link: 'https://ionicframework.com/blog/ioniconf-2021-conference-recap/', + date: 'July 29, 2021', + author: 'By Mike Hartington', + authorImage: + 'https://ionicframework.com/blog/wp-content/uploads/2018/08/mike-headshot-2-smaller-150x150.png', + category: 'ANNOUNCEMENTS', + category_link: 'https://ionicframework.com/blog//blog/category/announcements', + image: 'https://ionicframework.com/blog/wp-content/uploads/2021/06/og-imgx2.png', + content: + 'And with that, Ioniconf 2021 has concluded! Ioniconf, our online conference for Ionic developers and the wider web development community, featured twelve expert Ionic speakers and was attended by many thousands of Ionic community members. We’re thrilled by the community’s reception to the event and are already looking forward to our next event taking place in September. Read on for a recap and links to all recorded talks.', + }, + { + id: 3, + title: 'Announcing Identity Vault 5.0', + title_link: 'https://ionicframework.com/blog/announcing-identity-vault-5-0/', + date: 'July 28, 2021', + author: 'By Dallas James', + authorImage: + 'https://ionicframework.com/blog/wp-content/uploads/2021/07/dallas-james-150x150.jpg', + category: 'PRODUCT', + category_link: 'https://ionicframework.com/blog//blog/category/announcements', + image: 'https://ionicframework.com/blog/wp-content/uploads/2021/07/iv-5-feature-image.png', + content: + 'Today I’m excited to announce Identity Vault 5.0, the newest version of Ionic’s mobile biometrics solution. Featuring the latest in native security best practices, Identity Vault improves frontend security in any Ionic app by making it easy to add secure biometric authentication in minutes.', + }, + { + id: 4, + title: 'Building with Stencil: Clock Component', + title_link: 'https://ionicframework.com/blog/building-with-stencil-clock-component/', + date: 'July 22, 2021', + author: 'By Kevin Hoyt', + authorImage: 'https://ionicframework.com/blog/wp-content/uploads/2021/07/2520666-150x150.jpg', + category: 'ANNOUNCEMENTS', + category_link: 'https://ionicframework.com/blog//blog/category/announcements', + image: 'https://ionicframework.com/blog/wp-content/uploads/2021/07/Image-from-iOS.png', + content: + 'I have not seen a clock in a web-based user interface in a long time. This makes sense — they are pretty redundant these days. You have a clock on your watch, on your mobile device, and on your desktop, and those are just the digital versions available at a glance. Nonetheless, the process of building a clock can reveal a lot about how a platform works.', + }, + { + id: 5, + title: 'Building with Stencil: Calendar Component', + title_link: 'https://ionicframework.com/blog/building-with-stencil-calendar-component/', + date: 'July 19, 2021', + author: 'By Kevin Hoyt', + authorImage: 'https://ionicframework.com/blog/wp-content/uploads/2021/07/2520666-150x150.jpg', + category: 'TUTORIALS', + category_link: null, + image: + 'https://ionicframework.com/blog/wp-content/uploads/2021/07/ionic-blog-post-image_first-look-01.png', + content: + 'Take a look at the month view of a calendar and you will see several rows of numbers. The numbers themselves, increasing in value one after the other, are arranged in columns. HTML and CSS provide us with a number of tools to display content in rows and columns. Making a calendar component should be easy, right? Right?', + }, + { + id: 6, + title: 'Introducing the New Overlay Hooks for Ionic React', + title_link: + 'https://ionicframework.com/blog/introducing-the-new-overlay-hooks-for-ionic-react/', + date: 'July 14, 2021', + author: 'By Ely Lucas', + authorImage: 'https://secure.gravatar.com/avatar/45ad19965b4bde97e9f4396ea01ed184?s=32&r=g', + category: 'ENGINEERING', + category_link: null, + image: + 'https://ionicframework.com/blog/wp-content/uploads/2021/07/react-overlay-hooks-feature-image.png', + content: + 'Hello Friends! We know everyone is excited about the new features in Ionic Framework 6.0 beta, but that doesn’t mean we’re done with V5! In Ionic React 5.6, we packaged up a new set of hooks for controlling our overlay components that we think you might like. What is an overlay you ask? It’s the term we give components that display over your current content, such as alerts, modals, toasts, etc.', + }, + { + id: 7, + title: 'The Future of Stencil: Expanded Team, New Software Platform, and More', + title_link: + 'https://ionicframework.com/blog/the-future-of-stencil-expanded-team-new-software-platform-and-more/', + date: 'July 7, 2021', + author: 'By Nick Hyatt', + authorImage: + 'https://ionicframework.com/blog/wp-content/uploads/2018/11/Nick-Hyatt-Headshot-150x150.jpeg', + category: 'ANNOUNCEMENTS', + category_link: null, + image: + 'https://ionicframework.com/blog/wp-content/uploads/2021/07/stencil-future-feature-image.png', + content: + 'Today I’m excited to share some news about Stencil, Ionic’s open source toolchain that generates small, fast, and 100% standards-based Web Components that run in every browser. As you might have noticed, we’ve been actively increasing our investments across the entire Ionic App Platform, including the recent launch of Capacitor 3.0, Ionic Portals, tons of Appflow improvements, and the upcoming Ionic Framework v6.', + }, + { + id: 8, + title: 'Announcing the Ionic Framework v6 Beta', + title_link: 'https://ionicframework.com/blog/announcing-the-ionic-framework-v6-beta/', + date: 'June 29, 2021', + author: 'By Liam DeBeasi', + authorImage: + 'https://ionicframework.com/blog/wp-content/uploads/2020/01/ZNK4lRAJ_400x400-150x150.jpg', + category: 'ANNOUNCEMENTS', + category_link: null, + image: + 'https://ionicframework.com/blog/wp-content/uploads/2021/06/framework6-feature-image.png', + content: + 'Earlier this week I had the privilege of giving the Ionic Framework Update at Ioniconf 2021 where we announced the Ionic Framework v6 beta. Ionic Framework has come far from its roots as an AngularJS-only UI library to a truly cross-platform framework for building Web Native applications. As we look to the future of Ionic Framework, let’s talk about some of the improvements coming in Framework v6 and how you can get access to these improvements today.', + }, +]; diff --git a/03_source/mobile.trunk.1/src/pages/DemoBlogPostUi/style.scss b/03_source/mobile.trunk.1/src/pages/DemoBlogPostUi/style.scss new file mode 100644 index 0000000..83c6630 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoBlogPostUi/style.scss @@ -0,0 +1,79 @@ +.demo-blog-post-ui { + /* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + + /** Ionic CSS Variables **/ + * { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + } +} diff --git a/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/AppPages/Tab1.jsx b/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/AppPages/Tab1.jsx new file mode 100644 index 0000000..54ab2b9 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../components/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../components/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoReactTravelApp/AppPages/Tab2.jsx b/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/AppPages/Tab2.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactTravelApp/AppPages/Tab2.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/AppPages/Tab2.jsx diff --git a/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/notes.md b/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/NOTES.md similarity index 100% rename from 03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/notes.md rename to 03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/NOTES.md diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/components/CurrentWeather/WeatherProperty.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoKanbanBoard/TestComponents/CurrentWeather/WeatherProperty.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/components/CurrentWeather/WeatherProperty.tsx diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/components/CurrentWeather/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoKanbanBoard/TestComponents/CurrentWeather/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/components/CurrentWeather/index.tsx diff --git a/03_source/mobile/src/pages/DemoReactItemList/components/ExploreContainer.css b/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/components/ExploreContainer.css similarity index 100% rename from 03_source/mobile/src/pages/DemoReactItemList/components/ExploreContainer.css rename to 03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/components/ExploreContainer.css diff --git a/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/components/ExploreContainer.tsx b/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/components/ExploreContainer.tsx new file mode 100644 index 0000000..67b9c98 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/components/ExploreContainer.tsx @@ -0,0 +1,23 @@ +import './ExploreContainer.css'; + +interface ContainerProps {} + +const ExploreContainer: React.FC = () => { + return ( +
+ Ready to create an app? +

+ Start with Ionic{' '} + + UI Components + +

+
+ ); +}; + +export default ExploreContainer; diff --git a/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/components/MarkerInfoWindow.jsx b/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/components/MarkerInfoWindow.jsx new file mode 100644 index 0000000..cc2ea78 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/components/MarkerInfoWindow.jsx @@ -0,0 +1,69 @@ +import { + IonButton, + IonCol, + IonContent, + IonGrid, + IonIcon, + IonLabel, + IonNote, + IonRow, +} from '@ionic/react'; +import { + globeOutline, + heartOutline, + locationOutline, + navigateOutline, + phonePortraitOutline, +} from 'ionicons/icons'; + +export const MarkerInfoWindow = ({ marker, dismiss }) => { + return ( + + + + + +

{marker.title}

+ {marker.description} +
+
+
+ + + + + + + {marker.address} + + + + + + + + {marker.website} + + + + + + + + {marker.phone} + + + + + +   Favourite + + + +   Navigate + + +
+
+ ); +}; diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/components/SkeletonDashboard/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/TestComponents/SkeletonDashboard/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/components/SkeletonDashboard/index.tsx diff --git a/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/data/index.js b/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/data/index.js new file mode 100644 index 0000000..c2ce3b5 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/data/index.js @@ -0,0 +1,47 @@ +export const markers = [ + { + lat: 54.5258924, + lng: -6.0812478, + title: 'Man Lee', + description: 'Chinese Takeaway', + address: '122 Ballymacash Rd, Lisburn BT28 3EZ', + website: 'https://m.facebook.com/pages/Man-Lee/228415820694835', + phone: '028 92 662853', + }, + { + lat: 54.5097827, + lng: -6.0572343, + title: 'Cam Hing', + description: 'Chinese Takeaway', + address: '70 Longstone St, Lisburn BT28 1TR', + website: 'https://camhinglisbunr.com', + phone: '028 92 677928', + }, + { + lat: 54.5095162, + lng: -6.0595896, + title: 'Golden Garden', + description: 'Chinese Takeaway', + address: '140 Longstone St, Lisburn BT28 1TR', + website: 'https://golden-garden.business.site', + phone: '028 92 671311', + }, + { + lat: 54.5091808, + lng: -6.0363902, + title: 'Pagoda', + description: 'Chinese Takeaway', + address: '79 Sloan St, Lisburn BT27 5AG', + website: 'https://pagodalisburn.com', + phone: '028 92 665289', + }, + { + lat: 54.5989611, + lng: -5.9972126, + title: 'Little Wing', + description: 'Pizzeria', + address: '10 Ann St, Belfast BT1 4EF', + website: 'https://littlewingpizzeria.com', + phone: '028 90 247000', + }, +]; diff --git a/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/index.tsx new file mode 100644 index 0000000..1d471d0 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/index.tsx @@ -0,0 +1,44 @@ +// REQ0119/demo-capacitor-google-maps-tutorial +// +// RULES: +// T.B.A. +// + +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import Home from './pages/Home'; + +import './style.scss'; + +function DemoCapacitorGoogleMapsTutorial() { + return ( + + + + + + + + + + + + + + + + + ); +} + +export default DemoCapacitorGoogleMapsTutorial; diff --git a/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/pages/Home.css b/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/pages/Home.css new file mode 100644 index 0000000..912345b --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/pages/Home.css @@ -0,0 +1,5 @@ +capacitor-google-map { + display: inline-block; + width: 100%; + height: 86vh; +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/pages/Home.jsx b/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/pages/Home.jsx new file mode 100644 index 0000000..b800259 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/pages/Home.jsx @@ -0,0 +1,130 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonModal, + useIonRouter, + useIonViewWillEnter, +} from '@ionic/react'; +import { useRef, useState } from 'react'; +import './Home.css'; + +import { GoogleMap } from '@capacitor/google-maps'; +import { markers } from '../data'; +import { MarkerInfoWindow } from '../components/MarkerInfoWindow.jsx'; +import { chevronBackOutline } from 'ionicons/icons'; + +const Home = () => { + // This key is now dead! + // Replace with your own :) + // Remember to secure keys using env files or requesting from server! + // This was for demo purposes :) + const key = 'AIzaSyBJwKYcub1yNcDd2V8iu4ZfGvDi4eW_fpU'; + let newMap; + const mapRef = useRef(null); + + const [selectedMarker, setSelectedMarker] = useState(null); + + const [present, dismiss] = useIonModal(MarkerInfoWindow, { + marker: selectedMarker, + }); + + const modalOptions = { + initialBreakpoint: 0.4, + breakpoints: [0, 0.4], + backdropBreakpoint: 0, + onDidDismiss: () => dismiss(), + }; + + const [mapConfig, setMapConfig] = useState({ + zoom: 10, + center: { + lat: markers[0].lat, + lng: markers[0].lng, + }, + }); + + const markerClick = (marker) => { + setSelectedMarker( + markers.filter((m) => m.lat === marker.latitude && m.lng === marker.longitude)[0] + ); + present(modalOptions); + }; + + const addMapMarker = async (marker) => { + await newMap.addMarker({ + coordinate: { + lat: marker.lat, + lng: marker.lng, + }, + title: marker.title, + }); + }; + + const addMapMarkers = () => markers.forEach((marker) => addMapMarker(marker)); + + const createMap = async () => { + if (!mapRef.current) return; + + newMap = await GoogleMap.create({ + id: 'google-map', + element: mapRef.current, + apiKey: key, + config: mapConfig, + }); + + newMap.setOnMarkerClickListener((marker) => markerClick(marker)); + addMapMarkers(); + }; + + useIonViewWillEnter(() => createMap()); + + const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + Capacitor Google Map + + + handleBackClick()}> + + + + + + + + + Capacitor Google Map + + + handleBackClick()}> + + + + + + + + + + + + + + ); +}; + +export default Home; diff --git a/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/style.scss b/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/style.scss new file mode 100644 index 0000000..697843e --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoCapacitorGoogleMapsTutorial/style.scss @@ -0,0 +1,2 @@ +.demo-capacitor-google-maps-tutorial { +} diff --git a/03_source/mobile/src/pages/DemoClubHouse/AppPages/Tab1.css b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/AppPages/Tab1.css similarity index 100% rename from 03_source/mobile/src/pages/DemoClubHouse/AppPages/Tab1.css rename to 03_source/mobile.trunk.1/src/pages/DemoClubHouse/AppPages/Tab1.css diff --git a/03_source/mobile/src/pages/DemoClubHouse/AppPages/Tab1.jsx b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/AppPages/Tab1.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoClubHouse/AppPages/Tab1.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoClubHouse/AppPages/Tab1.jsx diff --git a/03_source/mobile/src/pages/DemoReactTabsMenusCustom/pages/Tab2.css b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/AppPages/Tab2.css similarity index 100% rename from 03_source/mobile/src/pages/DemoReactTabsMenusCustom/pages/Tab2.css rename to 03_source/mobile.trunk.1/src/pages/DemoClubHouse/AppPages/Tab2.css diff --git a/03_source/mobile/src/pages/DemoClubHouse/AppPages/Tab2.jsx b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/AppPages/Tab2.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoClubHouse/AppPages/Tab2.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoClubHouse/AppPages/Tab2.jsx diff --git a/03_source/mobile/src/pages/DemoReactTabsMenusCustom/pages/Tab3.css b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/AppPages/Tab3.css similarity index 100% rename from 03_source/mobile/src/pages/DemoReactTabsMenusCustom/pages/Tab3.css rename to 03_source/mobile.trunk.1/src/pages/DemoClubHouse/AppPages/Tab3.css diff --git a/03_source/mobile/src/pages/DemoClubHouse/AppPages/Tab3.jsx b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/AppPages/Tab3.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoClubHouse/AppPages/Tab3.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoClubHouse/AppPages/Tab3.jsx diff --git a/03_source/mobile/src/pages/DemoClubHouse/notes.md b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/NOTES.md similarity index 100% rename from 03_source/mobile/src/pages/DemoClubHouse/notes.md rename to 03_source/mobile.trunk.1/src/pages/DemoClubHouse/NOTES.md diff --git a/03_source/mobile.trunk.1/src/pages/DemoClubHouse/components/TalkCard.jsx b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/components/TalkCard.jsx new file mode 100644 index 0000000..bc48121 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/components/TalkCard.jsx @@ -0,0 +1,108 @@ +import { IonCardSubtitle, IonIcon, IonModal, IonNote, IonRow, useIonModal } from '@ionic/react'; +import { bulb, micOutline, personOutline } from 'ionicons/icons'; +import { useStoreState } from 'pullstate'; +import { useEffect, useState } from 'react'; + +import { CategoryStore } from '../store'; +import { getPeople } from '../store/PeopleStore'; +import { getCategory } from '../store/Selectors'; + +import styles from './TalkCard.module.css'; +import { TalkModal } from './TalkModal'; + +export const TalkCard = ({ upcoming = false, talk, pageRef }) => { + const talkCategory = useStoreState(CategoryStore, getCategory(talk.category_id)); + const [speakers, setSpeakers] = useState([]); + const [showModal, setShowModal] = useState(false); + + useEffect(() => { + setSpeakers(getPeople(talk.speakers)); + }, [talk]); + + // const [ present, dismiss ] = useIonModal(TalkModal, { + + // dismiss: () => dismiss(), + // talk, + // speakers, + // category: talkCategory + // }); + + // const handleShowTalk = () => { + + // console.log("in here"); + + // present({ + + // // presentingElement: pageRef.current + // }); + // } + + return ( + <> +
setShowModal(true)} + > +
+ + + {talkCategory.name} talks + +
+ +
+

{talk.title}

+
+ + {!upcoming && ( + + {speakers.map((speaker, index) => { + return ( +
+ avatar +
+ ); + })} +
+ )} + + {upcoming && ( +
+ {talk.time} +
+ + {talk.speakers} Speakers +
+
+ )} + + {!upcoming && ( +
+
+ + {talk.speakers} Speakers +
+ +
+ + {talk.audience} Audience +
+
+ )} +
+ + setShowModal(false)} + presentingElement={pageRef.current} + > + setShowModal(false)} + speakers={speakers} + talk={talk} + category={talkCategory} + /> + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoClubHouse/components/TalkCard.module.css b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/components/TalkCard.module.css similarity index 100% rename from 03_source/mobile/src/pages/DemoClubHouse/components/TalkCard.module.css rename to 03_source/mobile.trunk.1/src/pages/DemoClubHouse/components/TalkCard.module.css diff --git a/03_source/mobile.trunk.1/src/pages/DemoClubHouse/components/TalkModal.jsx b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/components/TalkModal.jsx new file mode 100644 index 0000000..ec9b741 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/components/TalkModal.jsx @@ -0,0 +1,110 @@ +import { + IonButton, + IonButtons, + IonCardSubtitle, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { bulb, exitOutline, micOutline, personOutline } from 'ionicons/icons'; +import { useStoreState } from 'pullstate'; +import { PeopleStore } from '../store'; +import { getAllPeople } from '../store/Selectors'; + +import styles from './TalkModal.module.css'; + +export const TalkModal = ({ dismiss, talk, category, speakers }) => { + const people = useStoreState(PeopleStore, getAllPeople); + + return ( + + + + Talk Room + + + + + {/* Leave Room */} + + + + + + + + + +
+ + {category.name} talks +
+
+
+ + + +

{talk.title}

+
+
+ + + +
+ + {talk.speakers} Speakers +
+
+
+ + + {speakers.map((speaker, index) => { + return ( + +
+ avatar +
+

{speaker.name.split(' ')[0]}

+
+ ); + })} +
+ + + +
+ + {talk.audience} Audience +
+
+
+ + + {[...Array(talk.audience)].map((audience, index) => { + return ( + +
+ avatar +
+

{people[Math.floor(Math.random() * 30)].name.split(' ')[0]}

+
+ ); + })} +
+
+
+
+ ); +}; diff --git a/03_source/mobile/src/pages/DemoClubHouse/components/TalkModal.module.css b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/components/TalkModal.module.css similarity index 100% rename from 03_source/mobile/src/pages/DemoClubHouse/components/TalkModal.module.css rename to 03_source/mobile.trunk.1/src/pages/DemoClubHouse/components/TalkModal.module.css diff --git a/03_source/mobile/src/pages/DemoOrderingApp/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/components1/CurrentWeather/WeatherProperty.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/TestComponents/CurrentWeather/WeatherProperty.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoClubHouse/components1/CurrentWeather/WeatherProperty.tsx diff --git a/03_source/mobile/src/pages/DemoOrderingApp/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/components1/CurrentWeather/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/TestComponents/CurrentWeather/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoClubHouse/components1/CurrentWeather/index.tsx diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/components1/SkeletonDashboard/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoKanbanBoard/TestComponents/SkeletonDashboard/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoClubHouse/components1/SkeletonDashboard/index.tsx diff --git a/03_source/mobile.trunk.1/src/pages/DemoClubHouse/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/index.tsx new file mode 100644 index 0000000..f7e8228 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/index.tsx @@ -0,0 +1,122 @@ +// REQ0119/demo-club-house +// +// RULES: +// T.B.A. +// + +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonLabel, + IonPage, + IonRouterOutlet, + IonRow, + IonTabBar, + IonTabButton, + IonTabs, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from './components1/SkeletonDashboard'; +import { + addCircle, + addCircleOutline, + chevronBack, + cloudOutline, + home, + homeOutline, + notifications, + notificationsOutline, + person, + personOutline, + refreshOutline, + search, + searchOutline, +} from 'ionicons/icons'; +import { CurrentWeather } from './components1/CurrentWeather'; +import { IonReactRouter } from '@ionic/react-router'; +import { Route, Redirect } from 'react-router'; +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; +import Tab3 from './AppPages/Tab3'; + +const DemoClubHouse = () => { + const tabs = [ + { + name: 'Home', + url: '/demo-club-house/home', + activeIcon: home, + icon: homeOutline, + component: Tab1, + }, + { + name: 'Search', + url: '/search', + activeIcon: search, + icon: searchOutline, + component: Tab2, + }, + { + name: 'Add', + url: '/add', + activeIcon: addCircle, + icon: addCircleOutline, + component: Tab3, + }, + { + name: 'Account', + url: '/account', + activeIcon: person, + icon: personOutline, + component: Tab3, + }, + { + name: 'Notifications', + url: '/notifications', + activeIcon: notifications, + icon: notificationsOutline, + component: Tab3, + }, + ]; + + const [activeTab, setActiveTab] = useState(tabs[0].name); + + return ( + setActiveTab(e.detail.tab)}> + + {tabs.map((tab, index) => { + return ( + + + + ); + })} + + + + + + + {tabs.map((tab, barIndex) => { + const active = tab.name === activeTab; + + return ( + + + + ); + })} + + + ); +}; + +export default DemoClubHouse; diff --git a/03_source/mobile.trunk.1/src/pages/DemoClubHouse/store/CategoryStore.js b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/store/CategoryStore.js new file mode 100644 index 0000000..81fe162 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/store/CategoryStore.js @@ -0,0 +1,24 @@ +import { Store } from 'pullstate'; + +const CategoryStore = new Store({ + categories: [ + { + id: 1, + name: 'Design', + }, + { + id: 2, + name: 'Javascript', + }, + { + id: 3, + name: 'Mobile', + }, + { + id: 4, + name: 'Business', + }, + ], +}); + +export default CategoryStore; diff --git a/03_source/mobile.trunk.1/src/pages/DemoClubHouse/store/PeopleStore.js b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/store/PeopleStore.js new file mode 100644 index 0000000..592aea0 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/store/PeopleStore.js @@ -0,0 +1,206 @@ +import { Store } from 'pullstate'; + +const PeopleStore = new Store({ + people: [ + { + name: 'Andrew Bennet', + image: '/avatars/Avatar-1.png', + }, + { + name: 'Elizabeth Moore', + image: '/avatars/Avatar-2.png', + }, + { + name: 'Oscar Clarke', + image: '/avatars/Avatar-3.png', + }, + { + name: 'Sandra Simpson', + image: '/avatars/Avatar-4.png', + }, + { + name: 'Sophia Price', + image: '/avatars/Avatar-5.png', + }, + { + name: 'Jasmine Ruiz', + image: '/avatars/Avatar-6.png', + }, + { + name: 'Adriana Bonny', + image: '/avatars/Avatar-7.png', + }, + { + name: 'Maya Watson', + image: '/avatars/Avatar-8.png', + }, + { + name: 'Tatum Porter', + image: '/avatars/Avatar-9.png', + }, + { + name: 'Jackson Watts', + image: '/avatars/Avatar-10.png', + }, + { + name: 'Lana Cooper', + image: '/avatars/Avatar-11.png', + }, + { + name: 'Mateo Hoffman', + image: '/avatars/Avatar-12.png', + }, + { + name: 'Harper James', + image: '/avatars/Avatar-13.png', + }, + { + name: 'Edgar Douglas', + image: '/avatars/Avatar-14.png', + }, + { + name: 'Lilly Hale', + image: '/avatars/Avatar-15.png', + }, + { + name: 'Jade Williams', + image: '/avatars/Avatar-16.png', + }, + { + name: 'Cayden Long', + image: '/avatars/Avatar-17.png', + }, + { + name: 'Millie Klein', + image: '/avatars/Avatar-18.png', + }, + { + name: 'Heidi Toffer', + image: '/avatars/Avatar-19.png', + }, + { + name: 'Alaya Bailey', + image: '/avatars/Avatar-20.png', + }, + { + name: 'Laura Diaz', + image: '/avatars/Avatar-21.png', + }, + { + name: 'Alina Gomez', + image: '/avatars/Avatar-22.png', + }, + { + name: 'Rachel Tiffin', + image: '/avatars/Avatar-23.png', + }, + { + name: 'Liz Faxty', + image: '/avatars/Avatar-24.png', + }, + { + name: 'Sarah Goodman', + image: '/avatars/Avatar-25.png', + }, + { + name: 'Melissa Bengin', + image: '/avatars/Avatar-26.png', + }, + { + name: 'Stephanie Morter', + image: '/avatars/Avatar-27.png', + }, + { + name: 'Rebecca Slims', + image: '/avatars/Avatar-28.png', + }, + { + name: 'Arielle May', + image: '/avatars/Avatar-29.png', + }, + { + name: 'Jack Boppes', + image: '/avatars/Avatar-30.png', + }, + { + name: 'Christina Rankin', + image: '/avatars/Avatar-31.png', + }, + { + name: 'Ronan Murf', + image: '/avatars/Avatar-32.png', + }, + { + name: 'Daniel Jackson', + image: '/avatars/Avatar-33.png', + }, + { + name: 'Richard Bales', + image: '/avatars/Avatar-34.png', + }, + { + name: 'Harmony Martin', + image: '/avatars/Avatar-35.png', + }, + { + name: 'Chris Huges', + image: '/avatars/Avatar-36.png', + }, + ], +}); + +export default PeopleStore; + +export const getPeople = (amount) => { + let tempPeople = [...PeopleStore.getRawState().people]; + return tempPeople.sort(() => Math.random() - Math.random()).slice(0, amount); +}; + +// export const markActiveAsDone = () => { + +// PeopleStore.update(state => { + +// const scoreboardIndex = state.scoreboards.findIndex(scoreboard => scoreboard.active === true); +// state.scoreboards[scoreboardIndex].done = true; +// }); +// } + +// export const addScoreboard = (players, details) => { + +// PeopleStore.update(s => { s.scoreboards = s.scoreboards.map(scoreboard => scoreboard.active = false) }); + +// PeopleStore.update(state => { + +// state.scoreboards.forEach((scoreboard, index) => { + +// state.scoreboards[index].active = false; +// }); +// }); + +// const newScoreboard = { + +// id: Date.now(), +// title: details.title, +// players: [ ...players ], +// active: true +// }; + +// const playersToSave = players.filter(p => p.saved === true); + +// PeopleStore.update(s => { s.scoreboards = [ ...s.scoreboards, newScoreboard ] }); +// PeopleStore.update(s => { s.players = [ ...s.players, ...playersToSave ] }); +// } + +// export const addScoreToPlayer = (scoreboardId, playerIndex) => { + +// PeopleStore.update(state => { + +// const scoreboardIndex = state.scoreboards.findIndex(scoreboard => scoreboard.id === parseInt(scoreboardId)); +// state.scoreboards[scoreboardIndex].players[playerIndex].score += 1; + +// state.scoreboards[scoreboardIndex].players.sort((a, b) => { +// if (a.score > b.score) return -1 +// return a.score < b.score ? 1 : 0 +// }); +// }); +// } diff --git a/03_source/mobile.trunk.1/src/pages/DemoClubHouse/store/Selectors.js b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/store/Selectors.js new file mode 100644 index 0000000..7af5b18 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/store/Selectors.js @@ -0,0 +1,25 @@ +import { createSelector } from 'reselect'; + +const getState = (state) => state; + +// General getters +export const getAllPeople = createSelector(getState, (state) => state.people); +export const getCategories = createSelector(getState, (state) => state.categories); +export const getTalks = createSelector(getState, (state) => state.talks); + +// Specific getters +export const getCategoryTalks = (categoryId) => + createSelector( + getState, + (state) => state.talks.filter((talk) => parseInt(talk.category_id) === parseInt(categoryId))[0] + ); +export const getTalk = (id) => + createSelector( + getState, + (state) => state.talks.filter((talk) => parseInt(talk.id) === parseInt(id))[0] + ); +export const getCategory = (id) => + createSelector( + getState, + (state) => state.categories.filter((category) => parseInt(category.id) === parseInt(id))[0] + ); diff --git a/03_source/mobile.trunk.1/src/pages/DemoClubHouse/store/TalkStore.js b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/store/TalkStore.js new file mode 100644 index 0000000..3c64c30 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/store/TalkStore.js @@ -0,0 +1,110 @@ +import { Store } from 'pullstate'; + +const TalkStore = new Store({ + talks: [ + { + id: 1, + title: 'The future of design systems', + date: '29th Oct 2021', + time: '5:00PM', + speakers: 3, + audience: 14, + category_id: 1, + }, + { + id: 2, + title: 'Lets talk about ReactJS', + date: '13th Nov 2021', + time: '2:00PM', + speakers: 4, + audience: 239, + category_id: 2, + }, + { + id: 3, + title: 'How Ionic can transform mobile development', + date: '21st Nov 2021', + time: '7:30PM', + speakers: 2, + audience: 371, + category_id: 3, + }, + { + id: 4, + title: 'Using capacitor to access native features', + date: '25th Nov 2021', + time: '4:15PM', + speakers: 2, + audience: 587, + category_id: 3, + }, + { + id: 5, + title: 'Does SASS give you an advantage?', + date: '29th Nov 2021', + time: '6:00PM', + speakers: 4, + audience: 97, + category_id: 1, + }, + { + id: 6, + title: 'Building a startup from the ground up', + date: '1st Dec 2021', + time: '9:00PM', + speakers: 4, + audience: 316, + category_id: 4, + }, + { + id: 7, + title: 'How we went from 9-5 to my own boss', + date: '12th Dec 2021', + time: '1:00PM', + speakers: 2, + audience: 33, + category_id: 4, + }, + { + id: 8, + title: 'Features of the beast, Angular', + date: '19th Dec 2021', + time: '3:30PM', + speakers: 3, + audience: 114, + category_id: 2, + }, + ], +}); + +export default TalkStore; + +export const addTalk = (details) => { + const newTalk = { + id: Date.now(), + title: details.title, + date: details.date, + time: details.time, + speakers: details.speakers, + audience: Math.random(900), + category_id: details.category_id, + }; + + TalkStore.update((s) => { + s.talks = [...s.talks, newTalk]; + }); +}; + +// export const addScoreToPlayer = (scoreboardId, playerIndex) => { + +// PeopleStore.update(state => { + +// const scoreboardIndex = state.scoreboards.findIndex(scoreboard => scoreboard.id === parseInt(scoreboardId)); +// state.scoreboards[scoreboardIndex].players[playerIndex].score += 1; + +// state.scoreboards[scoreboardIndex].players.sort((a, b) => { +// if (a.score > b.score) return -1 +// return a.score < b.score ? 1 : 0 +// }); +// }); +// } diff --git a/03_source/mobile.trunk.1/src/pages/DemoClubHouse/store/index.js b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/store/index.js new file mode 100644 index 0000000..11d5963 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/store/index.js @@ -0,0 +1,3 @@ +export { default as PeopleStore } from './PeopleStore'; +export { default as TalkStore } from './TalkStore'; +export { default as CategoryStore } from './CategoryStore'; diff --git a/03_source/mobile/src/pages/DemoClubHouse/style.scss b/03_source/mobile.trunk.1/src/pages/DemoClubHouse/style.scss similarity index 100% rename from 03_source/mobile/src/pages/DemoClubHouse/style.scss rename to 03_source/mobile.trunk.1/src/pages/DemoClubHouse/style.scss diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/AppPages/Tab1.jsx b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/AppPages/Tab1.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/AppPages/Tab1.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoColorTutorial/AppPages/Tab1.jsx diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/AppPages/Tab2.jsx b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/AppPages/Tab2.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/AppPages/Tab2.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoColorTutorial/AppPages/Tab2.jsx diff --git a/03_source/mobile/src/pages/DemoColorTutorial/notes.md b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/NOTES.md similarity index 100% rename from 03_source/mobile/src/pages/DemoColorTutorial/notes.md rename to 03_source/mobile.trunk.1/src/pages/DemoColorTutorial/NOTES.md diff --git a/03_source/mobile/src/pages/DemoQrScanner/components/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/TestComponents/CurrentWeather/WeatherProperty.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoQrScanner/components/CurrentWeather/WeatherProperty.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoColorTutorial/TestComponents/CurrentWeather/WeatherProperty.tsx diff --git a/03_source/mobile/src/pages/DemoQrScanner/components/CurrentWeather/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/TestComponents/CurrentWeather/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoQrScanner/components/CurrentWeather/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoColorTutorial/TestComponents/CurrentWeather/index.tsx diff --git a/03_source/mobile/src/pages/DemoOrderingApp/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/TestComponents/SkeletonDashboard/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/TestComponents/SkeletonDashboard/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoColorTutorial/TestComponents/SkeletonDashboard/index.tsx diff --git a/03_source/mobile/src/pages/DemoReactNotes/components/ExploreContainer.css b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/components/ExploreContainer.css similarity index 100% rename from 03_source/mobile/src/pages/DemoReactNotes/components/ExploreContainer.css rename to 03_source/mobile.trunk.1/src/pages/DemoColorTutorial/components/ExploreContainer.css diff --git a/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/components/ExploreContainer.tsx b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/components/ExploreContainer.tsx new file mode 100644 index 0000000..f003f7f --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/components/ExploreContainer.tsx @@ -0,0 +1,25 @@ +import './ExploreContainer.css'; + +interface ContainerProps { + name: string; +} + +const ExploreContainer: React.FC = ({ name }) => { + return ( +
+ {name} +

+ Explore{' '} + + UI Components + +

+
+ ); +}; + +export default ExploreContainer; diff --git a/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/index.tsx new file mode 100644 index 0000000..714ef07 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/index.tsx @@ -0,0 +1,82 @@ +// REQ0119/demo-color-tutorial +// +// RULES: +// T.B.A. +// + +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, ellipse, searchOutline, square, triangle } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +// import Tab1 from './AppPages/Tab1'; +// import Tab2 from './AppPages/Tab2'; + +import Tab1 from './pages/Tab1'; +import Tab2 from './pages/Tab2'; +import Tab3 from './pages/Tab3'; + +import './style.scss'; + +function DemoColorTutorial() { + return ( + + {/* + + + + + + + + + + + + */} + + + + + + + + + + + + + + + + + {/* + + + Dashboard + + + + Search + + */} + + {/* update path base from `/` to `/demo-color-tutorial` */} + + + Tab 1 + + + + Tab 2 + + + + Tab 3 + + + + ); +} + +export default DemoColorTutorial; diff --git a/03_source/mobile/src/pages/DemoReactLogin/style.scss b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/pages/Tab1.css similarity index 100% rename from 03_source/mobile/src/pages/DemoReactLogin/style.scss rename to 03_source/mobile.trunk.1/src/pages/DemoColorTutorial/pages/Tab1.css diff --git a/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/pages/Tab1.tsx b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/pages/Tab1.tsx new file mode 100644 index 0000000..af8f8ee --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/pages/Tab1.tsx @@ -0,0 +1,75 @@ +import { + IonBadge, + IonButton, + IonButtons, + IonCheckbox, + IonContent, + IonHeader, + IonIcon, + IonItem, + IonLabel, + IonNote, + IonPage, + IonRadio, + IonTitle, + IonToggle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab1.css'; +import { chevronBackOutline } from 'ionicons/icons'; + +const Tab1: React.FC = () => { + const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + Tab 1 + + + handleBackClick()}> + + + + + + + + + Tab 1 + + + + Button + + + Toggle + + + + + Checkbox + + + + + Badge + 14 notifications + + + + Note + 3 unread + + + + ); +}; + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoRestaurantFinder/AppPages/Tab2.css b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/pages/Tab2.css similarity index 100% rename from 03_source/mobile/src/pages/DemoRestaurantFinder/AppPages/Tab2.css rename to 03_source/mobile.trunk.1/src/pages/DemoColorTutorial/pages/Tab2.css diff --git a/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/pages/Tab2.tsx b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/pages/Tab2.tsx new file mode 100644 index 0000000..05458aa --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/pages/Tab2.tsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab2.css'; + +const Tab2: React.FC = () => { + return ( + + + + Tab 2 + + + + + + Tab 2 + + + + + + ); +}; + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoRestaurantFinder/AppPages/Tab3.css b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/pages/Tab3.css similarity index 100% rename from 03_source/mobile/src/pages/DemoRestaurantFinder/AppPages/Tab3.css rename to 03_source/mobile.trunk.1/src/pages/DemoColorTutorial/pages/Tab3.css diff --git a/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/pages/Tab3.tsx b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/pages/Tab3.tsx new file mode 100644 index 0000000..3a29b8a --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/pages/Tab3.tsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab3.css'; + +const Tab3: React.FC = () => { + return ( + + + + Tab 3 + + + + + + Tab 3 + + + + + + ); +}; + +export default Tab3; diff --git a/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/style.scss b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/style.scss new file mode 100644 index 0000000..5430344 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/style.scss @@ -0,0 +1,256 @@ +.demo-color-tutorial { + /* +Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ +*/ + + /** Ionic CSS Variables **/ + * { + /** alans color **/ + --ion-color-alans-color: #6c9400; + --ion-color-alans-color-rgb: 108, 148, 0; + --ion-color-alans-color-contrast: #ffffff; + --ion-color-alans-color-contrast-rgb: 255, 255, 255; + --ion-color-alans-color-shade: #5f8200; + --ion-color-alans-color-tint: #7b9f1a; + + /** primary **/ + --ion-color-primary: #be402f; + --ion-color-primary-rgb: 190, 64, 47; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #a73829; + --ion-color-primary-tint: #c55344; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + } + + .ion-color-alans-color { + --ion-color-base: var(--ion-color-alans-color); + --ion-color-base-rgb: var(--ion-color-alans-color-rgb); + --ion-color-contrast: var(--ion-color-alans-color-contrast); + --ion-color-contrast-rgb: var(--ion-color-alans-color-contrast-rgb); + --ion-color-shade: var(--ion-color-alans-color-shade); + --ion-color-tint: var(--ion-color-alans-color-tint); + } + + @media (prefers-color-scheme: dark) { + /* + * Dark Colors + * ------------------------------------------- + */ + + body { + --ion-color-primary: #428cff; + --ion-color-primary-rgb: 66, 140, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3a7be0; + --ion-color-primary-tint: #5598ff; + + --ion-color-secondary: #50c8ff; + --ion-color-secondary-rgb: 80, 200, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #46b0e0; + --ion-color-secondary-tint: #62ceff; + + --ion-color-tertiary: #6a64ff; + --ion-color-tertiary-rgb: 106, 100, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #5d58e0; + --ion-color-tertiary-tint: #7974ff; + + --ion-color-success: #2fdf75; + --ion-color-success-rgb: 47, 223, 117; + --ion-color-success-contrast: #000000; + --ion-color-success-contrast-rgb: 0, 0, 0; + --ion-color-success-shade: #29c467; + --ion-color-success-tint: #44e283; + + --ion-color-warning: #ffd534; + --ion-color-warning-rgb: 255, 213, 52; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0bb2e; + --ion-color-warning-tint: #ffd948; + + --ion-color-danger: #ff4961; + --ion-color-danger-rgb: 255, 73, 97; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #e04055; + --ion-color-danger-tint: #ff5b71; + + --ion-color-dark: #f4f5f8; + --ion-color-dark-rgb: 244, 245, 248; + --ion-color-dark-contrast: #000000; + --ion-color-dark-contrast-rgb: 0, 0, 0; + --ion-color-dark-shade: #d7d8da; + --ion-color-dark-tint: #f5f6f9; + + --ion-color-medium: #989aa2; + --ion-color-medium-rgb: 152, 154, 162; + --ion-color-medium-contrast: #000000; + --ion-color-medium-contrast-rgb: 0, 0, 0; + --ion-color-medium-shade: #86888f; + --ion-color-medium-tint: #a2a4ab; + + --ion-color-light: #222428; + --ion-color-light-rgb: 34, 36, 40; + --ion-color-light-contrast: #ffffff; + --ion-color-light-contrast-rgb: 255, 255, 255; + --ion-color-light-shade: #1e2023; + --ion-color-light-tint: #383a3e; + } + + /* + * iOS Dark Theme + * ------------------------------------------- + */ + + .ios body { + --ion-background-color: #000000; + --ion-background-color-rgb: 0, 0, 0; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-color-step-50: #0d0d0d; + --ion-color-step-100: #1a1a1a; + --ion-color-step-150: #262626; + --ion-color-step-200: #333333; + --ion-color-step-250: #404040; + --ion-color-step-300: #4d4d4d; + --ion-color-step-350: #595959; + --ion-color-step-400: #666666; + --ion-color-step-450: #737373; + --ion-color-step-500: #808080; + --ion-color-step-550: #8c8c8c; + --ion-color-step-600: #999999; + --ion-color-step-650: #a6a6a6; + --ion-color-step-700: #b3b3b3; + --ion-color-step-750: #bfbfbf; + --ion-color-step-800: #cccccc; + --ion-color-step-850: #d9d9d9; + --ion-color-step-900: #e6e6e6; + --ion-color-step-950: #f2f2f2; + + --ion-item-background: #000000; + + --ion-card-background: #1c1c1d; + } + + .ios ion-modal { + --ion-background-color: var(--ion-color-step-100); + --ion-toolbar-background: var(--ion-color-step-150); + --ion-toolbar-border-color: var(--ion-color-step-250); + } + + /* + * Material Design Dark Theme + * ------------------------------------------- + */ + + .md body { + --ion-background-color: #121212; + --ion-background-color-rgb: 18, 18, 18; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-border-color: #222222; + + --ion-color-step-50: #1e1e1e; + --ion-color-step-100: #2a2a2a; + --ion-color-step-150: #363636; + --ion-color-step-200: #414141; + --ion-color-step-250: #4d4d4d; + --ion-color-step-300: #595959; + --ion-color-step-350: #656565; + --ion-color-step-400: #717171; + --ion-color-step-450: #7d7d7d; + --ion-color-step-500: #898989; + --ion-color-step-550: #949494; + --ion-color-step-600: #a0a0a0; + --ion-color-step-650: #acacac; + --ion-color-step-700: #b8b8b8; + --ion-color-step-750: #c4c4c4; + --ion-color-step-800: #d0d0d0; + --ion-color-step-850: #dbdbdb; + --ion-color-step-900: #e7e7e7; + --ion-color-step-950: #f3f3f3; + + --ion-item-background: #1e1e1e; + + --ion-toolbar-background: #1f1f1f; + + --ion-tab-bar-background: #1f1f1f; + + --ion-card-background: #1e1e1e; + } + } +} diff --git a/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/theme/variables.css b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/theme/variables.css new file mode 100644 index 0000000..5f9d075 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoColorTutorial/theme/variables.css @@ -0,0 +1,254 @@ +/* +Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ +*/ + +/** Ionic CSS Variables **/ +:root { + /** alans color **/ + --ion-color-alans-color: #6c9400; + --ion-color-alans-color-rgb: 108, 148, 0; + --ion-color-alans-color-contrast: #ffffff; + --ion-color-alans-color-contrast-rgb: 255, 255, 255; + --ion-color-alans-color-shade: #5f8200; + --ion-color-alans-color-tint: #7b9f1a; + + /** primary **/ + --ion-color-primary: #be402f; + --ion-color-primary-rgb: 190, 64, 47; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #a73829; + --ion-color-primary-tint: #c55344; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; +} + +.ion-color-alans-color { + --ion-color-base: var(--ion-color-alans-color); + --ion-color-base-rgb: var(--ion-color-alans-color-rgb); + --ion-color-contrast: var(--ion-color-alans-color-contrast); + --ion-color-contrast-rgb: var(--ion-color-alans-color-contrast-rgb); + --ion-color-shade: var(--ion-color-alans-color-shade); + --ion-color-tint: var(--ion-color-alans-color-tint); +} + +@media (prefers-color-scheme: dark) { + /* + * Dark Colors + * ------------------------------------------- + */ + + body { + --ion-color-primary: #428cff; + --ion-color-primary-rgb: 66, 140, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3a7be0; + --ion-color-primary-tint: #5598ff; + + --ion-color-secondary: #50c8ff; + --ion-color-secondary-rgb: 80, 200, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #46b0e0; + --ion-color-secondary-tint: #62ceff; + + --ion-color-tertiary: #6a64ff; + --ion-color-tertiary-rgb: 106, 100, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #5d58e0; + --ion-color-tertiary-tint: #7974ff; + + --ion-color-success: #2fdf75; + --ion-color-success-rgb: 47, 223, 117; + --ion-color-success-contrast: #000000; + --ion-color-success-contrast-rgb: 0, 0, 0; + --ion-color-success-shade: #29c467; + --ion-color-success-tint: #44e283; + + --ion-color-warning: #ffd534; + --ion-color-warning-rgb: 255, 213, 52; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0bb2e; + --ion-color-warning-tint: #ffd948; + + --ion-color-danger: #ff4961; + --ion-color-danger-rgb: 255, 73, 97; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #e04055; + --ion-color-danger-tint: #ff5b71; + + --ion-color-dark: #f4f5f8; + --ion-color-dark-rgb: 244, 245, 248; + --ion-color-dark-contrast: #000000; + --ion-color-dark-contrast-rgb: 0, 0, 0; + --ion-color-dark-shade: #d7d8da; + --ion-color-dark-tint: #f5f6f9; + + --ion-color-medium: #989aa2; + --ion-color-medium-rgb: 152, 154, 162; + --ion-color-medium-contrast: #000000; + --ion-color-medium-contrast-rgb: 0, 0, 0; + --ion-color-medium-shade: #86888f; + --ion-color-medium-tint: #a2a4ab; + + --ion-color-light: #222428; + --ion-color-light-rgb: 34, 36, 40; + --ion-color-light-contrast: #ffffff; + --ion-color-light-contrast-rgb: 255, 255, 255; + --ion-color-light-shade: #1e2023; + --ion-color-light-tint: #383a3e; + } + + /* + * iOS Dark Theme + * ------------------------------------------- + */ + + .ios body { + --ion-background-color: #000000; + --ion-background-color-rgb: 0, 0, 0; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-color-step-50: #0d0d0d; + --ion-color-step-100: #1a1a1a; + --ion-color-step-150: #262626; + --ion-color-step-200: #333333; + --ion-color-step-250: #404040; + --ion-color-step-300: #4d4d4d; + --ion-color-step-350: #595959; + --ion-color-step-400: #666666; + --ion-color-step-450: #737373; + --ion-color-step-500: #808080; + --ion-color-step-550: #8c8c8c; + --ion-color-step-600: #999999; + --ion-color-step-650: #a6a6a6; + --ion-color-step-700: #b3b3b3; + --ion-color-step-750: #bfbfbf; + --ion-color-step-800: #cccccc; + --ion-color-step-850: #d9d9d9; + --ion-color-step-900: #e6e6e6; + --ion-color-step-950: #f2f2f2; + + --ion-item-background: #000000; + + --ion-card-background: #1c1c1d; + } + + .ios ion-modal { + --ion-background-color: var(--ion-color-step-100); + --ion-toolbar-background: var(--ion-color-step-150); + --ion-toolbar-border-color: var(--ion-color-step-250); + } + + /* + * Material Design Dark Theme + * ------------------------------------------- + */ + + .md body { + --ion-background-color: #121212; + --ion-background-color-rgb: 18, 18, 18; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-border-color: #222222; + + --ion-color-step-50: #1e1e1e; + --ion-color-step-100: #2a2a2a; + --ion-color-step-150: #363636; + --ion-color-step-200: #414141; + --ion-color-step-250: #4d4d4d; + --ion-color-step-300: #595959; + --ion-color-step-350: #656565; + --ion-color-step-400: #717171; + --ion-color-step-450: #7d7d7d; + --ion-color-step-500: #898989; + --ion-color-step-550: #949494; + --ion-color-step-600: #a0a0a0; + --ion-color-step-650: #acacac; + --ion-color-step-700: #b8b8b8; + --ion-color-step-750: #c4c4c4; + --ion-color-step-800: #d0d0d0; + --ion-color-step-850: #dbdbdb; + --ion-color-step-900: #e7e7e7; + --ion-color-step-950: #f3f3f3; + + --ion-item-background: #1e1e1e; + + --ion-toolbar-background: #1f1f1f; + + --ion-tab-bar-background: #1f1f1f; + + --ion-card-background: #1e1e1e; + } +} diff --git a/03_source/mobile/src/pages/DemoDictionaryApp/AppPages/Tab1.jsx b/03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/AppPages/Tab1.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoDictionaryApp/AppPages/Tab1.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/AppPages/Tab1.jsx diff --git a/03_source/mobile/src/pages/DemoDictionaryApp/AppPages/Tab2.jsx b/03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/AppPages/Tab2.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoDictionaryApp/AppPages/Tab2.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/AppPages/Tab2.jsx diff --git a/03_source/mobile/src/pages/DemoDictionaryApp/AppPages/Tab3.jsx b/03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/AppPages/Tab3.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoDictionaryApp/AppPages/Tab3.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/AppPages/Tab3.jsx diff --git a/03_source/mobile/src/pages/DemoDictionaryApp/components/NoFavourites.jsx b/03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/components/NoFavourites.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoDictionaryApp/components/NoFavourites.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/components/NoFavourites.jsx diff --git a/03_source/mobile/src/pages/DemoDictionaryApp/components/NoSearch.jsx b/03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/components/NoSearch.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoDictionaryApp/components/NoSearch.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/components/NoSearch.jsx diff --git a/03_source/mobile/src/pages/DemoDictionaryApp/components/WordCard.jsx b/03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/components/WordCard.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoDictionaryApp/components/WordCard.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/components/WordCard.jsx diff --git a/03_source/mobile/src/pages/DemoDictionaryApp/components/WordCardHeading.jsx b/03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/components/WordCardHeading.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoDictionaryApp/components/WordCardHeading.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/components/WordCardHeading.jsx diff --git a/03_source/mobile/src/pages/DemoDictionaryApp/components/WordMeaning.jsx b/03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/components/WordMeaning.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoDictionaryApp/components/WordMeaning.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/components/WordMeaning.jsx diff --git a/03_source/mobile/src/pages/DemoDictionaryApp/components/WordModal.jsx b/03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/components/WordModal.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoDictionaryApp/components/WordModal.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/components/WordModal.jsx diff --git a/03_source/mobile/src/pages/DemoDictionaryApp/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoDictionaryApp/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/index.tsx diff --git a/03_source/mobile/src/pages/DemoDictionaryApp/notes.md b/03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/notes.md similarity index 100% rename from 03_source/mobile/src/pages/DemoDictionaryApp/notes.md rename to 03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/notes.md diff --git a/03_source/mobile/src/pages/DemoDictionaryApp/store/Selectors.js b/03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/store/Selectors.js similarity index 100% rename from 03_source/mobile/src/pages/DemoDictionaryApp/store/Selectors.js rename to 03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/store/Selectors.js diff --git a/03_source/mobile/src/pages/DemoDictionaryApp/store/WordStore.js b/03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/store/WordStore.js similarity index 100% rename from 03_source/mobile/src/pages/DemoDictionaryApp/store/WordStore.js rename to 03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/store/WordStore.js diff --git a/03_source/mobile/src/pages/DemoDictionaryApp/store/index.js b/03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/store/index.js similarity index 100% rename from 03_source/mobile/src/pages/DemoDictionaryApp/store/index.js rename to 03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/store/index.js diff --git a/03_source/mobile/src/pages/DemoDictionaryApp/style.scss b/03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/style.scss similarity index 100% rename from 03_source/mobile/src/pages/DemoDictionaryApp/style.scss rename to 03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/style.scss diff --git a/03_source/mobile/src/pages/DemoDictionaryApp/utils.js b/03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/utils.js similarity index 100% rename from 03_source/mobile/src/pages/DemoDictionaryApp/utils.js rename to 03_source/mobile.trunk.1/src/pages/DemoDictionaryApp/utils.js diff --git a/03_source/mobile/src/pages/DemoFacebookClone/AppPages/Tab1.jsx b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/AppPages/Tab1.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoFacebookClone/AppPages/Tab1.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/AppPages/Tab1.jsx diff --git a/03_source/mobile/src/pages/DemoFacebookClone/AppPages/Tab2.jsx b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/AppPages/Tab2.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoFacebookClone/AppPages/Tab2.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/AppPages/Tab2.jsx diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/notes.md b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/NOTES.md similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/notes.md rename to 03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/NOTES.md diff --git a/03_source/mobile/src/pages/DemoReactDrawingCanvas/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/TestComponents/CurrentWeather/WeatherProperty.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactDrawingCanvas/TestComponents/CurrentWeather/WeatherProperty.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/TestComponents/CurrentWeather/WeatherProperty.tsx diff --git a/03_source/mobile/src/pages/DemoReactDrawingCanvas/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/TestComponents/CurrentWeather/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactDrawingCanvas/TestComponents/CurrentWeather/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/TestComponents/CurrentWeather/index.tsx diff --git a/03_source/mobile/src/pages/DemoQrScanner/components/SkeletonDashboard/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/TestComponents/SkeletonDashboard/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoQrScanner/components/SkeletonDashboard/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/TestComponents/SkeletonDashboard/index.tsx diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/components/ProductCard.module.css b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/components/ProductCard.module.css similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/components/ProductCard.module.css rename to 03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/components/ProductCard.module.css diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/components/ProductCard.tsx b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/components/ProductCard.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/components/ProductCard.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/components/ProductCard.tsx diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/data/CartStore.ts b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/data/CartStore.ts similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/data/CartStore.ts rename to 03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/data/CartStore.ts diff --git a/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/data/FavouritesStore.ts b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/data/FavouritesStore.ts new file mode 100644 index 0000000..958bd5f --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/data/FavouritesStore.ts @@ -0,0 +1,16 @@ +import { Store } from 'pullstate'; + +export const FavouritesStore = new Store({ + total: 0, + product_ids: [], +}); + +export const addToFavourites = (categorySlug, productID) => { + FavouritesStore.update((s) => { + if (s.product_ids.find((id) => id === `${categorySlug}/${parseInt(productID)}`)) { + s.product_ids = s.product_ids.filter((id) => id !== `${categorySlug}/${parseInt(productID)}`); + } else { + s.product_ids = [...s.product_ids, `${categorySlug}/${parseInt(productID)}`]; + } + }); +}; diff --git a/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/data/ProductStore.ts b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/data/ProductStore.ts new file mode 100644 index 0000000..88def31 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/data/ProductStore.ts @@ -0,0 +1,5 @@ +import { Store } from 'pullstate'; + +export const ProductStore = new Store({ + products: [], +}); diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/data/fetcher.ts b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/data/fetcher.ts similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/data/fetcher.ts rename to 03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/data/fetcher.ts diff --git a/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/index.tsx new file mode 100644 index 0000000..402186a --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/index.tsx @@ -0,0 +1,71 @@ +// REQ0119/demo-ecommerce-example +// +// RULES: +// T.B.A. +// + +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +// import Tab1 from './AppPages/Tab1'; +// import Tab2 from './AppPages/Tab2'; + +import Home from './pages/Home'; +import { fetchData } from './data/fetcher'; +import CategoryProducts from './pages/CategoryProducts'; +import Product from './pages/Product'; +import FavouriteProducts from './pages/FavouriteProducts'; +import CartProducts from './pages/CartProducts'; + +import './style.scss'; +import React, { useEffect } from 'react'; + +function DemoEcommerceExample(): React.JSX.Element { + useEffect(() => { + fetchData(); + }, []); + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoEcommerceExample; diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/module.d.ts b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/module.d.ts similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/module.d.ts rename to 03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/module.d.ts diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/pages/CartProducts.module.css b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/pages/CartProducts.module.css similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/pages/CartProducts.module.css rename to 03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/pages/CartProducts.module.css diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/pages/CartProducts.tsx b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/pages/CartProducts.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/pages/CartProducts.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/pages/CartProducts.tsx diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/pages/CategoryProducts.module.css b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/pages/CategoryProducts.module.css similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/pages/CategoryProducts.module.css rename to 03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/pages/CategoryProducts.module.css diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/pages/CategoryProducts.tsx b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/pages/CategoryProducts.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/pages/CategoryProducts.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/pages/CategoryProducts.tsx diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/pages/FavouriteProducts.tsx b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/pages/FavouriteProducts.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/pages/FavouriteProducts.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/pages/FavouriteProducts.tsx diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/pages/Home.module.css b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/pages/Home.module.css similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/pages/Home.module.css rename to 03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/pages/Home.module.css diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/pages/Home.tsx b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/pages/Home.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/pages/Home.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/pages/Home.tsx diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/pages/Product.module.css b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/pages/Product.module.css similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/pages/Product.module.css rename to 03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/pages/Product.module.css diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/pages/Product.tsx b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/pages/Product.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/pages/Product.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/pages/Product.tsx diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/style.scss b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/style.scss similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/style.scss rename to 03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/style.scss diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/theme/variables.css b/03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/theme/variables.css similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/theme/variables.css rename to 03_source/mobile.trunk.1/src/pages/DemoEcommerceExample/theme/variables.css diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/AppPages/Tab1.jsx b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/AppPages/Tab1.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/AppPages/Tab1.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoFacebookClone/AppPages/Tab1.jsx diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/AppPages/Tab2.jsx b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/AppPages/Tab2.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/AppPages/Tab2.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoFacebookClone/AppPages/Tab2.jsx diff --git a/03_source/mobile/src/pages/DemoFacebookClone/notes.md b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/NOTES.md similarity index 100% rename from 03_source/mobile/src/pages/DemoFacebookClone/notes.md rename to 03_source/mobile.trunk.1/src/pages/DemoFacebookClone/NOTES.md diff --git a/03_source/mobile/src/pages/DemoReactItemList/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/TestComponents/CurrentWeather/WeatherProperty.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactItemList/TestComponents/CurrentWeather/WeatherProperty.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoFacebookClone/TestComponents/CurrentWeather/WeatherProperty.tsx diff --git a/03_source/mobile/src/pages/DemoReactItemList/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/TestComponents/CurrentWeather/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactItemList/TestComponents/CurrentWeather/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoFacebookClone/TestComponents/CurrentWeather/index.tsx diff --git a/03_source/mobile/src/pages/DemoReactDrawingCanvas/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/TestComponents/SkeletonDashboard/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactDrawingCanvas/TestComponents/SkeletonDashboard/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoFacebookClone/TestComponents/SkeletonDashboard/index.tsx diff --git a/03_source/mobile/src/pages/DemoReactTravelApp/components/ExploreContainer.css b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/components/ExploreContainer.css similarity index 100% rename from 03_source/mobile/src/pages/DemoReactTravelApp/components/ExploreContainer.css rename to 03_source/mobile.trunk.1/src/pages/DemoFacebookClone/components/ExploreContainer.css diff --git a/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/components/ExploreContainer.tsx b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/components/ExploreContainer.tsx new file mode 100644 index 0000000..f003f7f --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/components/ExploreContainer.tsx @@ -0,0 +1,25 @@ +import './ExploreContainer.css'; + +interface ContainerProps { + name: string; +} + +const ExploreContainer: React.FC = ({ name }) => { + return ( +
+ {name} +

+ Explore{' '} + + UI Components + +

+
+ ); +}; + +export default ExploreContainer; diff --git a/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/components/Post.tsx b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/components/Post.tsx new file mode 100644 index 0000000..275e4ad --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/components/Post.tsx @@ -0,0 +1,96 @@ +import { + IonAvatar, + IonButton, + IonCol, + IonIcon, + IonImg, + IonItem, + IonLabel, + IonRow, + IonText, +} from '@ionic/react'; +import { + arrowRedoOutline, + chatboxOutline, + ellipsisHorizontal, + globe, + heart, + thumbsUp, + thumbsUpOutline, +} from 'ionicons/icons'; +import '../pages/Tab2.css'; +import React from 'react'; + +const Post = (props): React.JSX.Element => { + const { post } = props; + + return ( + + +
+ + + + + + +

{post.name}

+

+ {post.sponsored ? 'Sponsored' : post.time} +    + +

+
+ + +
+ + +

{post.message}

+
+ + {post.image && } + + {post.sponsored && ( +
+ +

ionicframework.com

+

Start building apps today!

+
+ + Learn more +
+ )} + +
+
+ + +
+ + {post.sponsored &&

{post.views} Views

} +
+ +
+ + + Like + + + + + Comment + + + + + Share + +
+
+
+
+ ); +}; + +export default Post; diff --git a/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/components/Tabs.tsx b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/components/Tabs.tsx new file mode 100644 index 0000000..8a1c7d2 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/components/Tabs.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { IonIcon, IonLabel, IonTabBar, IonTabButton, IonTabs, IonRouterOutlet } from '@ionic/react'; +import { Redirect, Route } from 'react-router-dom'; +import Tab1 from '../pages/Tab1'; +import Tab2 from '../pages/Tab2'; +import Tab3 from '../pages/Tab3'; +import { chatboxOutline, cogOutline, personOutline } from 'ionicons/icons'; + +const Tabs = (props): React.JSX.Element => { + return ( + + + } /> + } /> + } /> + + + + + + + + + + + + + + + ); +}; + +export default Tabs; diff --git a/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/index.tsx new file mode 100644 index 0000000..545fc4e --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/index.tsx @@ -0,0 +1,27 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +// import Tab1 from './AppPages/Tab1'; +// import Tab2 from './AppPages/Tab2'; + +import Tab1 from './pages/Tab1'; +import Tab2 from './pages/Tab2'; +import Tab3 from './pages/Tab3'; +import Tabs from './components/Tabs'; + +import './style.scss'; + +function DemoFacebookClone() { + return ( + + } /> + } /> + + + + ); +} + +export default DemoFacebookClone; diff --git a/03_source/mobile/src/pages/DemoFacebookClone/main/messages.js b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/main/messages.js similarity index 100% rename from 03_source/mobile/src/pages/DemoFacebookClone/main/messages.js rename to 03_source/mobile.trunk.1/src/pages/DemoFacebookClone/main/messages.js diff --git a/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/pages/MessageItem.tsx b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/pages/MessageItem.tsx new file mode 100644 index 0000000..4c4d3b3 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/pages/MessageItem.tsx @@ -0,0 +1,27 @@ +import { IonAvatar, IonBadge, IonImg, IonItem, IonLabel } from '@ionic/react'; +import React from 'react'; + +const MessageItem = (props): React.JSX.Element => { + return ( + + + + + {props.message.online &&
} + + +

{props.message.name}

+

This is a test message for a messenger item

+
+ +
+

{props.message.last_message_sent} min

+ {props.message.new_message_count > 0 && ( + {props.message.new_message_count} + )} +
+
+ ); +}; + +export default MessageItem; diff --git a/03_source/mobile/src/pages/DemoReactLogin/theme/variables.scss b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/pages/Tab1.css similarity index 100% rename from 03_source/mobile/src/pages/DemoReactLogin/theme/variables.scss rename to 03_source/mobile.trunk.1/src/pages/DemoFacebookClone/pages/Tab1.css diff --git a/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/pages/Tab1.tsx b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/pages/Tab1.tsx new file mode 100644 index 0000000..2ccdb20 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/pages/Tab1.tsx @@ -0,0 +1,73 @@ +import { + IonAvatar, + IonBackButton, + IonBadge, + IonButton, + IonButtons, + IonContent, + IonFab, + IonFabButton, + IonHeader, + IonIcon, + IonImg, + IonItem, + IonLabel, + IonList, + IonPage, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { addOutline, searchOutline } from 'ionicons/icons'; +import ExploreContainer from '../components/ExploreContainer'; +import { messages } from '../main/messages'; +import MessageItem from './MessageItem'; +import './Tab1.css'; + +const Tab1 = () => { + return ( + + + + Messages + + + + + + @93alan + + + + + + + + + + + + + Messages + + + + + {messages.map((message, index) => { + return ; + })} + + + + + + + + + + ); +}; + +export default Tab1; diff --git a/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/pages/Tab2.css b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/pages/Tab2.css new file mode 100644 index 0000000..6cb99f6 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/pages/Tab2.css @@ -0,0 +1,373 @@ +.timeline-toolbar { + + display: flex; + flex-direction: row !important; +} + +.toolbar-title { + + font-size: 1.8rem; + font-weight: 700; + margin-left: 0.5rem; +} + +.toolbar-icons { + + margin-right: 0.5rem; + background-color: white; +} + +.toolbar-icons ion-icon { + + color: black; + background-color:rgb(235, 235, 235); + border-radius: 500px; + font-size: 1.5rem; + margin-left: 0.5rem; + padding: 0.3rem; +} + +.second-toolbar { + + border-bottom: 0.1px solid rgb(144, 144, 144); + padding-top: 0.75rem; + background-color: white; + position: fixed; + width: 100%; + z-index: 9999999; +} + +.second-toolbar ion-col ion-icon { + + font-size: 1.75rem; + color: rgb(138, 138, 138); +} + +.second-toolbar ion-col ion-icon.selected { + + /* border-bottom: 5px solid var(--ion-color-primary); */ +} + +.selected-icon { + + border-bottom: 1.5px solid var(--ion-color-primary); +} + +.top-input-container { + + border-bottom: 0.1px solid rgb(200, 200, 200); + padding-top: 0.5rem; + padding-bottom: 0.5rem; + background-color: white; + margin-top: 3.5rem; +} + +.top-input { + + border: 1px solid rgb(149, 149, 149); + border-radius: 25px; + padding-left: 1rem !important; + height: 2.2rem; +} + +.below-input-label-container { + + border-bottom: 1px solid rgb(185, 185, 185); + background-color: white; +} + +.below-input-label { + + margin-top: 0.8rem; + margin-bottom: 0.8rem; +} + +.below-input-label:not(:last-child) { + + border-right: 1px solid rgb(167, 167, 167); +} + +.below-input-label ion-text { + + color: rgb(114, 114, 114); + font-size: 0.8rem; + font-weight: 700 !important; + margin-left: 1.7rem; +} + +.below-input-label ion-icon { + + font-size: 1.2rem; + margin-right: 0.5rem; + position: absolute; +} + +.timeline-bg { + + --background:rgb(218, 218, 218) !important; +} + +.rooms-container { + + background-color: white; + margin-top: 0.5rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + border-bottom: 0.1px solid rgb(144, 144, 144); + border-top: 0.1px solid rgb(144, 144, 144); +} + +.rooms-people-container { + + white-space: nowrap; + position: relative; + overflow-x: scroll; + overflow-y: hidden; + -webkit-overflow-scrolling: touch; + display: flex; + flex-direction: row; + width: 70%; +} + +.rooms-people-container ion-avatar { + + height: 2.5rem; + width: 2.5rem; +} + +.rooms-people-container div { + + margin-top: 0.4rem !important; + margin-right: 1rem !important; +} + +.rooms-create { + + border: 1px solid rgb(149, 225, 255); + border-radius: 70px; + justify-content: center; + flex-direction: row; + padding: 0.2rem !important; + +} + +.rooms-create ion-icon { + + font-size: 1.5rem !important; + margin-left: 0.5rem !important; + margin-top: 0.3rem !important; + float: left !important; +} + +.rooms-create ion-text { + + font-size: 0.7rem !important; + color:rgb(136, 136, 136) !important; + font-weight: 700 !important; + margin: 0 !important; + padding: 0 !important; + line-height: -1rem !important; +} + +.rooms-online { + + height: 13px !important; + width: 13px !important; + border-radius: 500px !important; + border: 2px solid white !important; + background-color: green !important; + z-index: 9999 !important; + position: absolute !important; + margin-left: 0.5rem !important; + margin-top: -0.8rem !important; +} + +.post { + + margin-top: 0.5rem !important; +} + +.post-container { + + background-color: white !important; +} + +.post-header { + + /* padding: none !important; */ + /* margin: none !important; */ +} + +.post-header { + + padding-top: 0.5rem !important; + padding-left: 0.5rem !important; + z-index: 9999; +} + +.post-header ion-avatar { + + /* border: 2px solid rgb(164, 222, 255); */ +} + +.post-header ion-label { + + margin-left: 0.5rem !important; + z-index: 99999 !important; +} + +.post-header ion-label h3 { + + font-weight: 600 !important; +} + +.post-header ion-label p ion-icon { + + position: absolute !important; + margin-top: 0.15rem !important; +} + +.post-header ion-icon { + + color: rgb(133, 133, 133) !important; +} + +.post-content { + + padding-left: 0.5rem !important; + margin-top: -0.7rem !important; +} + +.post-link { + + padding-left: 0.5rem !important; + background-color: rgb(245, 245, 245) !important; + display: flex !important; + flex-direction: row !important; + justify-content: space-between !important; + padding-top: 0.75rem !important; + padding-bottom: 0.75rem !important; +} + +.post-link ion-button { + + margin-right: 0.5rem !important; + --background: rgb(245, 245, 245) !important; + border: 1px solid rgb(98, 98, 98) !important; + border-radius: 8px !important; + color: rgb(98, 98, 98) !important; + text-transform: uppercase !important; + font-size: 0.8rem !important; +} + +.post-likes { + + padding-left: 0.5rem !important; + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + display: flex !important; + flex-direction: row !important; + justify-content: space-between !important; +} + +.post-like-icons { + + margin-top: 0.2rem !important; +} + +.post-likes ion-icon:not(:last-child) { + + background-color: rgb(70, 128, 255) !important; +} + +.post-likes ion-icon:last-child { + + background-color: rgb(255, 76, 76) !important; + margin-left: -0.5rem !important; +} + +.post-likes ion-icon { + + color: white !important; + border-radius: 100px !important; + padding: 0.2rem !important; + border: 2px solid white !important; +} + +.post-likes p { + + padding: none !important; + margin: none !important; + margin-top: 0.4rem !important; + font-size: 0.8rem !important; + margin-right: 0.5rem !important; + color: rgb(156, 156, 156) !important; + +} + +.post-image { + + padding: none !important; + margin: none !important; +} + +.post-image ion-img { + + width: 100% !important; +} + +.post-content p { + + font-size: 1rem !important; +} + +.post-actions { + + background-color: white !important; + display: flex !important; + flex-direction: row !important; + justify-content: space-between !important; + border-top: 0.1px solid rgb(228, 228, 228) !important; + padding-top: 0.5rem !important; + padding-bottom: 0.2rem !important; +} + +.post-actions ion-col { + + margin-left: -1rem !important; +} + +.post-actions ion-col:first-child { + + margin-right: -2rem !important; +} + +.post-actions ion-col:last-child { + + margin-right: 2rem !important; +} + +.post-actions ion-col ion-icon { + + font-size: 1.4rem !important; + color:rgb(105, 105, 105) !important; +} + +.post-actions ion-col ion-text { + + font-size: 0.8rem !important; + color: rgb(107, 107, 107) !important; + margin-top: 0.2rem !important; + margin-left: 0.5rem !important; + position: absolute !important; +} + +/* +
+

ionicbook

+ +
+ + +
+
*/ \ No newline at end of file diff --git a/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/pages/Tab2.tsx b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/pages/Tab2.tsx new file mode 100644 index 0000000..9b7f202 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/pages/Tab2.tsx @@ -0,0 +1,242 @@ +import { + IonAvatar, + IonButton, + IonButtons, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonImg, + IonInput, + IonItem, + IonLabel, + IonPage, + IonRouterLink, + IonRow, + IonSearchbar, + IonText, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import { + searchOutline, + chatboxOutline, + playCircleOutline, + bagOutline, + homeOutline, + menuOutline, + flagOutline, + notificationsOutline, + homeSharp, + home, + videocam, + images, + globe, + ellipse, + ellipsisHorizontal, + thumbsUp, + heart, + thumbsUpOutline, + shareOutline, + arrowRedoOutline, + chevronBackOutline, +} from 'ionicons/icons'; +import ExploreContainer from '../components/ExploreContainer'; +import Post from '../components/Post'; +import { messages, posts } from '../main/messages'; +import './Tab2.css'; +import React from 'react'; + +const Tab2 = (): React.JSX.Element => { + const router = useIonRouter(); + + function handleBackClick() { + router.goBack(); + } + + return ( + + + + + handleBackClick()}> + + + + + ionicbook + + + + + + + + + + + + + {/* */} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Live + + + + + Photo + + + + + Room + + + + + +
+ + + Create +
+ Room +
+
+
+ +
+ {messages.map((message, index) => { + if (index > 1) { + return ( +
+ + + + {message.online && } +
+ ); + } + })} +
+
+ + + +
+ + + + + + +

Ionic Framework

+

+ Sponsored    + +

+
+ + +
+ + +

Build cross-platform web native mobile apps with one codebase! 🎉

+
+ + {/* */} + + {/* */} + +
+ +

ionicframework.com

+

Start building apps today!

+
+ + Learn more +
+ +
+
+ + +
+ +

16K Views

+
+ +
+ + + Like + + + + + Comment + + + + + Share + +
+
+
+
+ + {posts.map((post, index) => { + return ; + })} +
+
+ ); +}; + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoSlidingProfile/AppPages/Tab3.css b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/pages/Tab3.css similarity index 100% rename from 03_source/mobile/src/pages/DemoSlidingProfile/AppPages/Tab3.css rename to 03_source/mobile.trunk.1/src/pages/DemoFacebookClone/pages/Tab3.css diff --git a/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/pages/Tab3.tsx b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/pages/Tab3.tsx new file mode 100644 index 0000000..3a29b8a --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/pages/Tab3.tsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab3.css'; + +const Tab3: React.FC = () => { + return ( + + + + Tab 3 + + + + + + Tab 3 + + + + + + ); +}; + +export default Tab3; diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/style.scss b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/style.scss similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/style.scss rename to 03_source/mobile.trunk.1/src/pages/DemoFacebookClone/style.scss diff --git a/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/theme/variables.css b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/theme/variables.css new file mode 100644 index 0000000..12e7354 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFacebookClone/theme/variables.css @@ -0,0 +1,352 @@ +/* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + +/** Ionic CSS Variables **/ +:root { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** purple **/ + --ion-color-tertiary: #894eb1; + --ion-color-tertiary-rgb: 235, 68, 90; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #6f00b9; + --ion-color-tertiary-tint: #8633bd; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; +} + +@media (prefers-color-scheme: dark1) { + /* + * Dark Colors + * ------------------------------------------- + */ + + body { + --ion-color-primary: #428cff; + --ion-color-primary-rgb: 66,140,255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255,255,255; + --ion-color-primary-shade: #3a7be0; + --ion-color-primary-tint: #5598ff; + + --ion-color-secondary: #50c8ff; + --ion-color-secondary-rgb: 80,200,255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255,255,255; + --ion-color-secondary-shade: #46b0e0; + --ion-color-secondary-tint: #62ceff; + + --ion-color-tertiary: #6a64ff; + --ion-color-tertiary-rgb: 106,100,255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255,255,255; + --ion-color-tertiary-shade: #5d58e0; + --ion-color-tertiary-tint: #7974ff; + + --ion-color-success: #2fdf75; + --ion-color-success-rgb: 47,223,117; + --ion-color-success-contrast: #000000; + --ion-color-success-contrast-rgb: 0,0,0; + --ion-color-success-shade: #29c467; + --ion-color-success-tint: #44e283; + + --ion-color-warning: #ffd534; + --ion-color-warning-rgb: 255,213,52; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0,0,0; + --ion-color-warning-shade: #e0bb2e; + --ion-color-warning-tint: #ffd948; + + --ion-color-danger: #ff4961; + --ion-color-danger-rgb: 255,73,97; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255,255,255; + --ion-color-danger-shade: #e04055; + --ion-color-danger-tint: #ff5b71; + + --ion-color-dark: #f4f5f8; + --ion-color-dark-rgb: 244,245,248; + --ion-color-dark-contrast: #000000; + --ion-color-dark-contrast-rgb: 0,0,0; + --ion-color-dark-shade: #d7d8da; + --ion-color-dark-tint: #f5f6f9; + + --ion-color-medium: #989aa2; + --ion-color-medium-rgb: 152,154,162; + --ion-color-medium-contrast: #000000; + --ion-color-medium-contrast-rgb: 0,0,0; + --ion-color-medium-shade: #86888f; + --ion-color-medium-tint: #a2a4ab; + + --ion-color-light: #222428; + --ion-color-light-rgb: 34,36,40; + --ion-color-light-contrast: #ffffff; + --ion-color-light-contrast-rgb: 255,255,255; + --ion-color-light-shade: #1e2023; + --ion-color-light-tint: #383a3e; + } + + /* + * iOS Dark Theme + * ------------------------------------------- + */ + + .ios body { + --ion-background-color: #000000; + --ion-background-color-rgb: 0,0,0; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255,255,255; + + --ion-color-step-50: #0d0d0d; + --ion-color-step-100: #1a1a1a; + --ion-color-step-150: #262626; + --ion-color-step-200: #333333; + --ion-color-step-250: #404040; + --ion-color-step-300: #4d4d4d; + --ion-color-step-350: #595959; + --ion-color-step-400: #666666; + --ion-color-step-450: #737373; + --ion-color-step-500: #808080; + --ion-color-step-550: #8c8c8c; + --ion-color-step-600: #999999; + --ion-color-step-650: #a6a6a6; + --ion-color-step-700: #b3b3b3; + --ion-color-step-750: #bfbfbf; + --ion-color-step-800: #cccccc; + --ion-color-step-850: #d9d9d9; + --ion-color-step-900: #e6e6e6; + --ion-color-step-950: #f2f2f2; + + --ion-item-background: #000000; + + --ion-card-background: #1c1c1d; + } + + .ios ion-modal { + --ion-background-color: var(--ion-color-step-100); + --ion-toolbar-background: var(--ion-color-step-150); + --ion-toolbar-border-color: var(--ion-color-step-250); + } + + + /* + * Material Design Dark Theme + * ------------------------------------------- + */ + + .md body { + --ion-background-color: #121212; + --ion-background-color-rgb: 18,18,18; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255,255,255; + + --ion-border-color: #222222; + + --ion-color-step-50: #1e1e1e; + --ion-color-step-100: #2a2a2a; + --ion-color-step-150: #363636; + --ion-color-step-200: #414141; + --ion-color-step-250: #4d4d4d; + --ion-color-step-300: #595959; + --ion-color-step-350: #656565; + --ion-color-step-400: #717171; + --ion-color-step-450: #7d7d7d; + --ion-color-step-500: #898989; + --ion-color-step-550: #949494; + --ion-color-step-600: #a0a0a0; + --ion-color-step-650: #acacac; + --ion-color-step-700: #b8b8b8; + --ion-color-step-750: #c4c4c4; + --ion-color-step-800: #d0d0d0; + --ion-color-step-850: #dbdbdb; + --ion-color-step-900: #e7e7e7; + --ion-color-step-950: #f3f3f3; + + --ion-item-background: #1e1e1e; + + --ion-toolbar-background: #1f1f1f; + + --ion-tab-bar-background: #1f1f1f; + + --ion-card-background: #1e1e1e; + } +} + +ion-header, +ion-toolbar { + + border-bottom: none !important; + --border-color: white; +} + +ion-title { + + --color: rgb(48, 48, 58); +} + +ion-tab-bar { + + border-top: none; +} + +ion-tab-button { + + --color: rgb(211, 211, 211); + height: 2rem; +} + +.tab-selected, +ion-icon.search { + + color: rgb(88, 88, 88) !important; +} + +.avatar { + + height: 3.5rem; + width: 3.5rem; +} + +.online { + + height: 20px; + width: 20px; + border-radius: 500px; + border: 3px solid white; + background-color: green; + z-index: 9999; + position: absolute; + margin-left: 2.5rem; + margin-top: 1.2rem; +} + +.contact-details { + + margin-top: 0rem; + margin-left: 1rem; + margin-right: 1rem; +} + +.stats { + + margin-top: -1rem; + margin-right: 0.5rem; +} + +.last-online { + + color: rgb(190, 190, 190); + font-size: 0.9rem; + margin-bottom: 0.5rem; +} + +.stats ion-badge { + + margin: 0 !important; + margin-top: -3rem !important; + border-radius: 2px !important; +} + + + +.contact-details h1 { + + font-size: 1.2rem; +} + +.contact-details p { + + font-size: 0.8rem; +} + +.message-item { + + padding-top: 1.5rem; +} + +.profile-avatar { + + margin: 0 !important; + padding: 0 !important; + height: 2rem; + width: 2rem; + border-radius: 500px !important; +} + +.profile-name { + + color:#c8c8c8; + font-weight: 600 !important; + font-size: 0.9rem !important; + margin-left: 0.5rem; +} + +.add-fab { + + opacity: 0.7; +} + +/* */ \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoFloatingTabs/AppPages/Tab1.jsx b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/AppPages/Tab1.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoFloatingTabs/AppPages/Tab1.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/AppPages/Tab1.jsx diff --git a/03_source/mobile/src/pages/DemoFloatingTabs/AppPages/Tab2.jsx b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/AppPages/Tab2.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoFloatingTabs/AppPages/Tab2.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/AppPages/Tab2.jsx diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/notes.md b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/NOTES.md similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/notes.md rename to 03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/NOTES.md diff --git a/03_source/mobile/src/pages/DemoReactLogin/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/TestComponents/CurrentWeather/WeatherProperty.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactLogin/TestComponents/CurrentWeather/WeatherProperty.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/TestComponents/CurrentWeather/WeatherProperty.tsx diff --git a/03_source/mobile/src/pages/DemoReactLogin/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/TestComponents/CurrentWeather/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactLogin/TestComponents/CurrentWeather/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/TestComponents/CurrentWeather/index.tsx diff --git a/03_source/mobile/src/pages/DemoReactItemList/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/TestComponents/SkeletonDashboard/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactItemList/TestComponents/SkeletonDashboard/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/TestComponents/SkeletonDashboard/index.tsx diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/components/CategorySlide.tsx b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/components/CategorySlide.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/components/CategorySlide.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/components/CategorySlide.tsx diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/components/ProductCard.module.css b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/components/ProductCard.module.css similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/components/ProductCard.module.css rename to 03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/components/ProductCard.module.css diff --git a/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/components/ProductCard.tsx b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/components/ProductCard.tsx new file mode 100644 index 0000000..422ee26 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/components/ProductCard.tsx @@ -0,0 +1,105 @@ +import { IonButton, IonCard, IonCardContent, IonCardHeader, IonCol, IonIcon } from '@ionic/react'; +import { arrowRedoOutline, cart, cartOutline, heart, heartOutline } from 'ionicons/icons'; +import { useEffect, useRef, useState } from 'react'; +import { Link } from 'react-router-dom'; +import { addToCart } from '../data/CartStore'; +import { addToFavourites, FavouritesStore } from '../data/FavouritesStore'; +import styles from './ProductCard.module.css'; + +const ProductCard = (props) => { + const { product, category, index, cartRef } = props; + const favourites = FavouritesStore.useState((s) => s.product_ids); + + const productCartRef = useRef(); + const productFavouriteRef = useRef(); + const [isFavourite, setIsFavourite] = useState(false); + + useEffect(() => { + const tempIsFavourite = favourites.find((f) => f === `${category.slug}/${product.id}`); + setIsFavourite(tempIsFavourite ? true : false); + }, [props.product, favourites]); + + const addProductToFavourites = (e, categorySlug, productID) => { + e.preventDefault(); + e.stopPropagation(); + addToFavourites(categorySlug, productID); + + productFavouriteRef.current.style.display = ''; + productFavouriteRef.current.classList.add('animate__fadeOutTopRight'); + + setTimeout(() => { + if (productCartRef.current) { + productFavouriteRef.current.classList.remove('animate__fadeOutTopRight'); + productFavouriteRef.current.style.display = 'none'; + } + }, 500); + }; + + const addProductToCart = (e, categorySlug, productID) => { + e.preventDefault(); + e.stopPropagation(); + + productCartRef.current.style.display = ''; + productCartRef.current.classList.add('animate__fadeOutUp'); + + setTimeout(() => { + cartRef.current.classList.add('animate__tada'); + addToCart(categorySlug, productID); + + setTimeout(() => { + cartRef.current.classList.remove('animate__tada'); + productCartRef.current.style.display = 'none'; + }, 500); + }, 500); + }; + + return ( + + + {/* */} + +
+ addProductToFavourites(e, category.slug, product.id)} + /> + + +
+ product pic +

{product.name}

+
+ + +
+ + {product.price} + + addProductToCart(e, category.slug, product.id)}> + + + + +
+
+ + {/*
*/} +
+ ); +}; + +export default ProductCard; diff --git a/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/data/CartStore.js b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/data/CartStore.js new file mode 100644 index 0000000..c9af57b --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/data/CartStore.js @@ -0,0 +1,18 @@ +import { Store } from 'pullstate'; + +export const CartStore = new Store({ + total: 0, + product_ids: [], +}); + +export const addToCart = (categorySlug, productID) => { + CartStore.update((s) => { + s.product_ids = [...s.product_ids, `${categorySlug}/${parseInt(productID)}`]; + }); +}; + +export const removeFromCart = (productIndex) => { + CartStore.update((s) => { + s.product_ids.splice(productIndex, 1); + }); +}; diff --git a/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/data/FavouritesStore.js b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/data/FavouritesStore.js new file mode 100644 index 0000000..958bd5f --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/data/FavouritesStore.js @@ -0,0 +1,16 @@ +import { Store } from 'pullstate'; + +export const FavouritesStore = new Store({ + total: 0, + product_ids: [], +}); + +export const addToFavourites = (categorySlug, productID) => { + FavouritesStore.update((s) => { + if (s.product_ids.find((id) => id === `${categorySlug}/${parseInt(productID)}`)) { + s.product_ids = s.product_ids.filter((id) => id !== `${categorySlug}/${parseInt(productID)}`); + } else { + s.product_ids = [...s.product_ids, `${categorySlug}/${parseInt(productID)}`]; + } + }); +}; diff --git a/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/data/ProductStore.js b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/data/ProductStore.js new file mode 100644 index 0000000..88def31 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/data/ProductStore.js @@ -0,0 +1,5 @@ +import { Store } from 'pullstate'; + +export const ProductStore = new Store({ + products: [], +}); diff --git a/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/data/fetcher.js b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/data/fetcher.js new file mode 100644 index 0000000..1d6d396 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/data/fetcher.js @@ -0,0 +1,62 @@ +import { ProductStore } from './ProductStore'; + +export const fetchData = async () => { + const json = [ + 'new.json', + 'chicken.json', + 'veggie.json', + 'burgers.json', + 'sides.json', + 'drinks.json', + 'kids.json', + ]; + + var products = []; + + json.forEach(async (category) => { + products = await fetchProducts(category); + + products.forEach((product) => { + product.price = `£${Math.floor(Math.random() * (10 - 4 + 1)).toFixed(2)}`; + }); + + let categoryName = category.replace('.json', ''); + categoryName = categoryName.replaceAll('_', ' '); + categoryName = uppercaseWords(categoryName); + + const foodCategory = { + name: categoryName, + slug: category.replace('.json', ''), + cover: products[1].image, + products, + }; + + ProductStore.update((s) => { + s.products = [...s.products, foodCategory]; + }); + }); + + return products; +}; + +const fetchProducts = async (category) => { + const response = await fetch(`categories/${category}`); + const data = await response.json(); + + // Set a category id + await data.forEach((d, i) => { + d.id = i + 1; + }); + + return data; +}; + +const uppercaseWords = (words) => { + words = words + .toLowerCase() + .split(' ') + .map((s) => s.charAt(0).toUpperCase() + s.substring(1)) + .join(' '); + + return words; +}; diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/index.tsx diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/module.d.ts b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/module.d.ts similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/module.d.ts rename to 03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/module.d.ts diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/pages/CartProducts.module.css b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/pages/CartProducts.module.css similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/pages/CartProducts.module.css rename to 03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/pages/CartProducts.module.css diff --git a/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/pages/CartProducts.tsx b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/pages/CartProducts.tsx new file mode 100644 index 0000000..a25f340 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/pages/CartProducts.tsx @@ -0,0 +1,166 @@ +import { + IonAvatar, + IonBadge, + IonButton, + IonButtons, + IonCardSubtitle, + IonCol, + IonContent, + IonFooter, + IonHeader, + IonIcon, + IonImg, + IonItem, + IonItemOption, + IonItemOptions, + IonItemSliding, + IonLabel, + IonList, + IonNote, + IonPage, + IonRow, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { cart, checkmarkSharp, chevronBackOutline, trashOutline } from 'ionicons/icons'; +import { useEffect, useRef, useState } from 'react'; +import { CartStore, removeFromCart } from '../data/CartStore'; +import { ProductStore } from '../data/ProductStore'; + +import styles from './CartProducts.module.css'; + +const CartProducts = () => { + const cartRef = useRef(); + const products = ProductStore.useState((s) => s.products); + const shopCart = CartStore.useState((s) => s.product_ids); + const [cartProducts, setCartProducts] = useState([]); + const [amountLoaded, setAmountLoaded] = useState(6); + + const [total, setTotal] = useState(0); + + useEffect(() => { + const getCartProducts = () => { + setCartProducts([]); + setTotal(0); + + shopCart.forEach((product) => { + var favouriteParts = product.split('/'); + var categorySlug = favouriteParts[0]; + var productID = favouriteParts[1]; + + const tempCategory = products.filter((p) => p.slug === categorySlug)[0]; + const tempProduct = tempCategory.products.filter( + (p) => parseInt(p.id) === parseInt(productID) + )[0]; + + const tempCartProduct = { + category: tempCategory, + product: tempProduct, + }; + + setTotal((prevTotal) => prevTotal + parseInt(tempProduct.price.replace('£', ''))); + setCartProducts((prevSearchResults) => [...prevSearchResults, tempCartProduct]); + }); + }; + + getCartProducts(); + }, [shopCart]); + + const fetchMore = async (e) => { + // Increment the amount loaded by 6 for the next iteration + setAmountLoaded((prevAmount) => prevAmount + 6); + e.target.complete(); + }; + + const removeProductFromCart = async (index) => { + removeFromCart(index); + }; + + return ( + + + + + + +  Categories + + + Cart + + + {shopCart.length} + + + + + + + + + + + cart + + + + + + + {cartProducts && cartProducts.length}{' '} + {cartProducts.length > 1 || cartProducts.length === 0 ? ' foods' : ' food'} found + + + + + + {cartProducts && + cartProducts.map((product, index) => { + if (index <= amountLoaded) { + return ( + + + + + + +

{product.category.name}

+

{product.product.name}

+
+ +
+ {product.product.price} +
+
+ + + removeProductFromCart(index)} + > + + + +
+ ); + } + })} +
+
+ + +
+ £{total.toFixed(2)} + + + +  Checkout + +
+
+
+ ); +}; + +export default CartProducts; diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/pages/CategoryProducts.module.css b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/pages/CategoryProducts.module.css similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/pages/CategoryProducts.module.css rename to 03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/pages/CategoryProducts.module.css diff --git a/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/pages/CategoryProducts.tsx b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/pages/CategoryProducts.tsx new file mode 100644 index 0000000..1c8dac6 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/pages/CategoryProducts.tsx @@ -0,0 +1,143 @@ +import { + IonBadge, + IonButton, + IonButtons, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonInfiniteScroll, + IonInfiniteScrollContent, + IonNote, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { cart, chevronBackOutline, searchOutline } from 'ionicons/icons'; +import { useEffect, useRef, useState } from 'react'; +import { useParams } from 'react-router'; +import ProductCard from '../components/ProductCard'; + +import { CartStore } from '../data/CartStore'; +import { ProductStore } from '../data/ProductStore'; + +import styles from './CategoryProducts.module.css'; + +const CategoryProducts = () => { + const params = useParams(); + const cartRef = useRef(); + const products = ProductStore.useState((s) => s.products); + const shopCart = CartStore.useState((s) => s.product_ids); + const [category, setCategory] = useState({}); + const [searchResults, setsearchResults] = useState([]); + const [amountLoaded, setAmountLoaded] = useState(6); + + useEffect(() => { + const categorySlug = params.slug; + const tempCategory = products.filter((p) => p.slug === categorySlug)[0]; + setCategory(tempCategory); + setsearchResults(tempCategory.products); + }, [params.slug]); + + const fetchMore = async (e) => { + // Increment the amount loaded by 6 for the next iteration + setAmountLoaded((prevAmount) => prevAmount + 6); + e.target.complete(); + }; + + const search = async (e) => { + const searchVal = e.target.value; + + if (searchVal !== '') { + const tempResults = category.products.filter((p) => + p.name.toLowerCase().includes(searchVal.toLowerCase()) + ); + setsearchResults(tempResults); + } else { + setsearchResults(category.products); + } + }; + + return ( + + + + + + +  Categories + + + {category && category.name} + + + + {shopCart.length} + + + + + + + + + + + + + + + + {searchResults && searchResults.length}{' '} + {searchResults.length > 1 || searchResults.length === 0 ? ' foods' : ' food'} found + + + + + + {searchResults && + searchResults.map((product, index) => { + if (index <= amountLoaded && product.image) { + return ( + + ); + } + })} + + + + + + + + + ); +}; + +export default CategoryProducts; diff --git a/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/pages/FavouriteProducts.tsx b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/pages/FavouriteProducts.tsx new file mode 100644 index 0000000..f88f065 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/pages/FavouriteProducts.tsx @@ -0,0 +1,131 @@ +import { + IonBadge, + IonButton, + IonButtons, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonInfiniteScroll, + IonInfiniteScrollContent, + IonNote, + IonPage, + IonRow, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { cart, chevronBackOutline } from 'ionicons/icons'; +import { useEffect, useRef, useState } from 'react'; +import ProductCard from '../components/ProductCard'; +import { CartStore } from '../data/CartStore'; +import { FavouritesStore } from '../data/FavouritesStore'; +import { ProductStore } from '../data/ProductStore'; + +import styles from './CategoryProducts.module.css'; + +const FavouriteProducts = () => { + const cartRef = useRef(); + const products = ProductStore.useState((s) => s.products); + const favourites = FavouritesStore.useState((s) => s.product_ids); + const shopCart = CartStore.useState((s) => s.product_ids); + const [searchResults, setSearchResults] = useState([]); + const [amountLoaded, setAmountLoaded] = useState(6); + + useEffect(() => { + const getFavourites = () => { + setSearchResults([]); + + favourites.forEach((favourite) => { + var favouriteParts = favourite.split('/'); + var categorySlug = favouriteParts[0]; + var productID = favouriteParts[1]; + + const tempCategory = products.filter((p) => p.slug === categorySlug)[0]; + const tempProduct = tempCategory.products.filter( + (p) => parseInt(p.id) === parseInt(productID) + )[0]; + + const tempFavourite = { + category: tempCategory, + product: tempProduct, + }; + + setSearchResults((prevSearchResults) => [...prevSearchResults, tempFavourite]); + }); + }; + + getFavourites(); + }, [favourites]); + + const fetchMore = async (e) => { + // Increment the amount loaded by 6 for the next iteration + setAmountLoaded((prevAmount) => prevAmount + 6); + e.target.complete(); + }; + + return ( + + + + + + +  Categories + + + Favourites + + + {shopCart.length} + + + + + + + + + + + + + {searchResults && searchResults.length}{' '} + {searchResults.length > 1 || searchResults.length === 0 + ? ' favourites' + : ' favourite'}{' '} + found + + + + + + {searchResults && + searchResults.map((product, index) => { + if (index <= amountLoaded) { + return ( + + ); + } + })} + + + + + + + + + ); +}; + +export default FavouriteProducts; diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/pages/Home.module.scss b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/pages/Home.module.scss similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/pages/Home.module.scss rename to 03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/pages/Home.module.scss diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/pages/Home.tsx b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/pages/Home.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/pages/Home.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/pages/Home.tsx diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/pages/Product.module.css b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/pages/Product.module.css similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/pages/Product.module.css rename to 03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/pages/Product.module.css diff --git a/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/pages/Product.tsx b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/pages/Product.tsx new file mode 100644 index 0000000..cdfc70e --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/pages/Product.tsx @@ -0,0 +1,211 @@ +import { + IonBadge, + IonButton, + IonButtons, + IonCard, + IonCardContent, + IonCardHeader, + IonCardSubtitle, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { + arrowRedoOutline, + cart, + cartOutline, + chevronBackOutline, + heart, + heartOutline, +} from 'ionicons/icons'; +import { useEffect, useRef, useState } from 'react'; +import { useParams } from 'react-router'; +import ProductCard from '../components/ProductCard'; +import { addToCart, CartStore } from '../data/CartStore'; +import { addToFavourites, FavouritesStore } from '../data/FavouritesStore'; +import { ProductStore } from '../data/ProductStore'; + +import styles from './Product.module.css'; + +const Product = () => { + const params = useParams(); + const cartRef = useRef(); + const products = ProductStore.useState((s) => s.products); + const favourites = FavouritesStore.useState((s) => s.product_ids); + const [isFavourite, setIsFavourite] = useState(false); + const shopCart = CartStore.useState((s) => s.product_ids); + const [product, setProduct] = useState({}); + const [category, setCategory] = useState({}); + + useEffect(() => { + const categorySlug = params.slug; + const productID = params.id; + const tempCategory = products.filter((p) => p.slug === categorySlug)[0]; + const tempProduct = tempCategory.products.filter( + (p) => parseInt(p.id) === parseInt(productID) + )[0]; + + const tempIsFavourite = favourites.find((f) => f === `${categorySlug}/${productID}`); + + setIsFavourite(tempIsFavourite); + setCategory(tempCategory); + setProduct(tempProduct); + }, [params.slug, params.id]); + + useEffect(() => { + const tempIsFavourite = favourites.find((f) => f === `${category.slug}/${product.id}`); + setIsFavourite(tempIsFavourite ? true : false); + }, [favourites, product]); + + const addProductToFavourites = (e, categorySlug, productID) => { + e.preventDefault(); + addToFavourites(categorySlug, productID); + + document.getElementById( + `placeholder_favourite_product_${categorySlug}_${productID}` + ).style.display = ''; + document + .getElementById(`placeholder_favourite_product_${categorySlug}_${productID}`) + .classList.add('animate__fadeOutTopRight'); + }; + + const addProductToCart = (e, categorySlug, productID) => { + e.preventDefault(); + + document.getElementById(`placeholder_cart_${categorySlug}_${productID}`).style.display = ''; + document + .getElementById(`placeholder_cart_${categorySlug}_${productID}`) + .classList.add('animate__fadeOutUp'); + + setTimeout(() => { + cartRef.current.classList.add('animate__tada'); + addToCart(categorySlug, productID); + + setTimeout(() => { + cartRef.current.classList.remove('animate__tada'); + }, 500); + }, 500); + }; + + return ( + + + + + + +  {category.name} + + + + View Food + + + {shopCart.length} + + + + + + + + + + + + + +
+ addProductToFavourites(e, category.slug, product.id)} + /> + + +
+ product pic +

{product.name}

+ {product.nutrition} +
+ + +
+ + {product.price} + + addProductToCart(e, category.slug, product.id)} + > + +   Add to Cart + + + +
+
+
+
+
+ + + + Similar foods... + + + + + {category && + category.products && + category.products.map((similar, index) => { + if (similar.id !== product.id && product.image && index < 4) { + return ( + + ); + } + })} + +
+
+
+ ); +}; + +export default Product; diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/style.scss b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/style.scss similarity index 100% rename from 03_source/mobile/src/pages/DemoKanbanBoard/style.scss rename to 03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/style.scss diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/theme/variables.css b/03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/theme/variables.css similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/theme/variables.css rename to 03_source/mobile.trunk.1/src/pages/DemoFastFoodApp/theme/variables.css diff --git a/03_source/mobile/src/pages/DemoInstagramClone/AppPages/Tab1.jsx b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/AppPages/Tab1.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoInstagramClone/AppPages/Tab1.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/AppPages/Tab1.jsx diff --git a/03_source/mobile/src/pages/DemoInstagramClone/AppPages/Tab2.jsx b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/AppPages/Tab2.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoInstagramClone/AppPages/Tab2.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/AppPages/Tab2.jsx diff --git a/03_source/mobile/src/pages/DemoFloatingTabs/notes.md b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/NOTES.md similarity index 100% rename from 03_source/mobile/src/pages/DemoFloatingTabs/notes.md rename to 03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/NOTES.md diff --git a/03_source/mobile/src/pages/DemoReactMarvelApp/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/TestComponents/CurrentWeather/WeatherProperty.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactMarvelApp/TestComponents/CurrentWeather/WeatherProperty.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/TestComponents/CurrentWeather/WeatherProperty.tsx diff --git a/03_source/mobile/src/pages/DemoReactMarvelApp/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/TestComponents/CurrentWeather/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactMarvelApp/TestComponents/CurrentWeather/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/TestComponents/CurrentWeather/index.tsx diff --git a/03_source/mobile/src/pages/DemoReactLogin/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/TestComponents/SkeletonDashboard/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactLogin/TestComponents/SkeletonDashboard/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/TestComponents/SkeletonDashboard/index.tsx diff --git a/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/components/ExploreContainer.scss b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/components/ExploreContainer.scss new file mode 100644 index 0000000..11ea3ba --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/components/ExploreContainer.scss @@ -0,0 +1,26 @@ +.floating-tab-bar { + .container { + text-align: center; + position: absolute; + left: 0; + right: 0; + top: 50%; + transform: translateY(-50%); + } + + .container strong { + font-size: 20px; + line-height: 26px; + } + + .container p { + font-size: 16px; + line-height: 22px; + color: #8c8c8c; + margin: 0; + } + + .container a { + text-decoration: none; + } +} diff --git a/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/components/ExploreContainer.tsx b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/components/ExploreContainer.tsx new file mode 100644 index 0000000..759cc76 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/components/ExploreContainer.tsx @@ -0,0 +1,21 @@ +import './ExploreContainer.scss'; + +const ExploreContainer = ({ name }) => { + return ( +
+ {name} +

+ Explore{' '} + + UI Components + +

+
+ ); +}; + +export default ExploreContainer; diff --git a/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/index.tsx new file mode 100644 index 0000000..71a067d --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/index.tsx @@ -0,0 +1,46 @@ +import { IonIcon, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cogOutline, homeOutline, listOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './pages/Tab1'; +import Tab2 from './pages/Tab2'; +import Tab3 from './pages/Tab3'; + +import './theme/style.scss'; +import './theme/floating-tab-bar.scss'; + +function DemoFloatingTabs() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} + +export default DemoFloatingTabs; diff --git a/03_source/mobile/src/pages/DemoReactMarvelApp/theme/variables.scss b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/pages/Tab1.css similarity index 100% rename from 03_source/mobile/src/pages/DemoReactMarvelApp/theme/variables.scss rename to 03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/pages/Tab1.css diff --git a/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/pages/Tab1.tsx b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/pages/Tab1.tsx new file mode 100644 index 0000000..c328244 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/pages/Tab1.tsx @@ -0,0 +1,49 @@ +import { + IonButton, + IonButtons, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab1.css'; +import React from 'react'; +import { chevronBackOutline } from 'ionicons/icons'; + +const Tab1 = (): React.JSX.Element => { + const router = useIonRouter(); + + function handleBackClick() { + router.goBack(); + } + + return ( + + + + Floating Tab Bar + + + handleBackClick()}> + + + + + + + + + Floating Tab Bar + + + + + + ); +}; + +export default Tab1; diff --git a/03_source/mobile/src/pages/DemoSlidingProfile/AppPages/Tab2.css b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/pages/Tab2.css similarity index 100% rename from 03_source/mobile/src/pages/DemoSlidingProfile/AppPages/Tab2.css rename to 03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/pages/Tab2.css diff --git a/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/pages/Tab2.tsx b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/pages/Tab2.tsx new file mode 100644 index 0000000..048ff80 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/pages/Tab2.tsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab2.css'; + +const Tab2 = (): React.JSX.Element => { + return ( + + + + Floating Tab Bar + + + + + + Floating Tab Bar + + + + + + ); +}; + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/style.scss b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/pages/Tab3.css similarity index 100% rename from 03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/style.scss rename to 03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/pages/Tab3.css diff --git a/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/pages/Tab3.tsx b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/pages/Tab3.tsx new file mode 100644 index 0000000..54cfd9e --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/pages/Tab3.tsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab3.css'; + +const Tab3 = (): React.JSX.Element => { + return ( + + + + Floating Tab Bar + + + + + + Floating Tab Bar + + + + + + ); +}; + +export default Tab3; diff --git a/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/theme/floating-tab-bar.scss b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/theme/floating-tab-bar.scss new file mode 100644 index 0000000..9533ab9 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/theme/floating-tab-bar.scss @@ -0,0 +1,27 @@ +.demo-floating-tabs { + * { + --ion-background-color: white; + --ion-tab-bar-color: rgb(92, 123, 207); + --ion-tab-bar-color-selected: rgb(255, 255, 255); + } + + ion-tab-bar { + --background: rgb(44, 83, 192); + box-shadow: 0px 1px 12px rgba(0, 0, 0, 0.4); + border-radius: 16px !important; + + height: 50px; + width: 90%; + padding-top: 5px; + padding-bottom: 5px; + + bottom: 20px; + position: relative; + margin: 0 auto !important; + border-top: none; + } + + ion-tab-button { + border-radius: 16px !important; + } +} diff --git a/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/theme/style.scss b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/theme/style.scss new file mode 100644 index 0000000..d202499 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoFloatingTabs/theme/style.scss @@ -0,0 +1,82 @@ +/* +Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ +*/ + +/** Ionic CSS Variables **/ + +.floating-tab-bar { + * { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + } +} diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/AppPages/Tab1.jsx b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/AppPages/Tab1.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoKanbanBoard/AppPages/Tab1.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoInstagramClone/AppPages/Tab1.jsx diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/AppPages/Tab2.jsx b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/AppPages/Tab2.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoKanbanBoard/AppPages/Tab2.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoInstagramClone/AppPages/Tab2.jsx diff --git a/03_source/mobile/src/pages/DemoInstagramClone/notes.md b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/NOTES.md similarity index 100% rename from 03_source/mobile/src/pages/DemoInstagramClone/notes.md rename to 03_source/mobile.trunk.1/src/pages/DemoInstagramClone/NOTES.md diff --git a/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/TestComponents/CurrentWeather/WeatherProperty.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/TestComponents/CurrentWeather/WeatherProperty.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoInstagramClone/TestComponents/CurrentWeather/WeatherProperty.tsx diff --git a/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/TestComponents/CurrentWeather/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/TestComponents/CurrentWeather/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoInstagramClone/TestComponents/CurrentWeather/index.tsx diff --git a/03_source/mobile/src/pages/DemoReactMarvelApp/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/TestComponents/SkeletonDashboard/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactMarvelApp/TestComponents/SkeletonDashboard/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoInstagramClone/TestComponents/SkeletonDashboard/index.tsx diff --git a/03_source/mobile/src/pages/DemoRestaurantFinder/components/ExploreContainer.css b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/components/ExploreContainer.css similarity index 100% rename from 03_source/mobile/src/pages/DemoRestaurantFinder/components/ExploreContainer.css rename to 03_source/mobile.trunk.1/src/pages/DemoInstagramClone/components/ExploreContainer.css diff --git a/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/components/ExploreContainer.tsx b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/components/ExploreContainer.tsx new file mode 100644 index 0000000..396477f --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/components/ExploreContainer.tsx @@ -0,0 +1,21 @@ +import './ExploreContainer.css'; + +const ExploreContainer = ({ name }): React.JSX.Element => { + return ( +
+ {name} +

+ Explore{' '} + + UI Components + +

+
+ ); +}; + +export default ExploreContainer; diff --git a/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/components/Feed.module.scss b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/components/Feed.module.scss new file mode 100644 index 0000000..4854d16 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/components/Feed.module.scss @@ -0,0 +1,221 @@ +.postsContainer { + + margin-top: 1.5rem; + margin-bottom: 1.5rem; +} + +.postContainer { + + display: flex; + flex-direction: column; + margin-top: 1rem; +} + +.postProfile { + + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + padding-right: 0.75rem; + padding-left: 0.75rem; +} + +.postProfile ion-router-link { + + display: flex !important; + flex-direction: row !important; +} + +.postProfileInfo ion-avatar { + + height: 2.2rem; + width: 2.2rem; +} + +.postProfileInfo p { + + margin: 0; + padding: 0; + margin-left: 0.5rem; + font-weight: 500; + font-size: 0.9rem; + color: black; +} + +.postProfileInfo { + + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + align-content: center; +} + +.postImage { + + border-top: 1px solid rgb(216, 216, 216); + margin-top: 0.5rem; + height: 20rem; + width: 100%; +} + +.postImageLike { + + font-size: 10rem; + color: rgb(231, 231, 231); + position: absolute; + left: 32vmin; + margin-top: 20vmin; + display: none; +} + +.postActionsContainer { + + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + padding-right: 0.75rem; + padding-left: 0.75rem; + margin-top: 0.5rem; +} + +.postActions { + + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.postActions ion-icon, +.postBookmark ion-icon { + + font-size: 1.5rem; +} + +.postActions ion-icon:not(:first-child) { + + padding-left: 0.7rem; +} + +.postLikesContainer { + + padding-left: 0.75rem; + margin-top: 0.5rem; +} + +.postLikesContainer p { + + margin: 0; + padding: 0; + font-weight: 200 !important; + font-size: 0.8rem; +} + +.postLikedName { + + font-weight: 600; +} + +.postCaption { + + padding-left: 0.75rem; + padding-right: 0.75rem; + margin-top: 0.3rem; +} + +.postCaption p { + + margin: 0; + padding: 0; + font-weight: 200 !important; + font-size: 0.8rem; +} + +.postName { + + color: black !important; + font-weight: 600 !important; +} + +.postName ion-router-link { + + color: black; +} + +.postComments { + + padding-left: 0.75rem; + padding-right: 0.75rem; + margin-top: 0.5rem; +} + +.postComments p { + + margin: 0; + padding: 0; + color:rgb(175, 175, 175); + font-size: 0.8rem; +} + +.postAddComment { + + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.postAddCommentProfile { + + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + align-items: center; + margin-top: 0.5rem; +} + +.postAddCommentProfile ion-avatar { + + height: 1.9rem; + width: 1.9rem; +} + +.postAddCommentProfile p { + + padding-left: 0.75rem; + font-size: 0.8rem; + color: rgb(175, 175, 175); +} + +.postAddCommentActions { + + +} + +.postAddCommentActions ion-icon { + + padding-left: 0.5rem; +} + +.postTime { + + padding-left: 0.75rem; + padding-right: 0.75rem; + margin-top: 0rem; +} + +.postTime p { + + margin: 0; + padding: 0; + color: rgb(175, 175, 175); + font-size: 0.6rem; +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/components/Feed.tsx b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/components/Feed.tsx new file mode 100644 index 0000000..dbcfeff --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/components/Feed.tsx @@ -0,0 +1,129 @@ +import { IonAvatar, IonIcon, IonRouterLink } from '@ionic/react'; +import { + addCircleOutline, + bookmarkOutline, + chatbubbleOutline, + ellipsisVertical, + heart, + heartOutline, + paperPlaneOutline, +} from 'ionicons/icons'; +import { likePost } from '../pages/PostStore'; +import { ProfilesStore } from '../pages/ProfilesStore'; +import { ProfileStore } from '../pages/ProfileStore'; +import styles from './Feed.module.scss'; + +const Feed = (props): React.JSX.Element => { + const { posts } = props; + const profile = ProfileStore.useState((s) => s.profile); + const profiles = ProfilesStore.useState((s) => s.profiles); + + const addLike = (event, postID, liked) => { + likePost(event, postID, liked); + }; + + return ( +
+ {posts.map((post, index) => { + const postProfile = profiles.filter((p) => p.id === post.profile_id)[0]; + + return ( +
+
+
+ + + post avatar + + + + +

{postProfile.username}

+
+
+ +
+ +
+
+ +
+ +
+ +
+
+ addLike(e, post.id, post.liked)} + /> + + +
+ +
+ +
+
+ +
+

+ Liked by alanmontgomery and{' '} + 2 others +

+
+ +
+

+ + + {postProfile.username} + + {' '} + {post.caption} +

+
+ +
+

View all {post.comments.length} comments

+
+ +
+
+ + add comment avatar + +

Add a comment...

+
+ +
+ + +
+
+ +
+

{post.time}

+
+
+ ); + })} +
+ ); +}; + +export default Feed; diff --git a/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/components/Stories.module.scss b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/components/Stories.module.scss new file mode 100644 index 0000000..aeb99cb --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/components/Stories.module.scss @@ -0,0 +1,98 @@ +$border: linear-gradient(to bottom, #d82b7e, #f57939); + +.stories { + + height: fit-content; + margin-top: -0.7rem; +} + +.storiesContainer { + + overflow-x: scroll; + overflow-y: hidden; + -webkit-overflow-scrolling: touch; + display: flex; + flex-direction: row; + width: 100%; +} + +.storiesContainer::-webkit-scrollbar { + + display: none; +} + +.story, +.yourStory { + + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + align-content: center; + margin: 0 auto; + width: 4rem !important; + margin-left: 1rem; +} + +.story:first-child, +.yourStory:first-child { + + margin-left: 0.75rem; +} + +.story p, +.yourStory p { + + text-align: center; + margin: 0; + padding: 0; + margin-top: 0.2rem; + color: rgb(95, 95, 95); + font-size: 0.7rem; + font-weight: 400; + width: 120%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + position: relative; + margin-top: 5rem; +} + +.story img, +.yourStory img { + + height: 3.5rem !important; + width: 3.5rem !important; + position: absolute; + border-radius: 500px; + background: $border; + padding: 0.1rem; +} + +.yourStory img { + + background:rgb(214, 214, 214); +} + +.storyAdd { + + position: absolute; + color: white; + background-color: var(--ion-color-primary); + width: 1rem; + height: 1rem; + text-align: center; + margin: 0 auto; + display: flex; + flex-direction: row; + align-items: center; + align-content: center; + justify-content: center; + border-radius: 500px; + border: 2px solid white; + + bottom: 20px; + right: 0; + padding: 0.5rem; + font-size: 0.9rem; +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/components/Stories.tsx b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/components/Stories.tsx new file mode 100644 index 0000000..c902b30 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/components/Stories.tsx @@ -0,0 +1,27 @@ +import { IonCol, IonRouterLink, IonRow } from '@ionic/react'; +import styles from './Stories.module.scss'; + +const Stories = (props): React.JSX.Element => { + const { profiles } = props; + + return ( + +
+ {profiles.map((story, index) => { + return ( + + story avatar + {index === 0 &&
+
} + + +

{index === 0 ? 'Your story' : story.username}

+
+
+ ); + })} +
+
+ ); +}; + +export default Stories; diff --git a/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/index.tsx new file mode 100644 index 0000000..ab4b29b --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/index.tsx @@ -0,0 +1,91 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { bagOutline, cloudOutline, home, playCircleOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +// import Tab1 from './AppPages/Tab1'; +// import Tab2 from './AppPages/Tab2'; + +import './style.scss'; +import Home from './pages/Home'; +import Tab2 from './pages/Tab2'; +import Tab3 from './pages/Tab3'; + +import './theme/variables.scss'; +import { ProfileStore } from './pages/ProfileStore'; +import Profile from './pages/Profile'; +import MyProfile from './pages/MyProfile'; + +function DemoInstagramClone() { + const profile = ProfileStore.useState((s) => s.profile); + + return ( + + + {/* + + + + + + + + + + */} + + + + + + + + + + + + + + + + + + + + + + + {/* + + + Dashboard + + + + Search + + */} + + + + + + + + + + + + + + + + + tab avatar + + + + ); +} + +export default DemoInstagramClone; diff --git a/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Home.module.scss b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Home.module.scss new file mode 100644 index 0000000..48a1bdb --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Home.module.scss @@ -0,0 +1,198 @@ +.postsContainer { + + margin-top: 1.5rem; + margin-bottom: 1.5rem; +} + +.postContainer { + + display: flex; + flex-direction: column; + margin-top: 1rem; +} + +.postProfile { + + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + padding-right: 0.75rem; + padding-left: 0.75rem; +} + +.postProfileInfo ion-avatar { + + height: 2.2rem; + width: 2.2rem; +} + +.postProfileInfo p { + + margin: 0; + padding: 0; + margin-left: 0.5rem; + font-weight: 500; + font-size: 0.9rem; +} + +.postProfileInfo { + + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + align-content: center; +} + +.postImage { + + border-top: 1px solid rgb(216, 216, 216); + margin-top: 0.5rem; + height: 20rem; + width: 100%; +} + +.postActionsContainer { + + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + padding-right: 0.75rem; + padding-left: 0.75rem; + margin-top: 0.5rem; +} + +.postActions { + + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.postActions ion-icon, +.postBookmark ion-icon { + + font-size: 1.5rem; +} + +.postActions ion-icon:not(:first-child) { + + padding-left: 0.7rem; +} + +.postLikesContainer { + + padding-left: 0.75rem; + margin-top: 0.5rem; +} + +.postLikesContainer p { + + margin: 0; + padding: 0; + font-weight: 200 !important; + font-size: 0.8rem; +} + +.postLikedName { + + font-weight: 600; +} + +.postCaption { + + padding-left: 0.75rem; + padding-right: 0.75rem; + margin-top: 0.3rem; +} + +.postCaption p { + + margin: 0; + padding: 0; + font-weight: 200 !important; + font-size: 0.8rem; +} + +.postName { + + font-weight: 600 !important; +} + +.postComments { + + padding-left: 0.75rem; + padding-right: 0.75rem; + margin-top: 0.5rem; +} + +.postComments p { + + margin: 0; + padding: 0; + color:rgb(175, 175, 175); + font-size: 0.8rem; +} + +.postAddComment { + + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.postAddCommentProfile { + + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + align-items: center; + margin-top: 0.5rem; +} + +.postAddCommentProfile ion-avatar { + + height: 1.9rem; + width: 1.9rem; +} + +.postAddCommentProfile p { + + padding-left: 0.75rem; + font-size: 0.8rem; + color: rgb(175, 175, 175); +} + +.postAddCommentActions { + + +} + +.postAddCommentActions ion-icon { + + padding-left: 0.5rem; +} + +.postTime { + + padding-left: 0.75rem; + padding-right: 0.75rem; + margin-top: 0rem; +} + +.postTime p { + + margin: 0; + padding: 0; + color: rgb(175, 175, 175); + font-size: 0.6rem; +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Home.tsx b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Home.tsx new file mode 100644 index 0000000..64f15f8 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Home.tsx @@ -0,0 +1,66 @@ +import { + IonButton, + IonButtons, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import { + addCircleOutline, + chevronBackOutline, + heartOutline, + paperPlaneOutline, +} from 'ionicons/icons'; +import Feed from '../components/Feed'; +import Stories from '../components/Stories'; +import { PostStore } from './PostStore'; +import { ProfilesStore } from './ProfilesStore'; + +const Home = (): React.JSX.Element => { + const profiles = ProfilesStore.useState((s) => s.profiles); + const posts = PostStore.useState((s) => s.posts); + + const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + + handleBackClick()}> + + + + main logo + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default Home; diff --git a/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/MyProfile.tsx b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/MyProfile.tsx new file mode 100644 index 0000000..f643088 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/MyProfile.tsx @@ -0,0 +1,160 @@ +import { + IonButton, + IonButtons, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonToolbar, + useIonViewWillEnter, +} from '@ionic/react'; +import { + addCircleOutline, + bookmarksOutline, + chevronDown, + gridOutline, + menuOutline, +} from 'ionicons/icons'; +import { useState } from 'react'; +import styles from './Profile.module.scss'; + +import { ProfilesStore } from './ProfilesStore'; +import { ProfileStore } from './ProfileStore'; + +const MyProfile = (): React.JSX.Element => { + const currentProfile = ProfileStore.useState((s) => s.profile); + const profiles = ProfilesStore.useState((s) => s.profiles); + const [profile, setProfile] = useState(false); + + useIonViewWillEnter(() => { + const profileID = currentProfile.id; + const tempProfile = profiles.filter((p) => parseInt(p.id) === parseInt(profileID))[0]; + setProfile(tempProfile); + }); + + return ( + + + + +

+ {profile.username} + +

+
+ + + + + + + + + +
+
+ + + + + + profile avatar + + + + + + + {profile.posts && profile.posts.length} + + Posts + + + + {profile.followers} + Followers + + + + {profile.following} + Following + + + + + + + +

+ {profile.firstname} {profile.surname} +

+

{profile.title}

+

{profile.bio}

+ + {profile.link} + +
+
+ + + + + Edit Profile + + + + + + Promotions + + + + + + Insights + + + +
+ + + + + + + + + + + + + {profile.posts && + profile.posts.map((post, index) => { + return ( + + post + + ); + })} + +
+
+ ); +}; + +export default MyProfile; diff --git a/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/PostStore.tsx b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/PostStore.tsx new file mode 100644 index 0000000..441b84b --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/PostStore.tsx @@ -0,0 +1,139 @@ +import { Store } from 'pullstate'; + +export const PostStore = new Store({ + posts: [ + { + id: 1, + image: 'https://ionic.io/img/ioniconf/ioniconf-open-graph.png', + caption: 'Ioniconf 2021! Register Now!', + likes: 73, + liked: false, + profile_id: 6, + time: '1 hour ago', + comments: [ + { + profile_id: 3, + comment: 'Test', + }, + ], + }, + + { + id: 2, + image: + 'https://creativetacos.com/wp-content/uploads/2019/08/Free-Chipper-Personal-Finance-App-Kit.jpg', + caption: 'Ionic React Hub! UI Components, Templates, Clones and more!', + likes: 73, + liked: true, + profile_id: 1, + time: '1 hour ago', + comments: [ + { + profile_id: 3, + comment: 'Test', + }, + { + profile_id: 3, + comment: 'Test', + }, + { + profile_id: 3, + comment: 'Test', + }, + ], + }, + + { + id: 3, + image: 'https://cdn.buttercms.com/AIcP6e8FRx6fgsKa7bvy', + caption: 'Join the first ever Ionic Event!', + likes: 73, + liked: false, + profile_id: 4, + time: '2 hours ago', + comments: [ + { + profile_id: 1, + comment: 'Test', + }, + { + profile_id: 2, + comment: 'Test', + }, + ], + }, + + { + id: 4, + image: 'https://ionicframework.com/img/meta/ionic-framework-og.png', + caption: 'Build cross platform mobile apps with the Ionic Framework!', + likes: 73, + liked: false, + profile_id: 2, + time: '3 hours ago', + comments: [ + { + profile_id: 1, + comment: 'Test', + }, + { + profile_id: 2, + comment: 'Test', + }, + ], + }, + ], +}); + +export const likePost = (event, postID, liked) => { + event.target.classList.add('animate__heartBeat'); + + if (!liked) { + document.getElementById(`postLike_${postID}`).style.display = 'inline'; + } + + setTimeout(() => { + event.target.classList.remove('animate__heartBeat'); + document.getElementById(`postLike_${postID}`).style.display = 'none'; + }, 850); + + PostStore.update((s) => { + s.posts.find((p, index) => + parseInt(p.id) === parseInt(postID) ? (s.posts[index].liked = liked ? false : true) : false + ); + }); + + if (liked) { + PostStore.update((s) => { + s.posts.find((p, index) => + parseInt(p.id) === parseInt(postID) + ? (s.posts[index].likes = s.posts[index].likes++) + : false + ); + }); + } else { + PostStore.update((s) => { + s.posts.find((p, index) => + parseInt(p.id) === parseInt(postID) + ? (s.posts[index].likes = s.posts[index].likes--) + : false + ); + }); + } +}; + +export const addPost = (newPost) => { + PostStore.update((s) => { + s.posts = [...s.posts, newPost]; + }); +}; + +export const addCommentToPost = (newComment, postID) => { + PostStore.update((s) => { + s.posts.find((p, index) => + parseInt(p.id) === parseInt(postID) + ? (s.posts[index].comments = [...s.posts[index].comments, newComment]) + : false + ); + }); +}; diff --git a/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Profile.module.scss b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Profile.module.scss new file mode 100644 index 0000000..c749657 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Profile.module.scss @@ -0,0 +1,100 @@ +.username { + + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + align-items: center; + font-weight: 600; +} + +.username ion-icon { + + font-size: 0.8rem; + margin-left: 0.3rem; +} + +.label { + + color: black; + font-size: 0.7rem; + font-weight: 500; + font-family: Arial, Helvetica, sans-serif !important; + text-transform: lowercase; +} + +.label::first-letter { + + text-transform: uppercase; +} + +.value { + + font-size: 1rem; +} + +.profileAvatar { + + border-radius: 500px; + width: 5.5rem; + height: auto; +} + +.profileInfo { + + display: flex; + flex-direction: column; + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.profileInfo p, +.profileInfo a { + + // padding: 0.1rem 0 0.1rem 0; + margin: 0; + font-size: 0.9rem; +} + +.profileUsername { + + font-weight: 600; +} + +.profileTitle { + + color: rgb(136, 136, 136); +} + +.profileLink { + + color: rgb(22, 60, 131); + text-decoration: none; +} + +.profileActions ion-button { + + height: 2.3rem; + --border-radius: 5px; + font-weight: 600; +} + +.lightButton { + + --color: rgb(65, 65, 65); + --color-activated: rgb(65, 65, 65); + --background-hover: white; + --background-focused: white; + --background-activated: white; + --border-color: rgb(231, 231, 231); + --border-width: 2px; + font-size: 0.8rem; +} + +.postCol { + + --ion-grid-column-padding: 0rem; + padding: 0.1rem; + padding-bottom: 0.01rem !important; + padding-top: 0.01rem !important; +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Profile.tsx b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Profile.tsx new file mode 100644 index 0000000..aa07b55 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Profile.tsx @@ -0,0 +1,183 @@ +import { + IonBackButton, + IonButton, + IonButtons, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonToolbar, + useIonViewWillEnter, +} from '@ionic/react'; +import { + addCircleOutline, + arrowBackOutline, + bookmarksOutline, + chevronDown, + ellipsisVertical, + gridOutline, + menuOutline, + personOutline, +} from 'ionicons/icons'; +import { useState } from 'react'; +import { useParams } from 'react-router'; +import styles from './Profile.module.scss'; + +import { ProfilesStore } from './ProfilesStore'; +import { ProfileStore } from './ProfileStore'; + +const Profile = (): React.JSX.Element => { + const params = useParams(); + const profiles = ProfilesStore.useState((s) => s.profiles); + const currentProfile = ProfileStore.useState((s) => s.profile); + const [profile, setProfile] = useState(false); + + useIonViewWillEnter(() => { + const profileID = params.id; + const tempProfile = profiles.filter((p) => parseInt(p.id) === parseInt(profileID))[0]; + setProfile(tempProfile); + }); + + return ( + + + + + {profile.id === currentProfile.id ? ( +

+ {profile.username} + +

+ ) : ( + <> + +

+ {profile.username} +

+ + )} +
+ + + {profile.id === currentProfile.id ? ( + <> + + + + + + + + ) : ( + + + + )} + +
+
+ + + + + + profile avatar + + + + + + + {profile.posts && profile.posts.length} + + Posts + + + + {profile.followers} + Followers + + + + {profile.following} + Following + + + + + + + +

+ {profile.firstname} {profile.surname} +

+

{profile.title}

+

{profile.bio}

+ + {profile.link} + +
+
+ + + + + Follow + + + + + + Message + + + + + + + + + +
+ + + + + + + + + + + + + {profile.posts && + profile.posts.map((post, index) => { + return ( + + post + + ); + })} + +
+
+ ); +}; + +export default Profile; diff --git a/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/ProfileStore.tsx b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/ProfileStore.tsx new file mode 100644 index 0000000..42f4d58 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/ProfileStore.tsx @@ -0,0 +1,20 @@ +import { Store } from 'pullstate'; + +export const ProfileStore = new Store({ + profile: { + id: 1, + firstname: 'Alan', + surname: 'Montgomery', + avatar: '/assets/alan.jpg', + followers: 0, + following: 0, + }, + posts: [], + feed: [], +}); + +export const addProfilePost = (newPost) => { + ProfileStore.update((s) => { + s.posts = [...s.posts, newPost]; + }); +}; diff --git a/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/ProfilesStore.tsx b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/ProfilesStore.tsx new file mode 100644 index 0000000..63813c5 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/ProfilesStore.tsx @@ -0,0 +1,172 @@ +import { Store } from 'pullstate'; + +export const ProfilesStore = new Store({ + profiles: [ + { + id: 1, + firstname: 'Alan', + surname: 'Montgomery', + username: 'alanmontgomery', + title: 'Mobile Team Lead', + bio: 'Full Stack 🤓 Mobile Team Lead/Senior React Dev', + link: 'alanmontgomery.co.uk', + avatar: '/assets/alan.jpg', + followers: '1,470', + following: '230', + posts: [ + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + ], + }, + { + id: 2, + firstname: 'Max', + surname: 'Lynch', + username: 'maxlynch', + title: 'CEO Ionic', + bio: 'Co-founder/CEO @ionicframework. Created @capacitorjs. Gamer. @ManUtd fan.', + link: 'maxlynch.com', + avatar: 'https://pbs.twimg.com/profile_images/1318970727173885953/bln98FNj_400x400.jpg', + followers: '21.1K', + following: '1,200', + posts: [{}, {}, {}, {}, {}, {}, {}, {}, {}], + }, + { + id: 3, + firstname: 'Ben', + surname: 'Sperry', + username: 'bensperry', + title: 'CDO Ionic', + bio: 'Co-founder / CDO @ionicframework. Creator of @ionicons. Product designer. Pixel junkie. Forest explorer.', + link: 'bensperry.com', + avatar: 'https://pbs.twimg.com/profile_images/1328390491126308864/jHHgl5Dm_400x400.jpg', + followers: '800', + following: '700', + posts: [{}, {}, {}, {}, {}, {}, {}, {}, {}], + }, + { + id: 4, + firstname: 'Matt', + surname: 'Netkow', + username: 'mattnetkow', + title: 'Head of Product Marketing', + bio: 'I help web developers build cross-platform Web Native apps. @IonicFramework: Head of Product Marketing', + link: 'webnative.tech', + avatar: 'https://pbs.twimg.com/profile_images/1323383930150621187/GKc0nVzi_400x400.jpg', + followers: '1,200', + following: '900', + posts: [{}, {}, {}, {}, {}, {}, {}, {}, {}], + }, + { + id: 5, + firstname: 'Liam', + surname: 'DeBeasi', + username: 'liamdebeasi', + title: 'Software Engineer', + bio: 'Software Engineer at @ionicframework', + link: 'liamdebeasi.com', + avatar: 'https://pbs.twimg.com/profile_images/1105953692669366273/ZNK4lRAJ_400x400.jpg', + followers: '871', + following: '510', + posts: [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}], + }, + { + id: 6, + firstname: 'Mike', + surname: 'Hartington', + username: 'mikehartington', + title: 'Senior Dev Rel', + bio: 'Google Developer Expert. Mediocre at best. he/him. npx mhartington', + link: 'mhartington.io', + avatar: 'https://pbs.twimg.com/profile_images/1084993841898446849/DJ8XtR6L_400x400.jpg', + followers: '12.3K', + following: '2,200', + posts: [ + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + ], + }, + { + id: 7, + firstname: 'Adam', + surname: 'Bradley', + username: 'adambradley', + title: 'Director of Technology', + bio: 'Proud dad, husband, veteran & dogs best friend. Typos are my own', + link: 'ionicframework.com', + avatar: 'https://pbs.twimg.com/profile_images/909075942320025600/hfYqicUk_400x400.jpg', + followers: '613', + following: '571', + posts: [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}], + }, + { + id: 8, + firstname: 'Brody', + surname: 'Kidd', + username: 'brodykidd', + title: 'Enterprise Account Manager', + bio: 'Enterprise Account Manager | @ionicframework | @getcapacitor | @stenciljs', + link: 'ionicframework.com', + avatar: 'https://pbs.twimg.com/profile_images/477539679567228928/JObyaUW__400x400.jpeg', + followers: '677', + following: '219', + posts: [{}, {}, {}, {}, {}, {}, {}], + }, + ], +}); + +export const addProfilePost = (newPost) => { + ProfilesStore.update((s) => { + s.posts = [...s.posts, newPost]; + }); +}; diff --git a/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/theme/variables.scss b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Tab1.css similarity index 100% rename from 03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/theme/variables.scss rename to 03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Tab1.css diff --git a/03_source/mobile/src/pages/DemoReactNotes/theme/variables.scss b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Tab2.css similarity index 100% rename from 03_source/mobile/src/pages/DemoReactNotes/theme/variables.scss rename to 03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Tab2.css diff --git a/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Tab2.tsx b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Tab2.tsx new file mode 100644 index 0000000..a46ce0c --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Tab2.tsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab2.css'; + +const Tab2 = (): React.JSX.Element => { + return ( + + + + Tab 2 + + + + + + Tab 2 + + + + + + ); +}; + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoReactPollApp/AppPages/Add.module.scss b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Tab3.css similarity index 100% rename from 03_source/mobile/src/pages/DemoReactPollApp/AppPages/Add.module.scss rename to 03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Tab3.css diff --git a/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Tab3.tsx b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Tab3.tsx new file mode 100644 index 0000000..094a323 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/pages/Tab3.tsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab3.css'; + +const Tab3 = (): React.JSX.Element => { + return ( + + + + Tab 3 + + + + + + Tab 3 + + + + + + ); +}; + +export default Tab3; diff --git a/03_source/mobile/src/pages/DemoQrScanner/style.scss b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/style.scss similarity index 100% rename from 03_source/mobile/src/pages/DemoQrScanner/style.scss rename to 03_source/mobile.trunk.1/src/pages/DemoInstagramClone/style.scss diff --git a/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/theme/variables.scss b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/theme/variables.scss new file mode 100644 index 0000000..64b2b41 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoInstagramClone/theme/variables.scss @@ -0,0 +1,126 @@ +.demo-instagram-clone { + /* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + + * { + font-family: Arial, Helvetica, sans-serif !important; + scroll-behavior: smooth; + } + + ::-webkit-scrollbar, + ::-webkit-scrollbar-thumb { + width: 0px; + } + + /** Ionic CSS Variables **/ + :root { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + } + + :root { + --ion-toolbar-background: white; + --ion-tab-bar-color: black; + --ion-tab-bar-color-selected: black; + --ion-tab-bar-border-color: rgb(235, 235, 235); + } + + ion-tab-button ion-icon { + font-size: 1.6rem; + } + + ion-tab-button img { + border-radius: 500px; + height: 1.8rem; + border: 1px solid black; + } + + ion-tab-bar { + height: 3rem; + } + + ion-toolbar { + --border-style: none; + --padding-start: 1rem; + --padding-end: 1rem; + --padding-top: 0.5rem; + } + + ion-toolbar ion-icon { + font-weight: 900 !important; + font-size: 1.6rem; + } + + ion-toolbar ion-button:not(:last-child) { + padding-right: 0.3rem; + } +} diff --git a/03_source/mobile/src/pages/DemoOrderingApp/AppPages/Tab1.jsx b/03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/AppPages/Tab1.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/AppPages/Tab1.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/AppPages/Tab1.jsx diff --git a/03_source/mobile/src/pages/DemoOrderingApp/AppPages/Tab2.jsx b/03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/AppPages/Tab2.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/AppPages/Tab2.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/AppPages/Tab2.jsx diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/notes.md b/03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/NOTES.md similarity index 100% rename from 03_source/mobile/src/pages/DemoKanbanBoard/notes.md rename to 03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/NOTES.md diff --git a/03_source/mobile/src/pages/DemoReactNotes/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/TestComponents/CurrentWeather/WeatherProperty.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactNotes/TestComponents/CurrentWeather/WeatherProperty.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/TestComponents/CurrentWeather/WeatherProperty.tsx diff --git a/03_source/mobile/src/pages/DemoReactNotes/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/TestComponents/CurrentWeather/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactNotes/TestComponents/CurrentWeather/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/TestComponents/CurrentWeather/index.tsx diff --git a/03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/TestComponents/SkeletonDashboard/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactMovieAppWithAlgolia/TestComponents/SkeletonDashboard/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/TestComponents/SkeletonDashboard/index.tsx diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/components/Board/BoardItem.tsx b/03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/components/Board/BoardItem.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoKanbanBoard/components/Board/BoardItem.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/components/Board/BoardItem.tsx diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/components/Board/ListItem.tsx b/03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/components/Board/ListItem.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoKanbanBoard/components/Board/ListItem.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/components/Board/ListItem.tsx diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/components/Menu.css b/03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/components/Menu.css similarity index 100% rename from 03_source/mobile/src/pages/DemoKanbanBoard/components/Menu.css rename to 03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/components/Menu.css diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/components/Menu.tsx b/03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/components/Menu.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoKanbanBoard/components/Menu.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/components/Menu.tsx diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoKanbanBoard/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/index.tsx diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/pages/Add.tsx b/03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/pages/Add.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoKanbanBoard/pages/Add.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/pages/Add.tsx diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/pages/Kanban.module.scss b/03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/pages/Kanban.module.scss similarity index 100% rename from 03_source/mobile/src/pages/DemoKanbanBoard/pages/Kanban.module.scss rename to 03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/pages/Kanban.module.scss diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/pages/Kanban.tsx b/03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/pages/Kanban.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoKanbanBoard/pages/Kanban.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/pages/Kanban.tsx diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/store/SettingsStore.ts b/03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/store/SettingsStore.ts similarity index 100% rename from 03_source/mobile/src/pages/DemoKanbanBoard/store/SettingsStore.ts rename to 03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/store/SettingsStore.ts diff --git a/03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/store/index.ts b/03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/store/index.ts new file mode 100644 index 0000000..d8347ce --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/store/index.ts @@ -0,0 +1 @@ +export { default as SettingsStore } from './SettingsStore'; diff --git a/03_source/mobile/src/pages/DemoReactOnboardingUi/style.scss b/03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/style.scss similarity index 100% rename from 03_source/mobile/src/pages/DemoReactOnboardingUi/style.scss rename to 03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/style.scss diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/theme/variables.scss b/03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/theme/variables.scss similarity index 100% rename from 03_source/mobile/src/pages/DemoKanbanBoard/theme/variables.scss rename to 03_source/mobile.trunk.1/src/pages/DemoKanbanBoard/theme/variables.scss diff --git a/03_source/mobile/src/pages/DemoList/notes.md b/03_source/mobile.trunk.1/src/pages/DemoList/NOTES.md similarity index 100% rename from 03_source/mobile/src/pages/DemoList/notes.md rename to 03_source/mobile.trunk.1/src/pages/DemoList/NOTES.md diff --git a/03_source/mobile.trunk.1/src/pages/DemoList/TestContent.tsx b/03_source/mobile.trunk.1/src/pages/DemoList/TestContent.tsx new file mode 100644 index 0000000..39fd43b --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoList/TestContent.tsx @@ -0,0 +1,13 @@ +import { format } from 'date-fns'; + +export const TestContent = { + eventDate: format(new Date(), 'yyyy-MM-dd'), + title: 'helloworld', + price: 123, + currency: 'HKD', + duration_m: 480, + ageBottom: 12, + ageTop: 48, + location: 'Hong Kong Island', + avatar: 'https://www.ionics.io/img/ionic-logo.png', +}; diff --git a/03_source/mobile.trunk.1/src/pages/DemoList/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoList/index.tsx new file mode 100644 index 0000000..a0444f6 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoList/index.tsx @@ -0,0 +1,861 @@ +// REQ0054/user-setting +// +// PURPOSE: +// - Provides functionality view user profile +// +// RULES: +// - T.B.A. +// +import React, { useEffect, useRef, useState } from 'react'; +import { + IonHeader, + IonToolbar, + IonTitle, + IonContent, + IonPage, + IonButtons, + IonMenuButton, + IonGrid, + IonRow, + IonCol, + useIonRouter, + IonButton, + IonIcon, + IonPopover, + IonAvatar, + IonImg, + IonItem, + IonLabel, + IonList, + IonModal, + IonSearchbar, + useIonModal, + IonInput, + IonNote, + IonText, +} from '@ionic/react'; +import SpeakerItem from '../../components/SpeakerItem'; +import { Speaker } from '../../models/Speaker'; +import { Session } from '../../models/Schedule'; +import { connect } from '../../data/connect'; +import * as selectors from '../../data/selectors'; +import '../SpeakerList.scss'; +import { getEvents } from '../../api/getEvents'; +import { format } from 'date-fns'; +import { Event } from './types'; +import { + alertCircleOutline, + alertOutline, + apps, + appsOutline, + book, + bookOutline, + brushOutline, + calculatorOutline, + car, + cart, + cartOutline, + cashOutline, + chatbubbleEllipses, + chatbubbleEllipsesOutline, + chatbubbleOutline, + chevronBackOutline, + chevronForward, + chevronForwardOutline, + cloudOutline, + codeSlashOutline, + codeWorkingOutline, + colorPaletteOutline, + createOutline, + document, + documentTextOutline, + filmOutline, + flashOutline, + gift, + giftOutline, + globeSharp, + gridOutline, + heart, + imageOutline, + imagesOutline, + keyOutline, + languageOutline, + layers, + layersOutline, + list, + listCircle, + listOutline, + logInOutline, + logoFacebook, + mapOutline, + menuOutline, + paperPlaneOutline, + people, + person, + personCircleOutline, + personOutline, + pizzaOutline, + qrCodeOutline, + refreshOutline, + restaurant, + restaurantOutline, + settingsOutline, + shareSocialOutline, + statsChart, + sunny, + swapHorizontal, + trashOutline, + walkOutline, +} from 'ionicons/icons'; +import AboutPopover from '../../components/AboutPopover'; +import { OverlayEventDetail } from '@ionic/react/dist/types/components/react-component-lib/interfaces'; +import PATHS from '../../PATHS'; +import { logoutUser, setAccessToken, setIsLoggedIn } from '../../data/user/user.actions'; + +interface OwnProps {} + +interface StateProps { + speakers: Speaker[]; + speakerSessions: { [key: string]: Session[] }; +} + +interface DispatchProps { + logoutUser: typeof logoutUser; + setAccessToken: typeof setAccessToken; + setIsLoggedIn: typeof setIsLoggedIn; +} + +interface SettingsProps extends OwnProps, StateProps, DispatchProps {} + +const DemoList: React.FC = ({ + speakers, + speakerSessions, + logoutUser, + setAccessToken, + setIsLoggedIn, +}) => { + const [events, setEvents] = useState([]); + const [showPopover, setShowPopover] = useState(false); + const [popoverEvent, setPopoverEvent] = useState(); + const modal = useRef(null); + + const router = useIonRouter(); + + useEffect(() => { + getEvents().then(({ data }) => { + console.log({ data }); + setEvents(data); + }); + }, []); + + function handleBackButtonClick() { + router.goBack(); + } + + function handleLanguageClick() { + router.push(PATHS.CHANGE_LANGUAGE); + } + + function handleNotImplementedClick() { + router.push(PATHS.NOT_IMPLEMENTED); + } + + function handleDemoPageClick() { + router.push(PATHS.DEMO_PAGE); + } + + function handleServiceAgreementClick() { + router.push(PATHS.SERVICE_AGREEMENT); + } + + function handlePrivacyAgreementClick() { + router.push(PATHS.PRIVACY_AGREEMENT); + } + + const [showLogoutConfirmModal, setShowLogoutConfirmModal] = useState(false); + function handleConfirmLogoutClick() { + setShowLogoutConfirmModal(true); + } + + function handleLogoutClick() { + setAccessToken(); + setIsLoggedIn(false); + + router.push('/tabs', 'forward', 'replace'); + + setShowLogoutConfirmModal(false); + } + function handleLogoutCancel() { + setShowLogoutConfirmModal(false); + } + + function handleDemoReactShopClick() { + router.push(PATHS.DEMO_REACT_SHOP); + } + + return ( + + + + + {/* */} + handleBackButtonClick()}> + + + + +
+ + Setting +
+
+
+ + + + + Setting + + + + + router.push(PATHS.DEMO_WEATHER_APP_UI)}> + + Weather App + + + + + + router.push(PATHS.DEMO_2FA_EXAMPLE, 'forward')}> + + + Demo 2FA Example{' '} + layout only, not functioning + + + + + + + router.push(PATHS.DEMO_REACT_THEME_SWITCHER, 'forward')} + > + + Demo React Theme Switcher + + + + + + router.push(PATHS.DEMO_SKELETON_TEXT, 'forward')}> + + Demo Skeleton Text + + + + + + router.push(PATHS.DEMO_STICKY_BOTTOM_SHEET_EXAMPLE, 'forward')} + > + + Demo Sticky Bottom Sheet Example + + + + + + router.push(PATHS.DEMO_STORAGE_EXAMPLE, 'forward')}> + + + Demo Storage Example{' '} + (need fix, message cannot display) + + + + + + + router.push(PATHS.DEMO_SWIPERJS_TUTORIAL, 'forward')} + > + + Demo SwiperJS Tutorial + + + + + + router.push(PATHS.DEMO_REACT_DRAWING_CANVAS, 'forward')} + > + + Demo React Drawing Canvas + + + + + + router.push(PATHS.DEMO_REACT_HOOK_FORM_EXAMPLE, 'forward')} + > + + Demo React Hook Form Example + + + + + + router.push(PATHS.DEMO_REACT_ITEM_LIST, 'forward')}> + + Demo React Item List + + + + + + router.push(PATHS.DEMO_REACT_LIFECYCLES, 'forward')} + > + + Demo React Lifecycles + + + + + + router.push(PATHS.DEMO_REACT_LOGIN, 'forward')}> + + + Demo React Login (missing back button) + + + + + + + router.push(PATHS.DEMO_REACT_MARVEL_APP, 'forward')} + > + + Demo React Marvel App + + + + + + router.push(PATHS.DEMO_REACT_MOVIE_APP_WITH_ALGOLIA, 'forward')} + > + + Demo React Movie App with Algolia + + + + + + router.push(PATHS.DEMO_REACT_NOTES, 'forward')}> + + + Demo React Notes{' '} + TODO: need update IonSlide + + + + + + + router.push(PATHS.DEMO_FACEBOOK_CLONE, 'forward')}> + + Demo Facebook Clone + + + + + + router.push(PATHS.DEMO_FAST_FOOD_APP, 'forward')}> + + + Demo Fast Food App ion-slide outstanding + + + + + + + router.push(PATHS.DEMO_FLOATING_TABS, 'forward')}> + + Demo Floating Tabs + + + + + + router.push(PATHS.DEMO_INSTAGRAM_CLONE, 'forward')}> + + Demo Instagram Clone + + + + + + router.push(PATHS.DEMO_KANBAN_BOARD, 'forward')}> + + + Demo Kanban Board{' '} + // TODO: fix missing ionslide in new ionic + + + + + + + router.push(PATHS.DEMO_ORDERING_APP, 'forward')}> + + + Demo Ordering App outstanding css + + + + + + + router.push(PATHS.DEMO_PROFILE_EXAMPLE, 'forward')}> + + Demo Profile Example + + + + + + router.push(PATHS.DEMO_PULLSTATE_TUTORIAL, 'forward')} + > + + Demo Pullstate Tutorial + + + + + + router.push(PATHS.DEMO_REACT_ADD_TO_CART, 'forward')} + > + + Demo React Add to Cart + + + + + + router.push(PATHS.DEMO_REACT_CALCULATOR, 'forward')} + > + + + Demo React Calculator css need fix + + + + + + + router.push(PATHS.DEMO_ACCORDION_TUTORIAL, 'forward')} + > + + Demo Accordion Tutorial + + + + + + router.push(PATHS.DEMO_BANKING_UI, 'forward')}> + + + Demo Banking UI{' '} + (in the middle, style outstanding) + + + + + + + router.push(PATHS.DEMO_CAPACITOR_GOOGLE_MAPS_TUTORIAL, 'forward')} + > + + + Demo Capacitor Google Maps Tutorial{' '} + need a google map api key + + + + + + + router.push(PATHS.DEMO_COLOR_TUTORIAL, 'forward')}> + + Demo Color Tutorial + + + + + + router.push(PATHS.DEMO_ECOMMERCE_EXAMPLE, 'forward')} + > + + + Demo Ecommerce Example{' '} + (fetch data not available at remote site) + + + + + + {/* + + router.push(paths.DEMO_REACT_WHATSAPP_CLONE, 'forward')}> + + + Demo React WhatsApp Clone (need to resolve path problem) + + + + + */} + + + router.push(PATHS.DEMO_REACT_POLL_APP, 'forward')}> + + + Demo React Poll App{' '} + (css temporary broken, ignored) + + + + + + + router.push(PATHS.DEMO_REACT_SWITCH_TABS, 'forward')} + > + + + Demo React Switch Tabs{' '} + (hardcoded back button) + + + + + + + router.push(PATHS.DEMO_REACT_OVERLAY_HOOKS, 'forward')} + > + + Demo React Overlay Hooks + + + + + + router.push(PATHS.DEMO_PINTEREST_FLOATING_TAB_BAR, 'forward')} + > + + + Demo Pinterest Floating Tab Bar{' '} + (css not work well) + + + + + + + router.push(PATHS.DEMO_RESTAURANT_FINDER, 'forward')} + > + + + Demo Restaurant Finder{' '} + need server for map showing + + + + + + + handleDemoReactShopClick()}> + + Demo React Shop + + + + + + { + router.push(PATHS.DEMO_CLUB_HOUSE, 'forward'); + }} + > + + Demo Club house + + + + + + { + router.push(PATHS.DEMO_SCORE_BOARD, 'forward'); + }} + > + + + Demo Score Board
+ (IonCard problem) +
+ +
+
+ + + router.push(PATHS.DEMO_QUOTE_APP, 'forward')}> + + Demo Quote App + + + + + + router.push(PATHS.DEMO_QR_SCANNER, 'forward')}> + + Demo Qr scanner + + + + + + router.push(PATHS.DEMO_SHOP_APP_UI, 'forward')}> + + Demo Shop App UI + + + + + + router.push(PATHS.DEMO_DICTIONARY_APP, 'forward')}> + + Demo Dictionary App + + + + + + router.push(PATHS.DEMO_RECIPE_APP, 'forward')}> + + Demo Recipe App + + + + + + router.push(PATHS.DEMO_SLIDING_PROFILE, 'forward')}> + + Demo Sliding Profile + + + + + + router.push(PATHS.DEMO_QUIZ_APP, 'forward')}> + + Demo Quiz App + + + + + + router.push(PATHS.DEMO_BLOG_POST_UI, 'forward')}> + + Demo Blog Post UI + + + + + + router.push(PATHS.DEMO_REACT_TRAVEL_APP, 'forward')} + > + + + Demo React Travel App (on hold) + + + + + + + router.push(PATHS.DEMO_REACT_PROFILE_DASHBOARD_UI, 'forward')} + > + + Demo React Profile Dashboard UI + + + + + {/* TODO: */} + + + router.push(PATHS.DEMO_REACT_QR_CODE, 'forward')}> + + + Demo React QR Code need update + + + + + + + router.push(PATHS.DEMO_REACT_QUOTES, 'forward')}> + + Demo React Quotes + + + + + + router.push(PATHS.DEMO_REACT_SHOP_UI, 'forward')}> + + + Demo React Shop UI lower priority + + + + + + + router.push(PATHS.DEMO_REACT_TABS_MENUS_CUSTOM, 'forward')} + > + + Demo React Tabs Menus Custom + + + + + + router.push(PATHS.DEMO_REACT_ONBOARDING_UI, 'forward')} + > + + + Demo React Onboarding UI{' '} + TODO: update IonSlide + + + + +
+ + {/* REQ0058/logout */} + + +
+
+ +
+
+ Logout +
+
+ Unable to receive notifications after logging out +
+ +
+ + Cancel + + + Logout + +
+
+
+
+
+ ); +}; + +export default connect({ + mapStateToProps: (state) => ({ + speakers: selectors.getSpeakers(state), + speakerSessions: selectors.getSpeakerSessions(state), + }), + mapDispatchToProps: { + logoutUser, + setAccessToken, + setIsLoggedIn, + }, + component: React.memo(DemoList), +}); diff --git a/03_source/mobile.trunk.1/src/pages/DemoList/style.scss b/03_source/mobile.trunk.1/src/pages/DemoList/style.scss new file mode 100644 index 0000000..5fae6e3 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoList/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile.trunk.1/src/pages/DemoList/types.ts b/03_source/mobile.trunk.1/src/pages/DemoList/types.ts new file mode 100644 index 0000000..2f4577f --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoList/types.ts @@ -0,0 +1,14 @@ +export interface Event { + eventDate: Date; + joinMembers: undefined; + title: string; + price: number; + currency: string; + duration_m: number; + ageBottom: number; + ageTop: number; + location: string; + avatar: string; + // + id: string; +} diff --git a/03_source/mobile/src/pages/DemoProfileExample/AppPages/Tab1.jsx b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/AppPages/Tab1.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoProfileExample/AppPages/Tab1.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoOrderingApp/AppPages/Tab1.jsx diff --git a/03_source/mobile/src/pages/DemoProfileExample/AppPages/Tab2.jsx b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/AppPages/Tab2.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoProfileExample/AppPages/Tab2.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoOrderingApp/AppPages/Tab2.jsx diff --git a/03_source/mobile/src/pages/DemoOrderingApp/notes.md b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/NOTES.md similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/notes.md rename to 03_source/mobile.trunk.1/src/pages/DemoOrderingApp/NOTES.md diff --git a/03_source/mobile/src/pages/DemoReactOnboardingUi/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/TestComponents/CurrentWeather/WeatherProperty.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactOnboardingUi/TestComponents/CurrentWeather/WeatherProperty.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoOrderingApp/TestComponents/CurrentWeather/WeatherProperty.tsx diff --git a/03_source/mobile/src/pages/DemoReactOnboardingUi/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/TestComponents/CurrentWeather/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactOnboardingUi/TestComponents/CurrentWeather/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoOrderingApp/TestComponents/CurrentWeather/index.tsx diff --git a/03_source/mobile/src/pages/DemoReactNotes/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/TestComponents/SkeletonDashboard/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactNotes/TestComponents/SkeletonDashboard/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoOrderingApp/TestComponents/SkeletonDashboard/index.tsx diff --git a/03_source/mobile/src/pages/DemoOrderingApp/components/CoffeeCard.module.css b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/components/CoffeeCard.module.css similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/components/CoffeeCard.module.css rename to 03_source/mobile.trunk.1/src/pages/DemoOrderingApp/components/CoffeeCard.module.css diff --git a/03_source/mobile/src/pages/DemoOrderingApp/components/CoffeeCard.tsx b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/components/CoffeeCard.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/components/CoffeeCard.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoOrderingApp/components/CoffeeCard.tsx diff --git a/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/components/CoffeeCardOffer.tsx b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/components/CoffeeCardOffer.tsx new file mode 100644 index 0000000..2d4db06 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/components/CoffeeCardOffer.tsx @@ -0,0 +1,31 @@ +import { IonCard, IonCardSubtitle, IonCardTitle, IonCol, IonRow } from '@ionic/react'; +import { Plus } from 'react-iconly'; + +import styles from './CoffeeCard.module.css'; + +const CoffeeCardOffer = (props) => { + const { offer } = props; + + return ( + + + + + + coffee + + + +
+ {offer.title} +

{offer.description}

+
+
+
+
+
+
+ ); +}; + +export default CoffeeCardOffer; diff --git a/03_source/mobile/src/pages/DemoOrderingApp/components/Tabs.tsx b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/components/Tabs.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/components/Tabs.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoOrderingApp/components/Tabs.tsx diff --git a/03_source/mobile/src/pages/DemoOrderingApp/components/ViewCoffeeCard.tsx b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/components/ViewCoffeeCard.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/components/ViewCoffeeCard.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoOrderingApp/components/ViewCoffeeCard.tsx diff --git a/03_source/mobile/src/pages/DemoOrderingApp/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoOrderingApp/index.tsx diff --git a/03_source/mobile/src/pages/DemoOrderingApp/module.d.ts b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/module.d.ts similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/module.d.ts rename to 03_source/mobile.trunk.1/src/pages/DemoOrderingApp/module.d.ts diff --git a/03_source/mobile/src/pages/DemoOrderingApp/pages/Cart.module.css b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/pages/Cart.module.css similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/pages/Cart.module.css rename to 03_source/mobile.trunk.1/src/pages/DemoOrderingApp/pages/Cart.module.css diff --git a/03_source/mobile/src/pages/DemoOrderingApp/pages/Cart.tsx b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/pages/Cart.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/pages/Cart.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoOrderingApp/pages/Cart.tsx diff --git a/03_source/mobile/src/pages/DemoOrderingApp/pages/Favourites.tsx b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/pages/Favourites.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/pages/Favourites.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoOrderingApp/pages/Favourites.tsx diff --git a/03_source/mobile/src/pages/DemoOrderingApp/pages/Home.scss b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/pages/Home.scss similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/pages/Home.scss rename to 03_source/mobile.trunk.1/src/pages/DemoOrderingApp/pages/Home.scss diff --git a/03_source/mobile/src/pages/DemoOrderingApp/pages/Home.tsx b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/pages/Home.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/pages/Home.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoOrderingApp/pages/Home.tsx diff --git a/03_source/mobile/src/pages/DemoOrderingApp/pages/ViewCoffee.module.css b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/pages/ViewCoffee.module.css similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/pages/ViewCoffee.module.css rename to 03_source/mobile.trunk.1/src/pages/DemoOrderingApp/pages/ViewCoffee.module.css diff --git a/03_source/mobile/src/pages/DemoOrderingApp/pages/ViewCoffee.tsx b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/pages/ViewCoffee.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/pages/ViewCoffee.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoOrderingApp/pages/ViewCoffee.tsx diff --git a/03_source/mobile/src/pages/DemoOrderingApp/pages/ViewCoffees.tsx b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/pages/ViewCoffees.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/pages/ViewCoffees.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoOrderingApp/pages/ViewCoffees.tsx diff --git a/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/store/CartStore.tsx b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/store/CartStore.tsx new file mode 100644 index 0000000..5562434 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/store/CartStore.tsx @@ -0,0 +1,20 @@ +import { Store } from 'pullstate'; + +const CartStore = new Store({ + total: 0, + coffee_ids: [], +}); + +export default CartStore; + +export const addToCart = (coffeeID) => { + CartStore.update((s) => { + s.coffee_ids = [...s.coffee_ids, `${parseInt(coffeeID)}`]; + }); +}; + +export const removeFromCart = (coffeeIndex) => { + CartStore.update((s) => { + s.coffee_ids.splice(coffeeIndex, 1); + }); +}; diff --git a/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/store/CoffeeOfferStore.tsx b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/store/CoffeeOfferStore.tsx new file mode 100644 index 0000000..600da3a --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/store/CoffeeOfferStore.tsx @@ -0,0 +1,15 @@ +import { Store } from 'pullstate'; + +const CoffeeOfferStore = new Store({ + offers: [ + { + id: 1, + title: 'Buy one get one free!', + description: 'Any time you buy a coffee using your loyalty card scheme, you can get one free', + image: + 'https://images.pexels.com/photos/861090/pexels-photo-861090.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260', + }, + ], +}); + +export default CoffeeOfferStore; diff --git a/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/store/CoffeeSizeStore.tsx b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/store/CoffeeSizeStore.tsx new file mode 100644 index 0000000..e066f0b --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/store/CoffeeSizeStore.tsx @@ -0,0 +1,20 @@ +import { Store } from 'pullstate'; + +const CoffeeSizeStore = new Store({ + sizes: [ + { + id: 1, + name: 'Small', + }, + { + id: 2, + name: 'Medium', + }, + { + id: 3, + name: 'Large', + }, + ], +}); + +export default CoffeeSizeStore; diff --git a/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/store/CoffeeStore.tsx b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/store/CoffeeStore.tsx new file mode 100644 index 0000000..24f142f --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/store/CoffeeStore.tsx @@ -0,0 +1,108 @@ +import { Store } from 'pullstate'; + +const CoffeeStore = new Store({ + coffees: [ + { + id: 1, + name: 'Cappuccino', + summary: 'With Milk', + extras: ['milk'], + description: + 'This is a beautiful cup of cappuccino, complimented with semi-skimmed milk. Comes in three different sizes.', + price: '3.20', + prices: [ + { + size_id: 1, + price: '3.20', + }, + { + size_id: 2, + price: '3.90', + }, + { + size_id: 3, + price: '4.20', + }, + ], + image: + 'https://images.pexels.com/photos/1170659/pexels-photo-1170659.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260', + }, + { + id: 2, + name: 'Lattè', + summary: 'With Caramel', + extras: ['caramel'], + description: + 'This is a beautiful cup of lattè, complimented with sweet caramel. Comes in three different sizes.', + price: '5.10', + prices: [ + { + size_id: 1, + price: '4.35', + }, + { + size_id: 2, + price: '4.85', + }, + { + size_id: 3, + price: '5.10', + }, + ], + image: + 'https://images.pexels.com/photos/2067399/pexels-photo-2067399.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260', + }, + { + id: 3, + name: 'Espresso', + summary: 'With 2 shots', + extras: ['2 shots'], + description: + 'This is a beautiful cup of espresso, complimented with 2 shots. Comes in three different sizes.', + price: '6.20', + prices: [ + { + size_id: 1, + price: '6.20', + }, + { + size_id: 2, + price: '6.80', + }, + { + size_id: 3, + price: '7.10', + }, + ], + image: + 'https://images.pexels.com/photos/302894/pexels-photo-302894.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260', + }, + { + id: 4, + name: 'Americano', + summary: 'With Milk', + extras: ['Milk'], + description: + 'This is a beautiful cup of Americano, complimented with full fat milk. Comes in three different sizes.', + price: '5.35', + prices: [ + { + size_id: 1, + price: '5.35', + }, + { + size_id: 2, + price: '5.70', + }, + { + size_id: 3, + price: '6.50', + }, + ], + image: + 'https://images.pexels.com/photos/6207297/pexels-photo-6207297.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260', + }, + ], +}); + +export default CoffeeStore; diff --git a/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/store/FavouriteStore.tsx b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/store/FavouriteStore.tsx new file mode 100644 index 0000000..07e3166 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/store/FavouriteStore.tsx @@ -0,0 +1,18 @@ +import { Store } from 'pullstate'; + +const FavouriteStore = new Store({ + total: 0, + coffee_ids: [], +}); + +export default FavouriteStore; + +export const addToFavourites = (coffeeID) => { + FavouriteStore.update((s) => { + if (s.coffee_ids.find((id) => id === parseInt(coffeeID))) { + s.coffee_ids = s.coffee_ids.filter((id) => id !== parseInt(coffeeID)); + } else { + s.coffee_ids = [...s.coffee_ids, parseInt(coffeeID)]; + } + }); +}; diff --git a/03_source/mobile/src/pages/DemoOrderingApp/store/Selectors.tsx b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/store/Selectors.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/store/Selectors.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoOrderingApp/store/Selectors.tsx diff --git a/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/store/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/store/index.tsx new file mode 100644 index 0000000..e6b0c0b --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/store/index.tsx @@ -0,0 +1,5 @@ +export { default as CoffeeStore } from './CoffeeStore'; +export { default as CoffeeSizeStore } from './CoffeeSizeStore'; +export { default as CoffeeOfferStore } from './CoffeeOfferStore'; +export { default as CartStore } from './CartStore'; +export { default as FavouriteStore } from './FavouriteStore'; diff --git a/03_source/mobile/src/pages/DemoOrderingApp/theme/variables.scss b/03_source/mobile.trunk.1/src/pages/DemoOrderingApp/theme/variables.scss similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/theme/variables.scss rename to 03_source/mobile.trunk.1/src/pages/DemoOrderingApp/theme/variables.scss diff --git a/03_source/mobile/src/pages/DemoRecipeApp/AppPages/Category.module.scss b/03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab1.css similarity index 100% rename from 03_source/mobile/src/pages/DemoRecipeApp/AppPages/Category.module.scss rename to 03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab1.css diff --git a/03_source/mobile/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab1.jsx b/03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab1.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab1.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab1.jsx diff --git a/03_source/mobile/src/pages/DemoRecipeApp/pages/Category.module.scss b/03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab2.css similarity index 100% rename from 03_source/mobile/src/pages/DemoRecipeApp/pages/Category.module.scss rename to 03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab2.css diff --git a/03_source/mobile/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab2.jsx b/03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab2.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab2.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab2.jsx diff --git a/03_source/mobile/src/pages/DemoStorageExample/pages/Home.scss b/03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab3.css similarity index 100% rename from 03_source/mobile/src/pages/DemoStorageExample/pages/Home.scss rename to 03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab3.css diff --git a/03_source/mobile/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab3.jsx b/03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab3.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab3.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab3.jsx diff --git a/03_source/mobile/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab4.jsx b/03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab4.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab4.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab4.jsx diff --git a/03_source/mobile/src/pages/DemoPinterestFloatingTabBar/notes.md b/03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/NOTES.md similarity index 100% rename from 03_source/mobile/src/pages/DemoPinterestFloatingTabBar/notes.md rename to 03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/NOTES.md diff --git a/03_source/mobile/src/pages/DemoSlidingProfile/components/ExploreContainer.css b/03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/components/ExploreContainer.css similarity index 100% rename from 03_source/mobile/src/pages/DemoSlidingProfile/components/ExploreContainer.css rename to 03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/components/ExploreContainer.css diff --git a/03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/components/ExploreContainer.jsx b/03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/components/ExploreContainer.jsx new file mode 100644 index 0000000..799b82a --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/components/ExploreContainer.jsx @@ -0,0 +1,21 @@ +import './ExploreContainer.css'; + +const ExploreContainer = ({ name }) => { + return ( +
+ {name} +

+ Explore{' '} + + UI Components + +

+
+ ); +}; + +export default ExploreContainer; diff --git a/03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/custom-tab-bar.scss b/03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/custom-tab-bar.scss new file mode 100644 index 0000000..1aa74fb --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/custom-tab-bar.scss @@ -0,0 +1,29 @@ +.custom-tab-bar { + * { + /* --ion-background-color: white; */ + --ion-tab-bar-color: var(--tab-color); + --ion-tab-bar-color-selected: var(--tab-color-selected); + } + + ion-tab-bar { + --background: var(--tab-background); + box-shadow: 0px 1px 8px rgba(0, 0, 0, 0.4); + border-radius: 50px !important; + + height: 50px; + width: 50%; + padding-top: 5px; + padding-bottom: 5px; + padding-left: 10px; + padding-right: 10px; + + bottom: 20px; + position: relative; + margin: 0 auto !important; + border-top: none; + } + + ion-tab-button { + border-radius: 16px !important; + } +} diff --git a/03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/index.tsx new file mode 100644 index 0000000..6ccb3e7 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/index.tsx @@ -0,0 +1,54 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { chatbubble, cloudOutline, home, person, search, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; +import Tab3 from './AppPages/Tab3'; +import Tab4 from './AppPages/Tab4'; + +import './style.scss'; +import './custom-tab-bar.scss'; + +function DemoPinterestFloatingTabBar() { + return ( + + + + + + + + + + + + + + + + + + + {/* */} + + + + + + + + + + + + + + + + + ); +} + +export default DemoPinterestFloatingTabBar; diff --git a/03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/style.scss b/03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/style.scss new file mode 100644 index 0000000..fae1253 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoPinterestFloatingTabBar/style.scss @@ -0,0 +1,253 @@ +/* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + +/** Ionic CSS Variables **/ +.demo-pinterest-floating-tab-bar { + * { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + } + + @media (prefers-color-scheme: dark) { + /* + * Dark Colors + * ------------------------------------------- + */ + + body { + --ion-color-primary: #428cff; + --ion-color-primary-rgb: 66, 140, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3a7be0; + --ion-color-primary-tint: #5598ff; + + --ion-color-secondary: #50c8ff; + --ion-color-secondary-rgb: 80, 200, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #46b0e0; + --ion-color-secondary-tint: #62ceff; + + --ion-color-tertiary: #6a64ff; + --ion-color-tertiary-rgb: 106, 100, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #5d58e0; + --ion-color-tertiary-tint: #7974ff; + + --ion-color-success: #2fdf75; + --ion-color-success-rgb: 47, 223, 117; + --ion-color-success-contrast: #000000; + --ion-color-success-contrast-rgb: 0, 0, 0; + --ion-color-success-shade: #29c467; + --ion-color-success-tint: #44e283; + + --ion-color-warning: #ffd534; + --ion-color-warning-rgb: 255, 213, 52; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0bb2e; + --ion-color-warning-tint: #ffd948; + + --ion-color-danger: #ff4961; + --ion-color-danger-rgb: 255, 73, 97; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #e04055; + --ion-color-danger-tint: #ff5b71; + + --ion-color-dark: #f4f5f8; + --ion-color-dark-rgb: 244, 245, 248; + --ion-color-dark-contrast: #000000; + --ion-color-dark-contrast-rgb: 0, 0, 0; + --ion-color-dark-shade: #d7d8da; + --ion-color-dark-tint: #f5f6f9; + + --ion-color-medium: #989aa2; + --ion-color-medium-rgb: 152, 154, 162; + --ion-color-medium-contrast: #000000; + --ion-color-medium-contrast-rgb: 0, 0, 0; + --ion-color-medium-shade: #86888f; + --ion-color-medium-tint: #a2a4ab; + + --ion-color-light: #222428; + --ion-color-light-rgb: 34, 36, 40; + --ion-color-light-contrast: #ffffff; + --ion-color-light-contrast-rgb: 255, 255, 255; + --ion-color-light-shade: #1e2023; + --ion-color-light-tint: #383a3e; + } + + /* + * iOS Dark Theme + * ------------------------------------------- + */ + + .ios body { + --ion-background-color: #000000; + --ion-background-color-rgb: 0, 0, 0; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-color-step-50: #0d0d0d; + --ion-color-step-100: #1a1a1a; + --ion-color-step-150: #262626; + --ion-color-step-200: #333333; + --ion-color-step-250: #404040; + --ion-color-step-300: #4d4d4d; + --ion-color-step-350: #595959; + --ion-color-step-400: #666666; + --ion-color-step-450: #737373; + --ion-color-step-500: #808080; + --ion-color-step-550: #8c8c8c; + --ion-color-step-600: #999999; + --ion-color-step-650: #a6a6a6; + --ion-color-step-700: #b3b3b3; + --ion-color-step-750: #bfbfbf; + --ion-color-step-800: #cccccc; + --ion-color-step-850: #d9d9d9; + --ion-color-step-900: #e6e6e6; + --ion-color-step-950: #f2f2f2; + + --ion-item-background: #000000; + + --ion-card-background: #1c1c1d; + } + + .ios ion-modal { + --ion-background-color: var(--ion-color-step-100); + --ion-toolbar-background: var(--ion-color-step-150); + --ion-toolbar-border-color: var(--ion-color-step-250); + } + + /* + * Material Design Dark Theme + * ------------------------------------------- + */ + + .md body { + --ion-background-color: #121212; + --ion-background-color-rgb: 18, 18, 18; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-border-color: #222222; + + --ion-color-step-50: #1e1e1e; + --ion-color-step-100: #2a2a2a; + --ion-color-step-150: #363636; + --ion-color-step-200: #414141; + --ion-color-step-250: #4d4d4d; + --ion-color-step-300: #595959; + --ion-color-step-350: #656565; + --ion-color-step-400: #717171; + --ion-color-step-450: #7d7d7d; + --ion-color-step-500: #898989; + --ion-color-step-550: #949494; + --ion-color-step-600: #a0a0a0; + --ion-color-step-650: #acacac; + --ion-color-step-700: #b8b8b8; + --ion-color-step-750: #c4c4c4; + --ion-color-step-800: #d0d0d0; + --ion-color-step-850: #dbdbdb; + --ion-color-step-900: #e7e7e7; + --ion-color-step-950: #f3f3f3; + + --ion-item-background: #1e1e1e; + + --ion-toolbar-background: #1f1f1f; + + --ion-tab-bar-background: #1f1f1f; + + --ion-card-background: #1e1e1e; + } + } + + :root { + /* Custom tab bar */ + --tab-background: rgb(251, 251, 251); + --tab-color: rgb(153, 153, 153); + --tab-color-selected: black; + } + + @media (prefers-color-scheme: dark) { + :root { + /* Custom tab bar */ + --tab-background: rgb(53, 53, 53); + --tab-color: rgb(83, 83, 83); + --tab-color-selected: white; + } + } +} diff --git a/03_source/mobile/src/pages/DemoPullstateTutorial/AppPages/Tab1.jsx b/03_source/mobile.trunk.1/src/pages/DemoProfileExample/AppPages/Tab1.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoPullstateTutorial/AppPages/Tab1.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoProfileExample/AppPages/Tab1.jsx diff --git a/03_source/mobile/src/pages/DemoPullstateTutorial/AppPages/Tab2.jsx b/03_source/mobile.trunk.1/src/pages/DemoProfileExample/AppPages/Tab2.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoPullstateTutorial/AppPages/Tab2.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoProfileExample/AppPages/Tab2.jsx diff --git a/03_source/mobile/src/pages/DemoProfileExample/notes.md b/03_source/mobile.trunk.1/src/pages/DemoProfileExample/NOTES.md similarity index 100% rename from 03_source/mobile/src/pages/DemoProfileExample/notes.md rename to 03_source/mobile.trunk.1/src/pages/DemoProfileExample/NOTES.md diff --git a/03_source/mobile/src/pages/DemoReactQuotes/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk.1/src/pages/DemoProfileExample/TestComponents/CurrentWeather/WeatherProperty.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactQuotes/TestComponents/CurrentWeather/WeatherProperty.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoProfileExample/TestComponents/CurrentWeather/WeatherProperty.tsx diff --git a/03_source/mobile/src/pages/DemoReactQuotes/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoProfileExample/TestComponents/CurrentWeather/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactQuotes/TestComponents/CurrentWeather/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoProfileExample/TestComponents/CurrentWeather/index.tsx diff --git a/03_source/mobile/src/pages/DemoReactOnboardingUi/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoProfileExample/TestComponents/SkeletonDashboard/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactOnboardingUi/TestComponents/SkeletonDashboard/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoProfileExample/TestComponents/SkeletonDashboard/index.tsx diff --git a/03_source/mobile.trunk.1/src/pages/DemoProfileExample/components/Figure.module.scss b/03_source/mobile.trunk.1/src/pages/DemoProfileExample/components/Figure.module.scss new file mode 100644 index 0000000..48ef851 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoProfileExample/components/Figure.module.scss @@ -0,0 +1,26 @@ +.figure { + padding: 1rem; +} + +.figure h6 { + font-size: 1.5rem; + font-weight: 200; +} + +.figure p { + color: rgb(255, 255, 255); + font-size: 0.9rem; + font-weight: 200; +} + +.figure:nth-child(1) { + background-color: rgb(157, 163, 141); +} + +.figure:nth-child(2) { + background-color: rgb(150, 155, 138); +} + +.figure:nth-child(3) { + background-color: rgb(135, 143, 120); +} diff --git a/03_source/mobile.trunk.1/src/pages/DemoProfileExample/components/Figure.tsx b/03_source/mobile.trunk.1/src/pages/DemoProfileExample/components/Figure.tsx new file mode 100644 index 0000000..57c6f2b --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoProfileExample/components/Figure.tsx @@ -0,0 +1,9 @@ +import { IonCol } from '@ionic/react'; +import styles from './Figure.module.scss'; + +export const Figure = (props): React.JSX.Element => ( + +
{props.count}
+

{props.title}

+
+); diff --git a/03_source/mobile.trunk.1/src/pages/DemoProfileExample/components/Post.module.scss b/03_source/mobile.trunk.1/src/pages/DemoProfileExample/components/Post.module.scss new file mode 100644 index 0000000..3b81ae7 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoProfileExample/components/Post.module.scss @@ -0,0 +1,55 @@ +.post { + border-bottom: 1px solid rgb(219, 219, 219); + padding-top: 1rem; + padding-bottom: 0.2rem; + + p { + font-size: 0.9rem; + padding: 0 !important; + margin: 0 !important; + } + + .postText { + color: rgb(107, 112, 97); + } +} + +.postAvatar { + height: 3.5rem; + width: 3.5rem; + margin-right: 1rem; + border: 3px solid rgba(218, 223, 208, 1); +} + +.postInfo { + display: flex; + flex-direction: row; + justify-content: space-between; + padding-right: 1rem; + color: rgb(190, 190, 190); +} + +.postReactions { + display: flex; + justify-content: space-between; + padding-right: 1rem; + margin-top: 0.5rem; + color: rgb(107, 112, 97); + + .postReaction { + display: flex; + align-items: center; + justify-content: space-between; + font-size: 0.9rem; + + ion-icon { + margin-right: 0.5rem; + } + + p { + padding: 0; + margin: 0; + font-size: 0.8rem; + } + } +} diff --git a/03_source/mobile.trunk.1/src/pages/DemoProfileExample/components/Post.tsx b/03_source/mobile.trunk.1/src/pages/DemoProfileExample/components/Post.tsx new file mode 100644 index 0000000..406e1dd --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoProfileExample/components/Post.tsx @@ -0,0 +1,35 @@ +import { IonAvatar, IonIcon, IonItem, IonLabel } from '@ionic/react'; +import { chatbubbleOutline, heart, heartOutline, shareSocialOutline } from 'ionicons/icons'; +import styles from './Post.module.scss'; + +export const Post = (props): React.JSX.Element => ( +
+ + + + + +
+

{props.post.date}

+

@93alan

+
+

{props.post.text}

+ +
+
+ +

{props.post.comments}

+
+
+ +

{props.post.likes}

+
+ +
+ +
+
+
+
+
+); diff --git a/03_source/mobile.trunk.1/src/pages/DemoProfileExample/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoProfileExample/index.tsx new file mode 100644 index 0000000..392e5a6 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoProfileExample/index.tsx @@ -0,0 +1,38 @@ +import { IonRouterOutlet, IonTabs } from '@ionic/react'; + +import { Route, Redirect } from 'react-router'; + +// import Tab1 from './AppPages/Tab1'; +// import Tab2 from './AppPages/Tab2'; + +import './style.scss'; +import Home from './pages/Home'; + +function DemoProfileExample(): React.JSX.Element { + return ( + + + + + + + + + + {/* + + + + Dashboard + + + + Search + + + */} + + ); +} + +export default DemoProfileExample; diff --git a/03_source/mobile.trunk.1/src/pages/DemoProfileExample/pages/Home.module.scss b/03_source/mobile.trunk.1/src/pages/DemoProfileExample/pages/Home.module.scss new file mode 100644 index 0000000..5db20c8 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoProfileExample/pages/Home.module.scss @@ -0,0 +1,90 @@ +$main-color: rgb(143, 149, 130); + +.page { + ion-toolbar { + --background: rgb(143, 149, 130) !important; + --color: white; + --border-style: none; + margin: 0 !important; + } +} + +.top { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + align-content: center; + background-color: rgb(143, 149, 130); + color: white; + padding-bottom: 1rem; +} + +.figures { + text-align: center; + background-color: rgb(143, 149, 130); + color: white; + + p, + h6 { + padding: 0; + margin: 0; + } +} + +.profileHeader { + ion-card-subtitle, + ion-card-title { + --color: white; + } + + ion-card-title { + font-size: 1.3rem; + } + + ion-card-subtitle { + --color: rgb(202, 211, 189); + } +} + +.avatar { + width: 7rem; + height: 7rem; + border: 5px solid rgba(218, 223, 208, 0.4); +} + +.avatarUpload { + display: flex; + flex-direction: row; + justify-content: center; + + background-color: rgb(255, 255, 255); + border: 3px solid rgba(218, 223, 208, 0.4); + color: rgb(80, 80, 80); + position: absolute; + padding: 0.3rem; + font-size: 1.1rem; + border-radius: 500px; + margin-top: -2.2rem; + margin-left: 5rem; +} + +.postActions { + display: flex; + flex-direction: row; + align-content: center; + align-items: center; + justify-content: space-between; + padding: 0.2rem; + padding-left: 1.3rem; + padding-right: 1.3rem; + color: rgb(149, 149, 149); + font-size: 0.9rem; + border-bottom: 1px solid rgba(218, 223, 208, 1); + + background-color: rgba(218, 223, 208, 0.4); + + ion-icon { + font-size: 1.2rem; + } +} diff --git a/03_source/mobile.trunk.1/src/pages/DemoProfileExample/pages/Home.tsx b/03_source/mobile.trunk.1/src/pages/DemoProfileExample/pages/Home.tsx new file mode 100644 index 0000000..d565f0a --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoProfileExample/pages/Home.tsx @@ -0,0 +1,130 @@ +import { + IonAvatar, + IonButton, + IonButtons, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonList, + IonPage, + IonRow, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import styles from './Home.module.scss'; +import { + arrowBackOutline, + cameraOutline, + chevronBackOutline, + filterOutline, + menuOutline, +} from 'ionicons/icons'; +import { Figure } from '../components/Figure'; +import { Post } from '../components/Post'; + +const Home = (): React.JSX.Element => { + const posts = [ + { + date: 'Mar 30', + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + comments: 13, + likes: 49, + liked: true, + }, + { + date: 'Mar 28', + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + comments: 1, + likes: 9, + }, + { + date: 'Mar 25', + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + comments: 119, + likes: 483, + }, + { + date: 'Mar 23', + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + comments: 27, + likes: 78, + }, + ]; + + const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + + handleBackClick()}> + + + + + + + + + + + + + + + + + + +
+ +
+
+
+ + + + Alan Montgomery + Mobile Team Lead + + +
+ + + +
+
+
+ + + + + + +
+

Posts by @93alan

+ +
+
+
+ + + {posts.map((post, index) => { + return ; + })} + +
+ + + ); +}; + +export default Home; diff --git a/03_source/mobile/src/pages/DemoReactQuotes/style.scss b/03_source/mobile.trunk.1/src/pages/DemoProfileExample/style.scss similarity index 100% rename from 03_source/mobile/src/pages/DemoReactQuotes/style.scss rename to 03_source/mobile.trunk.1/src/pages/DemoProfileExample/style.scss diff --git a/03_source/mobile.trunk.1/src/pages/DemoProfileExample/theme/variables.scss b/03_source/mobile.trunk.1/src/pages/DemoProfileExample/theme/variables.scss new file mode 100644 index 0000000..1a2dbc3 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoProfileExample/theme/variables.scss @@ -0,0 +1,79 @@ +.demo-profile-example { + /* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + + /** Ionic CSS Variables **/ + :root { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + } +} diff --git a/03_source/mobile/src/pages/DemoReactAddToCart/AppPages/Tab1.jsx b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/AppPages/Tab1.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactAddToCart/AppPages/Tab1.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/AppPages/Tab1.jsx diff --git a/03_source/mobile/src/pages/DemoReactAddToCart/AppPages/Tab2.jsx b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/AppPages/Tab2.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactAddToCart/AppPages/Tab2.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/AppPages/Tab2.jsx diff --git a/03_source/mobile/src/pages/DemoPullstateTutorial/notes.md b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/NOTES.md similarity index 100% rename from 03_source/mobile/src/pages/DemoPullstateTutorial/notes.md rename to 03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/NOTES.md diff --git a/03_source/mobile/src/pages/DemoReactShopUi/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/TestComponents/CurrentWeather/WeatherProperty.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactShopUi/TestComponents/CurrentWeather/WeatherProperty.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/TestComponents/CurrentWeather/WeatherProperty.tsx diff --git a/03_source/mobile/src/pages/DemoReactShopUi/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/TestComponents/CurrentWeather/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactShopUi/TestComponents/CurrentWeather/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/TestComponents/CurrentWeather/index.tsx diff --git a/03_source/mobile/src/pages/DemoReactQuotes/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/TestComponents/SkeletonDashboard/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactQuotes/TestComponents/SkeletonDashboard/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/TestComponents/SkeletonDashboard/index.tsx diff --git a/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/components/Person.tsx b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/components/Person.tsx new file mode 100644 index 0000000..2edc34d --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/components/Person.tsx @@ -0,0 +1,24 @@ +import { IonAvatar, IonButton, IonItem, IonLabel } from '@ionic/react'; +import { toggleFollowing } from '../store/PeopleStore'; + +export const Person = ({ person }): React.JSX.Element => { + return ( + + + avatar + + +

{person.name}

+

{person.title}

+
+ + toggleFollowing(person.id)} + > + {person.following ? 'Following' : 'Follow'} + +
+ ); +}; diff --git a/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/data/index.js b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/data/index.js new file mode 100644 index 0000000..7e84c0f --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/data/index.js @@ -0,0 +1,44 @@ +export const people = [ + { + id: 1, + name: 'Alan Montgomery', + title: 'Mobile Team Lead', + avatar: 'https://pbs.twimg.com/profile_images/1420489989163524096/GwHdYSky_400x400.jpg', + following: false, + }, + { + id: 2, + name: 'Max Lynch', + title: 'CEO | Co Founder', + avatar: 'https://pbs.twimg.com/profile_images/1318970727173885953/bln98FNj_400x400.jpg', + following: false, + }, + { + id: 3, + name: 'Mike Hartington', + title: 'Senior Dev Rel', + avatar: 'https://pbs.twimg.com/profile_images/1084993841898446849/DJ8XtR6L_400x400.jpg', + following: false, + }, + { + id: 4, + name: 'Matt Netkow', + title: 'Head of Product Marketing', + avatar: 'https://pbs.twimg.com/profile_images/1323383930150621187/GKc0nVzi_400x400.jpg', + following: false, + }, + { + id: 5, + name: 'Ben Sperry', + title: 'CDO | Co Founder', + avatar: 'https://pbs.twimg.com/profile_images/1407747959345795072/McJb-RvC_400x400.jpg', + following: false, + }, + { + id: 6, + name: 'Liam DeBeasi', + title: 'Software Engineer', + avatar: 'https://pbs.twimg.com/profile_images/1105953692669366273/ZNK4lRAJ_400x400.jpg', + following: false, + }, +]; diff --git a/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/index.tsx new file mode 100644 index 0000000..f5313e3 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/index.tsx @@ -0,0 +1,42 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { list, people } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +// import Tab1 from './AppPages/Tab1'; +// import Tab2 from './AppPages/Tab2'; + +import './style.scss'; +import Tab1 from './pages/Tab1'; +import Tab2 from './pages/Tab2'; + +function DemoPullstateTutorial() { + return ( + + + + + + + + + + + + + {/* */} + + + + List + + + + Following + + + + ); +} + +export default DemoPullstateTutorial; diff --git a/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/pages/Tab1.css b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/pages/Tab1.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/pages/Tab1.tsx b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/pages/Tab1.tsx new file mode 100644 index 0000000..98513db --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/pages/Tab1.tsx @@ -0,0 +1,63 @@ +import { + IonButton, + IonButtons, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import { PeopleStore } from '../store'; +import { Person } from '../components/Person'; +import './Tab1.css'; +import { useStoreState } from 'pullstate'; +import { getPeople } from '../store/Selectors'; +import { chevronBackOutline } from 'ionicons/icons'; + +const Tab1 = (): React.JSX.Element => { + const people = useStoreState(PeopleStore, getPeople); + + console.log(people); + + const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + List of People + + + handleBackClick()}> + + + + + + + + + List of People + + + handleBackClick()}> + + + + + + + {people.map((person, index) => { + return ; + })} + + + ); +}; + +export default Tab1; diff --git a/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/pages/Tab2.css b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/pages/Tab2.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/pages/Tab2.tsx b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/pages/Tab2.tsx new file mode 100644 index 0000000..7a95a49 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/pages/Tab2.tsx @@ -0,0 +1,34 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import { useStoreState } from 'pullstate'; +import { Person } from '../components/Person'; +import { PeopleStore } from '../store'; +import { getFollowing } from '../store/Selectors'; +import './Tab2.css'; +import React from 'react'; + +const Tab2 = (): React.JSX.Element => { + const people = useStoreState(PeopleStore, getFollowing); + + return ( + + + + Following + + + + + + Following + + + + {people.map((person, index) => { + return ; + })} + + + ); +}; + +export default Tab2; diff --git a/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/store/PeopleStore.js b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/store/PeopleStore.js new file mode 100644 index 0000000..f430293 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/store/PeopleStore.js @@ -0,0 +1,16 @@ +import { Store } from 'pullstate'; + +import { people } from '../data'; + +const PeopleStore = new Store({ + people: people, +}); + +export const toggleFollowing = (personId) => { + PeopleStore.update((s) => { + const personIndex = s.people.findIndex((person) => person.id === personId); + s.people[personIndex].following = !s.people[personIndex].following; + }); +}; + +export default PeopleStore; diff --git a/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/store/Selectors.js b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/store/Selectors.js new file mode 100644 index 0000000..04e3218 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/store/Selectors.js @@ -0,0 +1,9 @@ +import { createSelector } from 'reselect'; + +const getState = (state) => state; + +// Gets +export const getPeople = createSelector(getState, (state) => state.people); +export const getFollowing = createSelector(getState, (state) => + state.people.filter((person) => person.following) +); diff --git a/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/store/index.js b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/store/index.js new file mode 100644 index 0000000..1e43c95 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/store/index.js @@ -0,0 +1 @@ +export { default as PeopleStore } from './PeopleStore'; diff --git a/03_source/mobile/src/pages/DemoReactShopUi/style.scss b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/style.scss similarity index 100% rename from 03_source/mobile/src/pages/DemoReactShopUi/style.scss rename to 03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/style.scss diff --git a/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/theme/variables.scss b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/theme/variables.scss new file mode 100644 index 0000000..8606e9f --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoPullstateTutorial/theme/variables.scss @@ -0,0 +1,79 @@ +.demo-pullstate-tutorial { + /* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + + /** Ionic CSS Variables **/ + :root { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + } +} diff --git a/03_source/mobile/src/pages/DemoQrScanner/AppPages/Tab1.jsx b/03_source/mobile.trunk.1/src/pages/DemoQrScanner/AppPages/Tab1.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoQrScanner/AppPages/Tab1.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoQrScanner/AppPages/Tab1.jsx diff --git a/03_source/mobile/src/pages/DemoStickyBottomSheetExample/AppPages/Tab2.jsx b/03_source/mobile.trunk.1/src/pages/DemoQrScanner/AppPages/Tab2.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoStickyBottomSheetExample/AppPages/Tab2.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoQrScanner/AppPages/Tab2.jsx diff --git a/03_source/mobile/src/pages/DemoQrScanner/notes.md b/03_source/mobile.trunk.1/src/pages/DemoQrScanner/NOTES.md similarity index 100% rename from 03_source/mobile/src/pages/DemoQrScanner/notes.md rename to 03_source/mobile.trunk.1/src/pages/DemoQrScanner/NOTES.md diff --git a/03_source/mobile/src/pages/DemoStorageExample/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk.1/src/pages/DemoQrScanner/components/CurrentWeather/WeatherProperty.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoStorageExample/TestComponents/CurrentWeather/WeatherProperty.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoQrScanner/components/CurrentWeather/WeatherProperty.tsx diff --git a/03_source/mobile/src/pages/DemoStorageExample/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoQrScanner/components/CurrentWeather/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoStorageExample/TestComponents/CurrentWeather/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoQrScanner/components/CurrentWeather/index.tsx diff --git a/03_source/mobile/src/pages/DemoQrScanner/components/CustomFab.jsx b/03_source/mobile.trunk.1/src/pages/DemoQrScanner/components/CustomFab.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoQrScanner/components/CustomFab.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoQrScanner/components/CustomFab.jsx diff --git a/03_source/mobile/src/pages/DemoQrScanner/components/NoQRCodes.jsx b/03_source/mobile.trunk.1/src/pages/DemoQrScanner/components/NoQRCodes.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoQrScanner/components/NoQRCodes.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoQrScanner/components/NoQRCodes.jsx diff --git a/03_source/mobile/src/pages/DemoQrScanner/components/QRCodeScannedModal.jsx b/03_source/mobile.trunk.1/src/pages/DemoQrScanner/components/QRCodeScannedModal.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoQrScanner/components/QRCodeScannedModal.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoQrScanner/components/QRCodeScannedModal.jsx diff --git a/03_source/mobile/src/pages/DemoQrScanner/components/QRWebModal.jsx b/03_source/mobile.trunk.1/src/pages/DemoQrScanner/components/QRWebModal.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoQrScanner/components/QRWebModal.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoQrScanner/components/QRWebModal.jsx diff --git a/03_source/mobile/src/pages/DemoReactShopUi/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoQrScanner/components/SkeletonDashboard/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactShopUi/TestComponents/SkeletonDashboard/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoQrScanner/components/SkeletonDashboard/index.tsx diff --git a/03_source/mobile/src/pages/DemoQrScanner/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoQrScanner/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoQrScanner/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoQrScanner/index.tsx diff --git a/03_source/mobile/src/pages/DemoQrScanner/sounds/close.wav b/03_source/mobile.trunk.1/src/pages/DemoQrScanner/sounds/close.wav similarity index 100% rename from 03_source/mobile/src/pages/DemoQrScanner/sounds/close.wav rename to 03_source/mobile.trunk.1/src/pages/DemoQrScanner/sounds/close.wav diff --git a/03_source/mobile/src/pages/DemoQrScanner/sounds/open.wav b/03_source/mobile.trunk.1/src/pages/DemoQrScanner/sounds/open.wav similarity index 100% rename from 03_source/mobile/src/pages/DemoQrScanner/sounds/open.wav rename to 03_source/mobile.trunk.1/src/pages/DemoQrScanner/sounds/open.wav diff --git a/03_source/mobile/src/pages/DemoQrScanner/store/QRStore.js b/03_source/mobile.trunk.1/src/pages/DemoQrScanner/store/QRStore.js similarity index 100% rename from 03_source/mobile/src/pages/DemoQrScanner/store/QRStore.js rename to 03_source/mobile.trunk.1/src/pages/DemoQrScanner/store/QRStore.js diff --git a/03_source/mobile/src/pages/DemoQrScanner/store/Selectors.js b/03_source/mobile.trunk.1/src/pages/DemoQrScanner/store/Selectors.js similarity index 100% rename from 03_source/mobile/src/pages/DemoQrScanner/store/Selectors.js rename to 03_source/mobile.trunk.1/src/pages/DemoQrScanner/store/Selectors.js diff --git a/03_source/mobile/src/pages/DemoQrScanner/store/index.js b/03_source/mobile.trunk.1/src/pages/DemoQrScanner/store/index.js similarity index 100% rename from 03_source/mobile/src/pages/DemoQrScanner/store/index.js rename to 03_source/mobile.trunk.1/src/pages/DemoQrScanner/store/index.js diff --git a/03_source/mobile/src/pages/DemoReactTabsMenusCustom/style.scss b/03_source/mobile.trunk.1/src/pages/DemoQrScanner/style.scss similarity index 100% rename from 03_source/mobile/src/pages/DemoReactTabsMenusCustom/style.scss rename to 03_source/mobile.trunk.1/src/pages/DemoQrScanner/style.scss diff --git a/03_source/mobile/src/pages/DemoQuoteApp/AppPages/Home.jsx b/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/AppPages/Home.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoQuoteApp/AppPages/Home.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoQuoteApp/AppPages/Home.jsx diff --git a/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/AppPages/Quote.jsx b/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/AppPages/Quote.jsx new file mode 100644 index 0000000..fdc54b4 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/AppPages/Quote.jsx @@ -0,0 +1,102 @@ +import { + IonBackButton, + IonButton, + IonButtons, + IonCard, + IonCardContent, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonImg, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonToast, +} from '@ionic/react'; +import { bookmarkOutline, checkmarkOutline, copyOutline } from 'ionicons/icons'; +import { useStoreState } from 'pullstate'; +import { useEffect, useState } from 'react'; +import { useParams } from 'react-router'; +import { QuoteStore } from '../store'; +import { addSavedQuote, removeSavedQuote } from '../store/QuoteStore'; +import { getQuote, getSavedQuotes } from '../store/Selectors'; + +import { Clipboard } from '@capacitor/clipboard'; + +const Quote = () => { + const { id } = useParams(); + const quote = useStoreState(QuoteStore, getQuote(id)); + const saved = useStoreState(QuoteStore, getSavedQuotes); + const [bookmarked, setBookmarked] = useState(false); + + const [present] = useIonToast(); + + useEffect(() => { + setBookmarked(saved.includes(parseInt(id))); + }, [saved, id]); + + const copyQuote = async () => { + await Clipboard.write({ + string: quote.text, + }); + + present({ + header: 'Success', + message: 'Quote copied to clipboard!', + duration: 2500, + color: 'primary', + }); + }; + + return ( + + + + + + + Quote + + + + + + + Quote + + + + + + +

{quote.text}

+

- {quote.author}

+
+ + + + (bookmarked ? removeSavedQuote(quote.id) : addSavedQuote(quote.id))} + > + +  {bookmarked ? 'Bookmarked' : 'Save as Bookmark'} + + + + + + +  Copy Quote + + + +
+
+
+ ); +}; + +export default Quote; diff --git a/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/AppPages/Saved.jsx b/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/AppPages/Saved.jsx new file mode 100644 index 0000000..9b6e340 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/AppPages/Saved.jsx @@ -0,0 +1,83 @@ +import { + IonButtons, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonInfiniteScroll, + IonInfiniteScrollContent, + IonList, + IonMenuButton, + IonPage, + IonRow, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useStoreState } from 'pullstate'; +import { useState } from 'react'; +import { QuoteItem } from '../components/QuoteItem'; +import { QuoteStore } from '../store'; +import { getQuotes, getSavedQuotes } from '../store/Selectors'; + +const Saved = () => { + const quotes = useStoreState(QuoteStore, getQuotes); + const saved = useStoreState(QuoteStore, getSavedQuotes); + const [amountLoaded, setAmountLoaded] = useState(20); + + const fetchMore = async (e) => { + setAmountLoaded((amountLoaded) => amountLoaded + 20); + e.target.complete(); + }; + + return ( + + + + + + + Bookmarks + + + + + + + Bookmarks + + + + + {quotes.length > 0 && ( + + + {quotes.map((quote, index) => { + if (index <= amountLoaded && saved.includes(parseInt(quote.id))) { + return ; + } else return ''; + })} + + + + + + + )} + + {quotes.length < 1 && ( + + +

You haven't saved any bookmarks yet.

+
+
+ )} +
+
+
+ ); +}; + +export default Saved; diff --git a/03_source/mobile/src/pages/DemoQuoteApp/notes.md b/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/NOTES.md similarity index 100% rename from 03_source/mobile/src/pages/DemoQuoteApp/notes.md rename to 03_source/mobile.trunk.1/src/pages/DemoQuoteApp/NOTES.md diff --git a/03_source/mobile/src/pages/DemoReactQuotes/components/Menu.css b/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/components/Menu.css similarity index 100% rename from 03_source/mobile/src/pages/DemoReactQuotes/components/Menu.css rename to 03_source/mobile.trunk.1/src/pages/DemoQuoteApp/components/Menu.css diff --git a/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/components/Menu.jsx b/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/components/Menu.jsx new file mode 100644 index 0000000..a8bba47 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/components/Menu.jsx @@ -0,0 +1,67 @@ +import { + IonContent, + IonIcon, + IonItem, + IonLabel, + IonList, + IonListHeader, + IonMenu, + IonMenuToggle, + IonNote, +} from '@ionic/react'; + +import { useLocation } from 'react-router-dom'; +import { bookmarkOutline, bookmarkSharp, homeOutline, homeSharp } from 'ionicons/icons'; +import './Menu.css'; +import { useStoreState } from 'pullstate'; +import { QuoteStore } from '../store'; +import { getSavedQuotes } from '../store/Selectors'; + +const Menu = () => { + const location = useLocation(); + const saved = useStoreState(QuoteStore, getSavedQuotes); + + const appPages = [ + { + title: 'Home', + url: '/home', + iosIcon: homeOutline, + mdIcon: homeSharp, + }, + { + title: `Bookmarks (${saved.length})`, + url: '/saved', + iosIcon: bookmarkOutline, + mdIcon: bookmarkSharp, + }, + ]; + + return ( + + + + Ionic Quotes + hey there! + {appPages.map((appPage, index) => { + return ( + + + + {appPage.title} + + + ); + })} + + + + ); +}; + +export default Menu; diff --git a/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/components/QuoteItem.jsx b/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/components/QuoteItem.jsx new file mode 100644 index 0000000..2154d3d --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/components/QuoteItem.jsx @@ -0,0 +1,15 @@ +import { IonCol, IonItem, IonLabel } from '@ionic/react'; +import styles from './QuoteItem.module.css'; + +export const QuoteItem = ({ quote }) => { + return ( + + + +

{quote.text}

+

{quote.author}

+
+
+
+ ); +}; diff --git a/03_source/mobile/src/pages/DemoReactQuotes/components/QuoteItem.module.css b/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/components/QuoteItem.module.css similarity index 100% rename from 03_source/mobile/src/pages/DemoReactQuotes/components/QuoteItem.module.css rename to 03_source/mobile.trunk.1/src/pages/DemoQuoteApp/components/QuoteItem.module.css diff --git a/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/index.tsx new file mode 100644 index 0000000..765c40a --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/index.tsx @@ -0,0 +1,27 @@ +import { Route, Redirect } from 'react-router'; +// +import Quote from './AppPages/Quote'; +import Saved from './AppPages/Saved'; +import Home from './AppPages/Home'; +// +const DemoQuoteApp = () => { + return ( + <> + + + + + + + + + + + + + + + ); +}; + +export default DemoQuoteApp; diff --git a/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/store/QuoteStore.js b/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/store/QuoteStore.js new file mode 100644 index 0000000..87d0d15 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/store/QuoteStore.js @@ -0,0 +1,34 @@ +import { Store } from 'pullstate'; + +const QuoteStore = new Store({ + quotes: [], + saved: [], +}); + +export default QuoteStore; + +export const addSavedQuote = (id) => { + QuoteStore.update((s) => { + s.saved = [...s.saved, id]; + }); +}; + +export const removeSavedQuote = (id) => { + QuoteStore.update((s) => { + s.saved = s.saved.filter((savedId) => parseInt(savedId) !== parseInt(id)); + }); +}; + +export const fetchQuotes = async () => { + const response = await fetch('https://type.fit/api/quotes'); + const data = await response.json(); + + await data.filter((quote, index) => { + quote.id = Date.now() + index; + quote.image = `https://source.unsplash.com/random/1200x400?sig=${quote.id}`; + }); + + QuoteStore.update((s) => { + s.quotes = data; + }); +}; diff --git a/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/store/Selectors.js b/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/store/Selectors.js new file mode 100644 index 0000000..ac9aeae --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/store/Selectors.js @@ -0,0 +1,14 @@ +import { createSelector } from 'reselect'; + +const getState = (state) => state; + +// General getters +export const getQuotes = createSelector(getState, (state) => state.quotes); +export const getSavedQuotes = createSelector(getState, (state) => state.saved); + +// Specific getters +export const getQuote = (id) => + createSelector( + getState, + (state) => state.quotes.filter((q) => parseInt(q.id) === parseInt(id))[0] + ); diff --git a/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/store/index.js b/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/store/index.js new file mode 100644 index 0000000..71a69f1 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/store/index.js @@ -0,0 +1 @@ +export { default as QuoteStore } from './QuoteStore'; diff --git a/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/style.scss b/03_source/mobile.trunk.1/src/pages/DemoQuoteApp/style.scss new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile/src/pages/DemoReactCalculator/AppPages/Tab1.jsx b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/AppPages/Tab1.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactCalculator/AppPages/Tab1.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/AppPages/Tab1.jsx diff --git a/03_source/mobile/src/pages/DemoReactCalculator/AppPages/Tab2.jsx b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/AppPages/Tab2.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactCalculator/AppPages/Tab2.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/AppPages/Tab2.jsx diff --git a/03_source/mobile/src/pages/DemoReactAddToCart/notes.md b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/NOTES.md similarity index 100% rename from 03_source/mobile/src/pages/DemoReactAddToCart/notes.md rename to 03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/NOTES.md diff --git a/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..950c702 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/DemoReactAddToCart/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile/src/pages/DemoWeatherAppUi/components/CurrentWeather/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/TestComponents/CurrentWeather/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoWeatherAppUi/components/CurrentWeather/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/TestComponents/CurrentWeather/index.tsx diff --git a/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..455cec6 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,122 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/components/AddToCartButton.module.css b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/components/AddToCartButton.module.css new file mode 100644 index 0000000..3af5d30 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/components/AddToCartButton.module.css @@ -0,0 +1,14 @@ +.buttonContainer { + display: flex; + flex-direction: column; + justify-content: center; + align-content: center; + align-items: center; +} + +.button { + --padding-top: 1.75rem; + --padding-bottom: 1.75rem; + --padding-start: 1.75rem; + --padding-end: 1.75rem; +} diff --git a/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/components/AddToCartButton.tsx b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/components/AddToCartButton.tsx new file mode 100644 index 0000000..82a1789 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/components/AddToCartButton.tsx @@ -0,0 +1,60 @@ +import { CreateAnimation, IonButton, IonIcon } from '@ionic/react'; +import React, { useRef, useState } from 'react'; + +import styles from './AddToCartButton.module.css'; +import { cartOutline } from 'ionicons/icons'; + +const AddToCartButton = ({ + icon = true, + color = 'primary', + customOnClick = null, +}): React.JSX.Element => { + const iconRef = useRef(null); + const [hidden, setHidden] = useState(true); + + const floatStyle = { + display: hidden ? 'none' : '', + position: 'absolute', + }; + + const floatGrowAnimation = { + property: 'transform', + fromValue: 'translateY(0) scale(1)', + toValue: 'translateY(-55px) scale(1.2)', + }; + + const colorAnimation = { + property: 'color', + fromValue: 'white', + toValue: `var(--ion-color-${color}`, + }; + + const mainAnimation = { + duration: 700, + iterations: '1', + fromTo: [floatGrowAnimation, colorAnimation], + easing: 'cubic-bezier(0.25, 0.7, 0.25, 0.7)', + }; + + const handleClick = async () => { + setHidden(false); + await iconRef.current.animation.play(); + setHidden(true); + customOnClick && customOnClick(); + }; + + return ( +
+ + {!icon && 'Add to cart'} + {icon && } + + + + + +
+ ); +}; + +export default AddToCartButton; diff --git a/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/components/Product.module.css b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/components/Product.module.css new file mode 100644 index 0000000..fa0f815 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/components/Product.module.css @@ -0,0 +1,19 @@ +.priceContainer { + + display: flex; + flex-direction: row; + align-content: center; + justify-content: space-between; + align-items: center; +} + +.price { + + margin-top: 0.7rem; + border: 1px solid var(--ion-color-primary); + color: var(--ion-color-primary); + width: fit-content; + padding: 1rem; + border-radius: 10px; + font-weight: 600; +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/components/Product.tsx b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/components/Product.tsx new file mode 100644 index 0000000..272efcb --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/components/Product.tsx @@ -0,0 +1,61 @@ +import { + IonCard, + IonCardContent, + IonCardHeader, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonRow, + IonText, +} from '@ionic/react'; +import AddToCartButton from './AddToCartButton'; + +import styles from './Product.module.css'; + +const Product = ({ product }): React.JSX.Element => { + const handleAdd = (product) => { + console.log(`Product added: ${product.name}`); + console.log({ product }); + + // Do something + // Update Main Cart + // Global State Stuff + // API Call + // etc etc + }; + + return ( + + + {product.name} + {product.description} + + + + + + product + + + + + Product Features + {product.features.map((feature, index) => { + return

{feature}

; + })} +
+
+
+ + +
{product.price}
+ handleAdd(product)} /> +
+
+
+
+ ); +}; + +export default Product; diff --git a/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/index.tsx new file mode 100644 index 0000000..c0009af --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/index.tsx @@ -0,0 +1,31 @@ +import { IonRouterOutlet, IonTabs } from '@ionic/react'; + +import { Route, Redirect } from 'react-router'; + +import './theme/variables.scss'; +import Home from './pages/Home'; + +function DemoReactAddToCart() { + return ( + + + {/* + + + + + + + */} + + + + + + + + + ); +} + +export default DemoReactAddToCart; diff --git a/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/module.d.ts b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/module.d.ts new file mode 100644 index 0000000..d774364 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/module.d.ts @@ -0,0 +1,4 @@ +declare module '*.module.css' { + const classes: { readonly [key: string]: string }; + export default classes; +} diff --git a/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/pages/Home.css b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/pages/Home.css new file mode 100644 index 0000000..2877bc5 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/pages/Home.css @@ -0,0 +1,19 @@ +.price-container { + + display: flex; + flex-direction: row; + align-content: center; + justify-content: space-between; + align-items: center; +} + +.price { + + margin-top: 0.7rem; + border: 1px solid var(--ion-color-primary); + color: var(--ion-color-primary); + width: fit-content; + padding: 1rem; + border-radius: 10px; + font-weight: 600; +} \ No newline at end of file diff --git a/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/pages/Home.tsx b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/pages/Home.tsx new file mode 100644 index 0000000..50ee6ee --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/pages/Home.tsx @@ -0,0 +1,45 @@ +import { + IonButton, + IonButtons, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import './Home.css'; +import Product from '../components/Product'; +import { products } from './products'; +import React from 'react'; +import { chevronBackOutline } from 'ionicons/icons'; + +const Home = (): React.JSX.Element => { + const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + return ( + + + + Add To Cart Animation + + + handleBackClick()}> + + + + + + + {products.map((product) => { + return ; + })} + + + ); +}; + +export default Home; diff --git a/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/pages/products.ts b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/pages/products.ts new file mode 100644 index 0000000..8dbab04 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/pages/products.ts @@ -0,0 +1,44 @@ +export const products = [ + { + id: 1, + name: 'Macbook Pro', + description: "13.3' (2020) - M1, 256 GB SSD, Space Grey", + price: '£1,199', + image: '/assets/DemoReactAddToCart/macbook.jpeg', + features: [ + 'macOS 11.0 Big Sur', + 'Apple M1 chip', + 'RAM: 8 GB / Storage: 256 GB SSD', + 'Retina display', + 'Battery life: Up to 20 hours', + ], + }, + { + id: 2, + name: 'SONY A7', + description: 'SONY a7 III Mirrorless Camera - Black', + price: '£1,699', + image: '/assets/DemoReactAddToCart/camera.jpeg', + features: [ + '24.2 megapixels', + 'Full-frame 35 mm / 35.6 x 23.8 mm CMOS sensor', + 'Built-in WiFi / Bluetooth / NFC', + "3' tiltable LCD touchscreen", + '10 fps in continuous shooting mode', + ], + }, + { + id: 3, + name: 'HISENSE 55', + description: "55' Smart 4K Ultra HD HDR LED TV", + price: '£429', + image: '/assets/DemoReactAddToCart/tv.jpeg', + features: [ + 'Picture quality: 1600 PCI', + 'HDR: HDR10 / Hybrid Log-Gamma (HLG)', + 'Catch-up TV & 4K streaming', + 'Freeview HD with Freeview Play', + 'HDMI 2.0 x 3', + ], + }, +]; diff --git a/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/style.scss b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/style.scss new file mode 100644 index 0000000..e5ac297 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/DemoReactAddToCart/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/DemoReactAddToCart/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/DemoReactAddToCart/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/DemoReactAddToCart/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/theme/variables.scss b/03_source/mobile.trunk.1/src/pages/DemoReactAddToCart/theme/variables.scss new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile/src/pages/DemoReactDrawingCanvas/AppPages/Tab1.jsx b/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/AppPages/Tab1.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactDrawingCanvas/AppPages/Tab1.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoReactCalculator/AppPages/Tab1.jsx diff --git a/03_source/mobile/src/pages/DemoReactDrawingCanvas/AppPages/Tab2.jsx b/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/AppPages/Tab2.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactDrawingCanvas/AppPages/Tab2.jsx rename to 03_source/mobile.trunk.1/src/pages/DemoReactCalculator/AppPages/Tab2.jsx diff --git a/03_source/mobile/src/pages/DemoReactCalculator/notes.md b/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/NOTES.md similarity index 100% rename from 03_source/mobile/src/pages/DemoReactCalculator/notes.md rename to 03_source/mobile.trunk.1/src/pages/DemoReactCalculator/NOTES.md diff --git a/03_source/mobile/src/pages/DemoWeatherAppUi/components/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/TestComponents/CurrentWeather/WeatherProperty.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoWeatherAppUi/components/CurrentWeather/WeatherProperty.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoReactCalculator/TestComponents/CurrentWeather/WeatherProperty.tsx diff --git a/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoReactTabsMenusCustom/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/TestComponents/SkeletonDashboard/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactTabsMenusCustom/TestComponents/SkeletonDashboard/index.tsx rename to 03_source/mobile.trunk.1/src/pages/DemoReactCalculator/TestComponents/SkeletonDashboard/index.tsx diff --git a/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/components/Button.module.scss b/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/components/Button.module.scss new file mode 100644 index 0000000..d4adbd4 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/components/Button.module.scss @@ -0,0 +1,20 @@ +.demo-react-calculator { + .button, + .specialButton { + margin: 0.2rem; + padding: 1.5rem; + margin: 0.2rem; + border-radius: 15px; + font-size: 1.5rem; + } + + .button { + color: rgb(255, 255, 255); + background-color: rgb(58, 58, 58); + } + + .specialButton { + color: rgb(255, 255, 255); + background-color: var(--blue-color); + } +} diff --git a/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/components/Button.tsx b/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/components/Button.tsx new file mode 100644 index 0000000..398d37b --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/components/Button.tsx @@ -0,0 +1,17 @@ +import { IonCol } from '@ionic/react'; +import styles from './Button.module.scss'; + +const Button = (props): React.JSX.Element => { + const { value, special, clickEvent } = props; + + return ( + clickEvent(e, value)} + > + {value === '/' ? <>÷ : value === '*' ? <>× : value} + + ); +}; + +export default Button; diff --git a/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/components/ButtonRow.module.scss b/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/components/ButtonRow.module.scss new file mode 100644 index 0000000..216fb37 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/components/ButtonRow.module.scss @@ -0,0 +1,4 @@ +.demo-react-calculator { + .buttonRow { + } +} diff --git a/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/components/ButtonRow.tsx b/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/components/ButtonRow.tsx new file mode 100644 index 0000000..375c87f --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/components/ButtonRow.tsx @@ -0,0 +1,9 @@ +import { IonRow } from '@ionic/react'; +import styles from './ButtonRow.module.scss'; +import React from 'react'; + +const ButtonRow = (props): React.JSX.Element => { + return {props.children}; +}; + +export default ButtonRow; diff --git a/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/index.tsx b/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/index.tsx new file mode 100644 index 0000000..ee886a6 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/index.tsx @@ -0,0 +1,23 @@ +import { IonRouterOutlet, IonTabs } from '@ionic/react'; + +import { Route, Redirect } from 'react-router'; + +import './theme/variables.scss'; +import React from 'react'; +import Home from './pages/Home'; + +function DemoReactCalculator(): React.JSX.Element { + return ( + + + + + + + + + + ); +} + +export default DemoReactCalculator; diff --git a/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/module.d.ts b/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/module.d.ts new file mode 100644 index 0000000..77bc4ab --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/module.d.ts @@ -0,0 +1,8 @@ +declare module '*.module.css' { + const classes: { readonly [key: string]: string }; + export default classes; +} +declare module '*.module.scss' { + const classes: { readonly [key: string]: string }; + export default classes; +} diff --git a/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/pages/Home.module.scss b/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/pages/Home.module.scss new file mode 100644 index 0000000..a9d7f48 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/pages/Home.module.scss @@ -0,0 +1,40 @@ +.calculatorContainer { + padding-bottom: 2.5rem; + background-color: white; + padding-left: 1rem; + padding-right: 1rem; +} + +.sumContainer { + display: flex; + flex-direction: column; + align-content: center; + align-items: flex-end; + padding-right: 2rem; + margin-top: 2rem; + + background-color: rgba(99, 158, 226, 0.1); + padding: 2rem; + padding-top: 4rem; + padding-bottom: 4rem; +} + +.sumContainer h1, +.sumContainer h4, +.sumContainer p { + margin: 0; + padding: 0; +} + +.sumContainer h1 { + font-size: 4rem; +} + +.sumContainer p { + font-size: 2rem; + color: rgb(163, 163, 163); +} + +.sumContainer h4 { + color: rgb(197, 197, 197); +} diff --git a/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/pages/Home.tsx b/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/pages/Home.tsx new file mode 100644 index 0000000..e6e8e02 --- /dev/null +++ b/03_source/mobile.trunk.1/src/pages/DemoReactCalculator/pages/Home.tsx @@ -0,0 +1,127 @@ +import { + IonButton, + IonButtons, + IonContent, + IonFooter, + IonGrid, + IonHeader, + IonIcon, + IonPage, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import React, { useEffect, useState } from 'react'; +import Button from '../components/Button'; +import ButtonRow from '../components/ButtonRow'; +import styles from './Home.module.scss'; + +import { + checkmarkOutline, + chevronBackOutline, + chevronDownCircleOutline, + closeOutline, + heart, + languageOutline, + menuOutline, +} from 'ionicons/icons'; + +import { buttons } from '../utils/Buttons'; + +const Home = (): React.JSX.Element => { + const [showTitle, setShowTitle] = useState('_______'); + const [sum, setSum] = useState('0'); + const [sumHistory, setSumHistory] = useState('Ionic Calculator'); + + const handleClick = (e, operator) => { + const tempSumHistory = sumHistory.replace('Ionic Calculator', ''); + + if (operator === '=') { + calculate(); + } else if (operator === 'C') { + reset(); + } else if (operator === 'Del') { + backspace(); + } else { + setSumHistory(tempSumHistory + operator); + + e.target.classList.add('animate__headShake'); + + setTimeout(() => { + e.target.classList.remove('animate__headShake'); + }, 500); + } + }; + + useEffect(() => { + calculate(); + }, [sumHistory]); + + const calculate = () => { + try { + // eslint-disable-next-line no-eval + setSum(eval(sumHistory).length > 5 ? eval(sumHistory).toFixed(4) : eval(sumHistory)); + setShowTitle('Ionic Calculator'); + } catch (e) {} + }; + + const reset = () => { + setSumHistory('Ionic Calculator'); + setSum('0'); + setShowTitle('_______'); + }; + + const backspace = () => { + const tempSum = sumHistory.substr(0, sumHistory.length - 1); + setSumHistory(tempSum); + }; + + const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + return ( + + + + + handleBackClick()}> + + + + + + +
+ {showTitle &&

{showTitle}

} +

{sumHistory}

+

+ {sum.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')} +

+
+
+ + + + {buttons.map((buttonRow, index) => { + return ( + + {buttonRow.map((button) => { + return ( + + * + * + * ); + */ +type UseSetStateReturn = { + state: T; + resetState: (defaultState?: T) => void; + setState: (updateState: T | Partial) => void; + setField: (name: keyof T, updateValue: T[keyof T]) => void; +}; +declare function useSetState(initialState?: T): UseSetStateReturn; + +export { type UseSetStateReturn, useSetState }; diff --git a/03_source/mobile.trunk/src/main.tsx b/03_source/mobile.trunk/src/main.tsx new file mode 100644 index 0000000..964b804 --- /dev/null +++ b/03_source/mobile.trunk/src/main.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import App from './App'; +import * as serviceWorker from './serviceWorker'; + +ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( + + + +); +// If you want your app to work offline and load faster, you can change +// unregister() to register() below. Note this comes with some pitfalls. +// Learn more about service workers: https://bit.ly/CRA-PWA +serviceWorker.register(); diff --git a/03_source/mobile.trunk/src/models/Event.ts b/03_source/mobile.trunk/src/models/Event.ts new file mode 100644 index 0000000..31ac548 --- /dev/null +++ b/03_source/mobile.trunk/src/models/Event.ts @@ -0,0 +1,24 @@ +// 03_source/mobile/src/models/Event.ts + +export type IDateValue = string | number | null; + +export interface Event { + id: string; + createdAt: IDateValue; + updatedAt: IDateValue; + // + name: string; + code: string; + price: number; + // + eventDate: Date; + joinMembers: { email: string; avatar: string; sex: string }[]; + title: string; + currency: string; + duration_m: number; + ageBottom: number; + ageTop: number; + location: string; + avatar: string; + // +} diff --git a/03_source/mobile.trunk/src/models/Location.ts b/03_source/mobile.trunk/src/models/Location.ts new file mode 100644 index 0000000..9bc6d77 --- /dev/null +++ b/03_source/mobile.trunk/src/models/Location.ts @@ -0,0 +1,15 @@ +export interface Location { + id: number; + name: string; + lat: number; + lng: number; + center?: boolean; +} + +export interface LocationState { + locations: Location[]; +} + +export const initialState: LocationState = { + locations: [], +}; diff --git a/03_source/mobile.trunk/src/models/Order.ts b/03_source/mobile.trunk/src/models/Order.ts new file mode 100644 index 0000000..6d4b184 --- /dev/null +++ b/03_source/mobile.trunk/src/models/Order.ts @@ -0,0 +1,55 @@ +export type IDateValue = string | number | null; + +export type IOrderProductItem = { + id: string; + sku: string; + name: string; + price: number; + coverUrl: string; + quantity: number; +}; + +export type IOrderHistory = { + orderTime: IDateValue; + paymentTime: IDateValue; + deliveryTime: IDateValue; + completionTime: IDateValue; + timeline: { title: string; time: IDateValue }[]; +}; + +export type IOrderDelivery = { + shipBy: string; + speedy: string; + trackingNumber: string; +}; + +export type IOrderShippingAddress = { + fullAddress: string; + phoneNumber: string; +}; + +export type IOrderPayment = { + cardType: string; + cardNumber: string; +}; + +export interface IOrderItem { + id: string; + createdAt: IDateValue; + updatedAt: IDateValue; + // + taxes: number; + status: string; + shipping: number; + discount: number; + subtotal: number; + orderNumber: string; + totalAmount: number; + totalQuantity: number; + // + items: IOrderProductItem[]; + history: IOrderHistory | undefined; + delivery: IOrderDelivery; + shippingAddress: IOrderShippingAddress; + payment: IOrderPayment; +} diff --git a/03_source/mobile.trunk/src/models/Schedule.ts b/03_source/mobile.trunk/src/models/Schedule.ts new file mode 100644 index 0000000..f210ff8 --- /dev/null +++ b/03_source/mobile.trunk/src/models/Schedule.ts @@ -0,0 +1,20 @@ +export interface Schedule { + date: string; + groups: ScheduleGroup[]; +} + +export interface ScheduleGroup { + time: string; + sessions: Session[]; +} + +export interface Session { + id: number; + timeStart: string; + timeEnd: string; + name: string; + location: string; + description: string; + speakerNames: string[]; + tracks: string[]; +} diff --git a/03_source/mobile.trunk/src/models/SessionGroup.ts b/03_source/mobile.trunk/src/models/SessionGroup.ts new file mode 100644 index 0000000..93d46ad --- /dev/null +++ b/03_source/mobile.trunk/src/models/SessionGroup.ts @@ -0,0 +1,5 @@ +import { Session } from './Schedule'; +export interface SessionGroup { + startTime: string; + sessions: Session[]; +} diff --git a/03_source/mobile.trunk/src/models/Speaker.ts b/03_source/mobile.trunk/src/models/Speaker.ts new file mode 100644 index 0000000..35d8c78 --- /dev/null +++ b/03_source/mobile.trunk/src/models/Speaker.ts @@ -0,0 +1,12 @@ +export interface Speaker { + id: number; + name: string; + profilePic: string; + twitter: string; + instagram: string; + about: string; + title: string; + location: string; + email: string; + phone: string; +} diff --git a/03_source/mobile.trunk/src/pages/About.scss b/03_source/mobile.trunk/src/pages/About.scss new file mode 100644 index 0000000..f7d1ac0 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/About.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url("/assets/img/about/madison.jpg"); + } + + .about-header .austin { + background-image: url("/assets/img/about/austin.jpg"); + } + + .about-header .chicago { + background-image: url("/assets/img/about/chicago.jpg"); + } + + .about-header .seattle { + background-image: url("/assets/img/about/seattle.jpg"); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/NotImplemented/index copy.tsx b/03_source/mobile.trunk/src/pages/About.tsx similarity index 87% rename from 03_source/mobile/src/pages/NotImplemented/index copy.tsx rename to 03_source/mobile.trunk/src/pages/About.tsx index bd8219f..2c45965 100644 --- a/03_source/mobile/src/pages/NotImplemented/index copy.tsx +++ b/03_source/mobile.trunk/src/pages/About.tsx @@ -1,11 +1,3 @@ -// REQ0042/event-detail -// -// PURPOSE: -// - Provides functionality view event detail -// -// RULES: -// - T.B.A. -// import React, { useState } from 'react'; import { IonHeader, @@ -25,14 +17,14 @@ import { IonPopover, IonText, } from '@ionic/react'; -import './style.scss'; +import './About.scss'; import { ellipsisHorizontal, ellipsisVertical } from 'ionicons/icons'; -import AboutPopover from '../../components/AboutPopover'; +import AboutPopover from '../components/AboutPopover'; import { format, parseISO } from 'date-fns'; interface AboutProps {} -const EventDetail: React.FC = () => { +const About: React.FC = () => { const [showPopover, setShowPopover] = useState(false); const [popoverEvent, setPopoverEvent] = useState(); const [location, setLocation] = useState< @@ -98,14 +90,14 @@ const EventDetail: React.FC = () => {

About

- The Ionic Conference is a one-day event happening on{' '} - {displayDate(conferenceDate, 'MMM dd, yyyy')}, featuring talks from - the Ionic team. The conference focuses on building applications with - Ionic Framework, including topics such as app migration to the - latest version, React best practices, Webpack, Sass, and other - technologies commonly used in the Ionic ecosystem. Tickets are - completely sold out, and we're expecting over 1,000 developers — - making this the largest Ionic conference to date! + The Ionic Conference is a one-day event happening on {' '} + {displayDate(conferenceDate, 'MMM dd, yyyy')}, featuring talks from the + Ionic team. The conference focuses on building applications with Ionic + Framework, including topics such as app migration to the latest version, + React best practices, Webpack, Sass, and other technologies + commonly used in the Ionic ecosystem. Tickets are completely sold out, + and we're expecting over 1,000 developers — making this the largest + Ionic conference to date!

Details

@@ -176,4 +168,4 @@ const EventDetail: React.FC = () => { ); }; -export default React.memo(EventDetail); +export default React.memo(About); diff --git a/03_source/mobile.trunk/src/pages/Account.scss b/03_source/mobile.trunk/src/pages/Account.scss new file mode 100644 index 0000000..e3c2761 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/Account.scss @@ -0,0 +1,6 @@ +#account-page { + img { + max-width: 140px; + border-radius: 50%; + } +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/Account.tsx b/03_source/mobile.trunk/src/pages/Account.tsx new file mode 100644 index 0000000..56f89e0 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/Account.tsx @@ -0,0 +1,110 @@ +import React, { useState } from 'react'; +import { + IonHeader, + IonToolbar, + IonTitle, + IonContent, + IonPage, + IonButtons, + IonMenuButton, + IonList, + IonItem, + IonAlert, +} from '@ionic/react'; +import './Account.scss'; +import { setUsername } from '../data/user/user.actions'; +import { connect } from '../data/connect'; +import { RouteComponentProps } from 'react-router'; + +interface OwnProps extends RouteComponentProps {} + +interface StateProps { + username?: string; +} + +interface DispatchProps { + setUsername: typeof setUsername; +} + +interface AccountProps extends OwnProps, StateProps, DispatchProps {} + +const Account: React.FC = ({ setUsername, username }) => { + const [showAlert, setShowAlert] = useState(false); + + const clicked = (text: string) => { + console.log(`Clicked ${text}`); + }; + + return ( + + + + + + + Account + + + + {username && ( +
+ avatar +

{username}

+ + clicked('Update Picture')}> + Update Picture + + setShowAlert(true)}> + Change Username + + clicked('Change Password')}> + Change Password + + + Support + + + Logout + + +
+ )} +
+ { + setUsername(data.username); + }, + }, + ]} + inputs={[ + { + type: 'text', + name: 'username', + value: username, + placeholder: 'username', + }, + ]} + onDidDismiss={() => setShowAlert(false)} + /> +
+ ); +}; + +export default connect({ + mapStateToProps: (state) => ({ + username: state.user.username, + }), + mapDispatchToProps: { + setUsername, + }, + component: Account, +}); diff --git a/03_source/mobile.trunk/src/pages/ChangeLanguage/index.tsx b/03_source/mobile.trunk/src/pages/ChangeLanguage/index.tsx new file mode 100644 index 0000000..81a4ae6 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/ChangeLanguage/index.tsx @@ -0,0 +1,231 @@ +// REQ0041/home_discover_event_tab + +import React, { useEffect, useRef, useState } from 'react'; +import { + IonHeader, + IonToolbar, + IonTitle, + IonContent, + IonPage, + IonButtons, + IonMenuButton, + IonGrid, + IonRow, + IonCol, + useIonRouter, + IonButton, + IonIcon, + IonPopover, + IonAvatar, + IonImg, + IonItem, + IonLabel, + IonList, + IonModal, + IonSearchbar, + useIonModal, + IonInput, + IonRefresher, + IonRefresherContent, + RefresherEventDetail, + IonToast, + useIonToast, +} from '@ionic/react'; +import SpeakerItem from '../../components/SpeakerItem'; +import { Speaker } from '../../models/Speaker'; +import { Session } from '../../models/Schedule'; +import { connect } from '../../data/connect'; +import * as selectors from '../../data/selectors'; +import '../SpeakerList.scss'; +import { getEvents } from '../../api/getEvents'; +import { format } from 'date-fns'; +import { Event } from './types'; +import { + checkmarkOutline, + chevronBackOutline, + chevronDownCircleOutline, + closeOutline, + heart, + languageOutline, + menuOutline, +} from 'ionicons/icons'; +import AboutPopover from '../../components/AboutPopover'; +import Loading from '../../components/Loading'; + +interface OwnProps {} + +interface StateProps { + events: Event[]; +} + +interface DispatchProps {} + +interface SpeakerListProps extends OwnProps, StateProps, DispatchProps {} + +const EventList: React.FC = ({ events }) => { + const modal = useRef(null); + + const router = useIonRouter(); + + function handleShowPartyEventDetail(event_id: string) { + router.push(`/event_detail/${event_id}`); + } + + function handleRefresh(event: CustomEvent) { + setTimeout(() => { + // Any calls to load data go here + event.detail.complete(); + }, 2000); + } + + const [confirmChangeLanguage, setConfirmChangeLanguage] = + useState(false); + + function handleChangeToChinese() { + setConfirmChangeLanguage(true); + } + + function handleChangeToEnglish() { + setConfirmChangeLanguage(true); + } + + function handleChangeToJapanese() { + setConfirmChangeLanguage(true); + } + + function handleApplyChangeLanguage() { + setConfirmChangeLanguage(false); + } + + function handleBackClick() { + router.goBack(); + } + + function handleCancelChangeLanguage() { + setConfirmChangeLanguage(false); + } + + const [present] = useIonToast(); + + const presentToast = () => { + present({ + message: 'sorry but the function not yet implemented!', + duration: 1500, + position: 'bottom', + }); + + setConfirmChangeLanguage(false); + }; + + if (!events || events.length == 0) return ; + + return ( + + + + + {/* */} + + + + +
+ + Change Language +
+
+
+ + + + + Change Language + + + + + + Chinese + + + English + + + Japanese + + + + + {/* REQ0079/event-filter */} + setConfirmChangeLanguage(false)} + > + +
+
+ Confirm change language +
+
+ Change language to Chinese +
+
+ + + No + + + + Yes + +
+
+
+
+
+ ); +}; + +export default connect({ + mapStateToProps: (state) => ({ + events: selectors.getEvents(state), + }), + component: React.memo(EventList), +}); diff --git a/03_source/mobile.trunk/src/pages/ChangeLanguage/style.scss b/03_source/mobile.trunk/src/pages/ChangeLanguage/style.scss new file mode 100644 index 0000000..5fae6e3 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/ChangeLanguage/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile.trunk/src/pages/Demo2FaExample/AppPages/Tab1.jsx b/03_source/mobile.trunk/src/pages/Demo2FaExample/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/Demo2FaExample/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/Demo2FaExample/AppPages/Tab2.jsx b/03_source/mobile.trunk/src/pages/Demo2FaExample/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/Demo2FaExample/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/Demo2FaExample/NOTES.md b/03_source/mobile.trunk/src/pages/Demo2FaExample/NOTES.md new file mode 100644 index 0000000..a1700c9 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/Demo2FaExample/NOTES.md @@ -0,0 +1,13 @@ +--- +tags: mobile +--- + +# REQ0119/demo-2fa-example + +## description + +This is the Ionic implementation example of two-factor authentication (2FA) demonstrating various authentication flows and UI components. + +## relation + +- diff --git a/03_source/mobile.trunk/src/pages/Demo2FaExample/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk/src/pages/Demo2FaExample/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile.trunk/src/pages/Demo2FaExample/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile.trunk/src/pages/Demo2FaExample/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk/src/pages/Demo2FaExample/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/Demo2FaExample/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/Demo2FaExample/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk/src/pages/Demo2FaExample/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/Demo2FaExample/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/Demo2FaExample/components/Keypad.module.scss b/03_source/mobile.trunk/src/pages/Demo2FaExample/components/Keypad.module.scss new file mode 100644 index 0000000..38c85a2 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/Demo2FaExample/components/Keypad.module.scss @@ -0,0 +1,5 @@ +.keypad { + bottom: 0; + position: absolute; + width: 100%; +} diff --git a/03_source/mobile.trunk/src/pages/Demo2FaExample/components/Keypad.tsx b/03_source/mobile.trunk/src/pages/Demo2FaExample/components/Keypad.tsx new file mode 100644 index 0000000..db88125 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/Demo2FaExample/components/Keypad.tsx @@ -0,0 +1,118 @@ +import { IonRow } from "@ionic/react"; +import styles from "./Keypad.module.scss"; +import KeypadButton from "./KeypadButton"; + +const Keypad = (props: any): JSX.Element => { + const { activeIndex, handleClick, handleRemove, amount, correct } = props; + + const keypadButtons = [ + [ + { + value: "1", + handleClick: () => handleClick(activeIndex, 1), + small: false, + remove: false, + }, + { + value: "2", + handleClick: () => handleClick(activeIndex, 2), + small: false, + remove: false, + }, + { + value: "3", + handleClick: () => handleClick(activeIndex, 3), + small: false, + remove: false, + }, + ], + [ + { + value: "4", + handleClick: () => handleClick(activeIndex, 4), + small: false, + remove: false, + }, + { + value: "5", + handleClick: () => handleClick(activeIndex, 5), + small: false, + remove: false, + }, + { + value: "6", + handleClick: () => handleClick(activeIndex, 6), + small: false, + remove: false, + }, + ], + [ + { + value: "7", + handleClick: () => handleClick(activeIndex, 7), + small: false, + remove: false, + }, + { + value: "8", + handleClick: () => handleClick(activeIndex, 8), + small: false, + remove: false, + }, + { + value: "9", + handleClick: () => handleClick(activeIndex, 9), + small: false, + remove: false, + }, + ], + [ + { + value: "Resend", + handleClick: () => handleClick(activeIndex, 1), + small: true, + remove: false, + }, + { + value: "0", + handleClick: () => handleClick(activeIndex, 2), + small: false, + remove: false, + }, + { + value: "", + handleClick: () => handleRemove(), + small: true, + remove: true, + }, + ], + ]; + + return ( +
+ {keypadButtons.map((keypadRow, index) => { + const isDisabled = parseInt(activeIndex) === parseInt(amount); + + return ( + + {keypadRow.map((button, index2) => { + return ( + + ); + })} + + ); + })} +
+ ); +}; + +export default Keypad; diff --git a/03_source/mobile.trunk/src/pages/Demo2FaExample/components/KeypadButton.module.scss b/03_source/mobile.trunk/src/pages/Demo2FaExample/components/KeypadButton.module.scss new file mode 100644 index 0000000..400a722 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/Demo2FaExample/components/KeypadButton.module.scss @@ -0,0 +1,30 @@ +.logo { + height: 4rem; + width: auto; +} + +.keypadButton { + --background: none; + --color: black; + font-size: 2rem; + font-weight: 700; + --outline: none; + --border: none; + --box-shadow: none; + padding: none; + margin: none; + --background-hover: rgb(245, 245, 245) !important; + --background-focused: rgb(245, 245, 245) !important; + --background-activated: rgb(245, 245, 245) !important; +} + +.smallKeypadButton { + font-size: 1.4rem; + margin-top: 1rem; +} + +.keypad { + bottom: 0; + position: absolute; + width: 100%; +} diff --git a/03_source/mobile.trunk/src/pages/Demo2FaExample/components/KeypadButton.tsx b/03_source/mobile.trunk/src/pages/Demo2FaExample/components/KeypadButton.tsx new file mode 100644 index 0000000..320a13c --- /dev/null +++ b/03_source/mobile.trunk/src/pages/Demo2FaExample/components/KeypadButton.tsx @@ -0,0 +1,31 @@ +import { IonButton, IonCol, IonIcon } from "@ionic/react"; +import { backspaceOutline } from "ionicons/icons"; +import styles from "./KeypadButton.module.scss"; + +const KeypadButton = (props: any): JSX.Element => { + const { + small, + value, + remove, + handleClick, + isDisabled = false, + correct, + } = props; + + return ( + + + {!remove && value} + {remove && } + + + ); +}; + +export default KeypadButton; diff --git a/03_source/mobile.trunk/src/pages/Demo2FaExample/components/KeypadInput.module.scss b/03_source/mobile.trunk/src/pages/Demo2FaExample/components/KeypadInput.module.scss new file mode 100644 index 0000000..c54a1b8 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/Demo2FaExample/components/KeypadInput.module.scss @@ -0,0 +1,38 @@ +.keypadInput { + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + align-items: center; + font-size: 2.5rem; + width: 100%; + min-height: 2.5rem; + background-color: rgb(245, 245, 245); + border-radius: 4px; + color: rgb(207, 207, 207); + transition: 0.2s linear; +} + +.active { + background-color: rgba(26, 150, 251, 0.2); + border: 0 !important; + color: white !important; + transition: 0.2s linear; +} + +.filled { + color: rgb(151, 151, 151); + transition: 0.2s linear; +} + +.incorrect { + background-color: rgba(251, 26, 26, 0.2); + color: rgb(218, 67, 67); + transition: 0.2s linear; +} + +.correct { + background-color: rgba(26, 251, 120, 0.2); + color: rgb(67, 218, 112); + transition: 0.2s linear; +} diff --git a/03_source/mobile.trunk/src/pages/Demo2FaExample/components/KeypadInput.tsx b/03_source/mobile.trunk/src/pages/Demo2FaExample/components/KeypadInput.tsx new file mode 100644 index 0000000..5b6f532 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/Demo2FaExample/components/KeypadInput.tsx @@ -0,0 +1,28 @@ +import { IonCol } from "@ionic/react"; +import styles from "./KeypadInput.module.scss"; + +const KeypadInput = (props: any): JSX.Element => { + const { + value, + isActive = false, + isFilled = false, + incorrect, + correct, + } = props; + + return ( + +
+ {value} + {!isFilled && "0"} +
+
+ ); +}; + +export default KeypadInput; diff --git a/03_source/mobile.trunk/src/pages/Demo2FaExample/components/KeypadInputs.tsx b/03_source/mobile.trunk/src/pages/Demo2FaExample/components/KeypadInputs.tsx new file mode 100644 index 0000000..139e0f9 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/Demo2FaExample/components/KeypadInputs.tsx @@ -0,0 +1,50 @@ +import { IonRow } from "@ionic/react"; +import { useEffect, useRef } from "react"; +import KeypadInput from "./KeypadInput"; + +const KeypadInputs = (props: any): JSX.Element => { + const { values, activeIndex, incorrect, correct } = props; + const keypadRef = useRef(null); + + useEffect(() => { + if (incorrect && keypadRef.current) { + keypadRef.current.classList.add("incorrect"); + + setTimeout(() => { + if (keypadRef.current) { + keypadRef.current.classList.remove("incorrect"); + } + }, 1000); + } + }, [incorrect]); + + useEffect(() => { + if (correct) { + if (keypadRef.current) { + keypadRef.current.classList.add("correct"); + } + } + }, [correct]); + + return ( + + {values.map((value: string, index: number) => { + const isActive = parseInt(index.toString()) === parseInt(activeIndex); + const isFilled = value !== "" ? true : false; + + return ( + + ); + })} + + ); +}; + +export default KeypadInputs; diff --git a/03_source/mobile.trunk/src/pages/Demo2FaExample/index.tsx b/03_source/mobile.trunk/src/pages/Demo2FaExample/index.tsx new file mode 100644 index 0000000..d91e0db --- /dev/null +++ b/03_source/mobile.trunk/src/pages/Demo2FaExample/index.tsx @@ -0,0 +1,26 @@ +// REQ0119/demo-2fa-example +// +// RULES: +// T.B.A. +// +import { IonRouterOutlet, IonTabs } from '@ionic/react'; + +import { Route, Redirect } from 'react-router'; + +import Home from './pages/Home'; + +function Demo2FaExample() { + return ( + + + + + + + + + + ); +} + +export default Demo2FaExample; diff --git a/03_source/mobile.trunk/src/pages/Demo2FaExample/pages/Home.module.scss b/03_source/mobile.trunk/src/pages/Demo2FaExample/pages/Home.module.scss new file mode 100644 index 0000000..3d0a7fe --- /dev/null +++ b/03_source/mobile.trunk/src/pages/Demo2FaExample/pages/Home.module.scss @@ -0,0 +1,30 @@ +.logo { + height: 4rem; + width: auto; +} + +.incorrect { + color: rgb(218, 67, 67); +} + +.successContainer { +} + +.successText { + background-color: rgba(26, 251, 120, 0.2); + color: rgb(67, 218, 112); + padding: 1rem; +} + +.successContinue { + font-weight: 700; + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + align-items: center; +} + +.successContinue ion-icon { + margin-top: 0.2rem; +} diff --git a/03_source/mobile.trunk/src/pages/Demo2FaExample/pages/Home.tsx b/03_source/mobile.trunk/src/pages/Demo2FaExample/pages/Home.tsx new file mode 100644 index 0000000..1cd7885 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/Demo2FaExample/pages/Home.tsx @@ -0,0 +1,135 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonImg, + IonPage, + IonRow, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import { arrowForwardOutline, chevronBackOutline } from 'ionicons/icons'; +import styles from './Home.module.scss'; +import KeypadInputs from '../components/KeypadInputs'; +import Keypad from '../components/Keypad'; +import { JSX, useEffect, useRef, useState } from 'react'; + +const Home = (): JSX.Element => { + const correctCode = [5, 9, 2, 5]; + const [keypadValues, setKeypadValues] = useState(['', '', '', '']); + const [activeIndex, setActiveIndex] = useState(0); + const successRef = useRef(null); + + const [incorrect, setIncorrect] = useState(false); + const [correct, setCorrect] = useState(false); + + const tempValues: { [key: string]: any } = {}; + + const handleClick = (index: number, value: any) => { + const stringKey = index.toString(); + tempValues[stringKey] = value; + + setKeypadValues(value); + setActiveIndex((activeIndex) => activeIndex + 1); + }; + + const handleRemove = () => { + const tempValues = [...keypadValues]; + tempValues[activeIndex - 1] = ''; + + setKeypadValues(tempValues); + activeIndex > 0 && setActiveIndex((activeIndex) => activeIndex - 1); + setIncorrect(false); + setCorrect(false); + }; + + const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + useEffect(() => { + if (parseInt(activeIndex.toString()) === parseInt(keypadValues.length.toString())) { + var error = false; + + keypadValues.forEach((value, index) => { + if (parseInt(value) !== parseInt(correctCode[index].toString())) { + error = true; + return false; + } + }); + + if (error) { + setIncorrect(true); + } else { + setCorrect(true); + + setTimeout(() => { + if (successRef.current) { + successRef.current.classList.remove('hidden'); + successRef.current.classList.add('success'); + } + }, 900); + } + } + }, [activeIndex]); + + return ( + + + + + + handleBackClick()}> + + + + + + + + + +

Verification required

+

Enter your 4 digit verification code

+
+
+ + + +

+ Awesome! You may continue. +
+ + Continue   + + +

+
+
+ + + {incorrect &&

Wrong code entered

} + +
+
+
+ ); +}; + +export default Home; diff --git a/03_source/mobile.trunk/src/pages/Demo2FaExample/theme/variables.scss b/03_source/mobile.trunk/src/pages/Demo2FaExample/theme/variables.scss new file mode 100644 index 0000000..dc5200e --- /dev/null +++ b/03_source/mobile.trunk/src/pages/Demo2FaExample/theme/variables.scss @@ -0,0 +1,249 @@ +.demo-2fa-example { + /* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + + * { + font-family: 'Lato', sans-serif; + } + + .hidden { + display: none; + transition: 0.2s linear; + } + + /** Ionic CSS Variables **/ + :root { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + } + + .incorrect { + -webkit-animation: incorrect-animation 0.9s both; + animation: incorrect-animation 0.9s both; + } + + @-webkit-keyframes incorrect-animation { + 0% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 30% { + -webkit-transform: scale3d(1.25, 0.75, 1); + transform: scale3d(1.25, 0.75, 1); + } + + 40% { + -webkit-transform: scale3d(0.75, 1.25, 1); + transform: scale3d(0.75, 1.25, 1); + } + + 50% { + -webkit-transform: scale3d(1.15, 0.85, 1); + transform: scale3d(1.15, 0.85, 1); + } + + 65% { + -webkit-transform: scale3d(0.95, 1.05, 1); + transform: scale3d(0.95, 1.05, 1); + } + + 75% { + -webkit-transform: scale3d(1.05, 0.95, 1); + transform: scale3d(1.05, 0.95, 1); + } + + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + } + + @keyframes incorrect-animation { + 0% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + + 30% { + -webkit-transform: scale3d(1.25, 0.75, 1); + transform: scale3d(1.25, 0.75, 1); + } + + 40% { + -webkit-transform: scale3d(0.75, 1.25, 1); + transform: scale3d(0.75, 1.25, 1); + } + + 50% { + -webkit-transform: scale3d(1.15, 0.85, 1); + transform: scale3d(1.15, 0.85, 1); + } + + 65% { + -webkit-transform: scale3d(0.95, 1.05, 1); + transform: scale3d(0.95, 1.05, 1); + } + + 75% { + -webkit-transform: scale3d(1.05, 0.95, 1); + transform: scale3d(1.05, 0.95, 1); + } + + 100% { + -webkit-transform: scale3d(1, 1, 1); + transform: scale3d(1, 1, 1); + } + } + + .correct { + -webkit-animation: correct-animation 1s ease-in both; + animation: correct-animation 1s ease-in both; + } + + @-webkit-keyframes correct-animation { + 0% { + -webkit-transform: translateY(0) rotateX(0) scale(1); + transform: translateY(0) rotateX(0) scale(1); + -webkit-transform-origin: 50% 1400px; + transform-origin: 50% 1400px; + opacity: 1; + } + + 100% { + -webkit-transform: translateY(-600px) rotateX(-30deg) scale(0); + transform: translateY(-600px) rotateX(-30deg) scale(0); + -webkit-transform-origin: 50% 100%; + transform-origin: 50% 100%; + opacity: 1; + } + } + + @keyframes correct-animation { + 0% { + -webkit-transform: translateY(0) rotateX(0) scale(1); + transform: translateY(0) rotateX(0) scale(1); + -webkit-transform-origin: 50% 1400px; + transform-origin: 50% 1400px; + opacity: 1; + } + + 100% { + -webkit-transform: translateY(-600px) rotateX(-30deg) scale(0); + transform: translateY(-600px) rotateX(-30deg) scale(0); + -webkit-transform-origin: 50% 100%; + transform-origin: 50% 100%; + opacity: 1; + } + } + + .success { + -webkit-animation: success-animation 0.7s cubic-bezier(0.25, 0.46, 0.45, 0.94) both; + animation: success-animation 0.7s cubic-bezier(0.25, 0.46, 0.45, 0.94) both; + } + + @-webkit-keyframes success-animation { + 0% { + -webkit-transform: translateY(-600px) rotateX(-30deg) scale(0); + transform: translateY(-600px) rotateX(-30deg) scale(0); + -webkit-transform-origin: 50% 100%; + transform-origin: 50% 100%; + opacity: 0; + } + + 100% { + -webkit-transform: translateY(0) rotateX(0) scale(1); + transform: translateY(0) rotateX(0) scale(1); + -webkit-transform-origin: 50% 1400px; + transform-origin: 50% 1400px; + opacity: 1; + } + } + + @keyframes success-animation { + 0% { + -webkit-transform: translateY(-600px) rotateX(-30deg) scale(0); + transform: translateY(-600px) rotateX(-30deg) scale(0); + -webkit-transform-origin: 50% 100%; + transform-origin: 50% 100%; + opacity: 0; + } + + 100% { + -webkit-transform: translateY(0) rotateX(0) scale(1); + transform: translateY(0) rotateX(0) scale(1); + -webkit-transform-origin: 50% 1400px; + transform-origin: 50% 1400px; + opacity: 1; + } + } +} diff --git a/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/AppPages/Tab1.jsx b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/AppPages/Tab1.jsx new file mode 100644 index 0000000..54ab2b9 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../components/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../components/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/AppPages/Tab2.jsx b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/AppPages/Tab2.jsx new file mode 100644 index 0000000..216544f --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../components/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/NOTES.md b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/NOTES.md new file mode 100644 index 0000000..a746fff --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/NOTES.md @@ -0,0 +1 @@ +# REQ0120 diff --git a/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/components/Accordion.jsx b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/components/Accordion.jsx new file mode 100644 index 0000000..c679c72 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/components/Accordion.jsx @@ -0,0 +1,29 @@ +import { IonAccordion, IonAccordionGroup, IonIcon, IonItem, IonLabel, IonList } from '@ionic/react'; +import { topics } from '../data'; + +export const Accordion = () => { + return ( + + {topics.map((topic, index) => { + return ( + + + + {topic.header} + + + + {topic.options.map((option, index2) => { + return ( + + {option.label} + + ); + })} + + + ); + })} + + ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/components/Accordion.tsx b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/components/Accordion.tsx new file mode 100644 index 0000000..7e495fa --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/components/Accordion.tsx @@ -0,0 +1,30 @@ +import { IonAccordion, IonAccordionGroup, IonIcon, IonItem, IonLabel, IonList } from '@ionic/react'; +import { topics } from '../data'; +import React from 'react'; + +export const Accordion: React.FC = () => { + return ( + + {topics.map((topic: any, index: number) => { + return ( + + + + {topic.header} + + + + {topic.options.map((option: any, index2: number) => { + return ( + + {option.label} + + ); + })} + + + ); + })} + + ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/components/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/components/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/components/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/components/CurrentWeather/index.tsx b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/components/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/components/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/components/SkeletonDashboard/index.tsx b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/components/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/components/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/data.js b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/data.js new file mode 100644 index 0000000..457fe00 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/data.js @@ -0,0 +1,120 @@ +import { bicycleOutline, fastFoodOutline, filmOutline, gameControllerOutline, libraryOutline } from 'ionicons/icons'; + +export const topics = [ + { + header: 'Attractions', + color: 'primary', + icon: filmOutline, + options: [ + { + label: 'Cinema', + }, + { + label: 'Bowling Alley', + }, + { + label: 'Crazy Golf', + }, + { + label: 'Theme Park', + }, + ], + }, + { + header: 'Dining', + color: 'success', + icon: fastFoodOutline, + options: [ + { + label: 'Breakfast & Brunch', + }, + { + label: 'New American', + }, + { + label: 'Sushi Bars', + }, + { + label: 'Filipino Food', + }, + { + label: 'Asian Fusion', + }, + { + label: 'Ramen Houses', + }, + { + label: 'Dinner Venues', + }, + ], + }, + { + header: 'Gaming', + color: 'warning', + icon: gameControllerOutline, + options: [ + { + label: 'Xbox', + }, + { + label: 'Playstation', + }, + { + label: 'Nintendo Switch', + }, + { + label: 'PC', + }, + { + label: 'Mobile', + }, + { + label: 'Dreamcast', + }, + ], + }, + { + header: 'Exercise', + color: 'secondary', + icon: bicycleOutline, + options: [ + { + label: 'Yoga', + }, + { + label: 'Pilates', + }, + { + label: 'Weight Training', + }, + { + label: 'Cardio', + }, + { + label: 'Zumba', + }, + ], + }, + { + header: 'Education', + color: 'danger', + icon: libraryOutline, + options: [ + { + label: 'School', + }, + { + label: 'High School', + }, + { + label: 'University Bachelors', + }, + { + label: 'University Masters', + }, + { + label: 'University pHD', + }, + ], + }, +]; diff --git a/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/data.ts b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/data.ts new file mode 100644 index 0000000..10fb915 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/data.ts @@ -0,0 +1,126 @@ +import { + bicycleOutline, + fastFoodOutline, + filmOutline, + gameControllerOutline, + libraryOutline, +} from 'ionicons/icons'; + +export const topics: any = [ + { + header: 'Attractions', + color: 'primary', + icon: filmOutline, + options: [ + { + label: 'Cinema', + }, + { + label: 'Bowling Alley', + }, + { + label: 'Crazy Golf', + }, + { + label: 'Theme Park', + }, + ], + }, + { + header: 'Dining', + color: 'success', + icon: fastFoodOutline, + options: [ + { + label: 'Breakfast & Brunch', + }, + { + label: 'New American', + }, + { + label: 'Sushi Bars', + }, + { + label: 'Filipino Food', + }, + { + label: 'Asian Fusion', + }, + { + label: 'Ramen Houses', + }, + { + label: 'Dinner Venues', + }, + ], + }, + { + header: 'Gaming', + color: 'warning', + icon: gameControllerOutline, + options: [ + { + label: 'Xbox', + }, + { + label: 'Playstation', + }, + { + label: 'Nintendo Switch', + }, + { + label: 'PC', + }, + { + label: 'Mobile', + }, + { + label: 'Dreamcast', + }, + ], + }, + { + header: 'Exercise', + color: 'secondary', + icon: bicycleOutline, + options: [ + { + label: 'Yoga', + }, + { + label: 'Pilates', + }, + { + label: 'Weight Training', + }, + { + label: 'Cardio', + }, + { + label: 'Zumba', + }, + ], + }, + { + header: 'Education', + color: 'danger', + icon: libraryOutline, + options: [ + { + label: 'School', + }, + { + label: 'High School', + }, + { + label: 'University Bachelors', + }, + { + label: 'University Masters', + }, + { + label: 'University pHD', + }, + ], + }, +]; diff --git a/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/index.tsx b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/index.tsx new file mode 100644 index 0000000..c1f670a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/index.tsx @@ -0,0 +1,56 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +// import Tab1 from './AppPages/Tab1'; +// import Tab2 from './AppPages/Tab2'; + +import Topic from './pages/Topic'; +import Home from './pages/Home'; + +import './style.scss'; + +function DemoAccordionTutorial() { + return ( + + + {/* + + + + + + + */} + + + + + + + + + + + + {/* */} + + + {/* + + + + Dashboard + + + + Search + + + */} + + ); +} + +export default DemoAccordionTutorial; diff --git a/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/pages/Home.css b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/pages/Home.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/pages/Home.jsx b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/pages/Home.jsx new file mode 100644 index 0000000..a0add98 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/pages/Home.jsx @@ -0,0 +1,97 @@ +import { + IonAccordion, + IonAccordionGroup, + IonButton, + IonButtons, + IonContent, + IonHeader, + IonIcon, + IonItem, + IonLabel, + IonList, + IonPage, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import { Accordion } from '../components/Accordion'; +import { chevronBackOutline } from 'ionicons/icons'; + +const Home = () => { + const router = useIonRouter(); + + function handleBackClick() { + router.goBack(); + } + + return ( + + + + Accordion + + + handleBackClick()}> + + + + + + + + + Accordion + + + handleBackClick()}> + + + + + + + + + {/* + + + Languages + + + + + English + + + Spanish + + + Italian + + + + + + + Languages 2 + + + + + English + + + Spanish + + + Italian + + + + */} + + + ); +}; + +export default Home; diff --git a/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/pages/Home.tsx b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/pages/Home.tsx new file mode 100644 index 0000000..65a2e4a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/pages/Home.tsx @@ -0,0 +1,97 @@ +import { + IonAccordion, + IonAccordionGroup, + IonButton, + IonButtons, + IonContent, + IonHeader, + IonIcon, + IonItem, + IonLabel, + IonList, + IonPage, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import { Accordion } from '../components/Accordion'; +import { chevronBackOutline } from 'ionicons/icons'; + +const Home: React.FC = () => { + const router = useIonRouter(); + + function handleBackClick() { + router.goBack(); + } + + return ( + + + + Accordion + + + handleBackClick()}> + + + + + + + + + Accordion + + + handleBackClick()}> + + + + + + + + + {/* + + + Languages + + + + + English + + + Spanish + + + Italian + + + + + + + Languages 2 + + + + + English + + + Spanish + + + Italian + + + + */} + + + ); +}; + +export default Home; diff --git a/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/pages/Topic.jsx b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/pages/Topic.jsx new file mode 100644 index 0000000..32beb84 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/pages/Topic.jsx @@ -0,0 +1,39 @@ +import { IonBackButton, IonButtons, IonCol, IonContent, IonGrid, IonHeader, IonLabel, IonPage, IonRow, IonTitle, IonToolbar } from '@ionic/react'; +import { useParams } from 'react-router'; + +const Topic = () => { + + const { topic } = useParams(); + + return ( + + + + + + + + + { topic } + + + + + + { topic } + + + + + + + This is the page for the topic: { topic }. + + + + + + ); +}; + +export default Topic; diff --git a/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/pages/Topic.tsx b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/pages/Topic.tsx new file mode 100644 index 0000000..54c48e7 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/pages/Topic.tsx @@ -0,0 +1,49 @@ +import { + IonBackButton, + IonButtons, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonLabel, + IonPage, + IonRow, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useParams } from 'react-router'; + +const Topic = () => { + const { topic } = useParams<{ topic: string }>(); + + return ( + + + + + + + + {topic} + + + + + + {topic} + + + + + + + This is the page for the topic: {topic}. + + + + + + ); +}; + +export default Topic; diff --git a/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/style.scss b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/style.scss new file mode 100644 index 0000000..d12d506 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoAccordionTutorial/style.scss @@ -0,0 +1,237 @@ +.demo-accordion-tutorial { + /* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + + /** Ionic CSS Variables **/ + * { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + } + + @media (prefers-color-scheme: dark) { + /* + * Dark Colors + * ------------------------------------------- + */ + + body { + --ion-color-primary: #428cff; + --ion-color-primary-rgb: 66, 140, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3a7be0; + --ion-color-primary-tint: #5598ff; + + --ion-color-secondary: #50c8ff; + --ion-color-secondary-rgb: 80, 200, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #46b0e0; + --ion-color-secondary-tint: #62ceff; + + --ion-color-tertiary: #6a64ff; + --ion-color-tertiary-rgb: 106, 100, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #5d58e0; + --ion-color-tertiary-tint: #7974ff; + + --ion-color-success: #2fdf75; + --ion-color-success-rgb: 47, 223, 117; + --ion-color-success-contrast: #000000; + --ion-color-success-contrast-rgb: 0, 0, 0; + --ion-color-success-shade: #29c467; + --ion-color-success-tint: #44e283; + + --ion-color-warning: #ffd534; + --ion-color-warning-rgb: 255, 213, 52; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0bb2e; + --ion-color-warning-tint: #ffd948; + + --ion-color-danger: #ff4961; + --ion-color-danger-rgb: 255, 73, 97; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #e04055; + --ion-color-danger-tint: #ff5b71; + + --ion-color-dark: #f4f5f8; + --ion-color-dark-rgb: 244, 245, 248; + --ion-color-dark-contrast: #000000; + --ion-color-dark-contrast-rgb: 0, 0, 0; + --ion-color-dark-shade: #d7d8da; + --ion-color-dark-tint: #f5f6f9; + + --ion-color-medium: #989aa2; + --ion-color-medium-rgb: 152, 154, 162; + --ion-color-medium-contrast: #000000; + --ion-color-medium-contrast-rgb: 0, 0, 0; + --ion-color-medium-shade: #86888f; + --ion-color-medium-tint: #a2a4ab; + + --ion-color-light: #222428; + --ion-color-light-rgb: 34, 36, 40; + --ion-color-light-contrast: #ffffff; + --ion-color-light-contrast-rgb: 255, 255, 255; + --ion-color-light-shade: #1e2023; + --ion-color-light-tint: #383a3e; + } + + /* + * iOS Dark Theme + * ------------------------------------------- + */ + + .ios body { + --ion-background-color: #000000; + --ion-background-color-rgb: 0, 0, 0; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-color-step-50: #0d0d0d; + --ion-color-step-100: #1a1a1a; + --ion-color-step-150: #262626; + --ion-color-step-200: #333333; + --ion-color-step-250: #404040; + --ion-color-step-300: #4d4d4d; + --ion-color-step-350: #595959; + --ion-color-step-400: #666666; + --ion-color-step-450: #737373; + --ion-color-step-500: #808080; + --ion-color-step-550: #8c8c8c; + --ion-color-step-600: #999999; + --ion-color-step-650: #a6a6a6; + --ion-color-step-700: #b3b3b3; + --ion-color-step-750: #bfbfbf; + --ion-color-step-800: #cccccc; + --ion-color-step-850: #d9d9d9; + --ion-color-step-900: #e6e6e6; + --ion-color-step-950: #f2f2f2; + + --ion-item-background: #000000; + + --ion-card-background: #1c1c1d; + } + + .ios ion-modal { + --ion-background-color: var(--ion-color-step-100); + --ion-toolbar-background: var(--ion-color-step-150); + --ion-toolbar-border-color: var(--ion-color-step-250); + } + + /* + * Material Design Dark Theme + * ------------------------------------------- + */ + + .md body { + --ion-background-color: #121212; + --ion-background-color-rgb: 18, 18, 18; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-border-color: #222222; + + --ion-color-step-50: #1e1e1e; + --ion-color-step-100: #2a2a2a; + --ion-color-step-150: #363636; + --ion-color-step-200: #414141; + --ion-color-step-250: #4d4d4d; + --ion-color-step-300: #595959; + --ion-color-step-350: #656565; + --ion-color-step-400: #717171; + --ion-color-step-450: #7d7d7d; + --ion-color-step-500: #898989; + --ion-color-step-550: #949494; + --ion-color-step-600: #a0a0a0; + --ion-color-step-650: #acacac; + --ion-color-step-700: #b8b8b8; + --ion-color-step-750: #c4c4c4; + --ion-color-step-800: #d0d0d0; + --ion-color-step-850: #dbdbdb; + --ion-color-step-900: #e7e7e7; + --ion-color-step-950: #f3f3f3; + + --ion-item-background: #1e1e1e; + + --ion-toolbar-background: #1f1f1f; + + --ion-tab-bar-background: #1f1f1f; + + --ion-card-background: #1e1e1e; + } + } +} diff --git a/03_source/mobile.trunk/src/pages/DemoBankingUi/AppPages/Tab1.jsx b/03_source/mobile.trunk/src/pages/DemoBankingUi/AppPages/Tab1.jsx new file mode 100644 index 0000000..54ab2b9 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBankingUi/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../components/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../components/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/DemoBankingUi/AppPages/Tab2.jsx b/03_source/mobile.trunk/src/pages/DemoBankingUi/AppPages/Tab2.jsx new file mode 100644 index 0000000..216544f --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBankingUi/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../components/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/DemoBankingUi/NOTES.md b/03_source/mobile.trunk/src/pages/DemoBankingUi/NOTES.md new file mode 100644 index 0000000..54c3e76 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBankingUi/NOTES.md @@ -0,0 +1 @@ +# REQ0121 diff --git a/03_source/mobile/src/pages/DemoBankingUi/components/CardSlide.jsx b/03_source/mobile.trunk/src/pages/DemoBankingUi/components/CardSlide.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoBankingUi/components/CardSlide.jsx rename to 03_source/mobile.trunk/src/pages/DemoBankingUi/components/CardSlide.jsx diff --git a/03_source/mobile.trunk/src/pages/DemoBankingUi/components/CardSlide.module.css b/03_source/mobile.trunk/src/pages/DemoBankingUi/components/CardSlide.module.css new file mode 100644 index 0000000..7ef5587 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBankingUi/components/CardSlide.module.css @@ -0,0 +1,51 @@ +.customSlide { + display: flex; + flex-direction: column; +} + +.transactionList { + overflow: scroll; + width: 100vw; +} + +.balance { + font-weight: 300; + font-size: 1.5rem; + color: black; + + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + align-items: center; +} + +.poundSign { + font-weight: 800; + font-size: 1.2rem; +} + +.heading h6 { + padding: 0; + margin: 0; + text-align: left !important; + float: left !important; + text-align: left !important; + color: rgb(124, 124, 124); + font-weight: 400; +} + +.heading { + width: 83%; + padding: 0; + margin: 0; + margin-top: 0.75rem; +} + +.addButton { + --border-radius: 500px !important; + width: fit-content !important; + margin-top: 0.45rem; + margin-left: 1rem; + opacity: 0.6; +} diff --git a/03_source/mobile.trunk/src/pages/DemoBankingUi/components/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk/src/pages/DemoBankingUi/components/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBankingUi/components/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoBankingUi/components/CurrentWeather/index.tsx b/03_source/mobile.trunk/src/pages/DemoBankingUi/components/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBankingUi/components/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoBankingUi/components/DebitCard.jsx b/03_source/mobile.trunk/src/pages/DemoBankingUi/components/DebitCard.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoBankingUi/components/DebitCard.jsx rename to 03_source/mobile.trunk/src/pages/DemoBankingUi/components/DebitCard.jsx diff --git a/03_source/mobile.trunk/src/pages/DemoBankingUi/components/DebitCard.module.css b/03_source/mobile.trunk/src/pages/DemoBankingUi/components/DebitCard.module.css new file mode 100644 index 0000000..b2064a5 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBankingUi/components/DebitCard.module.css @@ -0,0 +1,188 @@ +@import url('https://fonts.googleapis.com/css?family=Space+Mono:400,400i,700,700i'); + +.card { + box-sizing: border-box; + font-family: 'Space Mono', monospace; + margin: 0 auto; +} + +.title { + margin-bottom: 30px; + color: #162969; +} + +.card { + width: 320px; + height: 190px; + -webkit-perspective: 600px; + -moz-perspective: 600px; + perspective: 600px; +} + +.card__part { + box-shadow: 1px 1px #aaa3a3; + top: 0; + position: absolute; + z-index: 1000; + left: 0; + display: inline-block; + width: 320px; + height: 190px; + background-repeat: no-repeat; + background-position: center; + background-size: cover; + border-radius: 8px; + + -webkit-transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); + -moz-transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); + -ms-transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); + -o-transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); + transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); + -webkit-transform-style: preserve-3d; + -moz-transform-style: preserve-3d; + -webkit-backface-visibility: hidden; + -moz-backface-visibility: hidden; +} + +.card_orange { + background: linear-gradient(to right bottom, #fd696b, #fa616e, #f65871, #c74261, #d62158); +} + +.card_blue { + background: linear-gradient(to right bottom, #699dfd, #61b5fa, #58aff6, #4b86c9, #2151d6); +} + +.card_black { + background: linear-gradient(to right bottom, #292929, #363636, #555555, #444444, #0f0f0f); +} + +.card_purple { + background: linear-gradient(to right bottom, #7a43df, #644897, #8964cf, #633cac, #512c96); +} + +.card__front { + padding: 18px; + -webkit-transform: rotateY(0); + -moz-transform: rotateY(0); +} + +.card__back { + padding: 18px 0; + -webkit-transform: rotateY(-180deg); + -moz-transform: rotateY(-180deg); +} + +.card__black_line { + margin-top: 5px; + height: 38px; + background-color: #303030; +} + +.card__logo { + height: 16px !important; +} + +.card__front_chip { + left: 1.2rem; + height: 1.5rem !important; + position: absolute; +} + +.card__front_logo { + position: absolute; + top: 18px; + right: 18px; +} + +.card__square { + border-radius: 5px; + height: 30px !important; +} + +.card_number { + display: block; + width: 100%; + word-spacing: 4px; + font-size: 20px; + letter-spacing: 2px; + color: #fff; + text-align: center; + margin-bottom: 20px; + margin-top: 20px; +} + +.card__space_75 { + width: 75%; + float: left; +} + +.card__space_25 { + width: 25%; + float: left; +} + +.card__label { + font-size: 10px; + text-transform: uppercase; + color: rgba(255, 255, 255, 0.8); + letter-spacing: 1px; +} + +.card__info { + margin-bottom: 0; + margin-top: 5px; + font-size: 16px; + line-height: 18px; + color: #fff; + letter-spacing: 1px; + text-transform: uppercase; +} + +.card__back_content { + padding: 15px 15px 0; +} +.card__secret__last { + color: #303030; + text-align: right; + margin: 0; + font-size: 14px; +} + +.card__secret { + padding: 5px 12px; + background-color: #fff; + position: relative; +} + +.card__secret:before { + content: ''; + position: absolute; + top: -3px; + left: -3px; + height: calc(100% + 6px); + width: calc(100% - 42px); + border-radius: 4px; + background: repeating-linear-gradient(45deg, #ededed, #ededed 5px, #f9f9f9 5px, #f9f9f9 10px); +} + +.card__back_logo { + position: absolute; + bottom: 15px; + right: 15px; +} + +.card__back_square { + position: absolute; + bottom: 15px; + left: 15px; +} + +.card:hover .card__front { + -webkit-transform: rotateY(180deg); + -moz-transform: rotateY(180deg); +} + +.card:hover .card__back { + -webkit-transform: rotateY(0deg); + -moz-transform: rotateY(0deg); +} diff --git a/03_source/mobile.trunk/src/pages/DemoBankingUi/components/SkeletonDashboard/index.tsx b/03_source/mobile.trunk/src/pages/DemoBankingUi/components/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..ae39e44 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBankingUi/components/SkeletonDashboard/index.tsx @@ -0,0 +1,118 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import React from 'react'; + +export const SkeletonDashboard = (): React.JSX.Element => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoBankingUi/components/TransactionItem.jsx b/03_source/mobile.trunk/src/pages/DemoBankingUi/components/TransactionItem.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoBankingUi/components/TransactionItem.jsx rename to 03_source/mobile.trunk/src/pages/DemoBankingUi/components/TransactionItem.jsx diff --git a/03_source/mobile.trunk/src/pages/DemoBankingUi/components/TransactionItem.module.css b/03_source/mobile.trunk/src/pages/DemoBankingUi/components/TransactionItem.module.css new file mode 100644 index 0000000..68649fd --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBankingUi/components/TransactionItem.module.css @@ -0,0 +1,48 @@ +.avatarImage { + /* background-color: var(--ion-color); */ + width: 2.5rem; + height: 2.5rem; + border-radius: 500px; + color: black; + font-size: 1.3rem; + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + align-items: center; + padding: 0.5rem !important; + border: 2px solid rgb(44, 44, 44); + margin-top: 0.2rem; +} + +.transactionItem { + flex-direction: row; + padding: 0; + margin: 0; +} + +.transactionItemContent { + padding-left: 3rem; + padding-right: 2rem; + display: flex !important; + flex-direction: row !important; + justify-content: space-between; + width: 100%; + align-content: center; + align-items: center; + margin-top: -0.2rem; + margin-bottom: -0.2rem; +} + +.transactionContent { + padding: 1rem; + text-align: left !important; +} + +.green { + color: rgb(0, 165, 0); +} + +.red { + color: red; +} diff --git a/03_source/mobile/src/pages/DemoBankingUi/data/AccountStore.js b/03_source/mobile.trunk/src/pages/DemoBankingUi/data/AccountStore.js similarity index 100% rename from 03_source/mobile/src/pages/DemoBankingUi/data/AccountStore.js rename to 03_source/mobile.trunk/src/pages/DemoBankingUi/data/AccountStore.js diff --git a/03_source/mobile.trunk/src/pages/DemoBankingUi/data/CardStore.js b/03_source/mobile.trunk/src/pages/DemoBankingUi/data/CardStore.js new file mode 100644 index 0000000..a50176d --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBankingUi/data/CardStore.js @@ -0,0 +1,6 @@ +import { Store } from 'pullstate'; + +export const CardStore = new Store({ + card_colors: ['orange', 'black', 'blue', 'purple'], + card_types: ['visa', 'mastercard'], +}); diff --git a/03_source/mobile.trunk/src/pages/DemoBankingUi/data/Utils.js b/03_source/mobile.trunk/src/pages/DemoBankingUi/data/Utils.js new file mode 100644 index 0000000..c943a9e --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBankingUi/data/Utils.js @@ -0,0 +1,9 @@ +export const formatBalance = (balance) => { + var formatter = new Intl.NumberFormat('en-GB', { + // style: 'currency', + currency: 'GBP', + minimumFractionDigits: 2, + }); + + return formatter.format(balance); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoBankingUi/index.tsx b/03_source/mobile.trunk/src/pages/DemoBankingUi/index.tsx new file mode 100644 index 0000000..338da1e --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBankingUi/index.tsx @@ -0,0 +1,42 @@ +import { IonRouterOutlet, IonTabs } from '@ionic/react'; + +import { Route, Redirect } from 'react-router'; + +// import Tab1 from './AppPages/Tab1'; +// import Tab2 from './AppPages/Tab2'; + +import Home from './pages/Home'; +import Account from './pages/Account'; +import AddCard from './pages/AddCard'; +import AddTransaction from './pages/AddTransaction'; + +import './style.scss'; +import React from 'react'; + +function DemoBankingUi(): React.JSX.Element { + return ( + + + + + + + + + + + + + + + + + + + + + + ); +} + +export default DemoBankingUi; diff --git a/03_source/mobile/src/pages/DemoBankingUi/pages/Account.jsx b/03_source/mobile.trunk/src/pages/DemoBankingUi/pages/Account.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoBankingUi/pages/Account.jsx rename to 03_source/mobile.trunk/src/pages/DemoBankingUi/pages/Account.jsx diff --git a/03_source/mobile.trunk/src/pages/DemoBankingUi/pages/Account.module.css b/03_source/mobile.trunk/src/pages/DemoBankingUi/pages/Account.module.css new file mode 100644 index 0000000..8e59143 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBankingUi/pages/Account.module.css @@ -0,0 +1,54 @@ +.accountPage ion-toolbar { + --border-style: none; + --padding-top: 1rem; + --padding-bottom: 1rem; + padding-left: 1rem; + padding-right: 1rem; +} + +.avatar { + border-radius: 500px; + border: 3px solid var(--ion-color-primary); + padding: 0.2rem; +} + +.profileDetails { +} + +.profileDetails h6, +.profileDetails h5 { + margin: 0; + padding: 0; +} + +.profileDetails h6 { + color: var(--ion-color-medium); +} + +.cards { + /* margin-top: */ +} + +.smallCard { + width: 15%; + height: 80%; + border-radius: 5px; + opacity: 0.7; +} + +.cardDescription { + padding-left: 1.5rem; +} + +.cardDescription h4 { + font-weight: 400; +} + +.cardItem { + --background: rgb(246, 246, 246); + --border-radius: 5px; + --padding-start: 1rem; + --padding-end: 1rem; + --padding-top: 0.5rem; + --padding-bottom: 0.5rem; +} diff --git a/03_source/mobile/src/pages/DemoBankingUi/pages/AddCard.jsx b/03_source/mobile.trunk/src/pages/DemoBankingUi/pages/AddCard.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoBankingUi/pages/AddCard.jsx rename to 03_source/mobile.trunk/src/pages/DemoBankingUi/pages/AddCard.jsx diff --git a/03_source/mobile.trunk/src/pages/DemoBankingUi/pages/AddTransaction.jsx b/03_source/mobile.trunk/src/pages/DemoBankingUi/pages/AddTransaction.jsx new file mode 100644 index 0000000..8461455 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBankingUi/pages/AddTransaction.jsx @@ -0,0 +1,165 @@ +import { useState } from 'react'; +import { + IonBackButton, + IonButton, + IonButtons, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonInput, + IonItem, + IonLabel, + IonPage, + IonRow, + IonSelect, + IonSelectOption, + IonTitle, + IonToggle, + IonToolbar, + useIonViewWillEnter, +} from '@ionic/react'; +import styles from './Account.module.css'; +import DebitCard from '../components/DebitCard'; +import { AccountStore, addCardToAccount, addTransactionToCard } from '../data/AccountStore'; +import { CardStore } from '../data/CardStore'; +import { addOutline, timerOutline } from 'ionicons/icons'; +import { useHistory, useParams } from 'react-router'; + +const AddTransaction = () => { + const cards = AccountStore.useState((s) => s.cards); + const profile = AccountStore.useState((s) => s.profile); + + const [cardID, setCardID] = useState(false); + const [card, setCard] = useState({}); + const [transactionName, setTransactionName] = useState('Test Transaction'); + const [transactionAmount, setTransactionAmount] = useState(0); + const [transactionDeposit, setTransactionDeposit] = useState(false); + + const history = useHistory(); + const params = useParams(); + const [adding, setAdding] = useState(false); + + useIonViewWillEnter(() => { + const tempCardID = params.card_id; + const tempCard = cards.filter((c) => parseInt(c.id) === parseInt(tempCardID))[0]; + setCardID(tempCardID); + setCard(tempCard); + }); + + const addTransaction = async () => { + setAdding(true); + + const newTransaction = { + name: transactionName, + amount: transactionAmount, + deposit: transactionDeposit, + }; + + await addTransactionToCard(newTransaction, cardID); + + setTimeout(() => { + setAdding(false); + history.goBack(); + }, 500); + }; + + return ( + + + + + + + + Add Transaction + + + + + + + + + + + + + + + Name + setTransactionName(e.currentTarget.value)} + /> + + + + + + Amount + setTransactionAmount(e.currentTarget.value)} + /> + + + + + + + + Deposit? + setTransactionDeposit(e.currentTarget.checked)} + /> + + + + + + + + {!adding && ( + <> + +   Add Transaction + + )} + + {adding && ( + <> + +   Adding... + + )} + + + + + + + ); +}; + +export default AddTransaction; diff --git a/03_source/mobile.trunk/src/pages/DemoBankingUi/pages/Home.jsx b/03_source/mobile.trunk/src/pages/DemoBankingUi/pages/Home.jsx new file mode 100644 index 0000000..01ea13c --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBankingUi/pages/Home.jsx @@ -0,0 +1,146 @@ +import { useRef, useState } from 'react'; +import { + IonButton, + IonButtons, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonPage, + IonTitle, + IonToolbar, + useIonRouter, + useIonViewDidEnter, +} from '@ionic/react'; +import styles from './Home.module.css'; +import { AccountStore } from '../data/AccountStore'; +import CardSlide from '../components/CardSlide'; +import { chevronBackOutline, searchOutline } from 'ionicons/icons'; + +// Import Swiper React components +import { Swiper, SwiperSlide } from 'swiper/react'; + +// Import Swiper styles +// import 'swiper/swiper.scss'; +import 'swiper/css'; + +import stylesS from './Home.module.scss'; + +const Home = () => { + const cards = AccountStore.useState((s) => s.cards); + const profile = AccountStore.useState((s) => s.profile); + + const [pageTitle, setPageTitle] = useState(cards[0].description); + const [mainColor, setMainColor] = useState(cards[0].color); + const [slideSpace, setSlideSpace] = useState(10); + + const slidesRef = useRef(null); + + useIonViewDidEnter(() => { + setSlideSpace(0); + }); + + const changeSlide = async (e) => { + const swiper = e; + const swiperIndex = swiper.activeIndex; + + setPageTitle(cards[swiperIndex].description); + setMainColor(cards[swiperIndex].color); + + document.getElementById(`slide_${swiperIndex}_balance`).classList.add('animate__headShake'); + + setTimeout(() => { + document + .getElementById(`slide_${swiperIndex}_balance`) + .classList.remove('animate__headShake'); + }, 1000); + }; + + const manageTouch = async (touched, e) => { + const swiper = e; + const swiperIndex = swiper.activeIndex; + + if (touched) { + document + .getElementById(`slide_${swiperIndex}_transactions`) + .classList.add('animate__fadeOut'); + } else { + document + .getElementById(`slide_${swiperIndex}_transactions`) + .classList.remove('animate__fadeOut'); + document.getElementById(`slide_${swiperIndex}_transactions`).classList.add('animate__fadeIn'); + } + }; + + const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + + {/* */} + + toolbar avatar + + + + {pageTitle} + + + handleBackClick()}> + + + {/* */} + + + + + + + + + + manageTouch(true, e)} + onTouchEnd={(e) => manageTouch(false, e)} + onSlideChange={(e) => changeSlide(e)} + > + {cards.map((card, index) => { + return ( + + + + ); + })} + + + + + ); +}; + +export default Home; diff --git a/03_source/mobile.trunk/src/pages/DemoBankingUi/pages/Home.module.css b/03_source/mobile.trunk/src/pages/DemoBankingUi/pages/Home.module.css new file mode 100644 index 0000000..5d5719c --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBankingUi/pages/Home.module.css @@ -0,0 +1,38 @@ +.homePage ion-toolbar { + --border-style: none; + --padding-top: 1rem; + --padding-bottom: 1rem; + padding-left: 1rem; + padding-right: 1rem; +} + +.customSlide { + display: flex; + flex-direction: column; +} + +.transactionList { + overflow: scroll; + width: 100vw; +} + +.balance { + font-weight: 300; + font-size: 1.5rem; + color: black; +} + +.poundSign { + font-weight: 800; + font-size: 1.2rem; +} + +.toolbarAvatarImage { + border-radius: 500px; + height: 100%; + width: auto; +} + +.helloworld { + background-color: gold; +} diff --git a/03_source/mobile.trunk/src/pages/DemoBankingUi/pages/Home.module.scss b/03_source/mobile.trunk/src/pages/DemoBankingUi/pages/Home.module.scss new file mode 100644 index 0000000..0065249 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBankingUi/pages/Home.module.scss @@ -0,0 +1,38 @@ +.homePage ion-toolbar { + --border-style: none; + --padding-top: 1rem; + --padding-bottom: 1rem; + padding-left: 1rem; + padding-right: 1rem; +} + +.customSlide { + display: flex; + flex-direction: column; +} + +.transactionList { + overflow: scroll; + width: 100vw; +} + +.balance { + font-weight: 300; + font-size: 1.5rem; + color: black; +} + +.poundSign { + font-weight: 800; + font-size: 1.2rem; +} + +.toolbarAvatarImage { + border-radius: 500px; + height: 32px; + width: auto; +} + +.helloworld { + background-color: cyan; +} diff --git a/03_source/mobile.trunk/src/pages/DemoBankingUi/style.scss b/03_source/mobile.trunk/src/pages/DemoBankingUi/style.scss new file mode 100644 index 0000000..378cae3 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBankingUi/style.scss @@ -0,0 +1,139 @@ +.demo-banking-ui { + /* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + + /** Ionic CSS Variables **/ + * { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + } + + ion-item { + --padding-start: 0; + --inner-padding-end: 0; + } + + ion-label { + margin-top: 12px; + margin-bottom: 12px; + } + + ion-item h2 { + font-weight: 600; + margin: 0; + } + + ion-item p { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + width: 95%; + } + + ion-item .date { + float: right; + align-items: center; + display: flex; + } + + ion-item ion-icon { + color: #c9c9ca; + } + + ion-item ion-note { + font-size: 15px; + margin-right: 8px; + font-weight: normal; + } + + ion-item ion-note.md { + margin-right: 14px; + } + + .dot { + display: block; + height: 12px; + width: 12px; + border-radius: 50%; + align-self: start; + margin: 16px 10px 16px 16px; + } + + .dot-unread { + background: var(--ion-color-primary); + } + + ion-footer ion-title { + font-size: 11px; + font-weight: normal; + } +} diff --git a/03_source/mobile.trunk/src/pages/DemoBlogPostUi/AppPages/BlogPost.css b/03_source/mobile.trunk/src/pages/DemoBlogPostUi/AppPages/BlogPost.css new file mode 100644 index 0000000..e754dfd --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBlogPostUi/AppPages/BlogPost.css @@ -0,0 +1,7 @@ +.view-post-footer { + + background-color: white; + padding-left: 1rem; + padding-right: 1rem; + padding-bottom: 1rem; +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoBlogPostUi/AppPages/BlogPost.jsx b/03_source/mobile.trunk/src/pages/DemoBlogPostUi/AppPages/BlogPost.jsx new file mode 100644 index 0000000..3c295ba --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBlogPostUi/AppPages/BlogPost.jsx @@ -0,0 +1,90 @@ +import { + IonBackButton, + IonBadge, + IonButton, + IonButtons, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonContent, + IonFooter, + IonGrid, + IonHeader, + IonIcon, + IonNote, + IonPage, + IonRow, + IonText, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { bookmarkOutline, shareOutline } from 'ionicons/icons'; +import { useParams } from 'react-router'; +import { blogPosts } from '../localData'; +import './BlogPost.css'; + +const BlogPost = () => { + const { id } = useParams(); + const post = blogPosts.filter((post) => parseInt(post.id) === parseInt(id))[0]; + + return ( + + + + Blog + + + + + + + post header + + + + + post author + + {post.author} + + + {post.date} + + + + + {post.title} + + + + + + {post.content} + + + + + + + +
+ + + + + + +
+ +
+ + {post.category} + +
+
+
+
+ ); +}; + +export default BlogPost; diff --git a/03_source/mobile.trunk/src/pages/DemoBlogPostUi/AppPages/Home.jsx b/03_source/mobile.trunk/src/pages/DemoBlogPostUi/AppPages/Home.jsx new file mode 100644 index 0000000..7fff026 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBlogPostUi/AppPages/Home.jsx @@ -0,0 +1,51 @@ +import { + IonButton, + IonButtons, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import { Post } from '../components/Post'; +import { blogPosts } from '../localData'; +import { chevronBackOutline } from 'ionicons/icons'; + +const Home = () => { + const router = useIonRouter(); + + function handleBackClick() { + router.goBack(); + } + + return ( + + + + Ionic Blog + + + handleBackClick()}> + + + + + + + + + Ionic Blog + + + + {blogPosts.map((post, index) => ( + + ))} + + + ); +}; + +export default Home; diff --git a/03_source/mobile.trunk/src/pages/DemoBlogPostUi/NOTES.md b/03_source/mobile.trunk/src/pages/DemoBlogPostUi/NOTES.md new file mode 100644 index 0000000..583ec8b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBlogPostUi/NOTES.md @@ -0,0 +1 @@ +# REQ0122 diff --git a/03_source/mobile.trunk/src/pages/DemoBlogPostUi/components/Post.css b/03_source/mobile.trunk/src/pages/DemoBlogPostUi/components/Post.css new file mode 100644 index 0000000..4b6c13d --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBlogPostUi/components/Post.css @@ -0,0 +1,37 @@ +.post-author-avatar { + height: 2rem; + width: 2rem; + border-radius: 500px; +} + +.post-title { + font-size: 1.4rem; + margin-top: 0.75rem; +} + +.post-content { + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; +} + +.post-footer { + display: flex; + flex-direction: row; + justify-content: space-between; + align-content: center; + width: 100%; + border-top: 2px solid rgb(245, 245, 245); + margin-top: 2rem; + padding-top: 1rem; +} + +.post-category { + margin-top: 1.1rem; +} + +.post-image { + width: 100%; +} diff --git a/03_source/mobile.trunk/src/pages/DemoBlogPostUi/components/Post.jsx b/03_source/mobile.trunk/src/pages/DemoBlogPostUi/components/Post.jsx new file mode 100644 index 0000000..96437dd --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBlogPostUi/components/Post.jsx @@ -0,0 +1,57 @@ +import { + IonBadge, + IonButton, + IonCard, + IonCardContent, + IonCardHeader, + IonCardSubtitle, + IonCardTitle, + IonIcon, + IonNote, + IonRow, +} from '@ionic/react'; +import { bookmarkOutline, shareOutline } from 'ionicons/icons'; + +import './Post.css'; + +export const Post = ({ post }) => { + return ( + + main post + + + + + post author + + {post.author} + + + {post.date} + + {post.title} + + + +

{post.content}

+ + +
+ + + + + + +
+ +
+ + {post.category} + +
+
+
+
+ ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoBlogPostUi/index.tsx b/03_source/mobile.trunk/src/pages/DemoBlogPostUi/index.tsx new file mode 100644 index 0000000..c924f7d --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBlogPostUi/index.tsx @@ -0,0 +1,27 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Home from './AppPages/Home'; +import BlogPost from './AppPages/BlogPost'; + +import './style.scss'; + +function DemoBlogPostUi() { + return ( + + + + + + + + + + + + ); +} + +export default DemoBlogPostUi; diff --git a/03_source/mobile/src/pages/DemoBlogPostUi/localData/index.js b/03_source/mobile.trunk/src/pages/DemoBlogPostUi/localData/index.js similarity index 100% rename from 03_source/mobile/src/pages/DemoBlogPostUi/localData/index.js rename to 03_source/mobile.trunk/src/pages/DemoBlogPostUi/localData/index.js diff --git a/03_source/mobile.trunk/src/pages/DemoBlogPostUi/style.scss b/03_source/mobile.trunk/src/pages/DemoBlogPostUi/style.scss new file mode 100644 index 0000000..83c6630 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoBlogPostUi/style.scss @@ -0,0 +1,79 @@ +.demo-blog-post-ui { + /* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + + /** Ionic CSS Variables **/ + * { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + } +} diff --git a/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/AppPages/Tab1.jsx b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/AppPages/Tab1.jsx new file mode 100644 index 0000000..54ab2b9 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../components/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../components/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/AppPages/Tab2.jsx b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/AppPages/Tab2.jsx new file mode 100644 index 0000000..216544f --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../components/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/NOTES.md b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/NOTES.md new file mode 100644 index 0000000..83a41c1 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/NOTES.md @@ -0,0 +1 @@ +# REQ0123 diff --git a/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/components/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/components/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/components/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/components/CurrentWeather/index.tsx b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/components/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/components/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/components/ExploreContainer.css b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/components/ExploreContainer.css new file mode 100644 index 0000000..e99f514 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/components/ExploreContainer.css @@ -0,0 +1,24 @@ +.container { + text-align: center; + position: absolute; + left: 0; + right: 0; + top: 50%; + transform: translateY(-50%); +} + +.container strong { + font-size: 20px; + line-height: 26px; +} + +.container p { + font-size: 16px; + line-height: 22px; + color: #8c8c8c; + margin: 0; +} + +.container a { + text-decoration: none; +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/components/ExploreContainer.tsx b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/components/ExploreContainer.tsx new file mode 100644 index 0000000..1b4b3c0 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/components/ExploreContainer.tsx @@ -0,0 +1,14 @@ +import './ExploreContainer.css'; + +interface ContainerProps { } + +const ExploreContainer: React.FC = () => { + return ( +
+ Ready to create an app? +

Start with Ionic UI Components

+
+ ); +}; + +export default ExploreContainer; diff --git a/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/components/MarkerInfoWindow.jsx b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/components/MarkerInfoWindow.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/components/MarkerInfoWindow.jsx rename to 03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/components/MarkerInfoWindow.jsx diff --git a/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/components/SkeletonDashboard/index.tsx b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/components/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/components/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/data/index.js b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/data/index.js similarity index 100% rename from 03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/data/index.js rename to 03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/data/index.js diff --git a/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/index.tsx b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/index.tsx new file mode 100644 index 0000000..70dcf42 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/index.tsx @@ -0,0 +1,34 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import Home from './pages/Home'; + +import './style.scss'; + +function DemoCapacitorGoogleMapsTutorial() { + return ( + + + + + + + + + + + + + + + + + ); +} + +export default DemoCapacitorGoogleMapsTutorial; diff --git a/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/pages/Home.css b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/pages/Home.css new file mode 100644 index 0000000..912345b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/pages/Home.css @@ -0,0 +1,5 @@ +capacitor-google-map { + display: inline-block; + width: 100%; + height: 86vh; +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/pages/Home.jsx b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/pages/Home.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoCapacitorGoogleMapsTutorial/pages/Home.jsx rename to 03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/pages/Home.jsx diff --git a/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/style.scss b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/style.scss new file mode 100644 index 0000000..697843e --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoCapacitorGoogleMapsTutorial/style.scss @@ -0,0 +1,2 @@ +.demo-capacitor-google-maps-tutorial { +} diff --git a/03_source/mobile.trunk/src/pages/DemoClubHouse/AppPages/Tab1.css b/03_source/mobile.trunk/src/pages/DemoClubHouse/AppPages/Tab1.css new file mode 100644 index 0000000..67366ad --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoClubHouse/AppPages/Tab1.css @@ -0,0 +1,12 @@ +.extra-padding { + + padding-right: 1.3rem !important; + padding-left: 1.3rem !important; +} + +.title { + + font-weight: 600; + font-size: 1.2rem; + margin-bottom: 0.5rem; +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoClubHouse/AppPages/Tab1.jsx b/03_source/mobile.trunk/src/pages/DemoClubHouse/AppPages/Tab1.jsx new file mode 100644 index 0000000..df15105 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoClubHouse/AppPages/Tab1.jsx @@ -0,0 +1,90 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonText, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import { chevronBack, personOutline } from 'ionicons/icons'; +import { useStoreState } from 'pullstate'; +import { TalkStore } from '../store'; +import { getTalks } from '../store/Selectors'; +import './Tab1.css'; + +import { TalkCard } from '../components/TalkCard'; +import { useRef } from 'react'; + +const Tab1 = () => { + const pageRef = useRef(null); + const talks = useStoreState(TalkStore, getTalks); + + const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + All Rooms + + + + + + + + + + + + + + + + + + + +

Upcoming

+
+
+
+ + + + + + + + + + +

Happening Now

+
+
+
+ + + + {talks.map((talk, talkIndex) => { + return talkIndex > 0 && ; + })} + + +
+
+
+ ); +}; + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/DemoClubHouse/AppPages/Tab2.css b/03_source/mobile.trunk/src/pages/DemoClubHouse/AppPages/Tab2.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk/src/pages/DemoClubHouse/AppPages/Tab2.jsx b/03_source/mobile.trunk/src/pages/DemoClubHouse/AppPages/Tab2.jsx new file mode 100644 index 0000000..a668cd0 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoClubHouse/AppPages/Tab2.jsx @@ -0,0 +1,23 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import './Tab2.css'; + +const Tab2 = () => { + return ( + + + + TO DO :) + + + + + + TO DO :) + + + + + ); +}; + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/DemoClubHouse/AppPages/Tab3.css b/03_source/mobile.trunk/src/pages/DemoClubHouse/AppPages/Tab3.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk/src/pages/DemoClubHouse/AppPages/Tab3.jsx b/03_source/mobile.trunk/src/pages/DemoClubHouse/AppPages/Tab3.jsx new file mode 100644 index 0000000..5717606 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoClubHouse/AppPages/Tab3.jsx @@ -0,0 +1,23 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import './Tab3.css'; + +const Tab3 = () => { + return ( + + + + TO DO :) + + + + + + TO DO :) + + + + + ); +}; + +export default Tab3; diff --git a/03_source/mobile.trunk/src/pages/DemoClubHouse/NOTES.md b/03_source/mobile.trunk/src/pages/DemoClubHouse/NOTES.md new file mode 100644 index 0000000..74774f7 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoClubHouse/NOTES.md @@ -0,0 +1 @@ +# REQ0124 diff --git a/03_source/mobile/src/pages/DemoClubHouse/components/TalkCard.jsx b/03_source/mobile.trunk/src/pages/DemoClubHouse/components/TalkCard.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoClubHouse/components/TalkCard.jsx rename to 03_source/mobile.trunk/src/pages/DemoClubHouse/components/TalkCard.jsx diff --git a/03_source/mobile.trunk/src/pages/DemoClubHouse/components/TalkCard.module.css b/03_source/mobile.trunk/src/pages/DemoClubHouse/components/TalkCard.module.css new file mode 100644 index 0000000..881aa99 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoClubHouse/components/TalkCard.module.css @@ -0,0 +1,105 @@ +.talkCard { + + display: flex; + flex-direction: column; + border-radius: 10px; + padding: 2rem; + box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.1); + background-color: white; +} + +.talkCard:not(:first-child) { + + margin-top: 2rem; +} + +.upcomingCard { + + background-color: var(--ion-color-primary); + color: white; +} + +.cardTitle { + + display: flex; + flex-direction: row; + align-content: center; + align-items: center; + color: white; +} + +.cardTitle ion-icon { + + border-radius: 500px; + padding: 0.2rem; + margin-right: 0.75rem; + background-color: var(--ion-color-primary); + margin-top: -0.2rem; +} + +.upcomingCard .cardTitle ion-icon { + + background-color: white; +} + +.talkTitle h3 { + + font-size: 1.3rem !important; + font-weight: 600; +} + +.talkDate { + + display: flex; + justify-content: space-between; + align-items: center; + align-content: center; + margin-top: 0.5rem; +} + +.talkSpeakers { + + display: flex; + flex-direction: row; + margin-top: 0.5rem; +} + +.talkSpeaker { + + display: flex; + align-items: center; + justify-content: center; + height: 3.5rem; + width: 3.5rem; + border-radius: 12px; + margin-right: 0.2rem; + background-color: var(--ion-color-primary); +} + +.talkSpeaker img { + + height: 3rem; + width: 3rem; +} + +.talkDetails { + + display: flex; + justify-content: space-between; + margin-top: 1.5rem; +} + +.detailCount { + + display: flex; + align-content: center; + align-items: center; + font-weight: 500; + font-size: 0.9rem; +} + +.detailCount ion-icon { + + font-size: 1.2rem; + margin-right: 0.3rem; +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoClubHouse/components/TalkModal.jsx b/03_source/mobile.trunk/src/pages/DemoClubHouse/components/TalkModal.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoClubHouse/components/TalkModal.jsx rename to 03_source/mobile.trunk/src/pages/DemoClubHouse/components/TalkModal.jsx diff --git a/03_source/mobile.trunk/src/pages/DemoClubHouse/components/TalkModal.module.css b/03_source/mobile.trunk/src/pages/DemoClubHouse/components/TalkModal.module.css new file mode 100644 index 0000000..3661f23 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoClubHouse/components/TalkModal.module.css @@ -0,0 +1,150 @@ +.talkCard { + + display: flex; + flex-direction: column; + border-radius: 10px; + padding: 2rem; + box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.1); + background-color: white; +} + +.talkCard:not(:first-child) { + + margin-top: 2rem; +} + +.upcomingCard { + + background-color: var(--ion-color-primary); + color: white; +} + +.cardTitle { + + display: flex; + flex-direction: row; + align-content: center; + align-items: center; + color: white; +} + +.cardTitle ion-icon { + + border-radius: 500px; + padding: 0.2rem; + margin-right: 0.75rem; + background-color: var(--ion-color-primary); + margin-top: -0.2rem; +} + +.upcomingCard .cardTitle ion-icon { + + background-color: white; +} + +.talkTitle { + + margin-top: -1rem; +} + +.talkTitle h3 { + + font-size: 1.3rem !important; + font-weight: 600; +} + +.talkDate { + + display: flex; + justify-content: space-between; + align-items: center; + align-content: center; + margin-top: 0.5rem; +} + +.talkSpeakers { + + display: flex; + flex-direction: row; + justify-content: space-between; + margin-top: 0.5rem; + margin-bottom: 1.5rem; +} + +.speakerContainer { + + display: flex; + flex-direction: column; + align-content: center; + align-items: center; + justify-content: space-between; +} + +.audienceContainer { + + margin-bottom: 1rem; + /* margin: 0.5rem; */ +} + +.talkSpeaker { + + display: flex; + align-items: center; + justify-content: center; + height: 3.5rem; + width: 3.5rem; + border-radius: 12px; + margin-right: 0.2rem; + background-color: var(--ion-color-primary); +} + +.audienceContainer .talkSpeaker { + + background-color: #f2efe5; + border: 2px solid #dfd9c7; +} + +.talkSpeaker img { + + height: 3rem; + width: 3rem; +} + +.speakerContainer p { + + margin: 0; + padding: 0; + margin-top: 0.2rem; + font-weight: 500; + font-size: 0.9rem; +} + +.talkDetails { + + display: flex; + justify-content: space-between; + margin-top: 1.5rem; +} + +.detailCount { + + display: flex; + align-content: center; + align-items: center; + font-weight: 600; + font-size: 0.9rem; + margin-top: 1rem; + margin-bottom: 0.3rem; +} + +.detailCount ion-icon { + + font-size: 1.2rem; + margin-right: 0.3rem; + margin-left: -0.4rem; +} + +.activeSpeaker { + + border: 3px solid rgb(255, 187, 0); +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoClubHouse/components1/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk/src/pages/DemoClubHouse/components1/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoClubHouse/components1/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoClubHouse/components1/CurrentWeather/index.tsx b/03_source/mobile.trunk/src/pages/DemoClubHouse/components1/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoClubHouse/components1/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoClubHouse/components1/SkeletonDashboard/index.tsx b/03_source/mobile.trunk/src/pages/DemoClubHouse/components1/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoClubHouse/components1/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile/src/pages/DemoClubHouse/index.tsx b/03_source/mobile.trunk/src/pages/DemoClubHouse/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoClubHouse/index.tsx rename to 03_source/mobile.trunk/src/pages/DemoClubHouse/index.tsx diff --git a/03_source/mobile/src/pages/DemoClubHouse/store/CategoryStore.js b/03_source/mobile.trunk/src/pages/DemoClubHouse/store/CategoryStore.js similarity index 100% rename from 03_source/mobile/src/pages/DemoClubHouse/store/CategoryStore.js rename to 03_source/mobile.trunk/src/pages/DemoClubHouse/store/CategoryStore.js diff --git a/03_source/mobile/src/pages/DemoClubHouse/store/PeopleStore.js b/03_source/mobile.trunk/src/pages/DemoClubHouse/store/PeopleStore.js similarity index 100% rename from 03_source/mobile/src/pages/DemoClubHouse/store/PeopleStore.js rename to 03_source/mobile.trunk/src/pages/DemoClubHouse/store/PeopleStore.js diff --git a/03_source/mobile/src/pages/DemoClubHouse/store/Selectors.js b/03_source/mobile.trunk/src/pages/DemoClubHouse/store/Selectors.js similarity index 100% rename from 03_source/mobile/src/pages/DemoClubHouse/store/Selectors.js rename to 03_source/mobile.trunk/src/pages/DemoClubHouse/store/Selectors.js diff --git a/03_source/mobile/src/pages/DemoClubHouse/store/TalkStore.js b/03_source/mobile.trunk/src/pages/DemoClubHouse/store/TalkStore.js similarity index 100% rename from 03_source/mobile/src/pages/DemoClubHouse/store/TalkStore.js rename to 03_source/mobile.trunk/src/pages/DemoClubHouse/store/TalkStore.js diff --git a/03_source/mobile/src/pages/DemoClubHouse/store/index.js b/03_source/mobile.trunk/src/pages/DemoClubHouse/store/index.js similarity index 100% rename from 03_source/mobile/src/pages/DemoClubHouse/store/index.js rename to 03_source/mobile.trunk/src/pages/DemoClubHouse/store/index.js diff --git a/03_source/mobile.trunk/src/pages/DemoClubHouse/style.scss b/03_source/mobile.trunk/src/pages/DemoClubHouse/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoClubHouse/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile.trunk/src/pages/DemoColorTutorial/AppPages/Tab1.jsx b/03_source/mobile.trunk/src/pages/DemoColorTutorial/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoColorTutorial/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/DemoColorTutorial/AppPages/Tab2.jsx b/03_source/mobile.trunk/src/pages/DemoColorTutorial/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoColorTutorial/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/DemoColorTutorial/NOTES.md b/03_source/mobile.trunk/src/pages/DemoColorTutorial/NOTES.md new file mode 100644 index 0000000..c42d003 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoColorTutorial/NOTES.md @@ -0,0 +1 @@ +# REQ0125 diff --git a/03_source/mobile.trunk/src/pages/DemoColorTutorial/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk/src/pages/DemoColorTutorial/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoColorTutorial/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoColorTutorial/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk/src/pages/DemoColorTutorial/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoColorTutorial/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoColorTutorial/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk/src/pages/DemoColorTutorial/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoColorTutorial/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoColorTutorial/components/ExploreContainer.css b/03_source/mobile.trunk/src/pages/DemoColorTutorial/components/ExploreContainer.css new file mode 100644 index 0000000..e99f514 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoColorTutorial/components/ExploreContainer.css @@ -0,0 +1,24 @@ +.container { + text-align: center; + position: absolute; + left: 0; + right: 0; + top: 50%; + transform: translateY(-50%); +} + +.container strong { + font-size: 20px; + line-height: 26px; +} + +.container p { + font-size: 16px; + line-height: 22px; + color: #8c8c8c; + margin: 0; +} + +.container a { + text-decoration: none; +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoColorTutorial/components/ExploreContainer.tsx b/03_source/mobile.trunk/src/pages/DemoColorTutorial/components/ExploreContainer.tsx new file mode 100644 index 0000000..354df7b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoColorTutorial/components/ExploreContainer.tsx @@ -0,0 +1,16 @@ +import './ExploreContainer.css'; + +interface ContainerProps { + name: string; +} + +const ExploreContainer: React.FC = ({ name }) => { + return ( +
+ {name} +

Explore UI Components

+
+ ); +}; + +export default ExploreContainer; diff --git a/03_source/mobile.trunk/src/pages/DemoColorTutorial/index.tsx b/03_source/mobile.trunk/src/pages/DemoColorTutorial/index.tsx new file mode 100644 index 0000000..6090ccb --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoColorTutorial/index.tsx @@ -0,0 +1,76 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, ellipse, searchOutline, square, triangle } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +// import Tab1 from './AppPages/Tab1'; +// import Tab2 from './AppPages/Tab2'; + +import Tab1 from './pages/Tab1'; +import Tab2 from './pages/Tab2'; +import Tab3 from './pages/Tab3'; + +import './style.scss'; + +function DemoColorTutorial() { + return ( + + {/* + + + + + + + + + + + + */} + + + + + + + + + + + + + + + + + {/* + + + Dashboard + + + + Search + + */} + + {/* update path base from `/` to `/demo-color-tutorial` */} + + + Tab 1 + + + + Tab 2 + + + + Tab 3 + + + + ); +} + +export default DemoColorTutorial; diff --git a/03_source/mobile.trunk/src/pages/DemoColorTutorial/pages/Tab1.css b/03_source/mobile.trunk/src/pages/DemoColorTutorial/pages/Tab1.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk/src/pages/DemoColorTutorial/pages/Tab1.tsx b/03_source/mobile.trunk/src/pages/DemoColorTutorial/pages/Tab1.tsx new file mode 100644 index 0000000..af8f8ee --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoColorTutorial/pages/Tab1.tsx @@ -0,0 +1,75 @@ +import { + IonBadge, + IonButton, + IonButtons, + IonCheckbox, + IonContent, + IonHeader, + IonIcon, + IonItem, + IonLabel, + IonNote, + IonPage, + IonRadio, + IonTitle, + IonToggle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab1.css'; +import { chevronBackOutline } from 'ionicons/icons'; + +const Tab1: React.FC = () => { + const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + Tab 1 + + + handleBackClick()}> + + + + + + + + + Tab 1 + + + + Button + + + Toggle + + + + + Checkbox + + + + + Badge + 14 notifications + + + + Note + 3 unread + + + + ); +}; + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/DemoColorTutorial/pages/Tab2.css b/03_source/mobile.trunk/src/pages/DemoColorTutorial/pages/Tab2.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk/src/pages/DemoColorTutorial/pages/Tab2.tsx b/03_source/mobile.trunk/src/pages/DemoColorTutorial/pages/Tab2.tsx new file mode 100644 index 0000000..05458aa --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoColorTutorial/pages/Tab2.tsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab2.css'; + +const Tab2: React.FC = () => { + return ( + + + + Tab 2 + + + + + + Tab 2 + + + + + + ); +}; + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/DemoColorTutorial/pages/Tab3.css b/03_source/mobile.trunk/src/pages/DemoColorTutorial/pages/Tab3.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk/src/pages/DemoColorTutorial/pages/Tab3.tsx b/03_source/mobile.trunk/src/pages/DemoColorTutorial/pages/Tab3.tsx new file mode 100644 index 0000000..3a29b8a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoColorTutorial/pages/Tab3.tsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab3.css'; + +const Tab3: React.FC = () => { + return ( + + + + Tab 3 + + + + + + Tab 3 + + + + + + ); +}; + +export default Tab3; diff --git a/03_source/mobile.trunk/src/pages/DemoColorTutorial/style.scss b/03_source/mobile.trunk/src/pages/DemoColorTutorial/style.scss new file mode 100644 index 0000000..5430344 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoColorTutorial/style.scss @@ -0,0 +1,256 @@ +.demo-color-tutorial { + /* +Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ +*/ + + /** Ionic CSS Variables **/ + * { + /** alans color **/ + --ion-color-alans-color: #6c9400; + --ion-color-alans-color-rgb: 108, 148, 0; + --ion-color-alans-color-contrast: #ffffff; + --ion-color-alans-color-contrast-rgb: 255, 255, 255; + --ion-color-alans-color-shade: #5f8200; + --ion-color-alans-color-tint: #7b9f1a; + + /** primary **/ + --ion-color-primary: #be402f; + --ion-color-primary-rgb: 190, 64, 47; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #a73829; + --ion-color-primary-tint: #c55344; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + } + + .ion-color-alans-color { + --ion-color-base: var(--ion-color-alans-color); + --ion-color-base-rgb: var(--ion-color-alans-color-rgb); + --ion-color-contrast: var(--ion-color-alans-color-contrast); + --ion-color-contrast-rgb: var(--ion-color-alans-color-contrast-rgb); + --ion-color-shade: var(--ion-color-alans-color-shade); + --ion-color-tint: var(--ion-color-alans-color-tint); + } + + @media (prefers-color-scheme: dark) { + /* + * Dark Colors + * ------------------------------------------- + */ + + body { + --ion-color-primary: #428cff; + --ion-color-primary-rgb: 66, 140, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3a7be0; + --ion-color-primary-tint: #5598ff; + + --ion-color-secondary: #50c8ff; + --ion-color-secondary-rgb: 80, 200, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #46b0e0; + --ion-color-secondary-tint: #62ceff; + + --ion-color-tertiary: #6a64ff; + --ion-color-tertiary-rgb: 106, 100, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #5d58e0; + --ion-color-tertiary-tint: #7974ff; + + --ion-color-success: #2fdf75; + --ion-color-success-rgb: 47, 223, 117; + --ion-color-success-contrast: #000000; + --ion-color-success-contrast-rgb: 0, 0, 0; + --ion-color-success-shade: #29c467; + --ion-color-success-tint: #44e283; + + --ion-color-warning: #ffd534; + --ion-color-warning-rgb: 255, 213, 52; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0bb2e; + --ion-color-warning-tint: #ffd948; + + --ion-color-danger: #ff4961; + --ion-color-danger-rgb: 255, 73, 97; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #e04055; + --ion-color-danger-tint: #ff5b71; + + --ion-color-dark: #f4f5f8; + --ion-color-dark-rgb: 244, 245, 248; + --ion-color-dark-contrast: #000000; + --ion-color-dark-contrast-rgb: 0, 0, 0; + --ion-color-dark-shade: #d7d8da; + --ion-color-dark-tint: #f5f6f9; + + --ion-color-medium: #989aa2; + --ion-color-medium-rgb: 152, 154, 162; + --ion-color-medium-contrast: #000000; + --ion-color-medium-contrast-rgb: 0, 0, 0; + --ion-color-medium-shade: #86888f; + --ion-color-medium-tint: #a2a4ab; + + --ion-color-light: #222428; + --ion-color-light-rgb: 34, 36, 40; + --ion-color-light-contrast: #ffffff; + --ion-color-light-contrast-rgb: 255, 255, 255; + --ion-color-light-shade: #1e2023; + --ion-color-light-tint: #383a3e; + } + + /* + * iOS Dark Theme + * ------------------------------------------- + */ + + .ios body { + --ion-background-color: #000000; + --ion-background-color-rgb: 0, 0, 0; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-color-step-50: #0d0d0d; + --ion-color-step-100: #1a1a1a; + --ion-color-step-150: #262626; + --ion-color-step-200: #333333; + --ion-color-step-250: #404040; + --ion-color-step-300: #4d4d4d; + --ion-color-step-350: #595959; + --ion-color-step-400: #666666; + --ion-color-step-450: #737373; + --ion-color-step-500: #808080; + --ion-color-step-550: #8c8c8c; + --ion-color-step-600: #999999; + --ion-color-step-650: #a6a6a6; + --ion-color-step-700: #b3b3b3; + --ion-color-step-750: #bfbfbf; + --ion-color-step-800: #cccccc; + --ion-color-step-850: #d9d9d9; + --ion-color-step-900: #e6e6e6; + --ion-color-step-950: #f2f2f2; + + --ion-item-background: #000000; + + --ion-card-background: #1c1c1d; + } + + .ios ion-modal { + --ion-background-color: var(--ion-color-step-100); + --ion-toolbar-background: var(--ion-color-step-150); + --ion-toolbar-border-color: var(--ion-color-step-250); + } + + /* + * Material Design Dark Theme + * ------------------------------------------- + */ + + .md body { + --ion-background-color: #121212; + --ion-background-color-rgb: 18, 18, 18; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-border-color: #222222; + + --ion-color-step-50: #1e1e1e; + --ion-color-step-100: #2a2a2a; + --ion-color-step-150: #363636; + --ion-color-step-200: #414141; + --ion-color-step-250: #4d4d4d; + --ion-color-step-300: #595959; + --ion-color-step-350: #656565; + --ion-color-step-400: #717171; + --ion-color-step-450: #7d7d7d; + --ion-color-step-500: #898989; + --ion-color-step-550: #949494; + --ion-color-step-600: #a0a0a0; + --ion-color-step-650: #acacac; + --ion-color-step-700: #b8b8b8; + --ion-color-step-750: #c4c4c4; + --ion-color-step-800: #d0d0d0; + --ion-color-step-850: #dbdbdb; + --ion-color-step-900: #e7e7e7; + --ion-color-step-950: #f3f3f3; + + --ion-item-background: #1e1e1e; + + --ion-toolbar-background: #1f1f1f; + + --ion-tab-bar-background: #1f1f1f; + + --ion-card-background: #1e1e1e; + } + } +} diff --git a/03_source/mobile.trunk/src/pages/DemoColorTutorial/theme/variables.css b/03_source/mobile.trunk/src/pages/DemoColorTutorial/theme/variables.css new file mode 100644 index 0000000..5f9d075 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoColorTutorial/theme/variables.css @@ -0,0 +1,254 @@ +/* +Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ +*/ + +/** Ionic CSS Variables **/ +:root { + /** alans color **/ + --ion-color-alans-color: #6c9400; + --ion-color-alans-color-rgb: 108, 148, 0; + --ion-color-alans-color-contrast: #ffffff; + --ion-color-alans-color-contrast-rgb: 255, 255, 255; + --ion-color-alans-color-shade: #5f8200; + --ion-color-alans-color-tint: #7b9f1a; + + /** primary **/ + --ion-color-primary: #be402f; + --ion-color-primary-rgb: 190, 64, 47; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #a73829; + --ion-color-primary-tint: #c55344; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; +} + +.ion-color-alans-color { + --ion-color-base: var(--ion-color-alans-color); + --ion-color-base-rgb: var(--ion-color-alans-color-rgb); + --ion-color-contrast: var(--ion-color-alans-color-contrast); + --ion-color-contrast-rgb: var(--ion-color-alans-color-contrast-rgb); + --ion-color-shade: var(--ion-color-alans-color-shade); + --ion-color-tint: var(--ion-color-alans-color-tint); +} + +@media (prefers-color-scheme: dark) { + /* + * Dark Colors + * ------------------------------------------- + */ + + body { + --ion-color-primary: #428cff; + --ion-color-primary-rgb: 66, 140, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3a7be0; + --ion-color-primary-tint: #5598ff; + + --ion-color-secondary: #50c8ff; + --ion-color-secondary-rgb: 80, 200, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #46b0e0; + --ion-color-secondary-tint: #62ceff; + + --ion-color-tertiary: #6a64ff; + --ion-color-tertiary-rgb: 106, 100, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #5d58e0; + --ion-color-tertiary-tint: #7974ff; + + --ion-color-success: #2fdf75; + --ion-color-success-rgb: 47, 223, 117; + --ion-color-success-contrast: #000000; + --ion-color-success-contrast-rgb: 0, 0, 0; + --ion-color-success-shade: #29c467; + --ion-color-success-tint: #44e283; + + --ion-color-warning: #ffd534; + --ion-color-warning-rgb: 255, 213, 52; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0bb2e; + --ion-color-warning-tint: #ffd948; + + --ion-color-danger: #ff4961; + --ion-color-danger-rgb: 255, 73, 97; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #e04055; + --ion-color-danger-tint: #ff5b71; + + --ion-color-dark: #f4f5f8; + --ion-color-dark-rgb: 244, 245, 248; + --ion-color-dark-contrast: #000000; + --ion-color-dark-contrast-rgb: 0, 0, 0; + --ion-color-dark-shade: #d7d8da; + --ion-color-dark-tint: #f5f6f9; + + --ion-color-medium: #989aa2; + --ion-color-medium-rgb: 152, 154, 162; + --ion-color-medium-contrast: #000000; + --ion-color-medium-contrast-rgb: 0, 0, 0; + --ion-color-medium-shade: #86888f; + --ion-color-medium-tint: #a2a4ab; + + --ion-color-light: #222428; + --ion-color-light-rgb: 34, 36, 40; + --ion-color-light-contrast: #ffffff; + --ion-color-light-contrast-rgb: 255, 255, 255; + --ion-color-light-shade: #1e2023; + --ion-color-light-tint: #383a3e; + } + + /* + * iOS Dark Theme + * ------------------------------------------- + */ + + .ios body { + --ion-background-color: #000000; + --ion-background-color-rgb: 0, 0, 0; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-color-step-50: #0d0d0d; + --ion-color-step-100: #1a1a1a; + --ion-color-step-150: #262626; + --ion-color-step-200: #333333; + --ion-color-step-250: #404040; + --ion-color-step-300: #4d4d4d; + --ion-color-step-350: #595959; + --ion-color-step-400: #666666; + --ion-color-step-450: #737373; + --ion-color-step-500: #808080; + --ion-color-step-550: #8c8c8c; + --ion-color-step-600: #999999; + --ion-color-step-650: #a6a6a6; + --ion-color-step-700: #b3b3b3; + --ion-color-step-750: #bfbfbf; + --ion-color-step-800: #cccccc; + --ion-color-step-850: #d9d9d9; + --ion-color-step-900: #e6e6e6; + --ion-color-step-950: #f2f2f2; + + --ion-item-background: #000000; + + --ion-card-background: #1c1c1d; + } + + .ios ion-modal { + --ion-background-color: var(--ion-color-step-100); + --ion-toolbar-background: var(--ion-color-step-150); + --ion-toolbar-border-color: var(--ion-color-step-250); + } + + /* + * Material Design Dark Theme + * ------------------------------------------- + */ + + .md body { + --ion-background-color: #121212; + --ion-background-color-rgb: 18, 18, 18; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-border-color: #222222; + + --ion-color-step-50: #1e1e1e; + --ion-color-step-100: #2a2a2a; + --ion-color-step-150: #363636; + --ion-color-step-200: #414141; + --ion-color-step-250: #4d4d4d; + --ion-color-step-300: #595959; + --ion-color-step-350: #656565; + --ion-color-step-400: #717171; + --ion-color-step-450: #7d7d7d; + --ion-color-step-500: #898989; + --ion-color-step-550: #949494; + --ion-color-step-600: #a0a0a0; + --ion-color-step-650: #acacac; + --ion-color-step-700: #b8b8b8; + --ion-color-step-750: #c4c4c4; + --ion-color-step-800: #d0d0d0; + --ion-color-step-850: #dbdbdb; + --ion-color-step-900: #e7e7e7; + --ion-color-step-950: #f3f3f3; + + --ion-item-background: #1e1e1e; + + --ion-toolbar-background: #1f1f1f; + + --ion-tab-bar-background: #1f1f1f; + + --ion-card-background: #1e1e1e; + } +} diff --git a/03_source/mobile.trunk/src/pages/DemoDictionaryApp/AppPages/Tab1.jsx b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/AppPages/Tab1.jsx new file mode 100644 index 0000000..d1d807c --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/AppPages/Tab1.jsx @@ -0,0 +1,114 @@ +import { + IonButton, + IonButtons, + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import { bookOutline, chevronBackOutline, heart, search } from 'ionicons/icons'; +import { useStoreState } from 'pullstate'; +import { useRef } from 'react'; +import { WordStore } from '../store'; +import { getFavourites, getSearchCount } from '../store/Selectors'; + +const Tab1 = () => { + const router = useIonRouter(); + const pageRef = useRef(null); + const favourites = useStoreState(WordStore, getFavourites); + const searchCount = useStoreState(WordStore, getSearchCount); + + function handleBackClick() { + router.goBack(); + } + + return ( + + + + Dashboard + + + handleBackClick()}> + + + + + + + + + Dashboard + + + handleBackClick()}> + + + + + + + + + + + + + Ionic Dictionary App +

Based on the English language

+
+
+
+
+ + + + + + Did you know? +

There are 171, 146 words in the English language!

+ + Search now → + +
+
+
+
+ + + + + + + {favourites.length} + Favourites + + + + + + + + {searchCount} + Searches + + + + +
+
+
+ ); +}; + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/DemoDictionaryApp/AppPages/Tab2.jsx b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/AppPages/Tab2.jsx new file mode 100644 index 0000000..bb59c51 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/AppPages/Tab2.jsx @@ -0,0 +1,83 @@ +import { + IonButton, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState, useRef } from 'react'; +import { NoSearch } from '../components/NoSearch'; +import { NoResultsWordCard, WordCard } from '../components/WordCard'; +import { WordStore } from '../store'; +import { searchWord } from '../utils'; + +const Tab2 = () => { + const pageRef = useRef(null); + const [searchTerm, setSearchTerm] = useState(''); + const [searchResult, setSearchResult] = useState(false); + const [animatedClass, setAnimatedClass] = useState(''); + + const performSearch = async () => { + setAnimatedClass('animate__slideOutRight'); + const result = searchTerm !== '' ? await searchWord(searchTerm) : undefined; + + setTimeout(() => setSearchResult(result === undefined ? 'none' : result), 250); + setTimeout(() => setAnimatedClass('animate__slideInLeft'), 250); + + WordStore.update((s) => { + s.searchCount++; + }); + }; + + return ( + + + + Search + + + + + + Search + + + + + + + setSearchTerm(e.target.value)} + /> + + + + + Search + + + + + {searchResult && searchResult !== 'none' && ( + + )} + + {searchResult && searchResult === 'none' && ( + + )} + + {!searchResult && } + + + + ); +}; + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/DemoDictionaryApp/AppPages/Tab3.jsx b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/AppPages/Tab3.jsx new file mode 100644 index 0000000..7d8298f --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/AppPages/Tab3.jsx @@ -0,0 +1,45 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import { useStoreState } from 'pullstate'; +import { useRef, useState } from 'react'; +import { NoFavourites } from '../components/NoFavourites'; +import { WordCard } from '../components/WordCard'; +import { WordStore } from '../store'; +import { getFavourites } from '../store/Selectors'; + +const Tab3 = () => { + const pageRef = useRef(null); + const favourites = useStoreState(WordStore, getFavourites); + const [animatedClass, setAnimatedClass] = useState('animate__slideInLeft'); + + return ( + + + + Favourites + + + + + + Favourites + + + + {favourites.map((favourite, index) => { + return ( + + ); + })} + + {favourites.length < 1 && } + + + ); +}; + +export default Tab3; diff --git a/03_source/mobile.trunk/src/pages/DemoDictionaryApp/NOTES.md b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/NOTES.md new file mode 100644 index 0000000..dbb9c22 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/NOTES.md @@ -0,0 +1 @@ +# REQ0126 diff --git a/03_source/mobile.trunk/src/pages/DemoDictionaryApp/components/NoFavourites.jsx b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/components/NoFavourites.jsx new file mode 100644 index 0000000..0f446f7 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/components/NoFavourites.jsx @@ -0,0 +1,22 @@ +import { IonCol, IonLabel, IonRow } from '@ionic/react'; + +export const NoFavourites = () => ( + + + +

You don't have any favourites yet!

+

+ Any time you see the heart icon, press it to add the related word to your favourites and + quickly access it from here. +

+ +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoDictionaryApp/components/NoSearch.jsx b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/components/NoSearch.jsx new file mode 100644 index 0000000..27f6fa3 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/components/NoSearch.jsx @@ -0,0 +1,23 @@ +import { IonCol, IonLabel, IonRow } from '@ionic/react'; + +export const NoSearch = () => ( + + + +

Search for a word in the English language

+

+ This app will give you word meaninigs, phonetics, origin and also an audio clip so you can + hear what it sounds like. +

+ +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoDictionaryApp/components/WordCard.jsx b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/components/WordCard.jsx new file mode 100644 index 0000000..9ec2346 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/components/WordCard.jsx @@ -0,0 +1,129 @@ +import { + IonBadge, + IonButton, + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonIcon, + IonNote, + IonRow, + useIonModal, +} from '@ionic/react'; +import { checkmarkCircleOutline, chevronForward, closeCircleOutline } from 'ionicons/icons'; +import WordModal from './WordModal'; + +export const WordCard = ({ word, animatedClass, pageRef }) => { + const closeModal = () => { + hideModal(); + }; + + const openModal = () => { + showModal({ + presentingElement: pageRef.current, + onDidDismiss: hideModal, + }); + }; + + const [showModal, hideModal] = useIonModal(WordModal, { + dismiss: closeModal, + word, + }); + + return ( + + + + + {word.word} +
+ {word.meanings && + word.meanings.map((meaning, index) => { + return ( + + {meaning.partOfSpeech} +   + + ); + })} +
+ {word.origin} + + + + {word.meanings.length} + meanings + + + + {word.phonetics.length} + phonetics + + + + + + audio + + + + + + + View  + + + + +
+
+
+
+ ); +}; + +export const NoResultsWordCard = ({ word, animatedClass }) => { + return ( + + + + + Whoops... +
+ no results  + found +
+ + No results have been found for your search criteria! Please try another word. + + + + + 0 + meanings + + + + 0 + phonetics + + + + + + audio + + +
+
+
+
+ ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoDictionaryApp/components/WordCardHeading.jsx b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/components/WordCardHeading.jsx new file mode 100644 index 0000000..ff23fc0 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/components/WordCardHeading.jsx @@ -0,0 +1,10 @@ +import { IonText } from "@ionic/react"; + +export const WordCardHeading = ({ text }) => ( + +
+ +

{ text }

+
+
+); \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoDictionaryApp/components/WordMeaning.jsx b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/components/WordMeaning.jsx new file mode 100644 index 0000000..1f5d3b7 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/components/WordMeaning.jsx @@ -0,0 +1,18 @@ +import { IonBadge } from '@ionic/react'; + +export const WordMeaning = ({ meaning, index }) => ( +
0 ? 'ion-padding-top' : ''}> + + {meaning.partOfSpeech} + +
+ {meaning.definitions.map((definition, index2) => { + return ( +

0 ? 'ion-padding-top' : ''}> + {index2 + 1}.  + {definition.definition} +

+ ); + })} +
+); diff --git a/03_source/mobile.trunk/src/pages/DemoDictionaryApp/components/WordModal.jsx b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/components/WordModal.jsx new file mode 100644 index 0000000..0896a4b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/components/WordModal.jsx @@ -0,0 +1,122 @@ +import { + IonBadge, + IonButton, + IonButtons, + IonCard, + IonCardContent, + IonCardHeader, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonNote, + IonPage, + IonRow, + IonText, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { heart, heartOutline, play } from 'ionicons/icons'; +import { useStoreState } from 'pullstate'; +import { WordStore } from '../store'; +import { getFavourites } from '../store/Selectors'; +import { addToFavourites } from '../store/WordStore'; +import { WordCardHeading } from './WordCardHeading'; +import { WordMeaning } from './WordMeaning'; + +const WordModal = ({ dismiss, word }) => { + const favourites = useStoreState(WordStore, getFavourites); + const isFavourite = favourites.includes(word); + const audio = word.phonetics[0] ? word.phonetics[0].audio : false; + + const playAudio = () => { + const audioElement = new Audio(`https:${audio}`); + audioElement.play(); + }; + + return ( + + + + + addToFavourites(word)}> + + + + View Word + + + Close + + + + + + + + + + {word.word} +
+ {word.meanings && + word.meanings.map((meaning, index) => { + return ( + + + {meaning.partOfSpeech} + +   + + ); + })} +
+ {word.origin} +
+
+
+
+ + {audio && ( + + + + + + + + + + + + + + + + + + )} + + + + + + + + {word.meanings && + word.meanings.map((meaning, index) => { + return ; + })} + + + + +
+
+
+ ); +}; + +export default WordModal; diff --git a/03_source/mobile.trunk/src/pages/DemoDictionaryApp/index.tsx b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/index.tsx new file mode 100644 index 0000000..8e1f78b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/index.tsx @@ -0,0 +1,44 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, heart, search, searchOutline, statsChart } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; +import Tab3 from './AppPages/Tab3'; + +import './style.scss'; + +function DemoDictionaryApp() { + return ( + + + + + + + + + + + + + + + {/* */} + + + + + + + + + + + + + ); +} + +export default DemoDictionaryApp; diff --git a/03_source/mobile.trunk/src/pages/DemoDictionaryApp/store/Selectors.js b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/store/Selectors.js new file mode 100644 index 0000000..eeb9eb0 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/store/Selectors.js @@ -0,0 +1,8 @@ +import { createSelector } from 'reselect'; + +const getState = state => state; + +// General getters +export const getFavourites = createSelector(getState, state => state.favourites); +export const getPopularWords = createSelector(getState, state => state.popularWords); +export const getSearchCount = createSelector(getState, state => state.searchCount); \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoDictionaryApp/store/WordStore.js b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/store/WordStore.js new file mode 100644 index 0000000..374c814 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/store/WordStore.js @@ -0,0 +1,29 @@ +import { Store } from "pullstate"; + +const WordStore = new Store({ + + favourites: [], + popularWords: [], + searchCount: 0 +}); + +export default WordStore; + +export const addToFavourites = (passedWord) => { + + const currentFavourites = WordStore.getRawState().favourites; + const added = !currentFavourites.includes(passedWord); + + WordStore.update(s => { + + if (currentFavourites.includes(passedWord)) { + + s.favourites = currentFavourites.filter(word => word !== passedWord); + } else { + + s.favourites = [ ...s.favourites, passedWord ]; + } + }); + + return added; +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoDictionaryApp/store/index.js b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/store/index.js new file mode 100644 index 0000000..b232728 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/store/index.js @@ -0,0 +1 @@ +export { default as WordStore } from "./WordStore"; diff --git a/03_source/mobile.trunk/src/pages/DemoDictionaryApp/style.scss b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/style.scss new file mode 100644 index 0000000..bb952e0 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/style.scss @@ -0,0 +1,240 @@ +/* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + +/** Ionic CSS Variables **/ +.demo-dictionary-app { + * { + /** primary **/ + --ion-color-primary: #953cd0; + --ion-color-primary-rgb: 149, 60, 208; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #8335b7; + --ion-color-primary-tint: #a050d5; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #ffffff; + --ion-color-light-rgb: 255, 255, 255; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #e0e0e0; + --ion-color-light-tint: #ffffff; + + --ion-background-color: #1e1b27 !important; + --ion-tab-bar-color-selected: #953cd0; + --ion-tab-bar-color: #412f6e; + --ion-text-color: white; + --ion-tab-bar-background: #191620; + --ion-toolbar-background: #191620 !important; + --ion-item-background: #000000 !important; + + --ion-card-background: #272333 !important; + --ion-modal-background: #272333 !important; + } + + ion-tab-bar { + --border-style: none; + border: none; + } + + /* + * Dark Colors + * ------------------------------------------- + */ + + ion-modal { + -ms-overflow-style: none; /* for Internet Explorer, Edge */ + scrollbar-width: none; /* for Firefox */ + overflow-y: scroll; + } + + body { + overflow: hidden !important; + --ion-color-primary: #953cd0; + --ion-color-primary-rgb: 149, 60, 208; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #8335b7; + --ion-color-primary-tint: #a050d5; + + --ion-color-secondary: #50c8ff; + --ion-color-secondary-rgb: 80, 200, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #46b0e0; + --ion-color-secondary-tint: #62ceff; + + --ion-color-tertiary: #6a64ff; + --ion-color-tertiary-rgb: 106, 100, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #5d58e0; + --ion-color-tertiary-tint: #7974ff; + + --ion-color-success: #2fdf75; + --ion-color-success-rgb: 47, 223, 117; + --ion-color-success-contrast: #000000; + --ion-color-success-contrast-rgb: 0, 0, 0; + --ion-color-success-shade: #29c467; + --ion-color-success-tint: #44e283; + + --ion-color-warning: #ffd534; + --ion-color-warning-rgb: 255, 213, 52; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0bb2e; + --ion-color-warning-tint: #ffd948; + + --ion-color-danger: #ff4961; + --ion-color-danger-rgb: 255, 73, 97; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #e04055; + --ion-color-danger-tint: #ff5b71; + + --ion-color-dark: #f4f5f8; + --ion-color-dark-rgb: 244, 245, 248; + --ion-color-dark-contrast: #000000; + --ion-color-dark-contrast-rgb: 0, 0, 0; + --ion-color-dark-shade: #d7d8da; + --ion-color-dark-tint: #f5f6f9; + + --ion-color-medium: #989aa2; + --ion-color-medium-rgb: 152, 154, 162; + --ion-color-medium-contrast: #000000; + --ion-color-medium-contrast-rgb: 0, 0, 0; + --ion-color-medium-shade: #86888f; + --ion-color-medium-tint: #a2a4ab; + + --ion-color-light: #ffffff; + --ion-color-light-rgb: 255, 255, 255; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #e0e0e0; + --ion-color-light-tint: #ffffff; + } + + /* + * iOS Dark Theme + * ------------------------------------------- + */ + + .ios body { + /* --ion-background-color: #000000; */ + /* --ion-background-color-rgb: 0,0,0; */ + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-color-step-50: #0d0d0d; + --ion-color-step-100: #1a1a1a; + --ion-color-step-150: #262626; + --ion-color-step-200: #333333; + --ion-color-step-250: #404040; + --ion-color-step-300: #4d4d4d; + --ion-color-step-350: #595959; + --ion-color-step-400: #666666; + --ion-color-step-450: #737373; + --ion-color-step-500: #808080; + --ion-color-step-550: #8c8c8c; + --ion-color-step-600: #999999; + --ion-color-step-650: #a6a6a6; + --ion-color-step-700: #b3b3b3; + --ion-color-step-750: #bfbfbf; + --ion-color-step-800: #cccccc; + --ion-color-step-850: #d9d9d9; + --ion-color-step-900: #e6e6e6; + --ion-color-step-950: #f2f2f2; + } + + /* + * Material Design Dark Theme + * ------------------------------------------- + */ + + .md body { + /* --ion-background-color: #121212; */ + /* --ion-background-color-rgb: 18,18,18; */ + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-border-color: #222222; + + --ion-color-step-50: #1e1e1e; + --ion-color-step-100: #2a2a2a; + --ion-color-step-150: #363636; + --ion-color-step-200: #414141; + --ion-color-step-250: #4d4d4d; + --ion-color-step-300: #595959; + --ion-color-step-350: #656565; + --ion-color-step-400: #717171; + --ion-color-step-450: #7d7d7d; + --ion-color-step-500: #898989; + --ion-color-step-550: #949494; + --ion-color-step-600: #a0a0a0; + --ion-color-step-650: #acacac; + --ion-color-step-700: #b8b8b8; + --ion-color-step-750: #c4c4c4; + --ion-color-step-800: #d0d0d0; + --ion-color-step-850: #dbdbdb; + --ion-color-step-900: #e7e7e7; + --ion-color-step-950: #f3f3f3; + } +} diff --git a/03_source/mobile.trunk/src/pages/DemoDictionaryApp/utils.js b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/utils.js new file mode 100644 index 0000000..fa5327f --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoDictionaryApp/utils.js @@ -0,0 +1,21 @@ +import { WordStore } from './store'; + +const API_URL = 'https://api.dictionaryapi.dev/api/v2/entries/en/'; + +export const searchWord = async (word, returnOne = true) => { + const response = await fetch(`${API_URL}${word.toLowerCase()}`); + const data = await response.json(); + + return returnOne ? data[0] : data; +}; + +export const fetchPopularWords = async () => { + const words = ['mobile', 'applications', 'ionic', 'framework']; + + words.forEach(async (word) => { + const wordData = await searchWord(word, false); + WordStore.update((s) => { + s.popularWords = [...s.popularWords, wordData[0]]; + }); + }); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoEcommerceExample/AppPages/Tab1.jsx b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/DemoEcommerceExample/AppPages/Tab2.jsx b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/DemoEcommerceExample/NOTES.md b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/NOTES.md new file mode 100644 index 0000000..056b5dc --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/NOTES.md @@ -0,0 +1 @@ +# REQ0127 diff --git a/03_source/mobile.trunk/src/pages/DemoEcommerceExample/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoEcommerceExample/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoEcommerceExample/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoEcommerceExample/components/ProductCard.module.css b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/components/ProductCard.module.css new file mode 100644 index 0000000..7cb75fd --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/components/ProductCard.module.css @@ -0,0 +1,72 @@ +.categoryPage ion-toolbar { + + --border-style: none; +} + +.categoryCard { + + display: flex; + flex-direction: column; + justify-content: center; + align-content: center; + align-items: center; + /* min-height: 20rem !important; */ +} + +.productCardActions { + + display: flex; + flex-direction: row; + justify-content: space-between; + width: 100%; + margin-bottom: 1rem; +} + +.productCardAction { + + font-size: 1.1rem; +} + +.productCardHeader { + + min-height: 17rem; +} + +.productCardHeader p { + + font-size: 0.8rem; + padding: 0; + margin: 0; + margin-top: 0.75rem; +} + +.categoryCardContent { + + display: flex; + flex-direction: column; +} + +.categoryCardContent ion-button { + + height: 1.5rem; + font-size: 0.8rem; +} + +.categoryCardContent p { + + font-size: 0.8rem; + padding: 0; + margin: 0; +} + +.categoryCard img { + + /* border-radius: 5px; */ + /* padding: 1rem; */ +} + +.productPrice { + + display: flex; + flex-direction: row; +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoEcommerceExample/components/ProductCard.tsx b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/components/ProductCard.tsx new file mode 100644 index 0000000..e3a0266 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/components/ProductCard.tsx @@ -0,0 +1,105 @@ +import { IonButton, IonCard, IonCardContent, IonCardHeader, IonCol, IonIcon } from '@ionic/react'; +import { arrowRedoOutline, cart, cartOutline, heart, heartOutline } from 'ionicons/icons'; +import { useEffect, useRef, useState } from 'react'; +import { addToCart } from '../data/CartStore'; +import { addToFavourites, FavouritesStore } from '../data/FavouritesStore'; +import styles from './ProductCard.module.css'; + +const ProductCard = (props): React.FC => { + const { product, category, index, cartRef } = props; + const favourites = FavouritesStore.useState((s) => s.product_ids); + + const productCartRef = useRef(); + const productFavouriteRef = useRef(); + const [isFavourite, setIsFavourite] = useState(false); + + useEffect(() => { + const tempIsFavourite = favourites.find((f) => f === `${category.slug}/${product.id}`); + setIsFavourite(tempIsFavourite ? true : false); + }, [props.product, favourites]); + + const addProductToFavourites = (e, categorySlug, productID) => { + e.preventDefault(); + e.stopPropagation(); + addToFavourites(categorySlug, productID); + + productFavouriteRef.current.style.display = ''; + productFavouriteRef.current.classList.add('animate__fadeOutTopRight'); + + setTimeout(() => { + if (productCartRef.current) { + productFavouriteRef.current.classList.remove('animate__fadeOutTopRight'); + productFavouriteRef.current.style.display = 'none'; + } + }, 500); + }; + + const addProductToCart = (e, categorySlug, productID) => { + e.preventDefault(); + e.stopPropagation(); + + productCartRef.current.style.display = ''; + productCartRef.current.classList.add('animate__fadeOutUp'); + + setTimeout(() => { + cartRef.current.classList.add('animate__tada'); + addToCart(categorySlug, productID); + + setTimeout(() => { + cartRef.current.classList.remove('animate__tada'); + productCartRef.current.style.display = 'none'; + }, 500); + }, 500); + }; + + return ( + + + +
+ addProductToFavourites(e, category.slug, product.id)} + /> + + +
+ product pic +

{product.name}

+
+ + +
+ + {product.price} + + addProductToCart(e, category.slug, product.id)}> + + + + +
+
+
+
+ ); +}; + +export default ProductCard; diff --git a/03_source/mobile.trunk/src/pages/DemoEcommerceExample/data/CartStore.ts b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/data/CartStore.ts new file mode 100644 index 0000000..c9af57b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/data/CartStore.ts @@ -0,0 +1,18 @@ +import { Store } from 'pullstate'; + +export const CartStore = new Store({ + total: 0, + product_ids: [], +}); + +export const addToCart = (categorySlug, productID) => { + CartStore.update((s) => { + s.product_ids = [...s.product_ids, `${categorySlug}/${parseInt(productID)}`]; + }); +}; + +export const removeFromCart = (productIndex) => { + CartStore.update((s) => { + s.product_ids.splice(productIndex, 1); + }); +}; diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/data/FavouritesStore.ts b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/data/FavouritesStore.ts similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/data/FavouritesStore.ts rename to 03_source/mobile.trunk/src/pages/DemoEcommerceExample/data/FavouritesStore.ts diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/data/ProductStore.ts b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/data/ProductStore.ts similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/data/ProductStore.ts rename to 03_source/mobile.trunk/src/pages/DemoEcommerceExample/data/ProductStore.ts diff --git a/03_source/mobile.trunk/src/pages/DemoEcommerceExample/data/fetcher.ts b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/data/fetcher.ts new file mode 100644 index 0000000..4d026ff --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/data/fetcher.ts @@ -0,0 +1,57 @@ +import { ProductStore } from './ProductStore'; + +export const fetchData = async () => { + const json = [ + 'beds.json', + 'armchairs.json', + 'coffee_tables.json', + 'cushions.json', + 'floor_lamps.json', + 'office_chairs.json', + ]; + + var products = []; + + json.forEach(async (category) => { + const products = await fetchProducts(category); + + let categoryName = category.replace('.json', ''); + categoryName = categoryName.replace('_', ' '); + categoryName = uppercaseWords(categoryName); + + const productCategory = { + name: categoryName, + slug: category.replace('.json', ''), + cover: products[6].image, + products, + }; + + ProductStore.update((s) => { + s.products = [...s.products, productCategory]; + }); + }); + + return products; +}; + +const fetchProducts = async (category) => { + const response = await fetch(`products/${category}`); + const data = await response.json(); + + // Set a product id + await data.forEach((d, i) => { + d.id = i + 1; + }); + + return data; +}; + +const uppercaseWords = (words) => { + words = words + .toLowerCase() + .split(' ') + .map((s) => s.charAt(0).toUpperCase() + s.substring(1)) + .join(' '); + + return words; +}; diff --git a/03_source/mobile/src/pages/DemoEcommerceExample/index.tsx b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoEcommerceExample/index.tsx rename to 03_source/mobile.trunk/src/pages/DemoEcommerceExample/index.tsx diff --git a/03_source/mobile.trunk/src/pages/DemoEcommerceExample/module.d.ts b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/module.d.ts new file mode 100644 index 0000000..d774364 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/module.d.ts @@ -0,0 +1,4 @@ +declare module '*.module.css' { + const classes: { readonly [key: string]: string }; + export default classes; +} diff --git a/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/CartProducts.module.css b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/CartProducts.module.css new file mode 100644 index 0000000..6be2867 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/CartProducts.module.css @@ -0,0 +1,31 @@ +.cartCheckout { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + margin: 1rem; +} + +.cartFooter { + border-top: 2px solid rgb(200, 200, 200); + background-color: white; +} + +.cartCheckout ion-card-subtitle { + font-size: 1.3rem; +} + +.cartItem ion-avatar { + height: 4rem; + width: 4rem; +} + +.cartSlider:not(:nth-child(1)) { + border-top: 2px solid rgb(236, 236, 236); +} + +.cartActions { + display: flex; + flex-direction: column; +} diff --git a/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/CartProducts.tsx b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/CartProducts.tsx new file mode 100644 index 0000000..208eb31 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/CartProducts.tsx @@ -0,0 +1,161 @@ +import { + IonAvatar, + IonBadge, + IonButton, + IonButtons, + IonCardSubtitle, + IonCol, + IonContent, + IonFooter, + IonHeader, + IonIcon, + IonImg, + IonItem, + IonItemOption, + IonItemOptions, + IonItemSliding, + IonLabel, + IonList, + IonNote, + IonPage, + IonRow, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { cart, checkmarkSharp, chevronBackOutline, trashOutline } from 'ionicons/icons'; +import React, { useEffect, useRef, useState } from 'react'; +import { CartStore, removeFromCart } from '../data/CartStore'; +import { ProductStore } from '../data/ProductStore'; + +import styles from './CartProducts.module.css'; + +const CartProducts = (): React.JSX.Element => { + const cartRef = useRef(null); + const products = ProductStore.useState((s) => s.products); + const shopCart = CartStore.useState((s) => s.product_ids); + const [cartProducts, setCartProducts] = useState([]); + const [amountLoaded, setAmountLoaded] = useState(6); + + const [total, setTotal] = useState(0); + + useEffect(() => { + const getCartProducts = () => { + setCartProducts([]); + setTotal(0); + + shopCart.forEach((product) => { + var favouriteParts = product.split('/'); + var categorySlug = favouriteParts[0]; + var productID = favouriteParts[1]; + + const tempCategory = products.filter((p) => p.slug === categorySlug)[0]; + const tempProduct = tempCategory.products.filter( + (p) => parseInt(p.id) === parseInt(productID) + )[0]; + + const tempCartProduct = { + category: tempCategory, + product: tempProduct, + }; + + setTotal((prevTotal) => prevTotal + parseInt(tempProduct.price.replace('£', ''))); + setCartProducts((prevSearchResults) => [...prevSearchResults, tempCartProduct]); + }); + }; + + getCartProducts(); + }, [shopCart]); + + const fetchMore = async (e) => { + // Increment the amount loaded by 6 for the next iteration + setAmountLoaded((prevAmount) => prevAmount + 6); + e.target.complete(); + }; + + const removeProductFromCart = async (index) => { + removeFromCart(index); + }; + + return ( + + + + + + +  Categories + + + Cart + + + {shopCart.length} + + + + + + + + + + + + {cartProducts && cartProducts.length}{' '} + {cartProducts.length > 1 || cartProducts.length === 0 ? ' products' : ' product'}{' '} + found + + + + + + {cartProducts && + cartProducts.map((product, index) => { + if (index <= amountLoaded) { + return ( + + + + + + +

{product.category.name}

+

{product.product.name}

+
+ +
+ {product.product.price} +
+
+ + + removeProductFromCart(index)} + > + + + +
+ ); + } + })} +
+
+ + +
+ £{total.toFixed(2)} + + + +  Checkout + +
+
+
+ ); +}; + +export default CartProducts; diff --git a/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/CategoryProducts.module.css b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/CategoryProducts.module.css new file mode 100644 index 0000000..e42a0b1 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/CategoryProducts.module.css @@ -0,0 +1,10 @@ +.categoryPage ion-toolbar { + + --border-style: none; +} + +.search { + + --background: rgb(240, 240, 240); + --color: black; +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/CategoryProducts.tsx b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/CategoryProducts.tsx new file mode 100644 index 0000000..ccbd65a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/CategoryProducts.tsx @@ -0,0 +1,134 @@ +import { + IonBadge, + IonButton, + IonButtons, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonInfiniteScroll, + IonInfiniteScrollContent, + IonNote, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { cart, chevronBackOutline, searchOutline } from 'ionicons/icons'; +import { useEffect, useRef, useState } from 'react'; +import { useParams } from 'react-router'; +import ProductCard from '../components/ProductCard'; + +import { CartStore } from '../data/CartStore'; +import { ProductStore } from '../data/ProductStore'; + +import styles from './CategoryProducts.module.css'; + +const CategoryProducts = () => { + const params = useParams(); + const cartRef = useRef(); + const products = ProductStore.useState((s) => s.products); + const shopCart = CartStore.useState((s) => s.product_ids); + const [category, setCategory] = useState({}); + const [searchResults, setsearchResults] = useState([]); + const [amountLoaded, setAmountLoaded] = useState(6); + + useEffect(() => { + const categorySlug = params.slug; + const tempCategory = products.filter((p) => p.slug === categorySlug)[0]; + setCategory(tempCategory); + setsearchResults(tempCategory.products); + }, [params.slug]); + + const fetchMore = async (e) => { + // Increment the amount loaded by 6 for the next iteration + setAmountLoaded((prevAmount) => prevAmount + 6); + e.target.complete(); + }; + + const search = async (e) => { + const searchVal = e.target.value; + + if (searchVal !== '') { + const tempResults = category.products.filter((p) => + p.name.toLowerCase().includes(searchVal.toLowerCase()) + ); + setsearchResults(tempResults); + } else { + setsearchResults(category.products); + } + }; + + return ( + + + + + + +  Categories + + + {category && category.name} + + + {shopCart.length} + + + + + + + + + + + + + + + {searchResults && searchResults.length}{' '} + {searchResults.length > 1 || searchResults.length === 0 ? ' products' : ' product'}{' '} + found + + + + + + {searchResults && + searchResults.map((product, index) => { + if (index <= amountLoaded && product.image) { + return ( + + ); + } + })} + + + + + + + + + ); +}; + +export default CategoryProducts; diff --git a/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/FavouriteProducts.tsx b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/FavouriteProducts.tsx new file mode 100644 index 0000000..7543053 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/FavouriteProducts.tsx @@ -0,0 +1,131 @@ +import { + IonBadge, + IonButton, + IonButtons, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonInfiniteScroll, + IonInfiniteScrollContent, + IonNote, + IonPage, + IonRow, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { cart, chevronBackOutline } from 'ionicons/icons'; +import { useEffect, useRef, useState } from 'react'; +import ProductCard from '../components/ProductCard'; +import { CartStore } from '../data/CartStore'; +import { FavouritesStore } from '../data/FavouritesStore'; +import { ProductStore } from '../data/ProductStore'; + +import styles from './CategoryProducts.module.css'; + +const FavouriteProducts = () => { + const cartRef = useRef(null); + const products = ProductStore.useState((s) => s.products); + const favourites = FavouritesStore.useState((s) => s.product_ids); + const shopCart = CartStore.useState((s) => s.product_ids); + const [searchResults, setSearchResults] = useState([]); + const [amountLoaded, setAmountLoaded] = useState(6); + + useEffect(() => { + const getFavourites = () => { + setSearchResults([]); + + favourites.forEach((favourite) => { + var favouriteParts = favourite.split('/'); + var categorySlug = favouriteParts[0]; + var productID = favouriteParts[1]; + + const tempCategory = products.filter((p) => p.slug === categorySlug)[0]; + const tempProduct = tempCategory.products.filter( + (p) => parseInt(p.id) === parseInt(productID) + )[0]; + + const tempFavourite = { + category: tempCategory, + product: tempProduct, + }; + + setSearchResults((prevSearchResults) => [...prevSearchResults, tempFavourite]); + }); + }; + + getFavourites(); + }, [favourites]); + + const fetchMore = async (e) => { + // Increment the amount loaded by 6 for the next iteration + setAmountLoaded((prevAmount) => prevAmount + 6); + e.target.complete(); + }; + + return ( + + + + + + +  Categories + + + Favourites + + + {shopCart.length} + + + + + + + + + + + + + {searchResults && searchResults.length}{' '} + {searchResults.length > 1 || searchResults.length === 0 + ? ' favourites' + : ' favourite'}{' '} + found + + + + + + {searchResults && + searchResults.map((product, index) => { + if (index <= amountLoaded) { + return ( + + ); + } + })} + + + + + + + + + ); +}; + +export default FavouriteProducts; diff --git a/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/Home.module.css b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/Home.module.css new file mode 100644 index 0000000..e543907 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/Home.module.css @@ -0,0 +1,42 @@ +.homePage ion-toolbar { + + --border-style: none; +} + +.logo { + + margin-top: 0.25rem; + color: var(--ion-color-primary); +} + +.categoryCard, +.categoryCardContent { + + display: flex; + flex-direction: column; + justify-content: center; + align-content: center; + align-items: center; +} + +.categoryCardContent ion-button { + + height: 1.5rem; + font-size: 0.8rem; +} + +.categoryCardContent { + + background-color: rgb(238, 238, 238); +} + +.categoryCardContent ion-card-subtitle { + + /* color: rgb(78, 78, 78); */ +} + +.categoryCard img { + + /* border-radius: 5px; */ + padding: 1rem; +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/Home.tsx b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/Home.tsx new file mode 100644 index 0000000..25cb577 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/Home.tsx @@ -0,0 +1,88 @@ +import { useState } from 'react'; +import { + IonBadge, + IonButton, + IonButtons, + IonCard, + IonCardContent, + IonCardSubtitle, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, +} from '@ionic/react'; + +import styles from './Home.module.css'; +import { cart, heart } from 'ionicons/icons'; + +import { ProductStore } from '../data/ProductStore'; +import { FavouritesStore } from '../data/FavouritesStore'; +import { CartStore } from '../data/CartStore'; + +const Home = () => { + const products = ProductStore.useState((s) => s.products); + const favourites = FavouritesStore.useState((s) => s.product_ids); + const shopCart = CartStore.useState((s) => s.product_ids); + + return ( + + + + Categories + + + Ionic Furniture + + + + {favourites.length} + + + + + {shopCart.length} + + + + + + + + + + + Categories + + + + + + {products.map((category, index) => { + return ( + + + category cover + + + {category.name} + + + + ); + })} + + + + + ); +}; + +export default Home; diff --git a/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/Product.module.css b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/Product.module.css new file mode 100644 index 0000000..253b7ed --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/Product.module.css @@ -0,0 +1,66 @@ +.categoryPage ion-toolbar { + + --border-style: none; +} + +.categoryCard { + + display: flex; + flex-direction: column; + justify-content: center; + align-content: center; + align-items: center; + text-align: center; +} + +.productCardActions { + + display: flex; + flex-direction: row; + justify-content: space-between; + width: 100%; + margin-bottom: 1rem; +} + +.productCardAction { + + font-size: 1.1rem; +} + +.productCardHeader { + + min-height: 17rem; +} + +.productCardHeader p { + + font-size: 1.2rem; + padding: 0; + margin: 0; + margin-top: 0.75rem; +} + +.categoryCardContent { + + display: flex; + flex-direction: column; + text-align: center; +} + +.categoryCardContent ion-button { + + font-size: 0.8rem; +} + +.categoryCardContent p { + + font-size: 1.5rem; + padding: 0; + margin: 0; +} + +.productPrice { + + display: flex; + flex-direction: row; +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/Product.tsx b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/Product.tsx new file mode 100644 index 0000000..c7c7f18 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/pages/Product.tsx @@ -0,0 +1,210 @@ +import { + IonBadge, + IonButton, + IonButtons, + IonCard, + IonCardContent, + IonCardHeader, + IonCardSubtitle, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { + arrowRedoOutline, + cart, + cartOutline, + chevronBackOutline, + heart, + heartOutline, +} from 'ionicons/icons'; +import { useEffect, useRef, useState } from 'react'; +import { useParams } from 'react-router'; +import ProductCard from '../components/ProductCard'; +import { addToCart, CartStore } from '../data/CartStore'; +import { addToFavourites, FavouritesStore } from '../data/FavouritesStore'; +import { ProductStore } from '../data/ProductStore'; + +import styles from './Product.module.css'; + +const Product = () => { + const params = useParams(); + const cartRef = useRef(null); + const products = ProductStore.useState((s) => s.products); + const favourites = FavouritesStore.useState((s) => s.product_ids); + const [isFavourite, setIsFavourite] = useState(false); + const shopCart = CartStore.useState((s) => s.product_ids); + const [product, setProduct] = useState({}); + const [category, setCategory] = useState({}); + + useEffect(() => { + const categorySlug = params.slug; + const productID = params.id; + const tempCategory = products.filter((p) => p.slug === categorySlug)[0]; + const tempProduct = tempCategory.products.filter( + (p) => parseInt(p.id) === parseInt(productID) + )[0]; + + const tempIsFavourite = favourites.find((f) => f === `${categorySlug}/${productID}`); + + setIsFavourite(tempIsFavourite); + setCategory(tempCategory); + setProduct(tempProduct); + }, [params.slug, params.id]); + + useEffect(() => { + const tempIsFavourite = favourites.find((f) => f === `${category.slug}/${product.id}`); + setIsFavourite(tempIsFavourite ? true : false); + }, [favourites, product]); + + const addProductToFavourites = (e, categorySlug, productID) => { + e.preventDefault(); + addToFavourites(categorySlug, productID); + + document.getElementById( + `placeholder_favourite_product_${categorySlug}_${productID}` + ).style.display = ''; + document + .getElementById(`placeholder_favourite_product_${categorySlug}_${productID}`) + .classList.add('animate__fadeOutTopRight'); + }; + + const addProductToCart = (e, categorySlug, productID) => { + e.preventDefault(); + + document.getElementById(`placeholder_cart_${categorySlug}_${productID}`).style.display = ''; + document + .getElementById(`placeholder_cart_${categorySlug}_${productID}`) + .classList.add('animate__fadeOutUp'); + + setTimeout(() => { + cartRef.current.classList.add('animate__tada'); + addToCart(categorySlug, productID); + + setTimeout(() => { + cartRef.current.classList.remove('animate__tada'); + }, 500); + }, 500); + }; + + return ( + + + + + + +  {category.name} + + + + View Product + + + {shopCart.length} + + + + + + + + + + + + + +
+ addProductToFavourites(e, category.slug, product.id)} + /> + + +
+ product pic +

{product.name}

+
+ + +
+ + {product.price} + + addProductToCart(e, category.slug, product.id)} + > + +   Add to Cart + + + +
+
+
+
+
+ + + + Similar products... + + + + + {category && + category.products && + category.products.map((similar, index) => { + if (similar.id !== product.id && product.image && index < 4) { + return ( + + ); + } + })} + +
+
+
+ ); +}; + +export default Product; diff --git a/03_source/mobile.trunk/src/pages/DemoEcommerceExample/style.scss b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile.trunk/src/pages/DemoEcommerceExample/theme/variables.css b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/theme/variables.css new file mode 100644 index 0000000..985de6f --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoEcommerceExample/theme/variables.css @@ -0,0 +1,81 @@ +/* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + +/** Ionic CSS Variables **/ +:root { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + + --ion-toolbar-color: black; + --ion-grid-column-padding: 0rem; + /* --ion-toolbar-background: var(--ion-color-warning); */ +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoFacebookClone/AppPages/Tab1.jsx b/03_source/mobile.trunk/src/pages/DemoFacebookClone/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFacebookClone/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/DemoFacebookClone/AppPages/Tab2.jsx b/03_source/mobile.trunk/src/pages/DemoFacebookClone/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFacebookClone/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/DemoFacebookClone/NOTES.md b/03_source/mobile.trunk/src/pages/DemoFacebookClone/NOTES.md new file mode 100644 index 0000000..bb7672c --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFacebookClone/NOTES.md @@ -0,0 +1 @@ +# REQ0128 diff --git a/03_source/mobile.trunk/src/pages/DemoFacebookClone/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk/src/pages/DemoFacebookClone/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFacebookClone/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoFacebookClone/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk/src/pages/DemoFacebookClone/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFacebookClone/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoFacebookClone/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk/src/pages/DemoFacebookClone/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFacebookClone/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoFacebookClone/components/ExploreContainer.css b/03_source/mobile.trunk/src/pages/DemoFacebookClone/components/ExploreContainer.css new file mode 100644 index 0000000..e99f514 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFacebookClone/components/ExploreContainer.css @@ -0,0 +1,24 @@ +.container { + text-align: center; + position: absolute; + left: 0; + right: 0; + top: 50%; + transform: translateY(-50%); +} + +.container strong { + font-size: 20px; + line-height: 26px; +} + +.container p { + font-size: 16px; + line-height: 22px; + color: #8c8c8c; + margin: 0; +} + +.container a { + text-decoration: none; +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoFacebookClone/components/ExploreContainer.tsx b/03_source/mobile.trunk/src/pages/DemoFacebookClone/components/ExploreContainer.tsx new file mode 100644 index 0000000..f003f7f --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFacebookClone/components/ExploreContainer.tsx @@ -0,0 +1,25 @@ +import './ExploreContainer.css'; + +interface ContainerProps { + name: string; +} + +const ExploreContainer: React.FC = ({ name }) => { + return ( +
+ {name} +

+ Explore{' '} + + UI Components + +

+
+ ); +}; + +export default ExploreContainer; diff --git a/03_source/mobile.trunk/src/pages/DemoFacebookClone/components/Post.tsx b/03_source/mobile.trunk/src/pages/DemoFacebookClone/components/Post.tsx new file mode 100644 index 0000000..275e4ad --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFacebookClone/components/Post.tsx @@ -0,0 +1,96 @@ +import { + IonAvatar, + IonButton, + IonCol, + IonIcon, + IonImg, + IonItem, + IonLabel, + IonRow, + IonText, +} from '@ionic/react'; +import { + arrowRedoOutline, + chatboxOutline, + ellipsisHorizontal, + globe, + heart, + thumbsUp, + thumbsUpOutline, +} from 'ionicons/icons'; +import '../pages/Tab2.css'; +import React from 'react'; + +const Post = (props): React.JSX.Element => { + const { post } = props; + + return ( + + +
+ + + + + + +

{post.name}

+

+ {post.sponsored ? 'Sponsored' : post.time} +    + +

+
+ + +
+ + +

{post.message}

+
+ + {post.image && } + + {post.sponsored && ( +
+ +

ionicframework.com

+

Start building apps today!

+
+ + Learn more +
+ )} + +
+
+ + +
+ + {post.sponsored &&

{post.views} Views

} +
+ +
+ + + Like + + + + + Comment + + + + + Share + +
+
+
+
+ ); +}; + +export default Post; diff --git a/03_source/mobile.trunk/src/pages/DemoFacebookClone/components/Tabs.tsx b/03_source/mobile.trunk/src/pages/DemoFacebookClone/components/Tabs.tsx new file mode 100644 index 0000000..8a1c7d2 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFacebookClone/components/Tabs.tsx @@ -0,0 +1,33 @@ +import React from 'react'; +import { IonIcon, IonLabel, IonTabBar, IonTabButton, IonTabs, IonRouterOutlet } from '@ionic/react'; +import { Redirect, Route } from 'react-router-dom'; +import Tab1 from '../pages/Tab1'; +import Tab2 from '../pages/Tab2'; +import Tab3 from '../pages/Tab3'; +import { chatboxOutline, cogOutline, personOutline } from 'ionicons/icons'; + +const Tabs = (props): React.JSX.Element => { + return ( + + + } /> + } /> + } /> + + + + + + + + + + + + + + + ); +}; + +export default Tabs; diff --git a/03_source/mobile.trunk/src/pages/DemoFacebookClone/index.tsx b/03_source/mobile.trunk/src/pages/DemoFacebookClone/index.tsx new file mode 100644 index 0000000..545fc4e --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFacebookClone/index.tsx @@ -0,0 +1,27 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +// import Tab1 from './AppPages/Tab1'; +// import Tab2 from './AppPages/Tab2'; + +import Tab1 from './pages/Tab1'; +import Tab2 from './pages/Tab2'; +import Tab3 from './pages/Tab3'; +import Tabs from './components/Tabs'; + +import './style.scss'; + +function DemoFacebookClone() { + return ( + + } /> + } /> + + + + ); +} + +export default DemoFacebookClone; diff --git a/03_source/mobile.trunk/src/pages/DemoFacebookClone/main/messages.js b/03_source/mobile.trunk/src/pages/DemoFacebookClone/main/messages.js new file mode 100644 index 0000000..0d37b93 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFacebookClone/main/messages.js @@ -0,0 +1,105 @@ +export const messages = [ + { + name: 'Ionic Framework', + avatar: 'https://pbs.twimg.com/profile_images/1148952014036054016/xxv7lLvp_400x400.jpg', + message: '', + online: false, + last_message_sent: 3, + new_message_count: 2, + }, + { + name: 'Capacitor JS', + avatar: 'https://pbs.twimg.com/profile_images/1268235262641004544/OLW1xl7t_400x400.png', + message: '', + online: true, + last_message_sent: 9, + new_message_count: 0, + }, + { + name: 'Max Lynch', + avatar: 'https://pbs.twimg.com/profile_images/1318970727173885953/bln98FNj_400x400.jpg', + message: '', + online: true, + last_message_sent: 15, + new_message_count: 0, + }, + { + name: 'Ben Sperry', + avatar: 'https://pbs.twimg.com/profile_images/1328390491126308864/jHHgl5Dm_400x400.jpg', + message: '', + online: false, + last_message_sent: 27, + new_message_count: 0, + }, + { + name: 'Matt Netkow', + avatar: 'https://pbs.twimg.com/profile_images/1323383930150621187/GKc0nVzi_400x400.jpg', + message: '', + online: false, + last_message_sent: 31, + new_message_count: 1, + }, + { + name: 'Liam DeBeasi', + avatar: 'https://pbs.twimg.com/profile_images/1105953692669366273/ZNK4lRAJ_400x400.jpg', + message: '', + online: true, + last_message_sent: 41, + new_message_count: 0, + }, + { + name: 'Mike Hartington', + avatar: 'https://pbs.twimg.com/profile_images/1084993841898446849/DJ8XtR6L_400x400.jpg', + message: '', + online: false, + last_message_sent: 47, + new_message_count: 0, + }, + { + name: 'Adam Bradley', + avatar: 'https://pbs.twimg.com/profile_images/909075942320025600/hfYqicUk_400x400.jpg', + message: '', + online: true, + last_message_sent: 51, + new_message_count: 0, + }, + { + name: 'Brody Kidd', + avatar: 'https://pbs.twimg.com/profile_images/477539679567228928/JObyaUW__400x400.jpeg', + message: '', + online: true, + last_message_sent: 53, + new_message_count: 0, + }, +]; + +export const posts = [ + { + name: 'Max Lynch', + sponsored: false, + time: '12 m', + avatar: 'https://pbs.twimg.com/profile_images/1318970727173885953/bln98FNj_400x400.jpg', + message: 'Join a global community of web native developers!', + views: '', + online: true, + }, + { + name: 'Ben Sperry', + sponsored: false, + image: 'https://ionicons.com/assets/img/meta/ionicons-og.png', + time: '1 h', + avatar: 'https://pbs.twimg.com/profile_images/1328390491126308864/jHHgl5Dm_400x400.jpg', + message: 'Check out all the cool IonIcons used on this IonicBook app!', + views: '', + online: false, + }, + { + name: 'Matt Netkow', + sponsored: false, + time: '2 h', + avatar: 'https://pbs.twimg.com/profile_images/1323383930150621187/GKc0nVzi_400x400.jpg', + message: 'I help web developers build cross-platform Web Native apps.', + views: '', + online: false, + }, +]; diff --git a/03_source/mobile.trunk/src/pages/DemoFacebookClone/pages/MessageItem.tsx b/03_source/mobile.trunk/src/pages/DemoFacebookClone/pages/MessageItem.tsx new file mode 100644 index 0000000..4c4d3b3 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFacebookClone/pages/MessageItem.tsx @@ -0,0 +1,27 @@ +import { IonAvatar, IonBadge, IonImg, IonItem, IonLabel } from '@ionic/react'; +import React from 'react'; + +const MessageItem = (props): React.JSX.Element => { + return ( + + + + + {props.message.online &&
} + + +

{props.message.name}

+

This is a test message for a messenger item

+
+ +
+

{props.message.last_message_sent} min

+ {props.message.new_message_count > 0 && ( + {props.message.new_message_count} + )} +
+
+ ); +}; + +export default MessageItem; diff --git a/03_source/mobile.trunk/src/pages/DemoFacebookClone/pages/Tab1.css b/03_source/mobile.trunk/src/pages/DemoFacebookClone/pages/Tab1.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk/src/pages/DemoFacebookClone/pages/Tab1.tsx b/03_source/mobile.trunk/src/pages/DemoFacebookClone/pages/Tab1.tsx new file mode 100644 index 0000000..2ccdb20 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFacebookClone/pages/Tab1.tsx @@ -0,0 +1,73 @@ +import { + IonAvatar, + IonBackButton, + IonBadge, + IonButton, + IonButtons, + IonContent, + IonFab, + IonFabButton, + IonHeader, + IonIcon, + IonImg, + IonItem, + IonLabel, + IonList, + IonPage, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { addOutline, searchOutline } from 'ionicons/icons'; +import ExploreContainer from '../components/ExploreContainer'; +import { messages } from '../main/messages'; +import MessageItem from './MessageItem'; +import './Tab1.css'; + +const Tab1 = () => { + return ( + + + + Messages + + + + + + @93alan + + + + + + + + + + + + + Messages + + + + + {messages.map((message, index) => { + return ; + })} + + + + + + + + + + ); +}; + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/DemoFacebookClone/pages/Tab2.css b/03_source/mobile.trunk/src/pages/DemoFacebookClone/pages/Tab2.css new file mode 100644 index 0000000..6cb99f6 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFacebookClone/pages/Tab2.css @@ -0,0 +1,373 @@ +.timeline-toolbar { + + display: flex; + flex-direction: row !important; +} + +.toolbar-title { + + font-size: 1.8rem; + font-weight: 700; + margin-left: 0.5rem; +} + +.toolbar-icons { + + margin-right: 0.5rem; + background-color: white; +} + +.toolbar-icons ion-icon { + + color: black; + background-color:rgb(235, 235, 235); + border-radius: 500px; + font-size: 1.5rem; + margin-left: 0.5rem; + padding: 0.3rem; +} + +.second-toolbar { + + border-bottom: 0.1px solid rgb(144, 144, 144); + padding-top: 0.75rem; + background-color: white; + position: fixed; + width: 100%; + z-index: 9999999; +} + +.second-toolbar ion-col ion-icon { + + font-size: 1.75rem; + color: rgb(138, 138, 138); +} + +.second-toolbar ion-col ion-icon.selected { + + /* border-bottom: 5px solid var(--ion-color-primary); */ +} + +.selected-icon { + + border-bottom: 1.5px solid var(--ion-color-primary); +} + +.top-input-container { + + border-bottom: 0.1px solid rgb(200, 200, 200); + padding-top: 0.5rem; + padding-bottom: 0.5rem; + background-color: white; + margin-top: 3.5rem; +} + +.top-input { + + border: 1px solid rgb(149, 149, 149); + border-radius: 25px; + padding-left: 1rem !important; + height: 2.2rem; +} + +.below-input-label-container { + + border-bottom: 1px solid rgb(185, 185, 185); + background-color: white; +} + +.below-input-label { + + margin-top: 0.8rem; + margin-bottom: 0.8rem; +} + +.below-input-label:not(:last-child) { + + border-right: 1px solid rgb(167, 167, 167); +} + +.below-input-label ion-text { + + color: rgb(114, 114, 114); + font-size: 0.8rem; + font-weight: 700 !important; + margin-left: 1.7rem; +} + +.below-input-label ion-icon { + + font-size: 1.2rem; + margin-right: 0.5rem; + position: absolute; +} + +.timeline-bg { + + --background:rgb(218, 218, 218) !important; +} + +.rooms-container { + + background-color: white; + margin-top: 0.5rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + border-bottom: 0.1px solid rgb(144, 144, 144); + border-top: 0.1px solid rgb(144, 144, 144); +} + +.rooms-people-container { + + white-space: nowrap; + position: relative; + overflow-x: scroll; + overflow-y: hidden; + -webkit-overflow-scrolling: touch; + display: flex; + flex-direction: row; + width: 70%; +} + +.rooms-people-container ion-avatar { + + height: 2.5rem; + width: 2.5rem; +} + +.rooms-people-container div { + + margin-top: 0.4rem !important; + margin-right: 1rem !important; +} + +.rooms-create { + + border: 1px solid rgb(149, 225, 255); + border-radius: 70px; + justify-content: center; + flex-direction: row; + padding: 0.2rem !important; + +} + +.rooms-create ion-icon { + + font-size: 1.5rem !important; + margin-left: 0.5rem !important; + margin-top: 0.3rem !important; + float: left !important; +} + +.rooms-create ion-text { + + font-size: 0.7rem !important; + color:rgb(136, 136, 136) !important; + font-weight: 700 !important; + margin: 0 !important; + padding: 0 !important; + line-height: -1rem !important; +} + +.rooms-online { + + height: 13px !important; + width: 13px !important; + border-radius: 500px !important; + border: 2px solid white !important; + background-color: green !important; + z-index: 9999 !important; + position: absolute !important; + margin-left: 0.5rem !important; + margin-top: -0.8rem !important; +} + +.post { + + margin-top: 0.5rem !important; +} + +.post-container { + + background-color: white !important; +} + +.post-header { + + /* padding: none !important; */ + /* margin: none !important; */ +} + +.post-header { + + padding-top: 0.5rem !important; + padding-left: 0.5rem !important; + z-index: 9999; +} + +.post-header ion-avatar { + + /* border: 2px solid rgb(164, 222, 255); */ +} + +.post-header ion-label { + + margin-left: 0.5rem !important; + z-index: 99999 !important; +} + +.post-header ion-label h3 { + + font-weight: 600 !important; +} + +.post-header ion-label p ion-icon { + + position: absolute !important; + margin-top: 0.15rem !important; +} + +.post-header ion-icon { + + color: rgb(133, 133, 133) !important; +} + +.post-content { + + padding-left: 0.5rem !important; + margin-top: -0.7rem !important; +} + +.post-link { + + padding-left: 0.5rem !important; + background-color: rgb(245, 245, 245) !important; + display: flex !important; + flex-direction: row !important; + justify-content: space-between !important; + padding-top: 0.75rem !important; + padding-bottom: 0.75rem !important; +} + +.post-link ion-button { + + margin-right: 0.5rem !important; + --background: rgb(245, 245, 245) !important; + border: 1px solid rgb(98, 98, 98) !important; + border-radius: 8px !important; + color: rgb(98, 98, 98) !important; + text-transform: uppercase !important; + font-size: 0.8rem !important; +} + +.post-likes { + + padding-left: 0.5rem !important; + padding-top: 0.5rem !important; + padding-bottom: 0.5rem !important; + display: flex !important; + flex-direction: row !important; + justify-content: space-between !important; +} + +.post-like-icons { + + margin-top: 0.2rem !important; +} + +.post-likes ion-icon:not(:last-child) { + + background-color: rgb(70, 128, 255) !important; +} + +.post-likes ion-icon:last-child { + + background-color: rgb(255, 76, 76) !important; + margin-left: -0.5rem !important; +} + +.post-likes ion-icon { + + color: white !important; + border-radius: 100px !important; + padding: 0.2rem !important; + border: 2px solid white !important; +} + +.post-likes p { + + padding: none !important; + margin: none !important; + margin-top: 0.4rem !important; + font-size: 0.8rem !important; + margin-right: 0.5rem !important; + color: rgb(156, 156, 156) !important; + +} + +.post-image { + + padding: none !important; + margin: none !important; +} + +.post-image ion-img { + + width: 100% !important; +} + +.post-content p { + + font-size: 1rem !important; +} + +.post-actions { + + background-color: white !important; + display: flex !important; + flex-direction: row !important; + justify-content: space-between !important; + border-top: 0.1px solid rgb(228, 228, 228) !important; + padding-top: 0.5rem !important; + padding-bottom: 0.2rem !important; +} + +.post-actions ion-col { + + margin-left: -1rem !important; +} + +.post-actions ion-col:first-child { + + margin-right: -2rem !important; +} + +.post-actions ion-col:last-child { + + margin-right: 2rem !important; +} + +.post-actions ion-col ion-icon { + + font-size: 1.4rem !important; + color:rgb(105, 105, 105) !important; +} + +.post-actions ion-col ion-text { + + font-size: 0.8rem !important; + color: rgb(107, 107, 107) !important; + margin-top: 0.2rem !important; + margin-left: 0.5rem !important; + position: absolute !important; +} + +/* +
+

ionicbook

+ +
+ + +
+
*/ \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoFacebookClone/pages/Tab2.tsx b/03_source/mobile.trunk/src/pages/DemoFacebookClone/pages/Tab2.tsx new file mode 100644 index 0000000..9b7f202 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFacebookClone/pages/Tab2.tsx @@ -0,0 +1,242 @@ +import { + IonAvatar, + IonButton, + IonButtons, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonImg, + IonInput, + IonItem, + IonLabel, + IonPage, + IonRouterLink, + IonRow, + IonSearchbar, + IonText, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import { + searchOutline, + chatboxOutline, + playCircleOutline, + bagOutline, + homeOutline, + menuOutline, + flagOutline, + notificationsOutline, + homeSharp, + home, + videocam, + images, + globe, + ellipse, + ellipsisHorizontal, + thumbsUp, + heart, + thumbsUpOutline, + shareOutline, + arrowRedoOutline, + chevronBackOutline, +} from 'ionicons/icons'; +import ExploreContainer from '../components/ExploreContainer'; +import Post from '../components/Post'; +import { messages, posts } from '../main/messages'; +import './Tab2.css'; +import React from 'react'; + +const Tab2 = (): React.JSX.Element => { + const router = useIonRouter(); + + function handleBackClick() { + router.goBack(); + } + + return ( + + + + + handleBackClick()}> + + + + + ionicbook + + + + + + + + + + + + + {/* */} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Live + + + + + Photo + + + + + Room + + + + + +
+ + + Create +
+ Room +
+
+
+ +
+ {messages.map((message, index) => { + if (index > 1) { + return ( +
+ + + + {message.online && } +
+ ); + } + })} +
+
+ + + +
+ + + + + + +

Ionic Framework

+

+ Sponsored    + +

+
+ + +
+ + +

Build cross-platform web native mobile apps with one codebase! 🎉

+
+ + {/* */} + + {/* */} + +
+ +

ionicframework.com

+

Start building apps today!

+
+ + Learn more +
+ +
+
+ + +
+ +

16K Views

+
+ +
+ + + Like + + + + + Comment + + + + + Share + +
+
+
+
+ + {posts.map((post, index) => { + return ; + })} +
+
+ ); +}; + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/DemoFacebookClone/pages/Tab3.css b/03_source/mobile.trunk/src/pages/DemoFacebookClone/pages/Tab3.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk/src/pages/DemoFacebookClone/pages/Tab3.tsx b/03_source/mobile.trunk/src/pages/DemoFacebookClone/pages/Tab3.tsx new file mode 100644 index 0000000..3a29b8a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFacebookClone/pages/Tab3.tsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab3.css'; + +const Tab3: React.FC = () => { + return ( + + + + Tab 3 + + + + + + Tab 3 + + + + + + ); +}; + +export default Tab3; diff --git a/03_source/mobile.trunk/src/pages/DemoFacebookClone/style.scss b/03_source/mobile.trunk/src/pages/DemoFacebookClone/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFacebookClone/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile.trunk/src/pages/DemoFacebookClone/theme/variables.css b/03_source/mobile.trunk/src/pages/DemoFacebookClone/theme/variables.css new file mode 100644 index 0000000..12e7354 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFacebookClone/theme/variables.css @@ -0,0 +1,352 @@ +/* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + +/** Ionic CSS Variables **/ +:root { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** purple **/ + --ion-color-tertiary: #894eb1; + --ion-color-tertiary-rgb: 235, 68, 90; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #6f00b9; + --ion-color-tertiary-tint: #8633bd; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; +} + +@media (prefers-color-scheme: dark1) { + /* + * Dark Colors + * ------------------------------------------- + */ + + body { + --ion-color-primary: #428cff; + --ion-color-primary-rgb: 66,140,255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255,255,255; + --ion-color-primary-shade: #3a7be0; + --ion-color-primary-tint: #5598ff; + + --ion-color-secondary: #50c8ff; + --ion-color-secondary-rgb: 80,200,255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255,255,255; + --ion-color-secondary-shade: #46b0e0; + --ion-color-secondary-tint: #62ceff; + + --ion-color-tertiary: #6a64ff; + --ion-color-tertiary-rgb: 106,100,255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255,255,255; + --ion-color-tertiary-shade: #5d58e0; + --ion-color-tertiary-tint: #7974ff; + + --ion-color-success: #2fdf75; + --ion-color-success-rgb: 47,223,117; + --ion-color-success-contrast: #000000; + --ion-color-success-contrast-rgb: 0,0,0; + --ion-color-success-shade: #29c467; + --ion-color-success-tint: #44e283; + + --ion-color-warning: #ffd534; + --ion-color-warning-rgb: 255,213,52; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0,0,0; + --ion-color-warning-shade: #e0bb2e; + --ion-color-warning-tint: #ffd948; + + --ion-color-danger: #ff4961; + --ion-color-danger-rgb: 255,73,97; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255,255,255; + --ion-color-danger-shade: #e04055; + --ion-color-danger-tint: #ff5b71; + + --ion-color-dark: #f4f5f8; + --ion-color-dark-rgb: 244,245,248; + --ion-color-dark-contrast: #000000; + --ion-color-dark-contrast-rgb: 0,0,0; + --ion-color-dark-shade: #d7d8da; + --ion-color-dark-tint: #f5f6f9; + + --ion-color-medium: #989aa2; + --ion-color-medium-rgb: 152,154,162; + --ion-color-medium-contrast: #000000; + --ion-color-medium-contrast-rgb: 0,0,0; + --ion-color-medium-shade: #86888f; + --ion-color-medium-tint: #a2a4ab; + + --ion-color-light: #222428; + --ion-color-light-rgb: 34,36,40; + --ion-color-light-contrast: #ffffff; + --ion-color-light-contrast-rgb: 255,255,255; + --ion-color-light-shade: #1e2023; + --ion-color-light-tint: #383a3e; + } + + /* + * iOS Dark Theme + * ------------------------------------------- + */ + + .ios body { + --ion-background-color: #000000; + --ion-background-color-rgb: 0,0,0; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255,255,255; + + --ion-color-step-50: #0d0d0d; + --ion-color-step-100: #1a1a1a; + --ion-color-step-150: #262626; + --ion-color-step-200: #333333; + --ion-color-step-250: #404040; + --ion-color-step-300: #4d4d4d; + --ion-color-step-350: #595959; + --ion-color-step-400: #666666; + --ion-color-step-450: #737373; + --ion-color-step-500: #808080; + --ion-color-step-550: #8c8c8c; + --ion-color-step-600: #999999; + --ion-color-step-650: #a6a6a6; + --ion-color-step-700: #b3b3b3; + --ion-color-step-750: #bfbfbf; + --ion-color-step-800: #cccccc; + --ion-color-step-850: #d9d9d9; + --ion-color-step-900: #e6e6e6; + --ion-color-step-950: #f2f2f2; + + --ion-item-background: #000000; + + --ion-card-background: #1c1c1d; + } + + .ios ion-modal { + --ion-background-color: var(--ion-color-step-100); + --ion-toolbar-background: var(--ion-color-step-150); + --ion-toolbar-border-color: var(--ion-color-step-250); + } + + + /* + * Material Design Dark Theme + * ------------------------------------------- + */ + + .md body { + --ion-background-color: #121212; + --ion-background-color-rgb: 18,18,18; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255,255,255; + + --ion-border-color: #222222; + + --ion-color-step-50: #1e1e1e; + --ion-color-step-100: #2a2a2a; + --ion-color-step-150: #363636; + --ion-color-step-200: #414141; + --ion-color-step-250: #4d4d4d; + --ion-color-step-300: #595959; + --ion-color-step-350: #656565; + --ion-color-step-400: #717171; + --ion-color-step-450: #7d7d7d; + --ion-color-step-500: #898989; + --ion-color-step-550: #949494; + --ion-color-step-600: #a0a0a0; + --ion-color-step-650: #acacac; + --ion-color-step-700: #b8b8b8; + --ion-color-step-750: #c4c4c4; + --ion-color-step-800: #d0d0d0; + --ion-color-step-850: #dbdbdb; + --ion-color-step-900: #e7e7e7; + --ion-color-step-950: #f3f3f3; + + --ion-item-background: #1e1e1e; + + --ion-toolbar-background: #1f1f1f; + + --ion-tab-bar-background: #1f1f1f; + + --ion-card-background: #1e1e1e; + } +} + +ion-header, +ion-toolbar { + + border-bottom: none !important; + --border-color: white; +} + +ion-title { + + --color: rgb(48, 48, 58); +} + +ion-tab-bar { + + border-top: none; +} + +ion-tab-button { + + --color: rgb(211, 211, 211); + height: 2rem; +} + +.tab-selected, +ion-icon.search { + + color: rgb(88, 88, 88) !important; +} + +.avatar { + + height: 3.5rem; + width: 3.5rem; +} + +.online { + + height: 20px; + width: 20px; + border-radius: 500px; + border: 3px solid white; + background-color: green; + z-index: 9999; + position: absolute; + margin-left: 2.5rem; + margin-top: 1.2rem; +} + +.contact-details { + + margin-top: 0rem; + margin-left: 1rem; + margin-right: 1rem; +} + +.stats { + + margin-top: -1rem; + margin-right: 0.5rem; +} + +.last-online { + + color: rgb(190, 190, 190); + font-size: 0.9rem; + margin-bottom: 0.5rem; +} + +.stats ion-badge { + + margin: 0 !important; + margin-top: -3rem !important; + border-radius: 2px !important; +} + + + +.contact-details h1 { + + font-size: 1.2rem; +} + +.contact-details p { + + font-size: 0.8rem; +} + +.message-item { + + padding-top: 1.5rem; +} + +.profile-avatar { + + margin: 0 !important; + padding: 0 !important; + height: 2rem; + width: 2rem; + border-radius: 500px !important; +} + +.profile-name { + + color:#c8c8c8; + font-weight: 600 !important; + font-size: 0.9rem !important; + margin-left: 0.5rem; +} + +.add-fab { + + opacity: 0.7; +} + +/* */ \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoFastFoodApp/AppPages/Tab1.jsx b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/DemoFastFoodApp/AppPages/Tab2.jsx b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/DemoFastFoodApp/NOTES.md b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/NOTES.md new file mode 100644 index 0000000..32dfb37 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/NOTES.md @@ -0,0 +1 @@ +# REQ0129 diff --git a/03_source/mobile.trunk/src/pages/DemoFastFoodApp/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoFastFoodApp/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoFastFoodApp/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoFastFoodApp/components/CategorySlide.tsx b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/components/CategorySlide.tsx new file mode 100644 index 0000000..0b189df --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/components/CategorySlide.tsx @@ -0,0 +1,15 @@ +import { Link } from 'react-router-dom'; + +export const CategorySlide = ({ name, path, image }) => ( + <>TODO: sorry but the ionic cannot provide ion-slide +); + +// import { IonSlide } from '@ionic/react'; +// export const CategorySlide = ({ name, path, image }) => ( +// +// +// category +//

{name}

+// +//
+// ); diff --git a/03_source/mobile.trunk/src/pages/DemoFastFoodApp/components/ProductCard.module.css b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/components/ProductCard.module.css new file mode 100644 index 0000000..1373e81 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/components/ProductCard.module.css @@ -0,0 +1,83 @@ +.categoryPage ion-toolbar { + + --border-style: none; +} + +.categoryCard { + + display: flex; + flex-direction: column; + justify-content: center; + align-content: center; + align-items: center; + /* min-height: 20rem !important; */ +} + +.productCardActions { + + display: flex; + flex-direction: row; + justify-content: space-between; + width: 100%; + margin-bottom: 1rem; +} + +.productCardAction { + + font-size: 1.1rem; +} + +.productCardHeader { + + min-height: 13rem; + margin: 0 !important; + padding: 0 !important; + padding: 1rem !important; +} + +.productCardHeader p { + + font-size: 0.8rem; + padding: 0; + margin: 0; + margin-top: 0.75rem; +} + +.categoryCardContent { + + display: flex; + flex-direction: column; +} + +.categoryCardContent ion-button { + + height: 1.5rem; + font-size: 0.8rem; +} + +.categoryCardContent p { + + font-size: 0.8rem; + padding: 0; + margin: 0; +} + +.categoryCard img { + + /* border-radius: 5px; */ + /* padding: 1rem; */ +} + +.productPrice { + + display: flex; + flex-direction: row; +} + +.food { + + text-align: center; + background-color: white; + border: 5px solid var(--ion-background-color); + border-radius: 30px; +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/components/ProductCard.tsx b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/components/ProductCard.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/components/ProductCard.tsx rename to 03_source/mobile.trunk/src/pages/DemoFastFoodApp/components/ProductCard.tsx diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/data/CartStore.js b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/data/CartStore.js similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/data/CartStore.js rename to 03_source/mobile.trunk/src/pages/DemoFastFoodApp/data/CartStore.js diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/data/FavouritesStore.js b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/data/FavouritesStore.js similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/data/FavouritesStore.js rename to 03_source/mobile.trunk/src/pages/DemoFastFoodApp/data/FavouritesStore.js diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/data/ProductStore.js b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/data/ProductStore.js similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/data/ProductStore.js rename to 03_source/mobile.trunk/src/pages/DemoFastFoodApp/data/ProductStore.js diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/data/fetcher.js b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/data/fetcher.js similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/data/fetcher.js rename to 03_source/mobile.trunk/src/pages/DemoFastFoodApp/data/fetcher.js diff --git a/03_source/mobile.trunk/src/pages/DemoFastFoodApp/index.tsx b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/index.tsx new file mode 100644 index 0000000..31b2b8b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/index.tsx @@ -0,0 +1,55 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +import './style.scss'; +import Home from './pages/Home'; +import FavouriteProducts from './pages/FavouriteProducts'; +import CartProducts from './pages/CartProducts'; +import CategoryProducts from './pages/CategoryProducts'; +import Product from './pages/Product'; + +function DemoFastFoodApp() { + return ( + + + {/* + + + + + + + */} + + + + + + + + + + + + + + + + + + + + + + + + + ); +} + +export default DemoFastFoodApp; diff --git a/03_source/mobile.trunk/src/pages/DemoFastFoodApp/module.d.ts b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/module.d.ts new file mode 100644 index 0000000..4af7be7 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/module.d.ts @@ -0,0 +1,9 @@ +declare module '*.module.css' { + const classes: { readonly [key: string]: string }; + export default classes; +} + +declare module '*.module.scss' { + const classes: { readonly [key: string]: string }; + export default classes; +} diff --git a/03_source/mobile.trunk/src/pages/DemoFastFoodApp/pages/CartProducts.module.css b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/pages/CartProducts.module.css new file mode 100644 index 0000000..851dbe1 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/pages/CartProducts.module.css @@ -0,0 +1,37 @@ +.cartCheckout { + + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + margin: 1rem; +} + +.cartFooter { + + border-top: 2px solid rgb(200, 200, 200); + background-color: white; +} + +.cartCheckout ion-card-subtitle { + + font-size: 1.3rem; +} + +.cartItem ion-avatar { + + height: 4rem; + width: 4rem; +} + +.cartSlider:not(:nth-child(1)) { + + border-top: 2px solid rgb(236, 236, 236); +} + +.cartActions { + + display: flex; + flex-direction: column; +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/pages/CartProducts.tsx b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/pages/CartProducts.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/pages/CartProducts.tsx rename to 03_source/mobile.trunk/src/pages/DemoFastFoodApp/pages/CartProducts.tsx diff --git a/03_source/mobile.trunk/src/pages/DemoFastFoodApp/pages/CategoryProducts.module.css b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/pages/CategoryProducts.module.css new file mode 100644 index 0000000..c0d5021 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/pages/CategoryProducts.module.css @@ -0,0 +1,69 @@ +.categoryPage ion-toolbar { + + --border-style: none; +} + +.foodResults { + + color: black; +} + +.chickenResults { + + color: white; +} + +.burgers { + + --ion-background-color: #7dd5ff; + --ion-toolbar-background: #7dd5ff; + --ion-text-color: black; + --ion-item-background: white !important; +} + +.sides { + + --ion-background-color: #ffd87d; + --ion-toolbar-background: #ffd87d; + --ion-text-color: black !important; + --ion-item-background: white !important; +} + +.chicken { + + --ion-background-color: #6477fe; + --ion-toolbar-background: #6477fe; + /* --ion-text-color: white; */ + --ion-item-background: white !important; + --ion-toolbar-color: white; +} + +.drinks { + + --ion-background-color: #fda9f3; + --ion-toolbar-background: #fda9f3; + /* --ion-text-color: white; */ + --ion-item-background: white !important; +} + +.veggie { + + --ion-background-color: #9fef79; + --ion-toolbar-background: #9fef79; + /* --ion-text-color: white !important; */ + --ion-item-background: white !important; +} + +.kids { + + --ion-background-color: #dc9afe; + --ion-toolbar-background: #dc9afe; + /* --ion-text-color: white; */ + --ion-item-background: white !important; +} + +.search { + + --background: rgb(240, 240, 240); + --color: black; +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/pages/CategoryProducts.tsx b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/pages/CategoryProducts.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/pages/CategoryProducts.tsx rename to 03_source/mobile.trunk/src/pages/DemoFastFoodApp/pages/CategoryProducts.tsx diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/pages/FavouriteProducts.tsx b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/pages/FavouriteProducts.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/pages/FavouriteProducts.tsx rename to 03_source/mobile.trunk/src/pages/DemoFastFoodApp/pages/FavouriteProducts.tsx diff --git a/03_source/mobile.trunk/src/pages/DemoFastFoodApp/pages/Home.module.scss b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/pages/Home.module.scss new file mode 100644 index 0000000..24a4522 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/pages/Home.module.scss @@ -0,0 +1,137 @@ +.homePage ion-toolbar { + + --border-style: none; +} + +.logo { + + margin-top: 0.25rem; + color: var(--ion-color-primary); +} + +.categoryCard, +.categoryCardContent { + + display: flex; + flex-direction: column; + justify-content: center; + align-content: center; + align-items: center; +} + +.categoryCardContent ion-button { + + height: 1.5rem; + font-size: 0.8rem; +} + +.categoryCardContent { + + background-color: rgb(238, 238, 238); +} + +.categoryCardContent ion-card-subtitle { + + /* color: rgb(78, 78, 78); */ +} + +.categoryCard img { + + /* border-radius: 5px; */ + padding: 1rem; +} + +.categorySlider { + + margin-top: 1rem; + + ion-slide { + + width: 60%; + margin-right: 10px; + margin-left: 10px; + + display: flex; + flex-direction: column; + justify-content: center; + + img { + + border-radius: 22px; + } + } +} + +.categorySquares { + + ion-row { + + .categorySquare { + + height: 4.5rem; + text-align: center; + display: flex; + justify-content: center; + align-content: center; + align-items: center; + border-radius: 22px; + + h4 { + + text-align: center; + } + } + + .categorySquare:nth-child(1) { + + background-color: rgb(105, 62, 5); + color: white; + } + + .categorySquare:nth-child(2) { + + background-color: rgb(83, 185, 0); + color: white; + } + + .categorySquare:nth-child(3) { + + background-color: rgb(255, 240, 24); + } + } +} + +.orderSection { + + padding: 2rem; + background-color: rgb(206, 41, 0); + color: white; + border-radius: 22px; + margin: 5px; + + h4 { + + margin-left: 1.5rem; + } +} + +.offerSection { + + padding: 2rem; + background-color: #ffd146; + color: black; + border-radius: 22px; + margin: 5px; + + h4, + ion-card-subtitle { + + margin-left: 1.5rem; + } + + ion-card-subtitle { + + color: rgb(255, 255, 255); + font-weight: 800; + } +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoFastFoodApp/pages/Home.tsx b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/pages/Home.tsx new file mode 100644 index 0000000..e4d306a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/pages/Home.tsx @@ -0,0 +1,140 @@ +import { useState } from 'react'; +import { + IonBadge, + IonButton, + IonButtons, + IonCard, + IonCardContent, + IonCardSubtitle, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonPage, + IonRow, + // IonSlide, + // IonSlides, + IonTitle, + IonToolbar, + useIonRouter, + useIonViewDidEnter, + useIonViewWillLeave, +} from '@ionic/react'; + +import styles from './Home.module.scss'; +import { cart, chevronBackOutline, heart } from 'ionicons/icons'; + +import { ProductStore } from '../data/ProductStore'; +import { FavouritesStore } from '../data/FavouritesStore'; +import { CartStore } from '../data/CartStore'; +import { Link } from 'react-router-dom'; +import { CategorySlide } from '../components/CategorySlide'; + +const Home = () => { + const products = ProductStore.useState((s) => s.products); + const favourites = FavouritesStore.useState((s) => s.product_ids); + const shopCart = CartStore.useState((s) => s.product_ids); + + useIonViewWillLeave(() => { + document.querySelector('#slider').stopAutoplay(); + }); + + useIonViewDidEnter(() => { + document.querySelector('#slider') && document.querySelector('#slider').startAutoplay(); + document.querySelector('#slider') && document.querySelector('#slider').update(); + }); + + const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + Popular + + {/* + const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + */} + + handleBackClick()}> + + + + + + Ionic Food + + + + {favourites.length} + + + + + {shopCart.length} + + + + + + + + + + + + order method + + + +

Kids eat free on any orders over £20.00

+ Valid until July → +
+
+
+ + + + Let's eat + + + + {/* + + + + + + + + + */} + + + + + order method + + + +

Order for a collection or get a local delivery

+
+
+
+
+
+ ); +}; + +export default Home; diff --git a/03_source/mobile.trunk/src/pages/DemoFastFoodApp/pages/Product.module.css b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/pages/Product.module.css new file mode 100644 index 0000000..253b7ed --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/pages/Product.module.css @@ -0,0 +1,66 @@ +.categoryPage ion-toolbar { + + --border-style: none; +} + +.categoryCard { + + display: flex; + flex-direction: column; + justify-content: center; + align-content: center; + align-items: center; + text-align: center; +} + +.productCardActions { + + display: flex; + flex-direction: row; + justify-content: space-between; + width: 100%; + margin-bottom: 1rem; +} + +.productCardAction { + + font-size: 1.1rem; +} + +.productCardHeader { + + min-height: 17rem; +} + +.productCardHeader p { + + font-size: 1.2rem; + padding: 0; + margin: 0; + margin-top: 0.75rem; +} + +.categoryCardContent { + + display: flex; + flex-direction: column; + text-align: center; +} + +.categoryCardContent ion-button { + + font-size: 0.8rem; +} + +.categoryCardContent p { + + font-size: 1.5rem; + padding: 0; + margin: 0; +} + +.productPrice { + + display: flex; + flex-direction: row; +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoFastFoodApp/pages/Product.tsx b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/pages/Product.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoFastFoodApp/pages/Product.tsx rename to 03_source/mobile.trunk/src/pages/DemoFastFoodApp/pages/Product.tsx diff --git a/03_source/mobile.trunk/src/pages/DemoFastFoodApp/style.scss b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile.trunk/src/pages/DemoFastFoodApp/theme/variables.css b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/theme/variables.css new file mode 100644 index 0000000..86f1b29 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFastFoodApp/theme/variables.css @@ -0,0 +1,87 @@ +/* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + +/** Ionic CSS Variables **/ +:root { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + + --ion-toolbar-color: black; + --ion-grid-column-padding: 0rem; + /* --ion-toolbar-background: var(--ion-color-warning); */ +} + +.non-link { + + text-decoration: none; + color: unset; +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoFloatingTabs/AppPages/Tab1.jsx b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/DemoFloatingTabs/AppPages/Tab2.jsx b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/DemoFloatingTabs/NOTES.md b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/NOTES.md new file mode 100644 index 0000000..9b48ea3 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/NOTES.md @@ -0,0 +1 @@ +# REQ0130 diff --git a/03_source/mobile.trunk/src/pages/DemoFloatingTabs/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoFloatingTabs/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoFloatingTabs/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoFloatingTabs/components/ExploreContainer.scss b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/components/ExploreContainer.scss new file mode 100644 index 0000000..11ea3ba --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/components/ExploreContainer.scss @@ -0,0 +1,26 @@ +.floating-tab-bar { + .container { + text-align: center; + position: absolute; + left: 0; + right: 0; + top: 50%; + transform: translateY(-50%); + } + + .container strong { + font-size: 20px; + line-height: 26px; + } + + .container p { + font-size: 16px; + line-height: 22px; + color: #8c8c8c; + margin: 0; + } + + .container a { + text-decoration: none; + } +} diff --git a/03_source/mobile.trunk/src/pages/DemoFloatingTabs/components/ExploreContainer.tsx b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/components/ExploreContainer.tsx new file mode 100644 index 0000000..759cc76 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/components/ExploreContainer.tsx @@ -0,0 +1,21 @@ +import './ExploreContainer.scss'; + +const ExploreContainer = ({ name }) => { + return ( +
+ {name} +

+ Explore{' '} + + UI Components + +

+
+ ); +}; + +export default ExploreContainer; diff --git a/03_source/mobile.trunk/src/pages/DemoFloatingTabs/index.tsx b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/index.tsx new file mode 100644 index 0000000..71a067d --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/index.tsx @@ -0,0 +1,46 @@ +import { IonIcon, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cogOutline, homeOutline, listOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './pages/Tab1'; +import Tab2 from './pages/Tab2'; +import Tab3 from './pages/Tab3'; + +import './theme/style.scss'; +import './theme/floating-tab-bar.scss'; + +function DemoFloatingTabs() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} + +export default DemoFloatingTabs; diff --git a/03_source/mobile.trunk/src/pages/DemoFloatingTabs/pages/Tab1.css b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/pages/Tab1.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk/src/pages/DemoFloatingTabs/pages/Tab1.tsx b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/pages/Tab1.tsx new file mode 100644 index 0000000..c328244 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/pages/Tab1.tsx @@ -0,0 +1,49 @@ +import { + IonButton, + IonButtons, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab1.css'; +import React from 'react'; +import { chevronBackOutline } from 'ionicons/icons'; + +const Tab1 = (): React.JSX.Element => { + const router = useIonRouter(); + + function handleBackClick() { + router.goBack(); + } + + return ( + + + + Floating Tab Bar + + + handleBackClick()}> + + + + + + + + + Floating Tab Bar + + + + + + ); +}; + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/DemoFloatingTabs/pages/Tab2.css b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/pages/Tab2.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk/src/pages/DemoFloatingTabs/pages/Tab2.tsx b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/pages/Tab2.tsx new file mode 100644 index 0000000..048ff80 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/pages/Tab2.tsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab2.css'; + +const Tab2 = (): React.JSX.Element => { + return ( + + + + Floating Tab Bar + + + + + + Floating Tab Bar + + + + + + ); +}; + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/DemoFloatingTabs/pages/Tab3.css b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/pages/Tab3.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk/src/pages/DemoFloatingTabs/pages/Tab3.tsx b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/pages/Tab3.tsx new file mode 100644 index 0000000..54cfd9e --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/pages/Tab3.tsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab3.css'; + +const Tab3 = (): React.JSX.Element => { + return ( + + + + Floating Tab Bar + + + + + + Floating Tab Bar + + + + + + ); +}; + +export default Tab3; diff --git a/03_source/mobile.trunk/src/pages/DemoFloatingTabs/theme/floating-tab-bar.scss b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/theme/floating-tab-bar.scss new file mode 100644 index 0000000..9533ab9 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/theme/floating-tab-bar.scss @@ -0,0 +1,27 @@ +.demo-floating-tabs { + * { + --ion-background-color: white; + --ion-tab-bar-color: rgb(92, 123, 207); + --ion-tab-bar-color-selected: rgb(255, 255, 255); + } + + ion-tab-bar { + --background: rgb(44, 83, 192); + box-shadow: 0px 1px 12px rgba(0, 0, 0, 0.4); + border-radius: 16px !important; + + height: 50px; + width: 90%; + padding-top: 5px; + padding-bottom: 5px; + + bottom: 20px; + position: relative; + margin: 0 auto !important; + border-top: none; + } + + ion-tab-button { + border-radius: 16px !important; + } +} diff --git a/03_source/mobile.trunk/src/pages/DemoFloatingTabs/theme/style.scss b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/theme/style.scss new file mode 100644 index 0000000..d202499 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoFloatingTabs/theme/style.scss @@ -0,0 +1,82 @@ +/* +Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ +*/ + +/** Ionic CSS Variables **/ + +.floating-tab-bar { + * { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + } +} diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/AppPages/Tab1.jsx b/03_source/mobile.trunk/src/pages/DemoInstagramClone/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/AppPages/Tab2.jsx b/03_source/mobile.trunk/src/pages/DemoInstagramClone/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/NOTES.md b/03_source/mobile.trunk/src/pages/DemoInstagramClone/NOTES.md new file mode 100644 index 0000000..8afd210 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/NOTES.md @@ -0,0 +1 @@ +# REQ0131 diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk/src/pages/DemoInstagramClone/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk/src/pages/DemoInstagramClone/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk/src/pages/DemoInstagramClone/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/components/ExploreContainer.css b/03_source/mobile.trunk/src/pages/DemoInstagramClone/components/ExploreContainer.css new file mode 100644 index 0000000..e99f514 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/components/ExploreContainer.css @@ -0,0 +1,24 @@ +.container { + text-align: center; + position: absolute; + left: 0; + right: 0; + top: 50%; + transform: translateY(-50%); +} + +.container strong { + font-size: 20px; + line-height: 26px; +} + +.container p { + font-size: 16px; + line-height: 22px; + color: #8c8c8c; + margin: 0; +} + +.container a { + text-decoration: none; +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/components/ExploreContainer.tsx b/03_source/mobile.trunk/src/pages/DemoInstagramClone/components/ExploreContainer.tsx new file mode 100644 index 0000000..396477f --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/components/ExploreContainer.tsx @@ -0,0 +1,21 @@ +import './ExploreContainer.css'; + +const ExploreContainer = ({ name }): React.JSX.Element => { + return ( +
+ {name} +

+ Explore{' '} + + UI Components + +

+
+ ); +}; + +export default ExploreContainer; diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/components/Feed.module.scss b/03_source/mobile.trunk/src/pages/DemoInstagramClone/components/Feed.module.scss new file mode 100644 index 0000000..4854d16 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/components/Feed.module.scss @@ -0,0 +1,221 @@ +.postsContainer { + + margin-top: 1.5rem; + margin-bottom: 1.5rem; +} + +.postContainer { + + display: flex; + flex-direction: column; + margin-top: 1rem; +} + +.postProfile { + + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + padding-right: 0.75rem; + padding-left: 0.75rem; +} + +.postProfile ion-router-link { + + display: flex !important; + flex-direction: row !important; +} + +.postProfileInfo ion-avatar { + + height: 2.2rem; + width: 2.2rem; +} + +.postProfileInfo p { + + margin: 0; + padding: 0; + margin-left: 0.5rem; + font-weight: 500; + font-size: 0.9rem; + color: black; +} + +.postProfileInfo { + + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + align-content: center; +} + +.postImage { + + border-top: 1px solid rgb(216, 216, 216); + margin-top: 0.5rem; + height: 20rem; + width: 100%; +} + +.postImageLike { + + font-size: 10rem; + color: rgb(231, 231, 231); + position: absolute; + left: 32vmin; + margin-top: 20vmin; + display: none; +} + +.postActionsContainer { + + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + padding-right: 0.75rem; + padding-left: 0.75rem; + margin-top: 0.5rem; +} + +.postActions { + + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.postActions ion-icon, +.postBookmark ion-icon { + + font-size: 1.5rem; +} + +.postActions ion-icon:not(:first-child) { + + padding-left: 0.7rem; +} + +.postLikesContainer { + + padding-left: 0.75rem; + margin-top: 0.5rem; +} + +.postLikesContainer p { + + margin: 0; + padding: 0; + font-weight: 200 !important; + font-size: 0.8rem; +} + +.postLikedName { + + font-weight: 600; +} + +.postCaption { + + padding-left: 0.75rem; + padding-right: 0.75rem; + margin-top: 0.3rem; +} + +.postCaption p { + + margin: 0; + padding: 0; + font-weight: 200 !important; + font-size: 0.8rem; +} + +.postName { + + color: black !important; + font-weight: 600 !important; +} + +.postName ion-router-link { + + color: black; +} + +.postComments { + + padding-left: 0.75rem; + padding-right: 0.75rem; + margin-top: 0.5rem; +} + +.postComments p { + + margin: 0; + padding: 0; + color:rgb(175, 175, 175); + font-size: 0.8rem; +} + +.postAddComment { + + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.postAddCommentProfile { + + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + align-items: center; + margin-top: 0.5rem; +} + +.postAddCommentProfile ion-avatar { + + height: 1.9rem; + width: 1.9rem; +} + +.postAddCommentProfile p { + + padding-left: 0.75rem; + font-size: 0.8rem; + color: rgb(175, 175, 175); +} + +.postAddCommentActions { + + +} + +.postAddCommentActions ion-icon { + + padding-left: 0.5rem; +} + +.postTime { + + padding-left: 0.75rem; + padding-right: 0.75rem; + margin-top: 0rem; +} + +.postTime p { + + margin: 0; + padding: 0; + color: rgb(175, 175, 175); + font-size: 0.6rem; +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/components/Feed.tsx b/03_source/mobile.trunk/src/pages/DemoInstagramClone/components/Feed.tsx new file mode 100644 index 0000000..dbcfeff --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/components/Feed.tsx @@ -0,0 +1,129 @@ +import { IonAvatar, IonIcon, IonRouterLink } from '@ionic/react'; +import { + addCircleOutline, + bookmarkOutline, + chatbubbleOutline, + ellipsisVertical, + heart, + heartOutline, + paperPlaneOutline, +} from 'ionicons/icons'; +import { likePost } from '../pages/PostStore'; +import { ProfilesStore } from '../pages/ProfilesStore'; +import { ProfileStore } from '../pages/ProfileStore'; +import styles from './Feed.module.scss'; + +const Feed = (props): React.JSX.Element => { + const { posts } = props; + const profile = ProfileStore.useState((s) => s.profile); + const profiles = ProfilesStore.useState((s) => s.profiles); + + const addLike = (event, postID, liked) => { + likePost(event, postID, liked); + }; + + return ( +
+ {posts.map((post, index) => { + const postProfile = profiles.filter((p) => p.id === post.profile_id)[0]; + + return ( +
+
+
+ + + post avatar + + + + +

{postProfile.username}

+
+
+ +
+ +
+
+ +
+ +
+ +
+
+ addLike(e, post.id, post.liked)} + /> + + +
+ +
+ +
+
+ +
+

+ Liked by alanmontgomery and{' '} + 2 others +

+
+ +
+

+ + + {postProfile.username} + + {' '} + {post.caption} +

+
+ +
+

View all {post.comments.length} comments

+
+ +
+
+ + add comment avatar + +

Add a comment...

+
+ +
+ + +
+
+ +
+

{post.time}

+
+
+ ); + })} +
+ ); +}; + +export default Feed; diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/components/Stories.module.scss b/03_source/mobile.trunk/src/pages/DemoInstagramClone/components/Stories.module.scss new file mode 100644 index 0000000..aeb99cb --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/components/Stories.module.scss @@ -0,0 +1,98 @@ +$border: linear-gradient(to bottom, #d82b7e, #f57939); + +.stories { + + height: fit-content; + margin-top: -0.7rem; +} + +.storiesContainer { + + overflow-x: scroll; + overflow-y: hidden; + -webkit-overflow-scrolling: touch; + display: flex; + flex-direction: row; + width: 100%; +} + +.storiesContainer::-webkit-scrollbar { + + display: none; +} + +.story, +.yourStory { + + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + align-content: center; + margin: 0 auto; + width: 4rem !important; + margin-left: 1rem; +} + +.story:first-child, +.yourStory:first-child { + + margin-left: 0.75rem; +} + +.story p, +.yourStory p { + + text-align: center; + margin: 0; + padding: 0; + margin-top: 0.2rem; + color: rgb(95, 95, 95); + font-size: 0.7rem; + font-weight: 400; + width: 120%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + position: relative; + margin-top: 5rem; +} + +.story img, +.yourStory img { + + height: 3.5rem !important; + width: 3.5rem !important; + position: absolute; + border-radius: 500px; + background: $border; + padding: 0.1rem; +} + +.yourStory img { + + background:rgb(214, 214, 214); +} + +.storyAdd { + + position: absolute; + color: white; + background-color: var(--ion-color-primary); + width: 1rem; + height: 1rem; + text-align: center; + margin: 0 auto; + display: flex; + flex-direction: row; + align-items: center; + align-content: center; + justify-content: center; + border-radius: 500px; + border: 2px solid white; + + bottom: 20px; + right: 0; + padding: 0.5rem; + font-size: 0.9rem; +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/components/Stories.tsx b/03_source/mobile.trunk/src/pages/DemoInstagramClone/components/Stories.tsx new file mode 100644 index 0000000..c902b30 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/components/Stories.tsx @@ -0,0 +1,27 @@ +import { IonCol, IonRouterLink, IonRow } from '@ionic/react'; +import styles from './Stories.module.scss'; + +const Stories = (props): React.JSX.Element => { + const { profiles } = props; + + return ( + +
+ {profiles.map((story, index) => { + return ( + + story avatar + {index === 0 &&
+
} + + +

{index === 0 ? 'Your story' : story.username}

+
+
+ ); + })} +
+
+ ); +}; + +export default Stories; diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/index.tsx b/03_source/mobile.trunk/src/pages/DemoInstagramClone/index.tsx new file mode 100644 index 0000000..ab4b29b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/index.tsx @@ -0,0 +1,91 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { bagOutline, cloudOutline, home, playCircleOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +// import Tab1 from './AppPages/Tab1'; +// import Tab2 from './AppPages/Tab2'; + +import './style.scss'; +import Home from './pages/Home'; +import Tab2 from './pages/Tab2'; +import Tab3 from './pages/Tab3'; + +import './theme/variables.scss'; +import { ProfileStore } from './pages/ProfileStore'; +import Profile from './pages/Profile'; +import MyProfile from './pages/MyProfile'; + +function DemoInstagramClone() { + const profile = ProfileStore.useState((s) => s.profile); + + return ( + + + {/* + + + + + + + + + + */} + + + + + + + + + + + + + + + + + + + + + + + {/* + + + Dashboard + + + + Search + + */} + + + + + + + + + + + + + + + + + tab avatar + + + + ); +} + +export default DemoInstagramClone; diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Home.module.scss b/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Home.module.scss new file mode 100644 index 0000000..48a1bdb --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Home.module.scss @@ -0,0 +1,198 @@ +.postsContainer { + + margin-top: 1.5rem; + margin-bottom: 1.5rem; +} + +.postContainer { + + display: flex; + flex-direction: column; + margin-top: 1rem; +} + +.postProfile { + + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + padding-right: 0.75rem; + padding-left: 0.75rem; +} + +.postProfileInfo ion-avatar { + + height: 2.2rem; + width: 2.2rem; +} + +.postProfileInfo p { + + margin: 0; + padding: 0; + margin-left: 0.5rem; + font-weight: 500; + font-size: 0.9rem; +} + +.postProfileInfo { + + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + align-content: center; +} + +.postImage { + + border-top: 1px solid rgb(216, 216, 216); + margin-top: 0.5rem; + height: 20rem; + width: 100%; +} + +.postActionsContainer { + + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + padding-right: 0.75rem; + padding-left: 0.75rem; + margin-top: 0.5rem; +} + +.postActions { + + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.postActions ion-icon, +.postBookmark ion-icon { + + font-size: 1.5rem; +} + +.postActions ion-icon:not(:first-child) { + + padding-left: 0.7rem; +} + +.postLikesContainer { + + padding-left: 0.75rem; + margin-top: 0.5rem; +} + +.postLikesContainer p { + + margin: 0; + padding: 0; + font-weight: 200 !important; + font-size: 0.8rem; +} + +.postLikedName { + + font-weight: 600; +} + +.postCaption { + + padding-left: 0.75rem; + padding-right: 0.75rem; + margin-top: 0.3rem; +} + +.postCaption p { + + margin: 0; + padding: 0; + font-weight: 200 !important; + font-size: 0.8rem; +} + +.postName { + + font-weight: 600 !important; +} + +.postComments { + + padding-left: 0.75rem; + padding-right: 0.75rem; + margin-top: 0.5rem; +} + +.postComments p { + + margin: 0; + padding: 0; + color:rgb(175, 175, 175); + font-size: 0.8rem; +} + +.postAddComment { + + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.postAddCommentProfile { + + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + align-items: center; + margin-top: 0.5rem; +} + +.postAddCommentProfile ion-avatar { + + height: 1.9rem; + width: 1.9rem; +} + +.postAddCommentProfile p { + + padding-left: 0.75rem; + font-size: 0.8rem; + color: rgb(175, 175, 175); +} + +.postAddCommentActions { + + +} + +.postAddCommentActions ion-icon { + + padding-left: 0.5rem; +} + +.postTime { + + padding-left: 0.75rem; + padding-right: 0.75rem; + margin-top: 0rem; +} + +.postTime p { + + margin: 0; + padding: 0; + color: rgb(175, 175, 175); + font-size: 0.6rem; +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Home.tsx b/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Home.tsx new file mode 100644 index 0000000..64f15f8 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Home.tsx @@ -0,0 +1,66 @@ +import { + IonButton, + IonButtons, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import { + addCircleOutline, + chevronBackOutline, + heartOutline, + paperPlaneOutline, +} from 'ionicons/icons'; +import Feed from '../components/Feed'; +import Stories from '../components/Stories'; +import { PostStore } from './PostStore'; +import { ProfilesStore } from './ProfilesStore'; + +const Home = (): React.JSX.Element => { + const profiles = ProfilesStore.useState((s) => s.profiles); + const posts = PostStore.useState((s) => s.posts); + + const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + + handleBackClick()}> + + + + main logo + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +export default Home; diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/MyProfile.tsx b/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/MyProfile.tsx new file mode 100644 index 0000000..f643088 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/MyProfile.tsx @@ -0,0 +1,160 @@ +import { + IonButton, + IonButtons, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonToolbar, + useIonViewWillEnter, +} from '@ionic/react'; +import { + addCircleOutline, + bookmarksOutline, + chevronDown, + gridOutline, + menuOutline, +} from 'ionicons/icons'; +import { useState } from 'react'; +import styles from './Profile.module.scss'; + +import { ProfilesStore } from './ProfilesStore'; +import { ProfileStore } from './ProfileStore'; + +const MyProfile = (): React.JSX.Element => { + const currentProfile = ProfileStore.useState((s) => s.profile); + const profiles = ProfilesStore.useState((s) => s.profiles); + const [profile, setProfile] = useState(false); + + useIonViewWillEnter(() => { + const profileID = currentProfile.id; + const tempProfile = profiles.filter((p) => parseInt(p.id) === parseInt(profileID))[0]; + setProfile(tempProfile); + }); + + return ( + + + + +

+ {profile.username} + +

+
+ + + + + + + + + +
+
+ + + + + + profile avatar + + + + + + + {profile.posts && profile.posts.length} + + Posts + + + + {profile.followers} + Followers + + + + {profile.following} + Following + + + + + + + +

+ {profile.firstname} {profile.surname} +

+

{profile.title}

+

{profile.bio}

+ + {profile.link} + +
+
+ + + + + Edit Profile + + + + + + Promotions + + + + + + Insights + + + +
+ + + + + + + + + + + + + {profile.posts && + profile.posts.map((post, index) => { + return ( + + post + + ); + })} + +
+
+ ); +}; + +export default MyProfile; diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/PostStore.tsx b/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/PostStore.tsx new file mode 100644 index 0000000..441b84b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/PostStore.tsx @@ -0,0 +1,139 @@ +import { Store } from 'pullstate'; + +export const PostStore = new Store({ + posts: [ + { + id: 1, + image: 'https://ionic.io/img/ioniconf/ioniconf-open-graph.png', + caption: 'Ioniconf 2021! Register Now!', + likes: 73, + liked: false, + profile_id: 6, + time: '1 hour ago', + comments: [ + { + profile_id: 3, + comment: 'Test', + }, + ], + }, + + { + id: 2, + image: + 'https://creativetacos.com/wp-content/uploads/2019/08/Free-Chipper-Personal-Finance-App-Kit.jpg', + caption: 'Ionic React Hub! UI Components, Templates, Clones and more!', + likes: 73, + liked: true, + profile_id: 1, + time: '1 hour ago', + comments: [ + { + profile_id: 3, + comment: 'Test', + }, + { + profile_id: 3, + comment: 'Test', + }, + { + profile_id: 3, + comment: 'Test', + }, + ], + }, + + { + id: 3, + image: 'https://cdn.buttercms.com/AIcP6e8FRx6fgsKa7bvy', + caption: 'Join the first ever Ionic Event!', + likes: 73, + liked: false, + profile_id: 4, + time: '2 hours ago', + comments: [ + { + profile_id: 1, + comment: 'Test', + }, + { + profile_id: 2, + comment: 'Test', + }, + ], + }, + + { + id: 4, + image: 'https://ionicframework.com/img/meta/ionic-framework-og.png', + caption: 'Build cross platform mobile apps with the Ionic Framework!', + likes: 73, + liked: false, + profile_id: 2, + time: '3 hours ago', + comments: [ + { + profile_id: 1, + comment: 'Test', + }, + { + profile_id: 2, + comment: 'Test', + }, + ], + }, + ], +}); + +export const likePost = (event, postID, liked) => { + event.target.classList.add('animate__heartBeat'); + + if (!liked) { + document.getElementById(`postLike_${postID}`).style.display = 'inline'; + } + + setTimeout(() => { + event.target.classList.remove('animate__heartBeat'); + document.getElementById(`postLike_${postID}`).style.display = 'none'; + }, 850); + + PostStore.update((s) => { + s.posts.find((p, index) => + parseInt(p.id) === parseInt(postID) ? (s.posts[index].liked = liked ? false : true) : false + ); + }); + + if (liked) { + PostStore.update((s) => { + s.posts.find((p, index) => + parseInt(p.id) === parseInt(postID) + ? (s.posts[index].likes = s.posts[index].likes++) + : false + ); + }); + } else { + PostStore.update((s) => { + s.posts.find((p, index) => + parseInt(p.id) === parseInt(postID) + ? (s.posts[index].likes = s.posts[index].likes--) + : false + ); + }); + } +}; + +export const addPost = (newPost) => { + PostStore.update((s) => { + s.posts = [...s.posts, newPost]; + }); +}; + +export const addCommentToPost = (newComment, postID) => { + PostStore.update((s) => { + s.posts.find((p, index) => + parseInt(p.id) === parseInt(postID) + ? (s.posts[index].comments = [...s.posts[index].comments, newComment]) + : false + ); + }); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Profile.module.scss b/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Profile.module.scss new file mode 100644 index 0000000..c749657 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Profile.module.scss @@ -0,0 +1,100 @@ +.username { + + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + align-items: center; + font-weight: 600; +} + +.username ion-icon { + + font-size: 0.8rem; + margin-left: 0.3rem; +} + +.label { + + color: black; + font-size: 0.7rem; + font-weight: 500; + font-family: Arial, Helvetica, sans-serif !important; + text-transform: lowercase; +} + +.label::first-letter { + + text-transform: uppercase; +} + +.value { + + font-size: 1rem; +} + +.profileAvatar { + + border-radius: 500px; + width: 5.5rem; + height: auto; +} + +.profileInfo { + + display: flex; + flex-direction: column; + padding-left: 0.75rem; + padding-right: 0.75rem; +} + +.profileInfo p, +.profileInfo a { + + // padding: 0.1rem 0 0.1rem 0; + margin: 0; + font-size: 0.9rem; +} + +.profileUsername { + + font-weight: 600; +} + +.profileTitle { + + color: rgb(136, 136, 136); +} + +.profileLink { + + color: rgb(22, 60, 131); + text-decoration: none; +} + +.profileActions ion-button { + + height: 2.3rem; + --border-radius: 5px; + font-weight: 600; +} + +.lightButton { + + --color: rgb(65, 65, 65); + --color-activated: rgb(65, 65, 65); + --background-hover: white; + --background-focused: white; + --background-activated: white; + --border-color: rgb(231, 231, 231); + --border-width: 2px; + font-size: 0.8rem; +} + +.postCol { + + --ion-grid-column-padding: 0rem; + padding: 0.1rem; + padding-bottom: 0.01rem !important; + padding-top: 0.01rem !important; +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Profile.tsx b/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Profile.tsx new file mode 100644 index 0000000..aa07b55 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Profile.tsx @@ -0,0 +1,183 @@ +import { + IonBackButton, + IonButton, + IonButtons, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonToolbar, + useIonViewWillEnter, +} from '@ionic/react'; +import { + addCircleOutline, + arrowBackOutline, + bookmarksOutline, + chevronDown, + ellipsisVertical, + gridOutline, + menuOutline, + personOutline, +} from 'ionicons/icons'; +import { useState } from 'react'; +import { useParams } from 'react-router'; +import styles from './Profile.module.scss'; + +import { ProfilesStore } from './ProfilesStore'; +import { ProfileStore } from './ProfileStore'; + +const Profile = (): React.JSX.Element => { + const params = useParams(); + const profiles = ProfilesStore.useState((s) => s.profiles); + const currentProfile = ProfileStore.useState((s) => s.profile); + const [profile, setProfile] = useState(false); + + useIonViewWillEnter(() => { + const profileID = params.id; + const tempProfile = profiles.filter((p) => parseInt(p.id) === parseInt(profileID))[0]; + setProfile(tempProfile); + }); + + return ( + + + + + {profile.id === currentProfile.id ? ( +

+ {profile.username} + +

+ ) : ( + <> + +

+ {profile.username} +

+ + )} +
+ + + {profile.id === currentProfile.id ? ( + <> + + + + + + + + ) : ( + + + + )} + +
+
+ + + + + + profile avatar + + + + + + + {profile.posts && profile.posts.length} + + Posts + + + + {profile.followers} + Followers + + + + {profile.following} + Following + + + + + + + +

+ {profile.firstname} {profile.surname} +

+

{profile.title}

+

{profile.bio}

+ + {profile.link} + +
+
+ + + + + Follow + + + + + + Message + + + + + + + + + +
+ + + + + + + + + + + + + {profile.posts && + profile.posts.map((post, index) => { + return ( + + post + + ); + })} + +
+
+ ); +}; + +export default Profile; diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/ProfileStore.tsx b/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/ProfileStore.tsx new file mode 100644 index 0000000..42f4d58 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/ProfileStore.tsx @@ -0,0 +1,20 @@ +import { Store } from 'pullstate'; + +export const ProfileStore = new Store({ + profile: { + id: 1, + firstname: 'Alan', + surname: 'Montgomery', + avatar: '/assets/alan.jpg', + followers: 0, + following: 0, + }, + posts: [], + feed: [], +}); + +export const addProfilePost = (newPost) => { + ProfileStore.update((s) => { + s.posts = [...s.posts, newPost]; + }); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/ProfilesStore.tsx b/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/ProfilesStore.tsx new file mode 100644 index 0000000..63813c5 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/ProfilesStore.tsx @@ -0,0 +1,172 @@ +import { Store } from 'pullstate'; + +export const ProfilesStore = new Store({ + profiles: [ + { + id: 1, + firstname: 'Alan', + surname: 'Montgomery', + username: 'alanmontgomery', + title: 'Mobile Team Lead', + bio: 'Full Stack 🤓 Mobile Team Lead/Senior React Dev', + link: 'alanmontgomery.co.uk', + avatar: '/assets/alan.jpg', + followers: '1,470', + following: '230', + posts: [ + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + ], + }, + { + id: 2, + firstname: 'Max', + surname: 'Lynch', + username: 'maxlynch', + title: 'CEO Ionic', + bio: 'Co-founder/CEO @ionicframework. Created @capacitorjs. Gamer. @ManUtd fan.', + link: 'maxlynch.com', + avatar: 'https://pbs.twimg.com/profile_images/1318970727173885953/bln98FNj_400x400.jpg', + followers: '21.1K', + following: '1,200', + posts: [{}, {}, {}, {}, {}, {}, {}, {}, {}], + }, + { + id: 3, + firstname: 'Ben', + surname: 'Sperry', + username: 'bensperry', + title: 'CDO Ionic', + bio: 'Co-founder / CDO @ionicframework. Creator of @ionicons. Product designer. Pixel junkie. Forest explorer.', + link: 'bensperry.com', + avatar: 'https://pbs.twimg.com/profile_images/1328390491126308864/jHHgl5Dm_400x400.jpg', + followers: '800', + following: '700', + posts: [{}, {}, {}, {}, {}, {}, {}, {}, {}], + }, + { + id: 4, + firstname: 'Matt', + surname: 'Netkow', + username: 'mattnetkow', + title: 'Head of Product Marketing', + bio: 'I help web developers build cross-platform Web Native apps. @IonicFramework: Head of Product Marketing', + link: 'webnative.tech', + avatar: 'https://pbs.twimg.com/profile_images/1323383930150621187/GKc0nVzi_400x400.jpg', + followers: '1,200', + following: '900', + posts: [{}, {}, {}, {}, {}, {}, {}, {}, {}], + }, + { + id: 5, + firstname: 'Liam', + surname: 'DeBeasi', + username: 'liamdebeasi', + title: 'Software Engineer', + bio: 'Software Engineer at @ionicframework', + link: 'liamdebeasi.com', + avatar: 'https://pbs.twimg.com/profile_images/1105953692669366273/ZNK4lRAJ_400x400.jpg', + followers: '871', + following: '510', + posts: [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}], + }, + { + id: 6, + firstname: 'Mike', + surname: 'Hartington', + username: 'mikehartington', + title: 'Senior Dev Rel', + bio: 'Google Developer Expert. Mediocre at best. he/him. npx mhartington', + link: 'mhartington.io', + avatar: 'https://pbs.twimg.com/profile_images/1084993841898446849/DJ8XtR6L_400x400.jpg', + followers: '12.3K', + following: '2,200', + posts: [ + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + ], + }, + { + id: 7, + firstname: 'Adam', + surname: 'Bradley', + username: 'adambradley', + title: 'Director of Technology', + bio: 'Proud dad, husband, veteran & dogs best friend. Typos are my own', + link: 'ionicframework.com', + avatar: 'https://pbs.twimg.com/profile_images/909075942320025600/hfYqicUk_400x400.jpg', + followers: '613', + following: '571', + posts: [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}], + }, + { + id: 8, + firstname: 'Brody', + surname: 'Kidd', + username: 'brodykidd', + title: 'Enterprise Account Manager', + bio: 'Enterprise Account Manager | @ionicframework | @getcapacitor | @stenciljs', + link: 'ionicframework.com', + avatar: 'https://pbs.twimg.com/profile_images/477539679567228928/JObyaUW__400x400.jpeg', + followers: '677', + following: '219', + posts: [{}, {}, {}, {}, {}, {}, {}], + }, + ], +}); + +export const addProfilePost = (newPost) => { + ProfilesStore.update((s) => { + s.posts = [...s.posts, newPost]; + }); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Tab1.css b/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Tab1.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Tab2.css b/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Tab2.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Tab2.tsx b/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Tab2.tsx new file mode 100644 index 0000000..a46ce0c --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Tab2.tsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab2.css'; + +const Tab2 = (): React.JSX.Element => { + return ( + + + + Tab 2 + + + + + + Tab 2 + + + + + + ); +}; + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Tab3.css b/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Tab3.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Tab3.tsx b/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Tab3.tsx new file mode 100644 index 0000000..094a323 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/pages/Tab3.tsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab3.css'; + +const Tab3 = (): React.JSX.Element => { + return ( + + + + Tab 3 + + + + + + Tab 3 + + + + + + ); +}; + +export default Tab3; diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/style.scss b/03_source/mobile.trunk/src/pages/DemoInstagramClone/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile.trunk/src/pages/DemoInstagramClone/theme/variables.scss b/03_source/mobile.trunk/src/pages/DemoInstagramClone/theme/variables.scss new file mode 100644 index 0000000..64b2b41 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoInstagramClone/theme/variables.scss @@ -0,0 +1,126 @@ +.demo-instagram-clone { + /* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + + * { + font-family: Arial, Helvetica, sans-serif !important; + scroll-behavior: smooth; + } + + ::-webkit-scrollbar, + ::-webkit-scrollbar-thumb { + width: 0px; + } + + /** Ionic CSS Variables **/ + :root { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + } + + :root { + --ion-toolbar-background: white; + --ion-tab-bar-color: black; + --ion-tab-bar-color-selected: black; + --ion-tab-bar-border-color: rgb(235, 235, 235); + } + + ion-tab-button ion-icon { + font-size: 1.6rem; + } + + ion-tab-button img { + border-radius: 500px; + height: 1.8rem; + border: 1px solid black; + } + + ion-tab-bar { + height: 3rem; + } + + ion-toolbar { + --border-style: none; + --padding-start: 1rem; + --padding-end: 1rem; + --padding-top: 0.5rem; + } + + ion-toolbar ion-icon { + font-weight: 900 !important; + font-size: 1.6rem; + } + + ion-toolbar ion-button:not(:last-child) { + padding-right: 0.3rem; + } +} diff --git a/03_source/mobile.trunk/src/pages/DemoKanbanBoard/AppPages/Tab1.jsx b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/DemoKanbanBoard/AppPages/Tab2.jsx b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/DemoKanbanBoard/NOTES.md b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/NOTES.md new file mode 100644 index 0000000..f7a8f29 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/NOTES.md @@ -0,0 +1 @@ +# REQ0132 diff --git a/03_source/mobile.trunk/src/pages/DemoKanbanBoard/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoKanbanBoard/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoKanbanBoard/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoKanbanBoard/components/Board/BoardItem.tsx b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/components/Board/BoardItem.tsx new file mode 100644 index 0000000..1882c7b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/components/Board/BoardItem.tsx @@ -0,0 +1,65 @@ +import { IonBadge, IonIcon, IonItem, IonLabel, IonReorder } from '@ionic/react'; +import { arrowBackOutline, arrowForwardOutline } from 'ionicons/icons'; + +import styles from '../../pages/Kanban.module.scss'; + +export const BoardItem = ({ + id, + item, + index, + type, + moveToggled, + handleMove, +}): React.JSX.Element => ( + +
+
+ +
+

{item.name}

+ + +  {type.name} + +
+

{item.summary}

+
+
+
+ {item.labels.map((label, index2) => { + return ( + + {label} +    + + ); + })} +
+ +
+ {id !== 1 && moveToggled && ( +
handleMove(e, 'Left', id, id - 1, item.id)} + > + +
+ )} + + {id !== 3 && moveToggled && ( +
handleMove(e, 'Right', id, id + 1, item.id)} + > + +
+ )} +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoKanbanBoard/components/Board/ListItem.tsx b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/components/Board/ListItem.tsx new file mode 100644 index 0000000..26d895d --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/components/Board/ListItem.tsx @@ -0,0 +1,22 @@ +import { IonIcon, IonItem, IonLabel, IonReorder } from '@ionic/react'; + +import styles from '../../pages/Kanban.module.scss'; + +export const ListItem = ({ id, item, index, type, moveToggled, handleMove }): React.JSX.Element => ( + + +
+ +

{item.name}

+

{item.summary}

+
+ +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoKanbanBoard/components/Menu.css b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/components/Menu.css new file mode 100644 index 0000000..0ca47a2 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/components/Menu.css @@ -0,0 +1,113 @@ +ion-menu ion-content { + --background: var(--ion-item-background, var(--ion-background-color, #fff)); +} + +ion-menu.md ion-content { + --padding-start: 8px; + --padding-end: 8px; + --padding-top: 20px; + --padding-bottom: 20px; +} + +ion-menu.md ion-list { + padding: 20px 0; +} + +ion-menu.md ion-note { + margin-bottom: 30px; +} + +ion-menu.md ion-list-header, ion-menu.md ion-note { + padding-left: 10px; +} + +ion-menu.md ion-list#inbox-list { + border-bottom: 1px solid var(--ion-color-step-150, #d7d8da); +} + +ion-menu.md ion-list#inbox-list ion-list-header { + font-size: 22px; + font-weight: 600; + min-height: 20px; +} + +ion-menu.md ion-list#labels-list ion-list-header { + font-size: 16px; + margin-bottom: 18px; + color: #757575; + min-height: 26px; +} + +ion-menu.md ion-item { + --padding-start: 10px; + --padding-end: 10px; + border-radius: 4px; +} + +ion-menu.md ion-item.selected { + --background: rgba(var(--ion-color-primary-rgb), 0.14); +} + +ion-menu.md ion-item.selected ion-icon { + color: var(--ion-color-primary); +} + +ion-menu.md ion-item ion-icon { + color: #616e7e; +} + +ion-menu.md ion-item ion-label { + font-weight: 500; +} + +ion-menu.ios ion-content { + --padding-bottom: 20px; +} + +ion-menu.ios ion-list { + padding: 20px 0 0 0; +} + +ion-menu.ios ion-note { + line-height: 24px; + margin-bottom: 20px; +} + +ion-menu.ios ion-item { + --padding-start: 16px; + --padding-end: 16px; + --min-height: 50px; +} + +ion-menu.ios ion-item ion-icon { + font-size: 24px; + color: #73849a; +} + +ion-menu.ios ion-item .selected ion-icon { + color: var(--ion-color-primary); +} + +ion-menu.ios ion-list#labels-list ion-list-header { + margin-bottom: 8px; +} + +ion-menu.ios ion-list-header, +ion-menu.ios ion-note { + padding-left: 16px; + padding-right: 16px; +} + +ion-menu.ios ion-note { + margin-bottom: 8px; +} + +ion-note { + display: inline-block; + font-size: 16px; + color: var(--ion-color-medium-shade); +} + +ion-item.selected { + --color: var(--ion-color-primary); +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoKanbanBoard/components/Menu.tsx b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/components/Menu.tsx new file mode 100644 index 0000000..7400d03 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/components/Menu.tsx @@ -0,0 +1,109 @@ +import { + IonContent, + IonIcon, + IonItem, + IonLabel, + IonList, + IonListHeader, + IonMenu, + IonMenuToggle, + IonNote, +} from '@ionic/react'; + +import { useLocation } from 'react-router-dom'; +import { + addOutline, + addSharp, + barChartOutline, + barChartSharp, + bookmarkOutline, + listOutline, + listSharp, +} from 'ionicons/icons'; +import './Menu.css'; + +import { SettingsStore } from '../store'; +import { handleViewChange } from '../store/SettingsStore'; + +const appPages = [ + { + title: 'Add Item', + url: '/page/add/0', + iosIcon: addOutline, + mdIcon: addSharp, + }, +]; + +const actions = [ + { + title: 'Board View', + slug: 'Board', + url: false, + onClick: () => handleViewChange('Board'), + iosIcon: barChartOutline, + mdIcon: barChartSharp, + }, + { + title: 'List View', + slug: 'List', + url: false, + onClick: () => handleViewChange('List'), + iosIcon: listOutline, + mdIcon: listSharp, + }, +]; + +const Menu = (): React.JSX.Element => { + const location = useLocation(); + const view = SettingsStore.useState((s) => s.view); + + return ( + + + + Welcome back + Ionic Kanban Board + {appPages.map((appPage, index) => { + return ( + + + + {appPage.title} + + + ); + })} + + + + Toggle View + View items in list or board view + + {actions.map((action, index) => { + return ( + + + + {action.title} + + + ); + })} + + + + ); +}; + +export default Menu; diff --git a/03_source/mobile.trunk/src/pages/DemoKanbanBoard/index.tsx b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/index.tsx new file mode 100644 index 0000000..b69b165 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/index.tsx @@ -0,0 +1,46 @@ +import { + IonIcon, + IonLabel, + IonRouterOutlet, + IonSplitPane, + IonTabBar, + IonTabButton, + IonTabs, +} from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +// import Tab1 from './AppPages/Tab1'; +// import Tab2 from './AppPages/Tab2'; + +// import './style.scss'; +import './theme/variables.scss'; +import Menu from './components/Menu'; +import Add from './pages/Add'; +import Kanban from './pages/Kanban'; + +function DemoKanbanBoard() { + return ( + + + + + + + + + + + + + + + + + + + ); +} + +export default DemoKanbanBoard; diff --git a/03_source/mobile.trunk/src/pages/DemoKanbanBoard/pages/Add.tsx b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/pages/Add.tsx new file mode 100644 index 0000000..fd2b994 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/pages/Add.tsx @@ -0,0 +1,152 @@ +import { + IonButton, + IonButtons, + IonContent, + IonHeader, + IonIcon, + IonInput, + IonItem, + IonLabel, + IonPage, + IonSelect, + IonSelectOption, + IonTextarea, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import { checkmarkSharp, chevronBackSharp } from 'ionicons/icons'; +import { useState } from 'react'; +import { useParams } from 'react-router'; +import { SettingsStore } from '../store'; +import { addItem } from '../store/SettingsStore'; + +const Add = (): React.JSX.Element => { + const categories = SettingsStore.useState((s) => s.categories); + const types = SettingsStore.useState((s) => s.types); + const router = useIonRouter(); + const { category_id = false } = useParams(); + + const useFormInput = (initialValue = '') => { + const [value, setValue] = useState(initialValue); + + const onChange = (e) => { + setValue(e.target.value); + }; + + return { + value, + onIonChange: onChange, + }; + }; + + const formFields = [ + { + fields: { + type: 'text', + id: 'name', + placeholder: 'Enter a name...', + }, + state: useFormInput(), + label: 'Name', + options: false, + }, + { + fields: { + type: 'select', + id: 'category_id', + placeholder: 'Select a category', + }, + state: useFormInput(parseInt(category_id)), + label: 'Category', + options: categories, + }, + { + fields: { + type: 'select', + id: 'type_id', + placeholder: 'Select a type...', + }, + state: useFormInput(), + label: 'Type', + options: types, + }, + { + fields: { + type: 'textarea', + id: 'summary', + placeholder: 'Enter a summary...', + }, + state: useFormInput(), + label: 'Summary', + options: false, + }, + ]; + + const add = () => { + const data = []; + + formFields.forEach((field) => { + data[field.fields.id] = field.state.value; + }); + + addItem(data); + router.goBack(); + }; + + return ( + + + + + router.goBack()}> + + + + Add Item + + + + + + + Add Item + + + + {formFields.map((formField, index) => { + const { state, fields, label, options } = formField; + + return ( + + {label} + + {fields.type === 'text' && } + {fields.type === 'textarea' && } + {fields.type === 'select' && ( + + {options.map((option) => { + const optionName = option.name.charAt(0).toUpperCase() + option.name.slice(1); + + return ( + + {optionName} + + ); + })} + + )} + + ); + })} + + + +   Save + + + + ); +}; + +export default Add; diff --git a/03_source/mobile.trunk/src/pages/DemoKanbanBoard/pages/Kanban.module.scss b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/pages/Kanban.module.scss new file mode 100644 index 0000000..b444de1 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/pages/Kanban.module.scss @@ -0,0 +1,180 @@ +.categorySlide { + display: flex; + flex-direction: column; +} + +@media only screen and (min-width: 768px) { + .categorySlide { + width: 33%; + } + + .categoryName { + margin-top: 1rem; + width: 92.6% !important; + } +} + +.categoryName { + background-color: #3273ec; + color: white; + width: 100%; + padding: 1rem; + font-size: 1.3rem; + margin-bottom: -0.75rem; +} + +.categoryNameList { + background-color: #3273ec; + color: white; + width: 100%; + padding: 1rem; + font-size: 1.3rem; +} + +.kanbanItemList { + margin: 0rem !important; + border-radius: 0 !important; + border: none !important; + border-bottom: 1px solid rgb(221, 221, 221) !important; +} + +.kanbanItem, +.kanbanItemList { + padding: 1.5rem; + border: 1px solid rgb(221, 221, 221); + border-radius: 5px; + background-color: white; + --background: white; + + .itemTitle { + display: flex; + flex-direction: row; + justify-content: space-between; + align-content: center; + align-items: center; + margin-bottom: 1rem; + + .itemType { + font-size: 0.75rem; + + ion-icon { + font-size: 0.75rem; + padding: 0 !important; + } + } + + h3 { + color: black; + font-weight: 450; + } + } + + .itemTitleList { + display: flex; + flex-direction: row; + justify-content: space-between; + align-content: center; + align-items: center; + margin-bottom: 0rem; + + ion-card-subtitle { + color: black; + margin-top: 0.25rem; + font-size: 0.6rem; + } + } + + .itemList { + display: flex !important; + flex-direction: row !important; + align-content: center; + justify-content: space-between; + align-items: center; + + ion-icon { + font-size: 2rem; + padding-left: 0.5rem; + } + } + + .itemType, + .itemTypeList { + display: flex; + flex-direction: row; + align-content: center; + align-items: center; + justify-content: space-between; + width: fit-content; + } + + .itemTypeList { + margin-right: 0.3rem; + } + + .itemLabels { + margin-top: 1rem; + } + + .itemLabelsList { + margin-top: 0.2rem; + + ion-badge { + font-size: 0.5rem; + } + } + + .itemActions { + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + align-items: center; + margin-top: 1rem; + + div { + width: 5rem; + background-color: rgb(238, 238, 238); + color: rgb(204, 204, 204); + margin-left: 0.2rem; + margin-right: 0.2rem; + + padding: 0.5rem; + border-radius: 5px; + } + + .moveRight { + text-align: right; + } + + .moveLeft { + text-align: left; + } + } +} + +.itemAdd { + display: flex; + flex-direction: row; + align-content: center; + justify-content: center; + align-items: center; + padding: 1rem; + width: 91.6% !important; + background-color: rgb(241, 241, 241); + color: rgb(179, 179, 179); + border: 2px dashed rgb(216, 216, 216); + border-radius: 5px; + font-size: 1.5rem; + margin-bottom: 2rem; + margin-top: 1rem; +} + +.kanbanItem { + margin: 0 !important; + margin-top: 1rem !important; + --padding: 0 !important; +} + +.listReorder { + padding: 0 !important; +} diff --git a/03_source/mobile.trunk/src/pages/DemoKanbanBoard/pages/Kanban.tsx b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/pages/Kanban.tsx new file mode 100644 index 0000000..0d56362 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/pages/Kanban.tsx @@ -0,0 +1,158 @@ +import { + IonButton, + IonButtons, + IonCardSubtitle, + IonContent, + IonHeader, + IonIcon, + IonMenuButton, + IonPage, + IonReorder, + IonReorderGroup, + // TODO: fix missing ionslide in new ionic + // IonSlide, + // IonSlides, + IonTitle, + IonToolbar, + useIonViewDidEnter, +} from '@ionic/react'; +import { addOutline, copyOutline, createOutline } from 'ionicons/icons'; +import React, { useEffect, useState } from 'react'; +import { Link } from 'react-router-dom'; +import { BoardItem } from '../components/Board/BoardItem'; +import { ListItem } from '../components/Board/ListItem'; +import { SettingsStore } from '../store'; + +import styles from './Kanban.module.scss'; + +const Kanban = (): React.JSX.Element => { + return <>// TODO: fix missing ionslide in new ionic; + const view = SettingsStore.useState((s) => s.view); + const categories = SettingsStore.useState((s) => s.categories); + const types = SettingsStore.useState((s) => s.types); + const items = SettingsStore.useState((s) => s.items); + + const [moveToggled, setMoveToggled] = useState(view === 'Board' ? true : false); + const [categoryItems, setCategoryItems] = useState([]); + + useEffect(() => { + setCategoryItems(items); + }, [items]); + + const handleReorder = (e) => { + // In a real world, production app + // In here we could re-arrange and sort array of items + // To be in sync with our state or simply save the new main array + + e.detail.complete(); + }; + + const handleMove = (e, direction, fromCategoryID, toCategoryID, itemID) => { + const fromIndex = categoryItems.findIndex((c) => c.id === fromCategoryID); + const toIndex = categoryItems.findIndex((c) => c.id === toCategoryID); + + const tempCategoryItems = [...categoryItems]; + const itemIndex = categoryItems[fromIndex].items.findIndex((i) => i.id === itemID); + const itemElement = document.querySelector(`#item_${fromCategoryID}_${itemIndex}`); + + const tempItem = { ...categoryItems[fromIndex].items[itemIndex] }; + tempCategoryItems[toIndex].items.push(tempItem); + + itemElement.classList.add(`animate__slideOut${direction}`); + + setTimeout(() => { + itemElement.classList.remove(`animate__slideOut${direction}`); + tempCategoryItems[fromIndex].items.splice(itemIndex, 1); + setCategoryItems(tempCategoryItems); + }, 700); + }; + + useIonViewDidEnter(() => { + document.querySelector('#slider') && document.querySelector('#slider').update(); + }); + + return ( + + + + + + + {view} View + {view === 'Board' && ( + + setMoveToggled(!moveToggled)}> + + + + )} + + + + + + + {view} View + + + + + {categoryItems.map(({ items, id }, index) => { + const name = categories.filter((c) => c.id === id)[0].name; + + return ( + + + {name} ({items.length}) + + + + {items.map((item, index) => { + const type = types.filter((t) => t.id === item.typeID)[0]; + + return ( + + {view === 'Board' && ( + + )} + {view === 'List' && ( + + )} + + ); + })} + + + + + + + ); + })} + + + + ); +}; + +export default Kanban; diff --git a/03_source/mobile.trunk/src/pages/DemoKanbanBoard/store/SettingsStore.ts b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/store/SettingsStore.ts new file mode 100644 index 0000000..93cd3a6 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/store/SettingsStore.ts @@ -0,0 +1,164 @@ +import { arrowUpOutline, bugOutline, flashOutline, keyOutline } from 'ionicons/icons'; +import { Store } from 'pullstate'; + +const SettingsStore = new Store({ + view: 'Board', + filter: '', + types: [ + { + id: 1, + name: 'bug', + color: '#ec1111', + icon: bugOutline, + }, + { + id: 2, + name: 'improvement', + color: '#0bbe28', + icon: arrowUpOutline, + }, + { + id: 3, + name: 'enhancement', + color: '#680bbe', + icon: flashOutline, + }, + { + id: 4, + name: 'task', + color: '#0b7ebe', + icon: keyOutline, + }, + ], + categories: [ + { id: 1, name: 'To Do' }, + { id: 2, name: 'In Progress' }, + { id: 3, name: 'Done' }, + ], + items: [ + { + id: 1, + items: [ + { + id: 1, + name: 'Adding projects', + summary: 'Give users the option to add projects and add items to projects', + labels: ['CSS', 'Design', 'Framework'], + typeID: 3, + }, + { + id: 2, + name: 'Add filter by labels', + summary: + 'Add the option to filter items by labels on the board to give the user more control', + labels: ['Filtering', 'Labels'], + typeID: 2, + }, + { + id: 3, + name: "Can't move items after add", + summary: + 'When the user adds a new item, for some reason, it breaks the ability to move items across categories', + labels: ['Move', 'Array', 'Object', 'Broke'], + typeID: 1, + }, + { + id: 4, + name: 'Hook a database up', + summary: 'Link app to a database like MongoDB or Firebase for sync and save', + labels: ['Database', 'MongoDB', 'Firebase'], + typeID: 4, + }, + { + id: 5, + name: 'Add a remove option', + summary: 'Allow the user to remove items from list and board views', + labels: ['Remove', 'Delete', 'Item'], + typeID: 1, + }, + ], + }, + { + id: 2, + items: [ + { + id: 1, + name: 'Add a list view', + summary: 'Allow users to view items on boards in a list view', + labels: ['Kanban', 'List', 'View'], + typeID: 3, + }, + { + id: 2, + name: 'Write some fake tickets', + summary: 'Fill out this app with some fake, real looking tickets', + labels: ['Tickets', 'Todo', 'Items'], + typeID: 4, + }, + ], + }, + { + id: 3, + items: [ + { + id: 1, + name: 'Fix bug with IonSlide', + summary: 'When navigating to the add screen, and coming back, the slides are stuck', + labels: ['Ionic', 'Slides', 'Stuck'], + typeID: 1, + }, + { + id: 2, + name: 'Add slick features', + summary: + 'Add the ability to move cards or items from one category to another, still using IonSlides and IonReorder', + labels: ['Ionic', 'IonSlides', 'IonReorder'], + typeID: 2, + }, + { + id: 3, + name: 'Drag and drop items', + summary: + 'Give the user the ability to drag and drop items while still maintaining click functionality and propagation within the item itself', + labels: ['DND', 'Drag', 'Drop', 'Main feature'], + typeID: 4, + }, + ], + }, + ], +}); + +export default SettingsStore; + +export const addItem = (item) => { + console.log(item); + + SettingsStore.update((s) => { + const tempItems = [...s.items]; + const itemCategoryIndex = tempItems.findIndex( + (t) => parseInt(t.id) === parseInt(item.category_id) + ); + + console.log({ itemCategoryIndex }); + + tempItems[itemCategoryIndex].items.push({ + name: item.name, + summary: item.summary, + typeID: item.type_id, + labels: ['test', 'test1', 'test2', 'test3'], + }); + + s.items = tempItems; + }); +}; + +// export const removeFromCart = coffeeIndex => { + +// SettingsStore.update(s => { s.coffee_ids.splice(coffeeIndex, 1) }); +// } + +export const handleViewChange = (view) => { + SettingsStore.update((s) => { + s.view = view; + }); +}; diff --git a/03_source/mobile/src/pages/DemoKanbanBoard/store/index.ts b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/store/index.ts similarity index 100% rename from 03_source/mobile/src/pages/DemoKanbanBoard/store/index.ts rename to 03_source/mobile.trunk/src/pages/DemoKanbanBoard/store/index.ts diff --git a/03_source/mobile.trunk/src/pages/DemoKanbanBoard/style.scss b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile.trunk/src/pages/DemoKanbanBoard/theme/variables.scss b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/theme/variables.scss new file mode 100644 index 0000000..11fac0c --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoKanbanBoard/theme/variables.scss @@ -0,0 +1,100 @@ +/* +Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ +*/ + +.demo-kanban-board { + /** Ionic CSS Variables **/ + * { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + } + + :root { + --ion-background-color: rgb(241, 241, 241); + } + + ion-badge { + border-radius: 5px; + } + + ion-item.input { + --background: #ffffff !important; + --color: rgb(53, 53, 53) !important; + margin: 1rem; + border-radius: 5px; + } + + ion-reorder-group { + padding: 1rem; + } +} diff --git a/03_source/mobile.trunk/src/pages/DemoList/NOTES.md b/03_source/mobile.trunk/src/pages/DemoList/NOTES.md new file mode 100644 index 0000000..9b91446 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoList/NOTES.md @@ -0,0 +1 @@ +# REQ0133 diff --git a/03_source/mobile.trunk/src/pages/DemoList/TestContent.tsx b/03_source/mobile.trunk/src/pages/DemoList/TestContent.tsx new file mode 100644 index 0000000..39fd43b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoList/TestContent.tsx @@ -0,0 +1,13 @@ +import { format } from 'date-fns'; + +export const TestContent = { + eventDate: format(new Date(), 'yyyy-MM-dd'), + title: 'helloworld', + price: 123, + currency: 'HKD', + duration_m: 480, + ageBottom: 12, + ageTop: 48, + location: 'Hong Kong Island', + avatar: 'https://www.ionics.io/img/ionic-logo.png', +}; diff --git a/03_source/mobile.trunk/src/pages/DemoList/index.tsx b/03_source/mobile.trunk/src/pages/DemoList/index.tsx new file mode 100644 index 0000000..a0444f6 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoList/index.tsx @@ -0,0 +1,861 @@ +// REQ0054/user-setting +// +// PURPOSE: +// - Provides functionality view user profile +// +// RULES: +// - T.B.A. +// +import React, { useEffect, useRef, useState } from 'react'; +import { + IonHeader, + IonToolbar, + IonTitle, + IonContent, + IonPage, + IonButtons, + IonMenuButton, + IonGrid, + IonRow, + IonCol, + useIonRouter, + IonButton, + IonIcon, + IonPopover, + IonAvatar, + IonImg, + IonItem, + IonLabel, + IonList, + IonModal, + IonSearchbar, + useIonModal, + IonInput, + IonNote, + IonText, +} from '@ionic/react'; +import SpeakerItem from '../../components/SpeakerItem'; +import { Speaker } from '../../models/Speaker'; +import { Session } from '../../models/Schedule'; +import { connect } from '../../data/connect'; +import * as selectors from '../../data/selectors'; +import '../SpeakerList.scss'; +import { getEvents } from '../../api/getEvents'; +import { format } from 'date-fns'; +import { Event } from './types'; +import { + alertCircleOutline, + alertOutline, + apps, + appsOutline, + book, + bookOutline, + brushOutline, + calculatorOutline, + car, + cart, + cartOutline, + cashOutline, + chatbubbleEllipses, + chatbubbleEllipsesOutline, + chatbubbleOutline, + chevronBackOutline, + chevronForward, + chevronForwardOutline, + cloudOutline, + codeSlashOutline, + codeWorkingOutline, + colorPaletteOutline, + createOutline, + document, + documentTextOutline, + filmOutline, + flashOutline, + gift, + giftOutline, + globeSharp, + gridOutline, + heart, + imageOutline, + imagesOutline, + keyOutline, + languageOutline, + layers, + layersOutline, + list, + listCircle, + listOutline, + logInOutline, + logoFacebook, + mapOutline, + menuOutline, + paperPlaneOutline, + people, + person, + personCircleOutline, + personOutline, + pizzaOutline, + qrCodeOutline, + refreshOutline, + restaurant, + restaurantOutline, + settingsOutline, + shareSocialOutline, + statsChart, + sunny, + swapHorizontal, + trashOutline, + walkOutline, +} from 'ionicons/icons'; +import AboutPopover from '../../components/AboutPopover'; +import { OverlayEventDetail } from '@ionic/react/dist/types/components/react-component-lib/interfaces'; +import PATHS from '../../PATHS'; +import { logoutUser, setAccessToken, setIsLoggedIn } from '../../data/user/user.actions'; + +interface OwnProps {} + +interface StateProps { + speakers: Speaker[]; + speakerSessions: { [key: string]: Session[] }; +} + +interface DispatchProps { + logoutUser: typeof logoutUser; + setAccessToken: typeof setAccessToken; + setIsLoggedIn: typeof setIsLoggedIn; +} + +interface SettingsProps extends OwnProps, StateProps, DispatchProps {} + +const DemoList: React.FC = ({ + speakers, + speakerSessions, + logoutUser, + setAccessToken, + setIsLoggedIn, +}) => { + const [events, setEvents] = useState([]); + const [showPopover, setShowPopover] = useState(false); + const [popoverEvent, setPopoverEvent] = useState(); + const modal = useRef(null); + + const router = useIonRouter(); + + useEffect(() => { + getEvents().then(({ data }) => { + console.log({ data }); + setEvents(data); + }); + }, []); + + function handleBackButtonClick() { + router.goBack(); + } + + function handleLanguageClick() { + router.push(PATHS.CHANGE_LANGUAGE); + } + + function handleNotImplementedClick() { + router.push(PATHS.NOT_IMPLEMENTED); + } + + function handleDemoPageClick() { + router.push(PATHS.DEMO_PAGE); + } + + function handleServiceAgreementClick() { + router.push(PATHS.SERVICE_AGREEMENT); + } + + function handlePrivacyAgreementClick() { + router.push(PATHS.PRIVACY_AGREEMENT); + } + + const [showLogoutConfirmModal, setShowLogoutConfirmModal] = useState(false); + function handleConfirmLogoutClick() { + setShowLogoutConfirmModal(true); + } + + function handleLogoutClick() { + setAccessToken(); + setIsLoggedIn(false); + + router.push('/tabs', 'forward', 'replace'); + + setShowLogoutConfirmModal(false); + } + function handleLogoutCancel() { + setShowLogoutConfirmModal(false); + } + + function handleDemoReactShopClick() { + router.push(PATHS.DEMO_REACT_SHOP); + } + + return ( + + + + + {/* */} + handleBackButtonClick()}> + + + + +
+ + Setting +
+
+
+ + + + + Setting + + + + + router.push(PATHS.DEMO_WEATHER_APP_UI)}> + + Weather App + + + + + + router.push(PATHS.DEMO_2FA_EXAMPLE, 'forward')}> + + + Demo 2FA Example{' '} + layout only, not functioning + + + + + + + router.push(PATHS.DEMO_REACT_THEME_SWITCHER, 'forward')} + > + + Demo React Theme Switcher + + + + + + router.push(PATHS.DEMO_SKELETON_TEXT, 'forward')}> + + Demo Skeleton Text + + + + + + router.push(PATHS.DEMO_STICKY_BOTTOM_SHEET_EXAMPLE, 'forward')} + > + + Demo Sticky Bottom Sheet Example + + + + + + router.push(PATHS.DEMO_STORAGE_EXAMPLE, 'forward')}> + + + Demo Storage Example{' '} + (need fix, message cannot display) + + + + + + + router.push(PATHS.DEMO_SWIPERJS_TUTORIAL, 'forward')} + > + + Demo SwiperJS Tutorial + + + + + + router.push(PATHS.DEMO_REACT_DRAWING_CANVAS, 'forward')} + > + + Demo React Drawing Canvas + + + + + + router.push(PATHS.DEMO_REACT_HOOK_FORM_EXAMPLE, 'forward')} + > + + Demo React Hook Form Example + + + + + + router.push(PATHS.DEMO_REACT_ITEM_LIST, 'forward')}> + + Demo React Item List + + + + + + router.push(PATHS.DEMO_REACT_LIFECYCLES, 'forward')} + > + + Demo React Lifecycles + + + + + + router.push(PATHS.DEMO_REACT_LOGIN, 'forward')}> + + + Demo React Login (missing back button) + + + + + + + router.push(PATHS.DEMO_REACT_MARVEL_APP, 'forward')} + > + + Demo React Marvel App + + + + + + router.push(PATHS.DEMO_REACT_MOVIE_APP_WITH_ALGOLIA, 'forward')} + > + + Demo React Movie App with Algolia + + + + + + router.push(PATHS.DEMO_REACT_NOTES, 'forward')}> + + + Demo React Notes{' '} + TODO: need update IonSlide + + + + + + + router.push(PATHS.DEMO_FACEBOOK_CLONE, 'forward')}> + + Demo Facebook Clone + + + + + + router.push(PATHS.DEMO_FAST_FOOD_APP, 'forward')}> + + + Demo Fast Food App ion-slide outstanding + + + + + + + router.push(PATHS.DEMO_FLOATING_TABS, 'forward')}> + + Demo Floating Tabs + + + + + + router.push(PATHS.DEMO_INSTAGRAM_CLONE, 'forward')}> + + Demo Instagram Clone + + + + + + router.push(PATHS.DEMO_KANBAN_BOARD, 'forward')}> + + + Demo Kanban Board{' '} + // TODO: fix missing ionslide in new ionic + + + + + + + router.push(PATHS.DEMO_ORDERING_APP, 'forward')}> + + + Demo Ordering App outstanding css + + + + + + + router.push(PATHS.DEMO_PROFILE_EXAMPLE, 'forward')}> + + Demo Profile Example + + + + + + router.push(PATHS.DEMO_PULLSTATE_TUTORIAL, 'forward')} + > + + Demo Pullstate Tutorial + + + + + + router.push(PATHS.DEMO_REACT_ADD_TO_CART, 'forward')} + > + + Demo React Add to Cart + + + + + + router.push(PATHS.DEMO_REACT_CALCULATOR, 'forward')} + > + + + Demo React Calculator css need fix + + + + + + + router.push(PATHS.DEMO_ACCORDION_TUTORIAL, 'forward')} + > + + Demo Accordion Tutorial + + + + + + router.push(PATHS.DEMO_BANKING_UI, 'forward')}> + + + Demo Banking UI{' '} + (in the middle, style outstanding) + + + + + + + router.push(PATHS.DEMO_CAPACITOR_GOOGLE_MAPS_TUTORIAL, 'forward')} + > + + + Demo Capacitor Google Maps Tutorial{' '} + need a google map api key + + + + + + + router.push(PATHS.DEMO_COLOR_TUTORIAL, 'forward')}> + + Demo Color Tutorial + + + + + + router.push(PATHS.DEMO_ECOMMERCE_EXAMPLE, 'forward')} + > + + + Demo Ecommerce Example{' '} + (fetch data not available at remote site) + + + + + + {/* + + router.push(paths.DEMO_REACT_WHATSAPP_CLONE, 'forward')}> + + + Demo React WhatsApp Clone (need to resolve path problem) + + + + + */} + + + router.push(PATHS.DEMO_REACT_POLL_APP, 'forward')}> + + + Demo React Poll App{' '} + (css temporary broken, ignored) + + + + + + + router.push(PATHS.DEMO_REACT_SWITCH_TABS, 'forward')} + > + + + Demo React Switch Tabs{' '} + (hardcoded back button) + + + + + + + router.push(PATHS.DEMO_REACT_OVERLAY_HOOKS, 'forward')} + > + + Demo React Overlay Hooks + + + + + + router.push(PATHS.DEMO_PINTEREST_FLOATING_TAB_BAR, 'forward')} + > + + + Demo Pinterest Floating Tab Bar{' '} + (css not work well) + + + + + + + router.push(PATHS.DEMO_RESTAURANT_FINDER, 'forward')} + > + + + Demo Restaurant Finder{' '} + need server for map showing + + + + + + + handleDemoReactShopClick()}> + + Demo React Shop + + + + + + { + router.push(PATHS.DEMO_CLUB_HOUSE, 'forward'); + }} + > + + Demo Club house + + + + + + { + router.push(PATHS.DEMO_SCORE_BOARD, 'forward'); + }} + > + + + Demo Score Board
+ (IonCard problem) +
+ +
+
+ + + router.push(PATHS.DEMO_QUOTE_APP, 'forward')}> + + Demo Quote App + + + + + + router.push(PATHS.DEMO_QR_SCANNER, 'forward')}> + + Demo Qr scanner + + + + + + router.push(PATHS.DEMO_SHOP_APP_UI, 'forward')}> + + Demo Shop App UI + + + + + + router.push(PATHS.DEMO_DICTIONARY_APP, 'forward')}> + + Demo Dictionary App + + + + + + router.push(PATHS.DEMO_RECIPE_APP, 'forward')}> + + Demo Recipe App + + + + + + router.push(PATHS.DEMO_SLIDING_PROFILE, 'forward')}> + + Demo Sliding Profile + + + + + + router.push(PATHS.DEMO_QUIZ_APP, 'forward')}> + + Demo Quiz App + + + + + + router.push(PATHS.DEMO_BLOG_POST_UI, 'forward')}> + + Demo Blog Post UI + + + + + + router.push(PATHS.DEMO_REACT_TRAVEL_APP, 'forward')} + > + + + Demo React Travel App (on hold) + + + + + + + router.push(PATHS.DEMO_REACT_PROFILE_DASHBOARD_UI, 'forward')} + > + + Demo React Profile Dashboard UI + + + + + {/* TODO: */} + + + router.push(PATHS.DEMO_REACT_QR_CODE, 'forward')}> + + + Demo React QR Code need update + + + + + + + router.push(PATHS.DEMO_REACT_QUOTES, 'forward')}> + + Demo React Quotes + + + + + + router.push(PATHS.DEMO_REACT_SHOP_UI, 'forward')}> + + + Demo React Shop UI lower priority + + + + + + + router.push(PATHS.DEMO_REACT_TABS_MENUS_CUSTOM, 'forward')} + > + + Demo React Tabs Menus Custom + + + + + + router.push(PATHS.DEMO_REACT_ONBOARDING_UI, 'forward')} + > + + + Demo React Onboarding UI{' '} + TODO: update IonSlide + + + + +
+ + {/* REQ0058/logout */} + + +
+
+ +
+
+ Logout +
+
+ Unable to receive notifications after logging out +
+ +
+ + Cancel + + + Logout + +
+
+
+
+
+ ); +}; + +export default connect({ + mapStateToProps: (state) => ({ + speakers: selectors.getSpeakers(state), + speakerSessions: selectors.getSpeakerSessions(state), + }), + mapDispatchToProps: { + logoutUser, + setAccessToken, + setIsLoggedIn, + }, + component: React.memo(DemoList), +}); diff --git a/03_source/mobile.trunk/src/pages/DemoList/style.scss b/03_source/mobile.trunk/src/pages/DemoList/style.scss new file mode 100644 index 0000000..5fae6e3 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoList/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile.trunk/src/pages/DemoList/types.ts b/03_source/mobile.trunk/src/pages/DemoList/types.ts new file mode 100644 index 0000000..2f4577f --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoList/types.ts @@ -0,0 +1,14 @@ +export interface Event { + eventDate: Date; + joinMembers: undefined; + title: string; + price: number; + currency: string; + duration_m: number; + ageBottom: number; + ageTop: number; + location: string; + avatar: string; + // + id: string; +} diff --git a/03_source/mobile.trunk/src/pages/DemoOrderingApp/AppPages/Tab1.jsx b/03_source/mobile.trunk/src/pages/DemoOrderingApp/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoOrderingApp/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/DemoOrderingApp/AppPages/Tab2.jsx b/03_source/mobile.trunk/src/pages/DemoOrderingApp/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoOrderingApp/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/DemoOrderingApp/NOTES.md b/03_source/mobile.trunk/src/pages/DemoOrderingApp/NOTES.md new file mode 100644 index 0000000..c026b37 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoOrderingApp/NOTES.md @@ -0,0 +1 @@ +# REQ0134 diff --git a/03_source/mobile.trunk/src/pages/DemoOrderingApp/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk/src/pages/DemoOrderingApp/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoOrderingApp/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoOrderingApp/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk/src/pages/DemoOrderingApp/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoOrderingApp/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoOrderingApp/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk/src/pages/DemoOrderingApp/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoOrderingApp/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoOrderingApp/components/CoffeeCard.module.css b/03_source/mobile.trunk/src/pages/DemoOrderingApp/components/CoffeeCard.module.css new file mode 100644 index 0000000..41919a3 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoOrderingApp/components/CoffeeCard.module.css @@ -0,0 +1,54 @@ +.coffeeCard { + + padding: 0.8rem; + border-radius: 20px; +} + +.coffeeCard img { + + border-radius: 20px; + height: 10rem; + width: 100%; +} + +.coffeeCard ion-card-title { + + margin-top: 1rem; + font-size: 1rem; +} + +.coffeePrice { + + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + margin-top: -0.2rem; +} + +.coffeeAddButton { + + color: var(--ion-color-main) !important; + margin-top: 0.5rem; +} + +.coffeeCardLong img { + + border-radius: 20px; + height: 5rem !important; + width: 100%; +} + +.coffeeCardLongDetails { + + margin-left: 1rem; + margin-top: -0.7rem; +} + +.coffeeCardLongDetails p { + + font-size: 0.8rem; + margin: 0; + margin-top: 0.2rem; +} diff --git a/03_source/mobile.trunk/src/pages/DemoOrderingApp/components/CoffeeCard.tsx b/03_source/mobile.trunk/src/pages/DemoOrderingApp/components/CoffeeCard.tsx new file mode 100644 index 0000000..358b630 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoOrderingApp/components/CoffeeCard.tsx @@ -0,0 +1,26 @@ +import { IonCard, IonCardSubtitle, IonCardTitle, IonCol } from '@ionic/react'; +import { ArrowRightSquare } from 'react-iconly'; + +import styles from './CoffeeCard.module.css'; + +const CoffeeCard = (props) => { + const { coffee } = props; + + return ( + + + coffee + {coffee.name} + {coffee.summary} +
+

${coffee.price}

+
+ +
+
+
+
+ ); +}; + +export default CoffeeCard; diff --git a/03_source/mobile/src/pages/DemoOrderingApp/components/CoffeeCardOffer.tsx b/03_source/mobile.trunk/src/pages/DemoOrderingApp/components/CoffeeCardOffer.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/components/CoffeeCardOffer.tsx rename to 03_source/mobile.trunk/src/pages/DemoOrderingApp/components/CoffeeCardOffer.tsx diff --git a/03_source/mobile.trunk/src/pages/DemoOrderingApp/components/Tabs.tsx b/03_source/mobile.trunk/src/pages/DemoOrderingApp/components/Tabs.tsx new file mode 100644 index 0000000..2f8443e --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoOrderingApp/components/Tabs.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { IonIcon, IonLabel, IonTabBar, IonTabButton, IonTabs, IonRouterOutlet } from '@ionic/react'; +import { Redirect, Route } from 'react-router-dom'; + +import { Home, Bag, Heart2, Notification } from 'react-iconly'; +import Homepage from '../pages/Home'; +import Favourites from '../pages/Favourites'; +import Cart from '../pages/Cart'; + +const Tabs = (props) => { + return ( + + + } + /> + } /> + } + /> + + + + + + + + + + + + + + + + + ); +}; + +export default Tabs; diff --git a/03_source/mobile.trunk/src/pages/DemoOrderingApp/components/ViewCoffeeCard.tsx b/03_source/mobile.trunk/src/pages/DemoOrderingApp/components/ViewCoffeeCard.tsx new file mode 100644 index 0000000..5c035ce --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoOrderingApp/components/ViewCoffeeCard.tsx @@ -0,0 +1,70 @@ +import { IonButton, IonCard, IonCardSubtitle, IonCardTitle, IonCol, IonRow } from '@ionic/react'; + +import { useRef } from 'react'; +import { Bag } from 'react-iconly'; +import { addToCart } from '../store/CartStore'; +import '../pages/Home.scss'; + +const ViewCoffeeCard = (props) => { + const { coffee, cartRef } = props; + const coffeeCartRef = useRef(); + + const addCoffeeToCart = (e, coffeeID) => { + e.preventDefault(); + e.stopPropagation(); + + coffeeCartRef.current.style.display = ''; + coffeeCartRef.current.classList.add('animate__fadeOutUp'); + + setTimeout(() => { + cartRef.current.classList.add('animate__tada'); + addToCart(coffeeID); + + setTimeout(() => { + cartRef.current.classList.remove('animate__tada'); + coffeeCartRef.current.style.display = 'none'; + }, 500); + }, 500); + }; + + return ( + + + + coffee type + {coffee.name} + {coffee.summary} + + + + + Description +

{coffee.description}

+ + + + + View → + + + + + addCoffeeToCart(e, coffee.id)}> + + + +
+ +
+
+
+
+
+ ); +}; + +export default ViewCoffeeCard; diff --git a/03_source/mobile.trunk/src/pages/DemoOrderingApp/index.tsx b/03_source/mobile.trunk/src/pages/DemoOrderingApp/index.tsx new file mode 100644 index 0000000..d5213c5 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoOrderingApp/index.tsx @@ -0,0 +1,41 @@ +import { IonRouterOutlet, IonTabs } from '@ionic/react'; + +import { Route, Redirect } from 'react-router'; + +import './theme/variables.scss'; + +import Tabs from './components/Tabs'; +import Homepage from './pages/Home'; +import ViewCoffee from './pages/ViewCoffee'; +import ViewCoffees from './pages/ViewCoffees'; +import React from 'react'; + +function DemoOrderingApp(): React.JSX.Element { + return ( + + + } /> + } + /> + } + /> + } + /> + + + + + + ); +} + +export default DemoOrderingApp; diff --git a/03_source/mobile.trunk/src/pages/DemoOrderingApp/module.d.ts b/03_source/mobile.trunk/src/pages/DemoOrderingApp/module.d.ts new file mode 100644 index 0000000..d774364 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoOrderingApp/module.d.ts @@ -0,0 +1,4 @@ +declare module '*.module.css' { + const classes: { readonly [key: string]: string }; + export default classes; +} diff --git a/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/Cart.module.css b/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/Cart.module.css new file mode 100644 index 0000000..427e2f6 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/Cart.module.css @@ -0,0 +1,39 @@ +.cartCheckout { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + margin: 1rem; +} + +.cartFooter { + border-top: 2px solid #141a22; + background-color: var(--ion-background-color); + padding-left: 1rem; + padding-right: 1rem; +} + +.cartCheckout ion-card-subtitle { + font-size: 1.3rem; + color: white !important; +} + +.cartItem { + padding: 1rem; +} + +.cartItem img { + height: 3rem; + width: 3rem; + border-radius: 10px; +} + +.cartSlider:not(:nth-child(1)) { + border-top: 2px solid var(--ion-background-color); +} + +.cartActions { + display: flex; + flex-direction: column; +} diff --git a/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/Cart.tsx b/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/Cart.tsx new file mode 100644 index 0000000..a9342fc --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/Cart.tsx @@ -0,0 +1,129 @@ +import { + IonBadge, + IonButton, + IonCardSubtitle, + IonCol, + IonContent, + IonFooter, + IonHeader, + IonItem, + IonItemOption, + IonItemOptions, + IonItemSliding, + IonLabel, + IonList, + IonNote, + IonPage, + IonRow, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { Delete, TickSquare } from 'react-iconly'; +import { useEffect, useState } from 'react'; +import { CartStore, CoffeeStore } from '../store'; +import { removeFromCart } from '../store/CartStore'; +import { getCartCoffees, getCoffees } from '../store/Selectors'; + +import styles from './Cart.module.css'; + +const Cart = (): React.JSX.Element => { + const coffees = CoffeeStore.useState(getCoffees); + const cart = CartStore.useState(getCartCoffees); + + const [cartProducts, setCartProducts] = useState([]); + const [amountLoaded, setAmountLoaded] = useState(6); + const [total, setTotal] = useState(0); + + useEffect(() => { + const getCartProducts = () => { + setCartProducts([]); + setTotal(0); + + cart.forEach((coffee) => { + var coffeeID = coffee; + const tempCoffee = coffees.filter((p) => parseInt(p.id) === parseInt(coffeeID))[0]; + + setTotal((prevTotal) => prevTotal + parseFloat(tempCoffee.price)); + setCartProducts((prevSearchResults) => [...prevSearchResults, tempCoffee]); + }); + }; + + getCartProducts(); + }, [cart]); + + const fetchMore = async (e) => { + // Increment the amount loaded by 6 for the next iteration + setAmountLoaded((prevAmount) => prevAmount + 6); + e.target.complete(); + }; + + const removeProductFromCart = async (index) => { + removeFromCart(index); + }; + + return ( + + + + Checkout + + + + + + + + {cartProducts && cartProducts.length}{' '} + {cartProducts.length > 1 || cartProducts.length === 0 ? ' coffees' : ' coffee'} found + + + + + + {cartProducts && + cartProducts.map((coffee, index) => { + if (index <= amountLoaded) { + return ( + + + cart coffee + +

{coffee.name}

+
+ +
+ ${coffee.price} +
+
+ + + removeProductFromCart(index)} + > + + + +
+ ); + } + })} +
+
+ + +
+ ${total.toFixed(2)} + + + +  Checkout + +
+
+
+ ); +}; + +export default Cart; diff --git a/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/Favourites.tsx b/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/Favourites.tsx new file mode 100644 index 0000000..cc74788 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/Favourites.tsx @@ -0,0 +1,113 @@ +import { + IonButtons, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonInfiniteScroll, + IonInfiniteScrollContent, + IonNote, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import { Bag } from 'react-iconly'; +import { useEffect, useRef, useState } from 'react'; +import ViewCoffeeCard from '../components/ViewCoffeeCard'; +import { CartStore, CoffeeStore, FavouriteStore } from '../store'; +import { getCartCoffees, getCoffees, getFavouriteCoffees } from '../store/Selectors'; +import './Home.scss'; + +const Favourites = (): React.JSX.Element => { + const cartRef = useRef(null); + const router = useIonRouter(); + + const coffees = CoffeeStore.useState(getCoffees); + const favourites = FavouriteStore.useState(getFavouriteCoffees); + const cart = CartStore.useState(getCartCoffees); + + const [searchResults, setSearchResults] = useState([]); + const [amountLoaded, setAmountLoaded] = useState(6); + + useEffect(() => { + const getFavourites = () => { + setSearchResults([]); + + favourites.forEach((favourite) => { + var coffeeID = favourite; + const tempCoffee = coffees.filter((c) => parseInt(c.id) === parseInt(coffeeID))[0]; + setSearchResults((prevSearchResults) => [...prevSearchResults, tempCoffee]); + }); + }; + + getFavourites(); + }, [favourites]); + + const fetchMore = async (e) => { + // Increment the amount loaded by 6 for the next iteration + setAmountLoaded((prevAmount) => prevAmount + 6); + e.target.complete(); + }; + + return ( + + + + Favourites + + +
router.goBack()} + > + 0 ? 'yellow-icon' : 'gray-icon'} /> +
+
+
+
+ + + + + + + {searchResults && searchResults.length}{' '} + {searchResults.length > 1 || searchResults.length === 0 + ? ' favourites' + : ' favourite'}{' '} + found + + + + + + {searchResults && + searchResults.map((coffee, index) => { + if (index <= amountLoaded) { + return ( + + ); + } + })} + + + + + + + +
+ ); +}; + +export default Favourites; diff --git a/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/Home.scss b/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/Home.scss new file mode 100644 index 0000000..a9fa354 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/Home.scss @@ -0,0 +1,11 @@ +.demo-ordering-app { + .main-heading { + font-size: 2.3rem; + font-weight: 700; + } + + .heading { + letter-spacing: -0.05rem; + margin-left: 1rem; + } +} diff --git a/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/Home.tsx b/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/Home.tsx new file mode 100644 index 0000000..c5c018b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/Home.tsx @@ -0,0 +1,115 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonPage, + IonRouterLink, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import { chevronBackOutline, searchSharp } from 'ionicons/icons'; +import { Category, Filter } from 'react-iconly'; +import CoffeeCard from '../components/CoffeeCard'; +import CoffeeCardOffer from '../components/CoffeeCardOffer'; + +import { CoffeeStore, CoffeeOfferStore } from '../store'; +import { getCoffees, getOffers } from '../store/Selectors'; +import './Home.scss'; + +const Homepage = (): React.JSX.Element => { + const router = useIonRouter(); + const coffees = CoffeeStore.useState(getCoffees); + const offers = CoffeeOfferStore.useState(getOffers); + + function handleBackClick() { + router.goBack(); + } + + return ( + + + + + handleBackClick()}> + + + +
+ +
+
+ + Ionic Coffee + + +
+ avatar +
+
+
+
+ + + + + + +

Find the best coffee near you

+
+
+
+
+ + + + + router.push('/coffees/true')} + searchIcon={searchSharp} + placeholder="Try 'Caramel Latte'" + /> + + + + +

Popular

+ + + + +
+ + + {coffees.map((coffee) => { + if (coffee.id <= 2) { + return ; + } + })} + + + + +

Special Offers

+
+
+ + {offers.map((offer) => { + return ; + })} +
+
+
+ ); +}; + +export default Homepage; diff --git a/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/ViewCoffee.module.css b/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/ViewCoffee.module.css new file mode 100644 index 0000000..f472b46 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/ViewCoffee.module.css @@ -0,0 +1,50 @@ +.checkout { + + border-top: 2px solid #141a22; + background-color: var(--ion-background-color); + padding-bottom: 1rem; +} + +.checkoutDetails { + + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + margin-left: 2rem; + margin-right: 1rem; + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} + +.checkoutDetails ion-button { + + width: 60%; + --border-radius: 10px; + height: 3.5rem; +} + +.priceDetails { + + /* margin-left: 2rem; */ + display: flex; + flex-direction: column; + justify-content: center; + align-content: center; + align-items: center; +} + +.priceDetails h4 { + + margin: 0; + padding: 0; + color: white !important; +} + +.extra { + + margin-top: 0.3rem; + padding: 0.75rem; + color: #475464; +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/ViewCoffee.tsx b/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/ViewCoffee.tsx new file mode 100644 index 0000000..93c9dd5 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/ViewCoffee.tsx @@ -0,0 +1,208 @@ +import { + IonBadge, + IonButton, + IonButtons, + IonCard, + IonCardSubtitle, + IonCol, + IonContent, + IonFooter, + IonGrid, + IonHeader, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import { useEffect, useRef, useState } from 'react'; +import { Bag, Heart, CaretLeft, InfoSquare } from 'react-iconly'; +import { useParams } from 'react-router'; +import { CoffeeSizeStore, CoffeeStore, FavouriteStore } from '../store'; +import { addToCart } from '../store/CartStore'; +import { addToFavourites } from '../store/FavouriteStore'; +import { getCoffee, getCoffeeSizes, getFavouriteCoffees } from '../store/Selectors'; +import './Home.scss'; + +import styles from './ViewCoffee.module.css'; + +const ViewCoffee = (props): React.JSX.Element => { + const router = useIonRouter(); + const params = useParams(); + const coffee = CoffeeStore.useState(getCoffee(params.id)); + const favourites = FavouriteStore.useState(getFavouriteCoffees); + const coffeeSizes = CoffeeSizeStore.useState(getCoffeeSizes); + const [selectedSize, setSelectedSize] = useState(false); + + const favouriteRef = useRef(); + const coffeeCartRef = useRef(); + const [isFavourite, setIsFavourite] = useState(false); + + const getPrice = () => + coffee.prices.filter((p) => parseInt(p.size_id) === parseInt(selectedSize))[0].price; + + useEffect(() => { + const coffeeID = params.id; + const tempIsFavourite = favourites.find((f) => parseInt(f) === parseInt(coffeeID)); + + setIsFavourite(tempIsFavourite); + }, [params.id, favourites]); + + const addCoffeeToFavourites = (e, coffeeID) => { + e.preventDefault(); + addToFavourites(coffeeID); + + favouriteRef.current.classList.add('animate__tada'); + + setTimeout(() => { + favouriteRef.current.classList.remove('animate__tada'); + }, 700); + }; + + const addCoffeeToCart = (e, coffeeID) => { + e.preventDefault(); + e.stopPropagation(); + + coffeeCartRef.current.style.display = ''; + coffeeCartRef.current.classList.add('animate__fadeOutUp'); + + setTimeout(() => { + addToCart(coffeeID); + + setTimeout(() => { + coffeeCartRef.current.style.display = 'none'; + }, 500); + }, 500); + }; + + return ( + + + + +
router.goBack()}> + +
+
+ + {coffee.name} + + +
addCoffeeToFavourites(e, coffee.id)} + > + +
+
+
+
+ + + + + + +

{coffee.name}

+ {coffee.summary} +
+
+
+
+ + + + + + coffee type + + + + + Description +

{coffee.description}

+ +
+
+ + + + Extras included + + + + {coffee.extras.map((extra, index) => { + return ( + + {extra} + + ); + })} + + + + + + + + Pick your size + + + {coffeeSizes.map((size) => { + return ( + + setSelectedSize(size.id)} + expand="block" + color={size.id === selectedSize ? 'main' : 'custom-light'} + fill={size.id === selectedSize ? 'outline' : 'solid'} + > + {size.name} + + + ); + })} + + + +
+
+ + +
+
+ Price +

${selectedSize ? getPrice() : '0.00'}

+
+ addCoffeeToCart(e, coffee.id)} + disabled={!selectedSize} + expand="block" + color="main" + > + Add to cart + + +
+ +
+
+
+
+ ); +}; + +export default ViewCoffee; diff --git a/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/ViewCoffees.tsx b/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/ViewCoffees.tsx new file mode 100644 index 0000000..2bc79e0 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoOrderingApp/pages/ViewCoffees.tsx @@ -0,0 +1,117 @@ +import { + IonButtons, + IonCardSubtitle, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, + useIonRouter, + useIonViewDidEnter, +} from '@ionic/react'; +import { searchSharp } from 'ionicons/icons'; +import { useRef, useState } from 'react'; +import { Bag, CaretLeft } from 'react-iconly'; +import { useParams } from 'react-router'; +import ViewCoffeeCard from '../components/ViewCoffeeCard'; +import { CartStore, CoffeeStore } from '../store'; +import { getCoffees, getCartCoffees } from '../store/Selectors'; +import './Home.scss'; + +const ViewCoffees = (props): React.JSX.Element => { + const router = useIonRouter(); + const params = useParams(); + const coffees = CoffeeStore.useState(getCoffees); + const cart = CartStore.useState(getCartCoffees); + const [results, setResults] = useState(coffees); + + const cartRef = useRef(); + const searchRef = useRef(); + + useIonViewDidEnter(() => { + if (params.from_search) { + setTimeout(() => { + searchRef.current.setFocus(); + }, 500); + } + }); + + const search = (e) => { + const searchTerm = e.currentTarget.value; + + if (searchTerm !== '') { + const searchTermLower = searchTerm.toLowerCase(); + + const newResults = coffees.filter((e) => e.name.toLowerCase().includes(searchTermLower)); + setResults(newResults); + } else { + setResults(coffees); + } + }; + + return ( + + + + +
router.goBack()}> + +
+
+ + Full Range + + +
router.push('/tabs/cart')} + > + 0 ? 'yellow-icon' : 'gray-icon'} /> +
+
+
+
+ + + + + + +

View Full Range

+ Our range of succulent coffee +
+
+
+
+ + + + + search(e)} + id="searchbar" + ref={searchRef} + searchIcon={searchSharp} + placeholder="Try 'Cappuccino'" + /> + + + + {results.map((coffee) => { + return ; + })} + +
+
+ ); +}; + +export default ViewCoffees; diff --git a/03_source/mobile/src/pages/DemoOrderingApp/store/CartStore.tsx b/03_source/mobile.trunk/src/pages/DemoOrderingApp/store/CartStore.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/store/CartStore.tsx rename to 03_source/mobile.trunk/src/pages/DemoOrderingApp/store/CartStore.tsx diff --git a/03_source/mobile/src/pages/DemoOrderingApp/store/CoffeeOfferStore.tsx b/03_source/mobile.trunk/src/pages/DemoOrderingApp/store/CoffeeOfferStore.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/store/CoffeeOfferStore.tsx rename to 03_source/mobile.trunk/src/pages/DemoOrderingApp/store/CoffeeOfferStore.tsx diff --git a/03_source/mobile/src/pages/DemoOrderingApp/store/CoffeeSizeStore.tsx b/03_source/mobile.trunk/src/pages/DemoOrderingApp/store/CoffeeSizeStore.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/store/CoffeeSizeStore.tsx rename to 03_source/mobile.trunk/src/pages/DemoOrderingApp/store/CoffeeSizeStore.tsx diff --git a/03_source/mobile/src/pages/DemoOrderingApp/store/CoffeeStore.tsx b/03_source/mobile.trunk/src/pages/DemoOrderingApp/store/CoffeeStore.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/store/CoffeeStore.tsx rename to 03_source/mobile.trunk/src/pages/DemoOrderingApp/store/CoffeeStore.tsx diff --git a/03_source/mobile/src/pages/DemoOrderingApp/store/FavouriteStore.tsx b/03_source/mobile.trunk/src/pages/DemoOrderingApp/store/FavouriteStore.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/store/FavouriteStore.tsx rename to 03_source/mobile.trunk/src/pages/DemoOrderingApp/store/FavouriteStore.tsx diff --git a/03_source/mobile.trunk/src/pages/DemoOrderingApp/store/Selectors.tsx b/03_source/mobile.trunk/src/pages/DemoOrderingApp/store/Selectors.tsx new file mode 100644 index 0000000..ef89efe --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoOrderingApp/store/Selectors.tsx @@ -0,0 +1,17 @@ +import { createSelector } from 'reselect'; + +const getState = (state) => state; + +// General getters +export const getCoffees = createSelector(getState, (state) => state.coffees); +export const getOffers = createSelector(getState, (state) => state.offers); +export const getCoffeeSizes = createSelector(getState, (state) => state.sizes); +export const getCartCoffees = createSelector(getState, (state) => state.coffee_ids); +export const getFavouriteCoffees = createSelector(getState, (state) => state.coffee_ids); + +// More specific getters +export const getCoffee = (id) => + createSelector( + getState, + (state) => state.coffees.filter((c) => parseInt(c.id) === parseInt(id))[0] + ); diff --git a/03_source/mobile/src/pages/DemoOrderingApp/store/index.tsx b/03_source/mobile.trunk/src/pages/DemoOrderingApp/store/index.tsx similarity index 100% rename from 03_source/mobile/src/pages/DemoOrderingApp/store/index.tsx rename to 03_source/mobile.trunk/src/pages/DemoOrderingApp/store/index.tsx diff --git a/03_source/mobile.trunk/src/pages/DemoOrderingApp/theme/variables.scss b/03_source/mobile.trunk/src/pages/DemoOrderingApp/theme/variables.scss new file mode 100644 index 0000000..1325221 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoOrderingApp/theme/variables.scss @@ -0,0 +1,278 @@ +.demo-ordering-app { + /* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + + /** Ionic CSS Variables **/ + :root { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + + /** custom **/ + --ion-color-main: rgb(221, 174, 21); + --ion-color-main-rgb: 221, 174, 21; + --ion-color-main-contrast: rgb(0, 0, 0); + --ion-color-main-contrast-rgb: 0, 0, 0; + --ion-color-main-shade: rgb(179, 142, 22); + --ion-color-main-tint: rgb(233, 195, 71); + + /** custom light **/ + --ion-color-custom-light: #141a22; + --ion-color-custom-light-contrast: #ffffff; + --ion-color-custom-light-shade: #12171d; + --ion-color-custom-light-tint: #232c38; + } + + .ion-color-main { + --ion-color-base: var(--ion-color-main); + --ion-color-base-rgb: var(--ion-color-main-rgb); + --ion-color-contrast: var(--ion-color-main-contrast); + --ion-color-contrast-rgb: var(--ion-color-main-contrast-rgb); + --ion-color-shade: var(--ion-color-main-shade); + --ion-color-tint: var(--ion-color-main-tint); + } + + .ion-color-custom-light { + --ion-color-base: var(--ion-color-custom-light); + --ion-color-contrast: var(--ion-color-custom-light-contrast); + --ion-color-shade: var(--ion-color-custom-light-shade); + --ion-color-tint: var(--ion-color-custom-light-tint); + } + + :root { + --main-orange-color: #d17842; + + --ion-background-color: #0e1016; + --ion-background-color-rgb: 0, 0, 0; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + --ion-item-background: #141a22; + + --ion-card-background: #141a22; + --ion-tab-bar-color: #4d5053; + --ion-tab-bar-background: #0e1016; + --ion-tab-bar-color-selected: rgb(221, 174, 21); + + /* --ion-toolbar-color: white; */ + --ion-toolbar-background: #0e1016; + --ion-toolbar-border-color: #0e1016; + --ion-tab-bar-border-color: #0e1016; + --ion-grid-column-padding: 0; + } + + .app-icon { + color: rgb(221, 174, 21); + } + + ion-toolbar { + --padding-start: 1rem; + --padding-end: 1rem; + --padding-top: 1rem; + --padding-bottom: 1rem; + } + + .inner-toolbar { + --padding-top: 0rem !important; + --padding-bottom: 0rem !important; + } + + .button-container-img img { + height: 2.3rem; + border-radius: 10px; + } + + .gray-icon { + color: var(--ion-tab-bar-color); + background-color: #1b2025; + } + + .yellow-icon { + color: var(--ion-color-main); + background-color: #1b2025; + } + + .button-container { + background-color: #1b2025; + border: 2px solid #1d232a; + padding: 0.5rem; + border-radius: 13px; + + display: flex; + flex-direction: row; + align-content: center; + align-items: center; + justify-content: center; + } + + .button-container-img { + background-color: #1b2025; + border: 2px solid #1d232a; + padding: 0.2rem; + border-radius: 13px; + + display: flex; + flex-direction: row; + align-content: center; + align-items: center; + justify-content: center; + } + + ion-card { + padding: 0.8rem; + border-radius: 20px; + } + + .coffee-card img { + border-radius: 20px; + height: 10rem; + width: 100%; + } + + .coffee-card-long img { + border-radius: 20px; + height: 5rem !important; + width: 100%; + } + + .coffee-card-long-details { + margin-left: 1rem; + margin-top: -0.7rem; + } + + .coffee-card-long-details p { + font-size: 0.8rem; + margin: 0; + margin-top: 0.2rem; + } + + .coffee-card ion-card-title { + margin-top: 1rem; + font-size: 1rem; + } + + .coffee-card .coffee-price { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + align-content: center; + margin-top: -0.2rem; + } + + .coffee-card .coffee-price .add-button { + color: var(--main-orange-color) !important; + margin-top: 0.5rem; + } + + .coffee-card .coffee-price .add-button svg { + color: white !important; + } + + .outer-heading { + margin-bottom: -1.3rem; + margin-left: 0.5rem; + margin-right: 1.3rem; + } + /* +.searchbar-input { + + padding: 1.5rem !important; +} + +.searchbar-search-icon { + + margin-top: 0.3rem; + margin-right: 3rem; + padding-right: 3rem; +} */ + /* +ion-tab-bar { + + bottom: 20px; + position: relative; + box-shadow: 0px 0px 1.5px rgba(255, 255, 255, 0.2); + border-radius: 16px; +width: 92%; +border-top: none; +margin: 0 auto; +height: 55px; +} + +ion-tab-button { + --padding-bottom: 8px; + --padding-top: 8px; +} */ + + .custom-margin-left { + margin-left: 0.2rem; + } +} diff --git a/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab1.css b/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab1.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab1.jsx b/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab1.jsx new file mode 100644 index 0000000..62e718a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab1.jsx @@ -0,0 +1,49 @@ +import { + IonButton, + IonButtons, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; + +import './Tab1.css'; +import { chevronBackOutline } from 'ionicons/icons'; + +const Tab1 = () => { + const router = useIonRouter(); + + function handleBackClick() { + router.goBack(); + } + + return ( + + + + Tab 1 + {/* */} + + handleBackClick()}> + + + + + + + + + Tab 1 + + + + + + ); +}; + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab2.css b/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab2.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab2.jsx b/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab2.jsx new file mode 100644 index 0000000..f09edf4 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab2.jsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab2.css'; + +const Tab2 = () => { + return ( + + + + Tab 2 + + + + + + Tab 2 + + + + + + ); +}; + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab3.css b/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab3.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab3.jsx b/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab3.jsx new file mode 100644 index 0000000..903b1a0 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab3.jsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab3.css'; + +const Tab3 = () => { + return ( + + + + Tab 3 + + + + + + Tab 3 + + + + + + ); +}; + +export default Tab3; diff --git a/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab4.jsx b/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab4.jsx new file mode 100644 index 0000000..9cb5fa3 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/AppPages/Tab4.jsx @@ -0,0 +1,25 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import ExploreContainer from '../components/ExploreContainer'; +import './Tab3.css'; + +const Tab4 = () => { + return ( + + + + Tab 3 + + + + + + Tab 4 + + + + + + ); +}; + +export default Tab4; diff --git a/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/NOTES.md b/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/NOTES.md new file mode 100644 index 0000000..41d58b8 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/NOTES.md @@ -0,0 +1 @@ +# REQ0135 diff --git a/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/components/ExploreContainer.css b/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/components/ExploreContainer.css new file mode 100644 index 0000000..e99f514 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/components/ExploreContainer.css @@ -0,0 +1,24 @@ +.container { + text-align: center; + position: absolute; + left: 0; + right: 0; + top: 50%; + transform: translateY(-50%); +} + +.container strong { + font-size: 20px; + line-height: 26px; +} + +.container p { + font-size: 16px; + line-height: 22px; + color: #8c8c8c; + margin: 0; +} + +.container a { + text-decoration: none; +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoPinterestFloatingTabBar/components/ExploreContainer.jsx b/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/components/ExploreContainer.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoPinterestFloatingTabBar/components/ExploreContainer.jsx rename to 03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/components/ExploreContainer.jsx diff --git a/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/custom-tab-bar.scss b/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/custom-tab-bar.scss new file mode 100644 index 0000000..1aa74fb --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/custom-tab-bar.scss @@ -0,0 +1,29 @@ +.custom-tab-bar { + * { + /* --ion-background-color: white; */ + --ion-tab-bar-color: var(--tab-color); + --ion-tab-bar-color-selected: var(--tab-color-selected); + } + + ion-tab-bar { + --background: var(--tab-background); + box-shadow: 0px 1px 8px rgba(0, 0, 0, 0.4); + border-radius: 50px !important; + + height: 50px; + width: 50%; + padding-top: 5px; + padding-bottom: 5px; + padding-left: 10px; + padding-right: 10px; + + bottom: 20px; + position: relative; + margin: 0 auto !important; + border-top: none; + } + + ion-tab-button { + border-radius: 16px !important; + } +} diff --git a/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/index.tsx b/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/index.tsx new file mode 100644 index 0000000..6ccb3e7 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/index.tsx @@ -0,0 +1,54 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { chatbubble, cloudOutline, home, person, search, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; +import Tab3 from './AppPages/Tab3'; +import Tab4 from './AppPages/Tab4'; + +import './style.scss'; +import './custom-tab-bar.scss'; + +function DemoPinterestFloatingTabBar() { + return ( + + + + + + + + + + + + + + + + + + + {/* */} + + + + + + + + + + + + + + + + + ); +} + +export default DemoPinterestFloatingTabBar; diff --git a/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/style.scss b/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/style.scss new file mode 100644 index 0000000..fae1253 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoPinterestFloatingTabBar/style.scss @@ -0,0 +1,253 @@ +/* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + +/** Ionic CSS Variables **/ +.demo-pinterest-floating-tab-bar { + * { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + } + + @media (prefers-color-scheme: dark) { + /* + * Dark Colors + * ------------------------------------------- + */ + + body { + --ion-color-primary: #428cff; + --ion-color-primary-rgb: 66, 140, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3a7be0; + --ion-color-primary-tint: #5598ff; + + --ion-color-secondary: #50c8ff; + --ion-color-secondary-rgb: 80, 200, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #46b0e0; + --ion-color-secondary-tint: #62ceff; + + --ion-color-tertiary: #6a64ff; + --ion-color-tertiary-rgb: 106, 100, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #5d58e0; + --ion-color-tertiary-tint: #7974ff; + + --ion-color-success: #2fdf75; + --ion-color-success-rgb: 47, 223, 117; + --ion-color-success-contrast: #000000; + --ion-color-success-contrast-rgb: 0, 0, 0; + --ion-color-success-shade: #29c467; + --ion-color-success-tint: #44e283; + + --ion-color-warning: #ffd534; + --ion-color-warning-rgb: 255, 213, 52; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0bb2e; + --ion-color-warning-tint: #ffd948; + + --ion-color-danger: #ff4961; + --ion-color-danger-rgb: 255, 73, 97; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #e04055; + --ion-color-danger-tint: #ff5b71; + + --ion-color-dark: #f4f5f8; + --ion-color-dark-rgb: 244, 245, 248; + --ion-color-dark-contrast: #000000; + --ion-color-dark-contrast-rgb: 0, 0, 0; + --ion-color-dark-shade: #d7d8da; + --ion-color-dark-tint: #f5f6f9; + + --ion-color-medium: #989aa2; + --ion-color-medium-rgb: 152, 154, 162; + --ion-color-medium-contrast: #000000; + --ion-color-medium-contrast-rgb: 0, 0, 0; + --ion-color-medium-shade: #86888f; + --ion-color-medium-tint: #a2a4ab; + + --ion-color-light: #222428; + --ion-color-light-rgb: 34, 36, 40; + --ion-color-light-contrast: #ffffff; + --ion-color-light-contrast-rgb: 255, 255, 255; + --ion-color-light-shade: #1e2023; + --ion-color-light-tint: #383a3e; + } + + /* + * iOS Dark Theme + * ------------------------------------------- + */ + + .ios body { + --ion-background-color: #000000; + --ion-background-color-rgb: 0, 0, 0; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-color-step-50: #0d0d0d; + --ion-color-step-100: #1a1a1a; + --ion-color-step-150: #262626; + --ion-color-step-200: #333333; + --ion-color-step-250: #404040; + --ion-color-step-300: #4d4d4d; + --ion-color-step-350: #595959; + --ion-color-step-400: #666666; + --ion-color-step-450: #737373; + --ion-color-step-500: #808080; + --ion-color-step-550: #8c8c8c; + --ion-color-step-600: #999999; + --ion-color-step-650: #a6a6a6; + --ion-color-step-700: #b3b3b3; + --ion-color-step-750: #bfbfbf; + --ion-color-step-800: #cccccc; + --ion-color-step-850: #d9d9d9; + --ion-color-step-900: #e6e6e6; + --ion-color-step-950: #f2f2f2; + + --ion-item-background: #000000; + + --ion-card-background: #1c1c1d; + } + + .ios ion-modal { + --ion-background-color: var(--ion-color-step-100); + --ion-toolbar-background: var(--ion-color-step-150); + --ion-toolbar-border-color: var(--ion-color-step-250); + } + + /* + * Material Design Dark Theme + * ------------------------------------------- + */ + + .md body { + --ion-background-color: #121212; + --ion-background-color-rgb: 18, 18, 18; + + --ion-text-color: #ffffff; + --ion-text-color-rgb: 255, 255, 255; + + --ion-border-color: #222222; + + --ion-color-step-50: #1e1e1e; + --ion-color-step-100: #2a2a2a; + --ion-color-step-150: #363636; + --ion-color-step-200: #414141; + --ion-color-step-250: #4d4d4d; + --ion-color-step-300: #595959; + --ion-color-step-350: #656565; + --ion-color-step-400: #717171; + --ion-color-step-450: #7d7d7d; + --ion-color-step-500: #898989; + --ion-color-step-550: #949494; + --ion-color-step-600: #a0a0a0; + --ion-color-step-650: #acacac; + --ion-color-step-700: #b8b8b8; + --ion-color-step-750: #c4c4c4; + --ion-color-step-800: #d0d0d0; + --ion-color-step-850: #dbdbdb; + --ion-color-step-900: #e7e7e7; + --ion-color-step-950: #f3f3f3; + + --ion-item-background: #1e1e1e; + + --ion-toolbar-background: #1f1f1f; + + --ion-tab-bar-background: #1f1f1f; + + --ion-card-background: #1e1e1e; + } + } + + :root { + /* Custom tab bar */ + --tab-background: rgb(251, 251, 251); + --tab-color: rgb(153, 153, 153); + --tab-color-selected: black; + } + + @media (prefers-color-scheme: dark) { + :root { + /* Custom tab bar */ + --tab-background: rgb(53, 53, 53); + --tab-color: rgb(83, 83, 83); + --tab-color-selected: white; + } + } +} diff --git a/03_source/mobile.trunk/src/pages/DemoProfileExample/AppPages/Tab1.jsx b/03_source/mobile.trunk/src/pages/DemoProfileExample/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoProfileExample/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/DemoProfileExample/AppPages/Tab2.jsx b/03_source/mobile.trunk/src/pages/DemoProfileExample/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoProfileExample/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/DemoProfileExample/NOTES.md b/03_source/mobile.trunk/src/pages/DemoProfileExample/NOTES.md new file mode 100644 index 0000000..ab323e8 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoProfileExample/NOTES.md @@ -0,0 +1 @@ +# REQ0136 diff --git a/03_source/mobile.trunk/src/pages/DemoProfileExample/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk/src/pages/DemoProfileExample/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoProfileExample/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoProfileExample/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk/src/pages/DemoProfileExample/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoProfileExample/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoProfileExample/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk/src/pages/DemoProfileExample/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoProfileExample/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoProfileExample/components/Figure.module.scss b/03_source/mobile.trunk/src/pages/DemoProfileExample/components/Figure.module.scss new file mode 100644 index 0000000..48ef851 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoProfileExample/components/Figure.module.scss @@ -0,0 +1,26 @@ +.figure { + padding: 1rem; +} + +.figure h6 { + font-size: 1.5rem; + font-weight: 200; +} + +.figure p { + color: rgb(255, 255, 255); + font-size: 0.9rem; + font-weight: 200; +} + +.figure:nth-child(1) { + background-color: rgb(157, 163, 141); +} + +.figure:nth-child(2) { + background-color: rgb(150, 155, 138); +} + +.figure:nth-child(3) { + background-color: rgb(135, 143, 120); +} diff --git a/03_source/mobile.trunk/src/pages/DemoProfileExample/components/Figure.tsx b/03_source/mobile.trunk/src/pages/DemoProfileExample/components/Figure.tsx new file mode 100644 index 0000000..57c6f2b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoProfileExample/components/Figure.tsx @@ -0,0 +1,9 @@ +import { IonCol } from '@ionic/react'; +import styles from './Figure.module.scss'; + +export const Figure = (props): React.JSX.Element => ( + +
{props.count}
+

{props.title}

+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoProfileExample/components/Post.module.scss b/03_source/mobile.trunk/src/pages/DemoProfileExample/components/Post.module.scss new file mode 100644 index 0000000..3b81ae7 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoProfileExample/components/Post.module.scss @@ -0,0 +1,55 @@ +.post { + border-bottom: 1px solid rgb(219, 219, 219); + padding-top: 1rem; + padding-bottom: 0.2rem; + + p { + font-size: 0.9rem; + padding: 0 !important; + margin: 0 !important; + } + + .postText { + color: rgb(107, 112, 97); + } +} + +.postAvatar { + height: 3.5rem; + width: 3.5rem; + margin-right: 1rem; + border: 3px solid rgba(218, 223, 208, 1); +} + +.postInfo { + display: flex; + flex-direction: row; + justify-content: space-between; + padding-right: 1rem; + color: rgb(190, 190, 190); +} + +.postReactions { + display: flex; + justify-content: space-between; + padding-right: 1rem; + margin-top: 0.5rem; + color: rgb(107, 112, 97); + + .postReaction { + display: flex; + align-items: center; + justify-content: space-between; + font-size: 0.9rem; + + ion-icon { + margin-right: 0.5rem; + } + + p { + padding: 0; + margin: 0; + font-size: 0.8rem; + } + } +} diff --git a/03_source/mobile.trunk/src/pages/DemoProfileExample/components/Post.tsx b/03_source/mobile.trunk/src/pages/DemoProfileExample/components/Post.tsx new file mode 100644 index 0000000..406e1dd --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoProfileExample/components/Post.tsx @@ -0,0 +1,35 @@ +import { IonAvatar, IonIcon, IonItem, IonLabel } from '@ionic/react'; +import { chatbubbleOutline, heart, heartOutline, shareSocialOutline } from 'ionicons/icons'; +import styles from './Post.module.scss'; + +export const Post = (props): React.JSX.Element => ( +
+ + + + + +
+

{props.post.date}

+

@93alan

+
+

{props.post.text}

+ +
+
+ +

{props.post.comments}

+
+
+ +

{props.post.likes}

+
+ +
+ +
+
+
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoProfileExample/index.tsx b/03_source/mobile.trunk/src/pages/DemoProfileExample/index.tsx new file mode 100644 index 0000000..392e5a6 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoProfileExample/index.tsx @@ -0,0 +1,38 @@ +import { IonRouterOutlet, IonTabs } from '@ionic/react'; + +import { Route, Redirect } from 'react-router'; + +// import Tab1 from './AppPages/Tab1'; +// import Tab2 from './AppPages/Tab2'; + +import './style.scss'; +import Home from './pages/Home'; + +function DemoProfileExample(): React.JSX.Element { + return ( + + + + + + + + + + {/* + + + + Dashboard + + + + Search + + + */} + + ); +} + +export default DemoProfileExample; diff --git a/03_source/mobile.trunk/src/pages/DemoProfileExample/pages/Home.module.scss b/03_source/mobile.trunk/src/pages/DemoProfileExample/pages/Home.module.scss new file mode 100644 index 0000000..5db20c8 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoProfileExample/pages/Home.module.scss @@ -0,0 +1,90 @@ +$main-color: rgb(143, 149, 130); + +.page { + ion-toolbar { + --background: rgb(143, 149, 130) !important; + --color: white; + --border-style: none; + margin: 0 !important; + } +} + +.top { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + align-content: center; + background-color: rgb(143, 149, 130); + color: white; + padding-bottom: 1rem; +} + +.figures { + text-align: center; + background-color: rgb(143, 149, 130); + color: white; + + p, + h6 { + padding: 0; + margin: 0; + } +} + +.profileHeader { + ion-card-subtitle, + ion-card-title { + --color: white; + } + + ion-card-title { + font-size: 1.3rem; + } + + ion-card-subtitle { + --color: rgb(202, 211, 189); + } +} + +.avatar { + width: 7rem; + height: 7rem; + border: 5px solid rgba(218, 223, 208, 0.4); +} + +.avatarUpload { + display: flex; + flex-direction: row; + justify-content: center; + + background-color: rgb(255, 255, 255); + border: 3px solid rgba(218, 223, 208, 0.4); + color: rgb(80, 80, 80); + position: absolute; + padding: 0.3rem; + font-size: 1.1rem; + border-radius: 500px; + margin-top: -2.2rem; + margin-left: 5rem; +} + +.postActions { + display: flex; + flex-direction: row; + align-content: center; + align-items: center; + justify-content: space-between; + padding: 0.2rem; + padding-left: 1.3rem; + padding-right: 1.3rem; + color: rgb(149, 149, 149); + font-size: 0.9rem; + border-bottom: 1px solid rgba(218, 223, 208, 1); + + background-color: rgba(218, 223, 208, 0.4); + + ion-icon { + font-size: 1.2rem; + } +} diff --git a/03_source/mobile.trunk/src/pages/DemoProfileExample/pages/Home.tsx b/03_source/mobile.trunk/src/pages/DemoProfileExample/pages/Home.tsx new file mode 100644 index 0000000..d565f0a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoProfileExample/pages/Home.tsx @@ -0,0 +1,130 @@ +import { + IonAvatar, + IonButton, + IonButtons, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonList, + IonPage, + IonRow, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import styles from './Home.module.scss'; +import { + arrowBackOutline, + cameraOutline, + chevronBackOutline, + filterOutline, + menuOutline, +} from 'ionicons/icons'; +import { Figure } from '../components/Figure'; +import { Post } from '../components/Post'; + +const Home = (): React.JSX.Element => { + const posts = [ + { + date: 'Mar 30', + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + comments: 13, + likes: 49, + liked: true, + }, + { + date: 'Mar 28', + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + comments: 1, + likes: 9, + }, + { + date: 'Mar 25', + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + comments: 119, + likes: 483, + }, + { + date: 'Mar 23', + text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', + comments: 27, + likes: 78, + }, + ]; + + const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + + handleBackClick()}> + + + + + + + + + + + + + + + + + + +
+ +
+
+
+ + + + Alan Montgomery + Mobile Team Lead + + +
+ + + +
+
+
+ + + + + + +
+

Posts by @93alan

+ +
+
+
+ + + {posts.map((post, index) => { + return ; + })} + +
+ + + ); +}; + +export default Home; diff --git a/03_source/mobile.trunk/src/pages/DemoProfileExample/style.scss b/03_source/mobile.trunk/src/pages/DemoProfileExample/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoProfileExample/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile.trunk/src/pages/DemoProfileExample/theme/variables.scss b/03_source/mobile.trunk/src/pages/DemoProfileExample/theme/variables.scss new file mode 100644 index 0000000..1a2dbc3 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoProfileExample/theme/variables.scss @@ -0,0 +1,79 @@ +.demo-profile-example { + /* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + + /** Ionic CSS Variables **/ + :root { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + } +} diff --git a/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/AppPages/Tab1.jsx b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/AppPages/Tab2.jsx b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/NOTES.md b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/NOTES.md new file mode 100644 index 0000000..d94875f --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/NOTES.md @@ -0,0 +1 @@ +# REQ0137 diff --git a/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/components/Person.tsx b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/components/Person.tsx new file mode 100644 index 0000000..2edc34d --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/components/Person.tsx @@ -0,0 +1,24 @@ +import { IonAvatar, IonButton, IonItem, IonLabel } from '@ionic/react'; +import { toggleFollowing } from '../store/PeopleStore'; + +export const Person = ({ person }): React.JSX.Element => { + return ( + + + avatar + + +

{person.name}

+

{person.title}

+
+ + toggleFollowing(person.id)} + > + {person.following ? 'Following' : 'Follow'} + +
+ ); +}; diff --git a/03_source/mobile/src/pages/DemoPullstateTutorial/data/index.js b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/data/index.js similarity index 100% rename from 03_source/mobile/src/pages/DemoPullstateTutorial/data/index.js rename to 03_source/mobile.trunk/src/pages/DemoPullstateTutorial/data/index.js diff --git a/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/index.tsx b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/index.tsx new file mode 100644 index 0000000..f5313e3 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/index.tsx @@ -0,0 +1,42 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { list, people } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +// import Tab1 from './AppPages/Tab1'; +// import Tab2 from './AppPages/Tab2'; + +import './style.scss'; +import Tab1 from './pages/Tab1'; +import Tab2 from './pages/Tab2'; + +function DemoPullstateTutorial() { + return ( + + + + + + + + + + + + + {/* */} + + + + List + + + + Following + + + + ); +} + +export default DemoPullstateTutorial; diff --git a/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/pages/Tab1.css b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/pages/Tab1.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/pages/Tab1.tsx b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/pages/Tab1.tsx new file mode 100644 index 0000000..98513db --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/pages/Tab1.tsx @@ -0,0 +1,63 @@ +import { + IonButton, + IonButtons, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import { PeopleStore } from '../store'; +import { Person } from '../components/Person'; +import './Tab1.css'; +import { useStoreState } from 'pullstate'; +import { getPeople } from '../store/Selectors'; +import { chevronBackOutline } from 'ionicons/icons'; + +const Tab1 = (): React.JSX.Element => { + const people = useStoreState(PeopleStore, getPeople); + + console.log(people); + + const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + List of People + + + handleBackClick()}> + + + + + + + + + List of People + + + handleBackClick()}> + + + + + + + {people.map((person, index) => { + return ; + })} + + + ); +}; + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/pages/Tab2.css b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/pages/Tab2.css new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/pages/Tab2.tsx b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/pages/Tab2.tsx new file mode 100644 index 0000000..7a95a49 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/pages/Tab2.tsx @@ -0,0 +1,34 @@ +import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react'; +import { useStoreState } from 'pullstate'; +import { Person } from '../components/Person'; +import { PeopleStore } from '../store'; +import { getFollowing } from '../store/Selectors'; +import './Tab2.css'; +import React from 'react'; + +const Tab2 = (): React.JSX.Element => { + const people = useStoreState(PeopleStore, getFollowing); + + return ( + + + + Following + + + + + + Following + + + + {people.map((person, index) => { + return ; + })} + + + ); +}; + +export default Tab2; diff --git a/03_source/mobile/src/pages/DemoPullstateTutorial/store/PeopleStore.js b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/store/PeopleStore.js similarity index 100% rename from 03_source/mobile/src/pages/DemoPullstateTutorial/store/PeopleStore.js rename to 03_source/mobile.trunk/src/pages/DemoPullstateTutorial/store/PeopleStore.js diff --git a/03_source/mobile/src/pages/DemoPullstateTutorial/store/Selectors.js b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/store/Selectors.js similarity index 100% rename from 03_source/mobile/src/pages/DemoPullstateTutorial/store/Selectors.js rename to 03_source/mobile.trunk/src/pages/DemoPullstateTutorial/store/Selectors.js diff --git a/03_source/mobile/src/pages/DemoPullstateTutorial/store/index.js b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/store/index.js similarity index 100% rename from 03_source/mobile/src/pages/DemoPullstateTutorial/store/index.js rename to 03_source/mobile.trunk/src/pages/DemoPullstateTutorial/store/index.js diff --git a/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/style.scss b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/theme/variables.scss b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/theme/variables.scss new file mode 100644 index 0000000..8606e9f --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoPullstateTutorial/theme/variables.scss @@ -0,0 +1,79 @@ +.demo-pullstate-tutorial { + /* Ionic Variables and Theming. For more info, please see: +http://ionicframework.com/docs/theming/ */ + + /** Ionic CSS Variables **/ + :root { + /** primary **/ + --ion-color-primary: #3880ff; + --ion-color-primary-rgb: 56, 128, 255; + --ion-color-primary-contrast: #ffffff; + --ion-color-primary-contrast-rgb: 255, 255, 255; + --ion-color-primary-shade: #3171e0; + --ion-color-primary-tint: #4c8dff; + + /** secondary **/ + --ion-color-secondary: #3dc2ff; + --ion-color-secondary-rgb: 61, 194, 255; + --ion-color-secondary-contrast: #ffffff; + --ion-color-secondary-contrast-rgb: 255, 255, 255; + --ion-color-secondary-shade: #36abe0; + --ion-color-secondary-tint: #50c8ff; + + /** tertiary **/ + --ion-color-tertiary: #5260ff; + --ion-color-tertiary-rgb: 82, 96, 255; + --ion-color-tertiary-contrast: #ffffff; + --ion-color-tertiary-contrast-rgb: 255, 255, 255; + --ion-color-tertiary-shade: #4854e0; + --ion-color-tertiary-tint: #6370ff; + + /** success **/ + --ion-color-success: #2dd36f; + --ion-color-success-rgb: 45, 211, 111; + --ion-color-success-contrast: #ffffff; + --ion-color-success-contrast-rgb: 255, 255, 255; + --ion-color-success-shade: #28ba62; + --ion-color-success-tint: #42d77d; + + /** warning **/ + --ion-color-warning: #ffc409; + --ion-color-warning-rgb: 255, 196, 9; + --ion-color-warning-contrast: #000000; + --ion-color-warning-contrast-rgb: 0, 0, 0; + --ion-color-warning-shade: #e0ac08; + --ion-color-warning-tint: #ffca22; + + /** danger **/ + --ion-color-danger: #eb445a; + --ion-color-danger-rgb: 235, 68, 90; + --ion-color-danger-contrast: #ffffff; + --ion-color-danger-contrast-rgb: 255, 255, 255; + --ion-color-danger-shade: #cf3c4f; + --ion-color-danger-tint: #ed576b; + + /** dark **/ + --ion-color-dark: #222428; + --ion-color-dark-rgb: 34, 36, 40; + --ion-color-dark-contrast: #ffffff; + --ion-color-dark-contrast-rgb: 255, 255, 255; + --ion-color-dark-shade: #1e2023; + --ion-color-dark-tint: #383a3e; + + /** medium **/ + --ion-color-medium: #92949c; + --ion-color-medium-rgb: 146, 148, 156; + --ion-color-medium-contrast: #ffffff; + --ion-color-medium-contrast-rgb: 255, 255, 255; + --ion-color-medium-shade: #808289; + --ion-color-medium-tint: #9d9fa6; + + /** light **/ + --ion-color-light: #f4f5f8; + --ion-color-light-rgb: 244, 245, 248; + --ion-color-light-contrast: #000000; + --ion-color-light-contrast-rgb: 0, 0, 0; + --ion-color-light-shade: #d7d8da; + --ion-color-light-tint: #f5f6f9; + } +} diff --git a/03_source/mobile.trunk/src/pages/DemoQrScanner/AppPages/Tab1.jsx b/03_source/mobile.trunk/src/pages/DemoQrScanner/AppPages/Tab1.jsx new file mode 100644 index 0000000..aaad468 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQrScanner/AppPages/Tab1.jsx @@ -0,0 +1,144 @@ +import { + getPlatforms, + IonButton, + IonButtons, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonModal, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useRef, useState } from 'react'; +import { SkeletonDashboard } from '../components/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../components/CurrentWeather'; +import { useStoreState } from 'pullstate'; +import { QRStore } from '../store'; +import { getCodes } from '../store/Selectors'; +import useSound from 'use-sound'; +import openSound from '../sounds/open.wav'; +import { QRCodeScannedModal } from '../components/QRCodeScannedModal'; +import { QRWebModal } from '../components/QRWebModal'; +import { NoQRCodes } from '../components/NoQRCodes'; +import { CustomFab } from '../components/CustomFab.jsx'; + +function Tab1() { + const pageRef = useRef(null); + const codes = useStoreState(QRStore, getCodes); + const [play] = useSound(openSound); + + const [QRData, setQRData] = useState(false); + + const handleScan = (data) => { + if (data) { + setQRData(data); + play(); + handleSuccess(data); + } + }; + + const handleError = (err) => { + console.error(err); + }; + + const start = async () => { + const platforms = getPlatforms(); + const isWeb = + platforms.includes('desktop') || platforms.includes('mobileweb') || platforms.includes('pwa'); + + if (!isWeb) { + // const data = await BarcodeScanner.scan(); + + // if (data) { + // handleSuccess(data); + // } + const result = await CapacitorBarcodeScanner.scanBarcode({ + hint: CapacitorBarcodeScannerTypeHint.ALL, + scanInstructions: 'Please scan a barcode', + scanButton: true, + scanText: 'Scan', + cameraDirection: CapacitorBarcodeScannerCameraDirection.BACK, + scanOrientation: CapacitorBarcodeScannerScanOrientation.ADAPTIVE, + android: { + scanningLibrary: CapacitorBarcodeScannerAndroidScanningLibrary.ZXING, + }, + }); + handleSuccess(result.ScanResult); + } else { + presentWebModal({ + presentingElement: pageRef.current, + }); + } + }; + + const handleSuccess = (data) => { + setQRData(data); + console.log(data); + dismissWebModal(); + + play(); + present({ + presentingElement: pageRef.current, + }); + }; + + const [present, dismiss] = useIonModal(QRCodeScannedModal, { + dismiss: () => dismiss(), + code: QRData, + set: () => setQRData(), + scan: () => start(), + }); + + const [presentWebModal, dismissWebModal] = useIonModal(QRWebModal, { + dismiss: () => dismissWebModal(), + set: () => setQRData(), + scan: handleScan, + error: handleError, + }); + + const router = useIonRouter(); + function handleBackButtonClick() { + router.goBack(); + } + + return ( + + + + QR Codes + + handleBackButtonClick()} + > + + + + + + + + QR Codes + + + + {codes.length < 1 && } + {codes.length > 0 && } + + + + + ); +} + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/DemoQrScanner/AppPages/Tab2.jsx b/03_source/mobile.trunk/src/pages/DemoQrScanner/AppPages/Tab2.jsx new file mode 100644 index 0000000..216544f --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQrScanner/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../components/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/DemoQrScanner/NOTES.md b/03_source/mobile.trunk/src/pages/DemoQrScanner/NOTES.md new file mode 100644 index 0000000..e77ff3c --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQrScanner/NOTES.md @@ -0,0 +1 @@ +# REQ0138 diff --git a/03_source/mobile.trunk/src/pages/DemoQrScanner/components/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk/src/pages/DemoQrScanner/components/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQrScanner/components/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoQrScanner/components/CurrentWeather/index.tsx b/03_source/mobile.trunk/src/pages/DemoQrScanner/components/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQrScanner/components/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoQrScanner/components/CustomFab.jsx b/03_source/mobile.trunk/src/pages/DemoQrScanner/components/CustomFab.jsx new file mode 100644 index 0000000..2e19fe8 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQrScanner/components/CustomFab.jsx @@ -0,0 +1,27 @@ +import { IonFab, IonFabButton, IonFabList, IonIcon } from '@ionic/react'; +import { addOutline, cameraOutline, qrCodeOutline } from 'ionicons/icons'; + +export const CustomFab = ({ start }) => { + return ( + + + + + + + + + + + + + + + + ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoQrScanner/components/NoQRCodes.jsx b/03_source/mobile.trunk/src/pages/DemoQrScanner/components/NoQRCodes.jsx new file mode 100644 index 0000000..08e6bd3 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQrScanner/components/NoQRCodes.jsx @@ -0,0 +1,15 @@ +import { IonCol, IonRow, IonText } from '@ionic/react'; + +export const NoQRCodes = () => ( + + +

It looks like you don't have any QR codes stored.

+ icon + +

+ Click the button in the bottom right to scan a code or + generate a code. +

+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoQrScanner/components/QRCodeScannedModal.jsx b/03_source/mobile.trunk/src/pages/DemoQrScanner/components/QRCodeScannedModal.jsx new file mode 100644 index 0000000..21f2c58 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQrScanner/components/QRCodeScannedModal.jsx @@ -0,0 +1,106 @@ +import { + IonButton, + IonButtons, + IonCard, + IonCardContent, + IonCardHeader, + IonCardTitle, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonIcon, + IonNote, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonToast, +} from '@ionic/react'; +import QRCode from 'react-qr-code'; +import { addQRCode } from '../store/QRStore'; + +import useSound from 'use-sound'; +import closeSound from '../sounds/close.wav'; +import { reloadOutline } from 'ionicons/icons'; + +export const QRCodeScannedModal = ({ dismiss, code, set, scan }) => { + const [play] = useSound(closeSound); + const [showToast] = useIonToast(); + + const handleDismiss = () => { + dismiss(); + play(); + }; + + const handleScanAgain = () => { + handleDismiss(); + + setTimeout(() => { + scan(); + }, 10); + }; + + const handleAdd = async () => { + addQRCode(code.text ? code.text : code, true); + showToast({ + header: 'Success!', + message: 'QR Code stored successfully.', + duration: 3000, + color: 'primary', + }); + + handleDismiss(); + }; + + return ( + + + + View QR Code + + Close + + + + + + + + + + + + + + + + + QR Code data + This is what the code represents + + +

{code.text ? code.text : code}

+
+
+
+
+ + + + + +   Scan again + + + + + Store → + + + +
+
+
+ ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoQrScanner/components/QRWebModal.jsx b/03_source/mobile.trunk/src/pages/DemoQrScanner/components/QRWebModal.jsx new file mode 100644 index 0000000..8d1775d --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQrScanner/components/QRWebModal.jsx @@ -0,0 +1,45 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonGrid, + IonHeader, + IonPage, + IonRow, + IonTitle, + IonToolbar, +} from '@ionic/react'; +// import QrReader from "react-qr-reader"; + +export const QRWebModal = ({ dismiss, set, scan, error }) => { + return ( + + + + Scan QR Code + + Close + + + + + + + + + {/* + + */} + + + + + + ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoQrScanner/components/SkeletonDashboard/index.tsx b/03_source/mobile.trunk/src/pages/DemoQrScanner/components/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQrScanner/components/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoQrScanner/index.tsx b/03_source/mobile.trunk/src/pages/DemoQrScanner/index.tsx new file mode 100644 index 0000000..9afe772 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQrScanner/index.tsx @@ -0,0 +1,38 @@ +import { IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs } from '@ionic/react'; + +import { cloudOutline, searchOutline } from 'ionicons/icons'; +import { Route, Redirect } from 'react-router'; + +import Tab1 from './AppPages/Tab1'; +import Tab2 from './AppPages/Tab2'; + +function DemoQrScanner() { + return ( + + + + + + + + + + + + + {/* */} + + + + Dashboard + + + + Search + + + + ); +} + +export default DemoQrScanner; diff --git a/03_source/mobile.trunk/src/pages/DemoQrScanner/sounds/close.wav b/03_source/mobile.trunk/src/pages/DemoQrScanner/sounds/close.wav new file mode 100644 index 0000000..7ca4e4e Binary files /dev/null and b/03_source/mobile.trunk/src/pages/DemoQrScanner/sounds/close.wav differ diff --git a/03_source/mobile.trunk/src/pages/DemoQrScanner/sounds/open.wav b/03_source/mobile.trunk/src/pages/DemoQrScanner/sounds/open.wav new file mode 100644 index 0000000..78c8254 Binary files /dev/null and b/03_source/mobile.trunk/src/pages/DemoQrScanner/sounds/open.wav differ diff --git a/03_source/mobile.trunk/src/pages/DemoQrScanner/store/QRStore.js b/03_source/mobile.trunk/src/pages/DemoQrScanner/store/QRStore.js new file mode 100644 index 0000000..2878652 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQrScanner/store/QRStore.js @@ -0,0 +1,19 @@ +import { Store } from 'pullstate'; + +const QRStore = new Store({ + codes: [], +}); + +export default QRStore; + +export const addQRCode = (data, scanned = false) => { + QRStore.update((s) => { + s.codes = [...s.codes, { id: new Date(), data, scanned }]; + }); +}; + +export const removeQRCode = (id) => { + QRStore.update((s) => { + s.codes = s.codes.filter((code) => code.id !== id); + }); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoQrScanner/store/Selectors.js b/03_source/mobile.trunk/src/pages/DemoQrScanner/store/Selectors.js new file mode 100644 index 0000000..d74c840 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQrScanner/store/Selectors.js @@ -0,0 +1,6 @@ +import { createSelector } from 'reselect'; + +const getState = (state) => state; + +// General getters +export const getCodes = createSelector(getState, (state) => state.codes); diff --git a/03_source/mobile.trunk/src/pages/DemoQrScanner/store/index.js b/03_source/mobile.trunk/src/pages/DemoQrScanner/store/index.js new file mode 100644 index 0000000..18573c9 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQrScanner/store/index.js @@ -0,0 +1 @@ +export { default as QRStore } from './QRStore'; diff --git a/03_source/mobile.trunk/src/pages/DemoQrScanner/style.scss b/03_source/mobile.trunk/src/pages/DemoQrScanner/style.scss new file mode 100644 index 0000000..37c1e1a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQrScanner/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile/src/pages/DemoQuizApp/AppPages/Home.jsx b/03_source/mobile.trunk/src/pages/DemoQuizApp/AppPages/Home.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoQuizApp/AppPages/Home.jsx rename to 03_source/mobile.trunk/src/pages/DemoQuizApp/AppPages/Home.jsx diff --git a/03_source/mobile.trunk/src/pages/DemoQuizApp/AppPages/Home.module.scss b/03_source/mobile.trunk/src/pages/DemoQuizApp/AppPages/Home.module.scss new file mode 100644 index 0000000..d0bd1ef --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQuizApp/AppPages/Home.module.scss @@ -0,0 +1,43 @@ +.title { + + height: 10rem; + margin-top: 30%; +} + +.buttons { + + position: absolute; + bottom: 3rem; + width: 100%; +} + +.playButton { + + height: 4rem; + --border-radius: 500px; + width: fit-content; + --padding-start: 5rem; + --padding-end: 5rem; + margin: 0 auto; +} + +.helpButton { + + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + text-align: center; + width: fit-content; + margin: 0 auto; + margin-top: 3rem; + opacity: 70%; + --border-radius: 10rem !important; + --padding-end: 1.25rem; + + ion-icon { + + margin-top: 0.2rem; + margin-right: 0.5rem; + } +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoQuizApp/AppPages/Questions.jsx b/03_source/mobile.trunk/src/pages/DemoQuizApp/AppPages/Questions.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoQuizApp/AppPages/Questions.jsx rename to 03_source/mobile.trunk/src/pages/DemoQuizApp/AppPages/Questions.jsx diff --git a/03_source/mobile/src/pages/DemoQuizApp/AppPages/Quiz.jsx b/03_source/mobile.trunk/src/pages/DemoQuizApp/AppPages/Quiz.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoQuizApp/AppPages/Quiz.jsx rename to 03_source/mobile.trunk/src/pages/DemoQuizApp/AppPages/Quiz.jsx diff --git a/03_source/mobile.trunk/src/pages/DemoQuizApp/AppPages/Quiz.module.scss b/03_source/mobile.trunk/src/pages/DemoQuizApp/AppPages/Quiz.module.scss new file mode 100644 index 0000000..babf39e --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQuizApp/AppPages/Quiz.module.scss @@ -0,0 +1,46 @@ +.difficultyContainer { + + margin-top: -2rem !important; +} + +.startButton { + + background-color: #994ec1; + padding: 1.25rem; + margin: 1rem; + margin-top: -1rem; + border-radius: 5px; + text-align: center; + color: white; + border: 2px solid #632485; +} + +.questionTitle { + + font-size: 1rem; +} + +.answerButton { + + height: fit-content; + --padding-top: 1rem; + --padding-bottom: 1rem; +} + +.mainGrid { + + // margin-top: -2rem; +} + +.mainRow { + + margin-top: -2rem; +} + +.emoji { + + font-size: 4rem; + padding: 0; + margin: 0; + padding-top: 1rem; +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoQuizApp/notes.md b/03_source/mobile.trunk/src/pages/DemoQuizApp/NOTES.md similarity index 100% rename from 03_source/mobile/src/pages/DemoQuizApp/notes.md rename to 03_source/mobile.trunk/src/pages/DemoQuizApp/NOTES.md diff --git a/03_source/mobile/src/pages/DemoQuizApp/components/Answer.jsx b/03_source/mobile.trunk/src/pages/DemoQuizApp/components/Answer.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoQuizApp/components/Answer.jsx rename to 03_source/mobile.trunk/src/pages/DemoQuizApp/components/Answer.jsx diff --git a/03_source/mobile/src/pages/DemoQuizApp/components/CompletedCard.jsx b/03_source/mobile.trunk/src/pages/DemoQuizApp/components/CompletedCard.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoQuizApp/components/CompletedCard.jsx rename to 03_source/mobile.trunk/src/pages/DemoQuizApp/components/CompletedCard.jsx diff --git a/03_source/mobile.trunk/src/pages/DemoQuizApp/components/Quiz.module.scss b/03_source/mobile.trunk/src/pages/DemoQuizApp/components/Quiz.module.scss new file mode 100644 index 0000000..babf39e --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQuizApp/components/Quiz.module.scss @@ -0,0 +1,46 @@ +.difficultyContainer { + + margin-top: -2rem !important; +} + +.startButton { + + background-color: #994ec1; + padding: 1.25rem; + margin: 1rem; + margin-top: -1rem; + border-radius: 5px; + text-align: center; + color: white; + border: 2px solid #632485; +} + +.questionTitle { + + font-size: 1rem; +} + +.answerButton { + + height: fit-content; + --padding-top: 1rem; + --padding-bottom: 1rem; +} + +.mainGrid { + + // margin-top: -2rem; +} + +.mainRow { + + margin-top: -2rem; +} + +.emoji { + + font-size: 4rem; + padding: 0; + margin: 0; + padding-top: 1rem; +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoQuizApp/components/QuizStats.jsx b/03_source/mobile.trunk/src/pages/DemoQuizApp/components/QuizStats.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoQuizApp/components/QuizStats.jsx rename to 03_source/mobile.trunk/src/pages/DemoQuizApp/components/QuizStats.jsx diff --git a/03_source/mobile/src/pages/DemoQuizApp/components/Settings.jsx b/03_source/mobile.trunk/src/pages/DemoQuizApp/components/Settings.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoQuizApp/components/Settings.jsx rename to 03_source/mobile.trunk/src/pages/DemoQuizApp/components/Settings.jsx diff --git a/03_source/mobile.trunk/src/pages/DemoQuizApp/components/Settings.module.scss b/03_source/mobile.trunk/src/pages/DemoQuizApp/components/Settings.module.scss new file mode 100644 index 0000000..1aeadf8 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQuizApp/components/Settings.module.scss @@ -0,0 +1,20 @@ +.category { + + height: 4rem; + border: 5px solid rgb(255, 255, 255); + background-color: #994ec1; + color: white; + border-radius: 10px; + text-align: center; + justify-content: center; + align-content: center; + display: flex; + align-items: center; + font-weight: 700; +} + +.chosen { + + border: 2px solid #3a1d49; + font-weight: 700; +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoQuizApp/index.tsx b/03_source/mobile.trunk/src/pages/DemoQuizApp/index.tsx new file mode 100644 index 0000000..6138886 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQuizApp/index.tsx @@ -0,0 +1,33 @@ +import { IonRouterOutlet, IonTabs } from '@ionic/react'; + +import { Route, Redirect } from 'react-router'; + +import Home from './AppPages/Home'; +import Quiz from './AppPages/Quiz'; +import Questions from './AppPages/Questions'; + +import './style.scss'; + +function DemoQuizApp() { + return ( + + + + + + + + + + + + + + + + + + ); +} + +export default DemoQuizApp; diff --git a/03_source/mobile/src/pages/DemoQuizApp/questions/index.js b/03_source/mobile.trunk/src/pages/DemoQuizApp/questions/index.js similarity index 100% rename from 03_source/mobile/src/pages/DemoQuizApp/questions/index.js rename to 03_source/mobile.trunk/src/pages/DemoQuizApp/questions/index.js diff --git a/03_source/mobile/src/pages/DemoQuizApp/store/Selectors.js b/03_source/mobile.trunk/src/pages/DemoQuizApp/store/Selectors.js similarity index 100% rename from 03_source/mobile/src/pages/DemoQuizApp/store/Selectors.js rename to 03_source/mobile.trunk/src/pages/DemoQuizApp/store/Selectors.js diff --git a/03_source/mobile/src/pages/DemoQuizApp/store/SettingsStore.js b/03_source/mobile.trunk/src/pages/DemoQuizApp/store/SettingsStore.js similarity index 100% rename from 03_source/mobile/src/pages/DemoQuizApp/store/SettingsStore.js rename to 03_source/mobile.trunk/src/pages/DemoQuizApp/store/SettingsStore.js diff --git a/03_source/mobile/src/pages/DemoQuizApp/store/index.js b/03_source/mobile.trunk/src/pages/DemoQuizApp/store/index.js similarity index 100% rename from 03_source/mobile/src/pages/DemoQuizApp/store/index.js rename to 03_source/mobile.trunk/src/pages/DemoQuizApp/store/index.js diff --git a/03_source/mobile.trunk/src/pages/DemoQuizApp/style.scss b/03_source/mobile.trunk/src/pages/DemoQuizApp/style.scss new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk/src/pages/DemoQuoteApp/AppPages/Home.jsx b/03_source/mobile.trunk/src/pages/DemoQuoteApp/AppPages/Home.jsx new file mode 100644 index 0000000..3769f25 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQuoteApp/AppPages/Home.jsx @@ -0,0 +1,74 @@ +import { + IonButtons, + IonContent, + IonGrid, + IonHeader, + IonInfiniteScroll, + IonInfiniteScrollContent, + IonList, + IonMenuButton, + IonPage, + IonRow, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useStoreState } from 'pullstate'; +import { useState } from 'react'; +import { QuoteItem } from '../components/QuoteItem'; +import { QuoteStore } from '../store'; +import { getQuotes } from '../store/Selectors'; + +const Home = () => { + const quotes = useStoreState(QuoteStore, getQuotes); + const [amountLoaded, setAmountLoaded] = useState(20); + + const fetchMore = async (e) => { + setAmountLoaded((amountLoaded) => amountLoaded + 20); + e.target.complete(); + }; + + return ( + + + + + + + Home + + + + + + + Home + + + + + {/* TODO: the source of the quote is already broken */} + the source broken + + + + {quotes.map((quote, index) => { + if (index <= amountLoaded && quote.author) { + return ; + } else return ''; + })} + + + + + + + + + + ); +}; + +export default Home; diff --git a/03_source/mobile/src/pages/DemoQuoteApp/AppPages/Quote.jsx b/03_source/mobile.trunk/src/pages/DemoQuoteApp/AppPages/Quote.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoQuoteApp/AppPages/Quote.jsx rename to 03_source/mobile.trunk/src/pages/DemoQuoteApp/AppPages/Quote.jsx diff --git a/03_source/mobile/src/pages/DemoQuoteApp/AppPages/Saved.jsx b/03_source/mobile.trunk/src/pages/DemoQuoteApp/AppPages/Saved.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoQuoteApp/AppPages/Saved.jsx rename to 03_source/mobile.trunk/src/pages/DemoQuoteApp/AppPages/Saved.jsx diff --git a/03_source/mobile.trunk/src/pages/DemoQuoteApp/NOTES.md b/03_source/mobile.trunk/src/pages/DemoQuoteApp/NOTES.md new file mode 100644 index 0000000..bc5c8ee --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQuoteApp/NOTES.md @@ -0,0 +1 @@ +# REQ0140 diff --git a/03_source/mobile.trunk/src/pages/DemoQuoteApp/components/Menu.css b/03_source/mobile.trunk/src/pages/DemoQuoteApp/components/Menu.css new file mode 100644 index 0000000..0ca47a2 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQuoteApp/components/Menu.css @@ -0,0 +1,113 @@ +ion-menu ion-content { + --background: var(--ion-item-background, var(--ion-background-color, #fff)); +} + +ion-menu.md ion-content { + --padding-start: 8px; + --padding-end: 8px; + --padding-top: 20px; + --padding-bottom: 20px; +} + +ion-menu.md ion-list { + padding: 20px 0; +} + +ion-menu.md ion-note { + margin-bottom: 30px; +} + +ion-menu.md ion-list-header, ion-menu.md ion-note { + padding-left: 10px; +} + +ion-menu.md ion-list#inbox-list { + border-bottom: 1px solid var(--ion-color-step-150, #d7d8da); +} + +ion-menu.md ion-list#inbox-list ion-list-header { + font-size: 22px; + font-weight: 600; + min-height: 20px; +} + +ion-menu.md ion-list#labels-list ion-list-header { + font-size: 16px; + margin-bottom: 18px; + color: #757575; + min-height: 26px; +} + +ion-menu.md ion-item { + --padding-start: 10px; + --padding-end: 10px; + border-radius: 4px; +} + +ion-menu.md ion-item.selected { + --background: rgba(var(--ion-color-primary-rgb), 0.14); +} + +ion-menu.md ion-item.selected ion-icon { + color: var(--ion-color-primary); +} + +ion-menu.md ion-item ion-icon { + color: #616e7e; +} + +ion-menu.md ion-item ion-label { + font-weight: 500; +} + +ion-menu.ios ion-content { + --padding-bottom: 20px; +} + +ion-menu.ios ion-list { + padding: 20px 0 0 0; +} + +ion-menu.ios ion-note { + line-height: 24px; + margin-bottom: 20px; +} + +ion-menu.ios ion-item { + --padding-start: 16px; + --padding-end: 16px; + --min-height: 50px; +} + +ion-menu.ios ion-item ion-icon { + font-size: 24px; + color: #73849a; +} + +ion-menu.ios ion-item .selected ion-icon { + color: var(--ion-color-primary); +} + +ion-menu.ios ion-list#labels-list ion-list-header { + margin-bottom: 8px; +} + +ion-menu.ios ion-list-header, +ion-menu.ios ion-note { + padding-left: 16px; + padding-right: 16px; +} + +ion-menu.ios ion-note { + margin-bottom: 8px; +} + +ion-note { + display: inline-block; + font-size: 16px; + color: var(--ion-color-medium-shade); +} + +ion-item.selected { + --color: var(--ion-color-primary); +} \ No newline at end of file diff --git a/03_source/mobile/src/pages/DemoReactQuotes/components/Menu.tsx b/03_source/mobile.trunk/src/pages/DemoQuoteApp/components/Menu.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoReactQuotes/components/Menu.tsx rename to 03_source/mobile.trunk/src/pages/DemoQuoteApp/components/Menu.jsx diff --git a/03_source/mobile/src/pages/DemoQuoteApp/components/QuoteItem.jsx b/03_source/mobile.trunk/src/pages/DemoQuoteApp/components/QuoteItem.jsx similarity index 100% rename from 03_source/mobile/src/pages/DemoQuoteApp/components/QuoteItem.jsx rename to 03_source/mobile.trunk/src/pages/DemoQuoteApp/components/QuoteItem.jsx diff --git a/03_source/mobile.trunk/src/pages/DemoQuoteApp/components/QuoteItem.module.css b/03_source/mobile.trunk/src/pages/DemoQuoteApp/components/QuoteItem.module.css new file mode 100644 index 0000000..8baadf6 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQuoteApp/components/QuoteItem.module.css @@ -0,0 +1,21 @@ +.quoteItem { + + --quote-item-background: rgb(49, 117, 226); + + border: 2px solid rgb(154, 204, 245); + border-radius: 10px; + --background: var(--quote-item-background); + background: var(--quote-item-background); + color: white; + padding: 1rem; +} + +.quoteText p { + + color: rgb(25, 51, 93); +} + +.quoteText h1:hover { + + color: white; +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoQuoteApp/index.tsx b/03_source/mobile.trunk/src/pages/DemoQuoteApp/index.tsx new file mode 100644 index 0000000..765c40a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoQuoteApp/index.tsx @@ -0,0 +1,27 @@ +import { Route, Redirect } from 'react-router'; +// +import Quote from './AppPages/Quote'; +import Saved from './AppPages/Saved'; +import Home from './AppPages/Home'; +// +const DemoQuoteApp = () => { + return ( + <> + + + + + + + + + + + + + + + ); +}; + +export default DemoQuoteApp; diff --git a/03_source/mobile/src/pages/DemoReactQuotes/store/QuoteStore.tsx b/03_source/mobile.trunk/src/pages/DemoQuoteApp/store/QuoteStore.js similarity index 100% rename from 03_source/mobile/src/pages/DemoReactQuotes/store/QuoteStore.tsx rename to 03_source/mobile.trunk/src/pages/DemoQuoteApp/store/QuoteStore.js diff --git a/03_source/mobile/src/pages/DemoReactQuotes/store/Selectors.tsx b/03_source/mobile.trunk/src/pages/DemoQuoteApp/store/Selectors.js similarity index 100% rename from 03_source/mobile/src/pages/DemoReactQuotes/store/Selectors.tsx rename to 03_source/mobile.trunk/src/pages/DemoQuoteApp/store/Selectors.js diff --git a/03_source/mobile/src/pages/DemoReactQuotes/store/index.tsx b/03_source/mobile.trunk/src/pages/DemoQuoteApp/store/index.js similarity index 100% rename from 03_source/mobile/src/pages/DemoReactQuotes/store/index.tsx rename to 03_source/mobile.trunk/src/pages/DemoQuoteApp/store/index.js diff --git a/03_source/mobile.trunk/src/pages/DemoQuoteApp/style.scss b/03_source/mobile.trunk/src/pages/DemoQuoteApp/style.scss new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk/src/pages/DemoReactAddToCart/AppPages/Tab1.jsx b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/DemoReactAddToCart/AppPages/Tab2.jsx b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/DemoReactAddToCart/NOTES.md b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/NOTES.md new file mode 100644 index 0000000..18c63ae --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/NOTES.md @@ -0,0 +1 @@ +# REQ0141 diff --git a/03_source/mobile.trunk/src/pages/DemoReactAddToCart/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..950c702 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/DemoReactAddToCart/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoReactAddToCart/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoReactAddToCart/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..455cec6 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,122 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoReactAddToCart/components/AddToCartButton.module.css b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/components/AddToCartButton.module.css new file mode 100644 index 0000000..3af5d30 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/components/AddToCartButton.module.css @@ -0,0 +1,14 @@ +.buttonContainer { + display: flex; + flex-direction: column; + justify-content: center; + align-content: center; + align-items: center; +} + +.button { + --padding-top: 1.75rem; + --padding-bottom: 1.75rem; + --padding-start: 1.75rem; + --padding-end: 1.75rem; +} diff --git a/03_source/mobile.trunk/src/pages/DemoReactAddToCart/components/AddToCartButton.tsx b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/components/AddToCartButton.tsx new file mode 100644 index 0000000..82a1789 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/components/AddToCartButton.tsx @@ -0,0 +1,60 @@ +import { CreateAnimation, IonButton, IonIcon } from '@ionic/react'; +import React, { useRef, useState } from 'react'; + +import styles from './AddToCartButton.module.css'; +import { cartOutline } from 'ionicons/icons'; + +const AddToCartButton = ({ + icon = true, + color = 'primary', + customOnClick = null, +}): React.JSX.Element => { + const iconRef = useRef(null); + const [hidden, setHidden] = useState(true); + + const floatStyle = { + display: hidden ? 'none' : '', + position: 'absolute', + }; + + const floatGrowAnimation = { + property: 'transform', + fromValue: 'translateY(0) scale(1)', + toValue: 'translateY(-55px) scale(1.2)', + }; + + const colorAnimation = { + property: 'color', + fromValue: 'white', + toValue: `var(--ion-color-${color}`, + }; + + const mainAnimation = { + duration: 700, + iterations: '1', + fromTo: [floatGrowAnimation, colorAnimation], + easing: 'cubic-bezier(0.25, 0.7, 0.25, 0.7)', + }; + + const handleClick = async () => { + setHidden(false); + await iconRef.current.animation.play(); + setHidden(true); + customOnClick && customOnClick(); + }; + + return ( +
+ + {!icon && 'Add to cart'} + {icon && } + + + + + +
+ ); +}; + +export default AddToCartButton; diff --git a/03_source/mobile.trunk/src/pages/DemoReactAddToCart/components/Product.module.css b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/components/Product.module.css new file mode 100644 index 0000000..fa0f815 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/components/Product.module.css @@ -0,0 +1,19 @@ +.priceContainer { + + display: flex; + flex-direction: row; + align-content: center; + justify-content: space-between; + align-items: center; +} + +.price { + + margin-top: 0.7rem; + border: 1px solid var(--ion-color-primary); + color: var(--ion-color-primary); + width: fit-content; + padding: 1rem; + border-radius: 10px; + font-weight: 600; +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoReactAddToCart/components/Product.tsx b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/components/Product.tsx new file mode 100644 index 0000000..272efcb --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/components/Product.tsx @@ -0,0 +1,61 @@ +import { + IonCard, + IonCardContent, + IonCardHeader, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonRow, + IonText, +} from '@ionic/react'; +import AddToCartButton from './AddToCartButton'; + +import styles from './Product.module.css'; + +const Product = ({ product }): React.JSX.Element => { + const handleAdd = (product) => { + console.log(`Product added: ${product.name}`); + console.log({ product }); + + // Do something + // Update Main Cart + // Global State Stuff + // API Call + // etc etc + }; + + return ( + + + {product.name} + {product.description} + + + + + + product + + + + + Product Features + {product.features.map((feature, index) => { + return

{feature}

; + })} +
+
+
+ + +
{product.price}
+ handleAdd(product)} /> +
+
+
+
+ ); +}; + +export default Product; diff --git a/03_source/mobile.trunk/src/pages/DemoReactAddToCart/index.tsx b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/index.tsx new file mode 100644 index 0000000..c0009af --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/index.tsx @@ -0,0 +1,31 @@ +import { IonRouterOutlet, IonTabs } from '@ionic/react'; + +import { Route, Redirect } from 'react-router'; + +import './theme/variables.scss'; +import Home from './pages/Home'; + +function DemoReactAddToCart() { + return ( + + + {/* + + + + + + + */} + + + + + + + + + ); +} + +export default DemoReactAddToCart; diff --git a/03_source/mobile.trunk/src/pages/DemoReactAddToCart/module.d.ts b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/module.d.ts new file mode 100644 index 0000000..d774364 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/module.d.ts @@ -0,0 +1,4 @@ +declare module '*.module.css' { + const classes: { readonly [key: string]: string }; + export default classes; +} diff --git a/03_source/mobile.trunk/src/pages/DemoReactAddToCart/pages/Home.css b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/pages/Home.css new file mode 100644 index 0000000..2877bc5 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/pages/Home.css @@ -0,0 +1,19 @@ +.price-container { + + display: flex; + flex-direction: row; + align-content: center; + justify-content: space-between; + align-items: center; +} + +.price { + + margin-top: 0.7rem; + border: 1px solid var(--ion-color-primary); + color: var(--ion-color-primary); + width: fit-content; + padding: 1rem; + border-radius: 10px; + font-weight: 600; +} \ No newline at end of file diff --git a/03_source/mobile.trunk/src/pages/DemoReactAddToCart/pages/Home.tsx b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/pages/Home.tsx new file mode 100644 index 0000000..50ee6ee --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/pages/Home.tsx @@ -0,0 +1,45 @@ +import { + IonButton, + IonButtons, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import './Home.css'; +import Product from '../components/Product'; +import { products } from './products'; +import React from 'react'; +import { chevronBackOutline } from 'ionicons/icons'; + +const Home = (): React.JSX.Element => { + const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + return ( + + + + Add To Cart Animation + + + handleBackClick()}> + + + + + + + {products.map((product) => { + return ; + })} + + + ); +}; + +export default Home; diff --git a/03_source/mobile.trunk/src/pages/DemoReactAddToCart/pages/products.ts b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/pages/products.ts new file mode 100644 index 0000000..8dbab04 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/pages/products.ts @@ -0,0 +1,44 @@ +export const products = [ + { + id: 1, + name: 'Macbook Pro', + description: "13.3' (2020) - M1, 256 GB SSD, Space Grey", + price: '£1,199', + image: '/assets/DemoReactAddToCart/macbook.jpeg', + features: [ + 'macOS 11.0 Big Sur', + 'Apple M1 chip', + 'RAM: 8 GB / Storage: 256 GB SSD', + 'Retina display', + 'Battery life: Up to 20 hours', + ], + }, + { + id: 2, + name: 'SONY A7', + description: 'SONY a7 III Mirrorless Camera - Black', + price: '£1,699', + image: '/assets/DemoReactAddToCart/camera.jpeg', + features: [ + '24.2 megapixels', + 'Full-frame 35 mm / 35.6 x 23.8 mm CMOS sensor', + 'Built-in WiFi / Bluetooth / NFC', + "3' tiltable LCD touchscreen", + '10 fps in continuous shooting mode', + ], + }, + { + id: 3, + name: 'HISENSE 55', + description: "55' Smart 4K Ultra HD HDR LED TV", + price: '£429', + image: '/assets/DemoReactAddToCart/tv.jpeg', + features: [ + 'Picture quality: 1600 PCI', + 'HDR: HDR10 / Hybrid Log-Gamma (HLG)', + 'Catch-up TV & 4K streaming', + 'Freeview HD with Freeview Play', + 'HDMI 2.0 x 3', + ], + }, +]; diff --git a/03_source/mobile.trunk/src/pages/DemoReactAddToCart/style.scss b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/style.scss new file mode 100644 index 0000000..e5ac297 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/style.scss @@ -0,0 +1,103 @@ +#about-page { + ion-toolbar { + position: absolute; + + top: 0; + left: 0; + right: 0; + + --background: transparent; + --color: white; + } + + ion-toolbar ion-back-button, + ion-toolbar ion-button, + ion-toolbar ion-menu-button { + --color: white; + } + + .about-header { + position: relative; + + width: 100%; + height: 30%; + } + + .about-header .about-image { + position: absolute; + + top: 0; + left: 0; + bottom: 0; + right: 0; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; + + opacity: 0; + + transition: opacity 500ms ease-in-out; + } + + .about-header .madison { + background-image: url('/assets/DemoReactAddToCart/WeatherDemo/img/about/madison.jpg'); + } + + .about-header .austin { + background-image: url('/assets/DemoReactAddToCart/WeatherDemo/img/about/austin.jpg'); + } + + .about-header .chicago { + background-image: url('/assets/DemoReactAddToCart/WeatherDemo/img/about/chicago.jpg'); + } + + .about-header .seattle { + background-image: url('/assets/DemoReactAddToCart/WeatherDemo/img/about/seattle.jpg'); + } + + .about-info { + position: relative; + margin-top: -10px; + border-radius: 10px; + background: var(--ion-background-color, #fff); + z-index: 2; // display rounded border above header image + } + + .about-info h3 { + margin-top: 0; + } + + .about-info ion-list { + padding-top: 0; + } + + .about-info p { + line-height: 130%; + + color: var(--ion-color-dark); + } + + .about-info ion-icon { + margin-inline-end: 32px; + } + + /* + * iOS Only + */ + + .ios .about-info { + --ion-padding: 19px; + } + + .ios .about-info h3 { + font-weight: 700; + } +} + +#date-input-popover { + --offset-y: -var(--ion-safe-area-bottom); + + --max-width: 90%; + --width: 336px; +} diff --git a/03_source/mobile.trunk/src/pages/DemoReactAddToCart/theme/variables.scss b/03_source/mobile.trunk/src/pages/DemoReactAddToCart/theme/variables.scss new file mode 100644 index 0000000..e69de29 diff --git a/03_source/mobile.trunk/src/pages/DemoReactCalculator/AppPages/Tab1.jsx b/03_source/mobile.trunk/src/pages/DemoReactCalculator/AppPages/Tab1.jsx new file mode 100644 index 0000000..a24d76a --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactCalculator/AppPages/Tab1.jsx @@ -0,0 +1,96 @@ +import { + IonButton, + IonButtons, + IonCol, + IonContent, + IonHeader, + IonIcon, + IonPage, + IonRow, + IonTitle, + IonToolbar, + useIonRouter, +} from '@ionic/react'; + +import { Geolocation } from '@capacitor/geolocation'; +import { useEffect, useState } from 'react'; +import { SkeletonDashboard } from '../TestComponents/SkeletonDashboard'; +import { chevronBackOutline, refreshOutline } from 'ionicons/icons'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab1() { + const router = useIonRouter(); + + const [currentWeather, setCurrentWeather] = useState(false); + + useEffect(() => { + getCurrentPosition(); + }, []); + + const getCurrentPosition = async () => { + setCurrentWeather(false); + const coordinates = await Geolocation.getCurrentPosition(); + getAddress(coordinates.coords); + }; + + const getAddress = async (coords) => { + const query = `${coords.latitude},${coords.longitude}`; + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${query}` + ); + + const data = await response.json(); + console.log(data); + setCurrentWeather(data); + }; + + // const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + + return ( + + + + My Weather + + + getCurrentPosition()}> + + + + + + handleBackClick()}> + + + + + + + + + Dashboard + + + + + +

Here's your location based weather

+
+
+ +
+ {currentWeather ? ( + + ) : ( + + )} +
+
+
+ ); +} + +export default Tab1; diff --git a/03_source/mobile.trunk/src/pages/DemoReactCalculator/AppPages/Tab2.jsx b/03_source/mobile.trunk/src/pages/DemoReactCalculator/AppPages/Tab2.jsx new file mode 100644 index 0000000..c258179 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactCalculator/AppPages/Tab2.jsx @@ -0,0 +1,81 @@ +import { + IonButton, + IonCol, + IonContent, + IonHeader, + IonPage, + IonRow, + IonSearchbar, + IonTitle, + IonToolbar, +} from '@ionic/react'; +import { useState } from 'react'; +import { CurrentWeather } from '../TestComponents/CurrentWeather'; + +function Tab2() { + const [search, setSearch] = useState(''); + const [currentWeather, setCurrentWeather] = useState(false); + + const performSearch = async () => { + getAddress(search); + }; + + const getAddress = async (city) => { + const response = await fetch( + `https://api.weatherapi.com/v1/current.json?key=f93eb660b2424258bf5155016210712&q=${city}&aqi=no` + ); + const data = await response.json(); + + if (data && data.current && data.location) { + setCurrentWeather(data); + } + }; + + return ( + + + + Search + + + + + + Search + + + + + + setSearch(e.target.value)} + /> + + + + + Search + + + + +
+ {currentWeather ? ( + + ) : ( +

Your search result will appear here

+ )} +
+
+
+ ); +} + +export default Tab2; diff --git a/03_source/mobile.trunk/src/pages/DemoReactCalculator/NOTES.md b/03_source/mobile.trunk/src/pages/DemoReactCalculator/NOTES.md new file mode 100644 index 0000000..c8b12fa --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactCalculator/NOTES.md @@ -0,0 +1 @@ +# REQ0142 diff --git a/03_source/mobile.trunk/src/pages/DemoReactCalculator/TestComponents/CurrentWeather/WeatherProperty.tsx b/03_source/mobile.trunk/src/pages/DemoReactCalculator/TestComponents/CurrentWeather/WeatherProperty.tsx new file mode 100644 index 0000000..52949af --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactCalculator/TestComponents/CurrentWeather/WeatherProperty.tsx @@ -0,0 +1,62 @@ +import { IonCardSubtitle, IonCol, IonIcon, IonNote, IonRow } from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; +import { useEffect, useState } from 'react'; + +export const WeatherProperty = ({ type, currentWeather }: { type: any; currentWeather: any }) => { + const [property, setProperty] = useState(false); + + const properties = { + wind: { + isIcon: false, + icon: '/assets/WeatherDemo/wind.png', + alt: 'wind', + label: 'Wind', + value: `${currentWeather.current.wind_mph}mph`, + }, + feelsLike: { + isIcon: true, + icon: thermometerOutline, + alt: 'feels like', + label: 'Feels like', + value: `${currentWeather.current.feelslike_c}°C`, + }, + indexUV: { + isIcon: true, + icon: sunnyOutline, + alt: 'index uv', + label: 'Index UV', + value: currentWeather.current.uv, + }, + pressure: { + isIcon: true, + icon: pulseOutline, + alt: 'pressure', + label: 'Pressure', + value: `${currentWeather.current.pressure_mb} mbar`, + }, + }; + + useEffect(() => { + setProperty(properties[type]); + }, [type]); + + return ( + + + + {!property.isIcon && ( + {property.alt} + )} + {property.isIcon && ( + + )} + + + + {property.label} + {property.value} + + + + ); +}; diff --git a/03_source/mobile.trunk/src/pages/DemoReactCalculator/TestComponents/CurrentWeather/index.tsx b/03_source/mobile.trunk/src/pages/DemoReactCalculator/TestComponents/CurrentWeather/index.tsx new file mode 100644 index 0000000..ceb4332 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactCalculator/TestComponents/CurrentWeather/index.tsx @@ -0,0 +1,48 @@ +import { IonCard, IonCardContent, IonGrid, IonRow, IonText, IonCardTitle } from '@ionic/react'; +import { WeatherProperty } from './WeatherProperty'; + +export const CurrentWeather = ({ currentWeather }: { currentWeather: any }) => ( + + + + +

+ {currentWeather.location.region},{' '} + {currentWeather.location.country} +

+
+ +
+ condition + + +

{currentWeather.current.condition.text}

+
+ + +

{new Date(currentWeather.location.localtime).toDateString()}

+
+
+ + + {currentWeather.current.temp_c}℃ + + + + + + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoReactCalculator/TestComponents/SkeletonDashboard/index.tsx b/03_source/mobile.trunk/src/pages/DemoReactCalculator/TestComponents/SkeletonDashboard/index.tsx new file mode 100644 index 0000000..234fb9b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactCalculator/TestComponents/SkeletonDashboard/index.tsx @@ -0,0 +1,117 @@ +import { + IonCard, + IonCardContent, + IonCardSubtitle, + IonCardTitle, + IonCol, + IonGrid, + IonIcon, + IonNote, + IonRow, + IonSkeletonText, + IonText, + IonThumbnail, +} from '@ionic/react'; +import { pulseOutline, sunnyOutline, thermometerOutline } from 'ionicons/icons'; + +export const SkeletonDashboard = () => ( + + + + +

+ +

+
+ +
+ + + + + +

+ +

+
+ + +

+ +

+
+
+ + + + + + + + + + + wind + + + + Wind + + + + + + + + + + + + + + + Feels like + + + + + + + + + + + + + + + + + Index UV + + + + + + + + + + + + + + + Pressure + + + + + + + + +
+
+
+); diff --git a/03_source/mobile.trunk/src/pages/DemoReactCalculator/components/Button.module.scss b/03_source/mobile.trunk/src/pages/DemoReactCalculator/components/Button.module.scss new file mode 100644 index 0000000..d4adbd4 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactCalculator/components/Button.module.scss @@ -0,0 +1,20 @@ +.demo-react-calculator { + .button, + .specialButton { + margin: 0.2rem; + padding: 1.5rem; + margin: 0.2rem; + border-radius: 15px; + font-size: 1.5rem; + } + + .button { + color: rgb(255, 255, 255); + background-color: rgb(58, 58, 58); + } + + .specialButton { + color: rgb(255, 255, 255); + background-color: var(--blue-color); + } +} diff --git a/03_source/mobile.trunk/src/pages/DemoReactCalculator/components/Button.tsx b/03_source/mobile.trunk/src/pages/DemoReactCalculator/components/Button.tsx new file mode 100644 index 0000000..398d37b --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactCalculator/components/Button.tsx @@ -0,0 +1,17 @@ +import { IonCol } from '@ionic/react'; +import styles from './Button.module.scss'; + +const Button = (props): React.JSX.Element => { + const { value, special, clickEvent } = props; + + return ( + clickEvent(e, value)} + > + {value === '/' ? <>÷ : value === '*' ? <>× : value} + + ); +}; + +export default Button; diff --git a/03_source/mobile.trunk/src/pages/DemoReactCalculator/components/ButtonRow.module.scss b/03_source/mobile.trunk/src/pages/DemoReactCalculator/components/ButtonRow.module.scss new file mode 100644 index 0000000..216fb37 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactCalculator/components/ButtonRow.module.scss @@ -0,0 +1,4 @@ +.demo-react-calculator { + .buttonRow { + } +} diff --git a/03_source/mobile.trunk/src/pages/DemoReactCalculator/components/ButtonRow.tsx b/03_source/mobile.trunk/src/pages/DemoReactCalculator/components/ButtonRow.tsx new file mode 100644 index 0000000..375c87f --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactCalculator/components/ButtonRow.tsx @@ -0,0 +1,9 @@ +import { IonRow } from '@ionic/react'; +import styles from './ButtonRow.module.scss'; +import React from 'react'; + +const ButtonRow = (props): React.JSX.Element => { + return {props.children}; +}; + +export default ButtonRow; diff --git a/03_source/mobile.trunk/src/pages/DemoReactCalculator/index.tsx b/03_source/mobile.trunk/src/pages/DemoReactCalculator/index.tsx new file mode 100644 index 0000000..ee886a6 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactCalculator/index.tsx @@ -0,0 +1,23 @@ +import { IonRouterOutlet, IonTabs } from '@ionic/react'; + +import { Route, Redirect } from 'react-router'; + +import './theme/variables.scss'; +import React from 'react'; +import Home from './pages/Home'; + +function DemoReactCalculator(): React.JSX.Element { + return ( + + + + + + + + + + ); +} + +export default DemoReactCalculator; diff --git a/03_source/mobile.trunk/src/pages/DemoReactCalculator/module.d.ts b/03_source/mobile.trunk/src/pages/DemoReactCalculator/module.d.ts new file mode 100644 index 0000000..77bc4ab --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactCalculator/module.d.ts @@ -0,0 +1,8 @@ +declare module '*.module.css' { + const classes: { readonly [key: string]: string }; + export default classes; +} +declare module '*.module.scss' { + const classes: { readonly [key: string]: string }; + export default classes; +} diff --git a/03_source/mobile.trunk/src/pages/DemoReactCalculator/pages/Home.module.scss b/03_source/mobile.trunk/src/pages/DemoReactCalculator/pages/Home.module.scss new file mode 100644 index 0000000..a9d7f48 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactCalculator/pages/Home.module.scss @@ -0,0 +1,40 @@ +.calculatorContainer { + padding-bottom: 2.5rem; + background-color: white; + padding-left: 1rem; + padding-right: 1rem; +} + +.sumContainer { + display: flex; + flex-direction: column; + align-content: center; + align-items: flex-end; + padding-right: 2rem; + margin-top: 2rem; + + background-color: rgba(99, 158, 226, 0.1); + padding: 2rem; + padding-top: 4rem; + padding-bottom: 4rem; +} + +.sumContainer h1, +.sumContainer h4, +.sumContainer p { + margin: 0; + padding: 0; +} + +.sumContainer h1 { + font-size: 4rem; +} + +.sumContainer p { + font-size: 2rem; + color: rgb(163, 163, 163); +} + +.sumContainer h4 { + color: rgb(197, 197, 197); +} diff --git a/03_source/mobile.trunk/src/pages/DemoReactCalculator/pages/Home.tsx b/03_source/mobile.trunk/src/pages/DemoReactCalculator/pages/Home.tsx new file mode 100644 index 0000000..e6e8e02 --- /dev/null +++ b/03_source/mobile.trunk/src/pages/DemoReactCalculator/pages/Home.tsx @@ -0,0 +1,127 @@ +import { + IonButton, + IonButtons, + IonContent, + IonFooter, + IonGrid, + IonHeader, + IonIcon, + IonPage, + IonToolbar, + useIonRouter, +} from '@ionic/react'; +import React, { useEffect, useState } from 'react'; +import Button from '../components/Button'; +import ButtonRow from '../components/ButtonRow'; +import styles from './Home.module.scss'; + +import { + checkmarkOutline, + chevronBackOutline, + chevronDownCircleOutline, + closeOutline, + heart, + languageOutline, + menuOutline, +} from 'ionicons/icons'; + +import { buttons } from '../utils/Buttons'; + +const Home = (): React.JSX.Element => { + const [showTitle, setShowTitle] = useState('_______'); + const [sum, setSum] = useState('0'); + const [sumHistory, setSumHistory] = useState('Ionic Calculator'); + + const handleClick = (e, operator) => { + const tempSumHistory = sumHistory.replace('Ionic Calculator', ''); + + if (operator === '=') { + calculate(); + } else if (operator === 'C') { + reset(); + } else if (operator === 'Del') { + backspace(); + } else { + setSumHistory(tempSumHistory + operator); + + e.target.classList.add('animate__headShake'); + + setTimeout(() => { + e.target.classList.remove('animate__headShake'); + }, 500); + } + }; + + useEffect(() => { + calculate(); + }, [sumHistory]); + + const calculate = () => { + try { + // eslint-disable-next-line no-eval + setSum(eval(sumHistory).length > 5 ? eval(sumHistory).toFixed(4) : eval(sumHistory)); + setShowTitle('Ionic Calculator'); + } catch (e) {} + }; + + const reset = () => { + setSumHistory('Ionic Calculator'); + setSum('0'); + setShowTitle('_______'); + }; + + const backspace = () => { + const tempSum = sumHistory.substr(0, sumHistory.length - 1); + setSumHistory(tempSum); + }; + + const router = useIonRouter(); + function handleBackClick() { + router.goBack(); + } + return ( + + + + + handleBackClick()}> + + + + + + +
+ {showTitle &&

{showTitle}

} +

{sumHistory}

+

+ {sum.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')} +

+
+
+ + + + {buttons.map((buttonRow, index) => { + return ( + + {buttonRow.map((button) => { + return ( +