From 03912ab7bfc1650f40ed6b079d81ca562777e490 Mon Sep 17 00:00:00 2001 From: Farah Khatib Date: Wed, 18 Oct 2023 19:47:10 +0400 Subject: [PATCH] speakers page + other fixes added speaker's page. added facebook icon added privacy policy and code of conduct page --- lib/app/app.dart | 16 +-- lib/layout/page_frame.dart | 36 +++++- lib/pages/flutteristas_conference.dart | 16 ++- lib/pages/speakers_page.dart | 165 ++++++++++++++----------- lib/pages/welcome_page.dart | 14 ++- web/images/facebook-svgrepo-com.svg | 4 + web/images/medium-svgrepo-com.svg | 4 + web/styles.css | 155 +++++++++++++++++++++++ 8 files changed, 325 insertions(+), 85 deletions(-) create mode 100644 web/images/facebook-svgrepo-com.svg create mode 100644 web/images/medium-svgrepo-com.svg diff --git a/lib/app/app.dart b/lib/app/app.dart index 8e2599d..c8756f2 100644 --- a/lib/app/app.dart +++ b/lib/app/app.dart @@ -1,17 +1,17 @@ import 'package:flutteristas/layout/page_frame.dart'; //import 'package:flutteristas/pages/agenda_page.dart'; import 'package:flutteristas/pages/code_of_conduct.dart'; -import 'package:flutteristas/pages/column2_page.dart'; -import 'package:flutteristas/pages/column3_page.dart'; +// import 'package:flutteristas/pages/column2_page.dart'; +// import 'package:flutteristas/pages/column3_page.dart'; //import 'package:flutteristas/pages/contact_us_page.dart'; import 'package:flutteristas/pages/flutteristas_conference.dart'; -import 'package:flutteristas/pages/follow_us_page.dart'; +// import 'package:flutteristas/pages/follow_us_page.dart'; import 'package:flutteristas/pages/not_found_page.dart'; import 'package:flutteristas/pages/privacy_page.dart'; -import 'package:flutteristas/pages/two_sections.dart'; +// import 'package:flutteristas/pages/two_sections.dart'; import 'package:flutteristas/pages/welcome_page.dart'; -import 'package:flutteristas/pages/product_page_1.dart'; -import 'package:flutteristas/pages/product_page_note.dart'; +// import 'package:flutteristas/pages/product_page_1.dart'; +// import 'package:flutteristas/pages/product_page_note.dart'; import 'package:flutteristas/pages/product_page.dart'; import 'package:jaspr/components.dart'; import 'package:jaspr/html.dart'; @@ -29,8 +29,8 @@ final routes = [ // ProductPageWithNote.route, // FollowUsPage.route, // ContactUsPage.route, - // CodeOfConduct.route, - // PrivacyPolicyPage.route, + CodeOfConduct.route, + PrivacyPolicyPage.route, ]; class App extends StatelessComponent { diff --git a/lib/layout/page_frame.dart b/lib/layout/page_frame.dart index a9a59f7..074f70f 100644 --- a/lib/layout/page_frame.dart +++ b/lib/layout/page_frame.dart @@ -1,6 +1,8 @@ import 'dart:html'; import 'package:flutteristas/layout/top_menu.dart'; +import 'package:flutteristas/pages/code_of_conduct.dart'; +import 'package:flutteristas/pages/privacy_page.dart'; import 'package:jaspr/html.dart'; import 'package:jaspr_router/jaspr_router.dart'; @@ -76,6 +78,32 @@ class _PageFrameState extends State { ]), footer([ followUsSection(), + div(classes: [ + 'privacy-section' + ], [ + a( + href: PrivacyPolicyPage.route.path, + events: { + 'click': (event) { + Router.of(context).push(PrivacyPolicyPage.route.path); + (event as Event).preventDefault(); + }, + }, + [text('Privacy policy')], + ), + a( + href: CodeOfConduct.route.path, + events: { + 'click': (event) { + Router.of(context).push(CodeOfConduct.route.path); + (event as Event).preventDefault(); + }, + }, + [ + text('Code of conduct'), + ], + ) + ]), p([ text('Copyright © 2023 Flutteristas.org. All rights reserved. ') ]), @@ -127,7 +155,13 @@ class _PageFrameState extends State { classes: ['social-icon'], src: '/images/github-color-svgrepo-com.svg', ) - ], href: 'https://github.com/TheFlutteristas') + ], href: 'https://github.com/TheFlutteristas'), + a([ + img( + classes: ['social-icon'], + src: '/images/facebook-svgrepo-com.svg', + ) + ], href: 'https://www.facebook.com/people/Flutteristas/61552442970613/') ]) ]); } diff --git a/lib/pages/flutteristas_conference.dart b/lib/pages/flutteristas_conference.dart index a944f43..6f86fde 100644 --- a/lib/pages/flutteristas_conference.dart +++ b/lib/pages/flutteristas_conference.dart @@ -1,6 +1,5 @@ -import 'package:flutteristas/pages/agenda_page.dart'; +// import 'package:flutteristas/pages/agenda_page.dart'; import 'package:flutteristas/pages/speakers_page.dart'; -// import 'package:jaspr/components.dart'; import 'package:jaspr/html.dart'; import 'package:jaspr_router/jaspr_router.dart'; @@ -55,6 +54,19 @@ class FlutteristasConferencePage extends StatelessComponent { ], ); + yield div(classes: [ + 'speakers-section' + ], [ + h2([Text('Speakers')]), + div(classes: [ + 'speakers-container' + ], [ + SpeakersList( + projectId: 'flutteristas-website-dev-default-rtdb', + ) + ]) + ]); + // yield AgendaTalkList( // projectId: 'flutteristas-website-dev-default-rtdb', // ); diff --git a/lib/pages/speakers_page.dart b/lib/pages/speakers_page.dart index b34e7e2..67f9c0e 100644 --- a/lib/pages/speakers_page.dart +++ b/lib/pages/speakers_page.dart @@ -1,28 +1,8 @@ import 'dart:convert'; - -import 'package:intl/intl.dart'; import 'package:jaspr/components.dart'; import 'package:jaspr/html.dart'; -import 'package:jaspr_router/jaspr_router.dart'; import 'package:http/http.dart' as http; -// class AgendaPage extends StatelessComponent { -// const AgendaPage({super.key}); -// -// static final route = Route( -// path: '/agenda', -// title: 'Agenda', -// builder: (context, state) => AgendaPage(), -// ); -// -// @override -// Iterable build(BuildContext context) sync* { -// yield AgendaTalkList( -// projectId: 'flutteristas-website-dev-default-rtdb', -// ); -// } -// } - class SpeakersList extends StatefulComponent { const SpeakersList({ super.key, @@ -48,7 +28,6 @@ class _SpeakersState extends State { // see: https://firebase.google.com/docs/reference/rest/database final url = "https://${component.projectId}.firebaseio.com/speakers/conference_year/2023.json"; - // https: //flutteristas-website-dev-default-rtdb.firebaseio.com/speakers/conference_year/2023 final resp = await http.get(Uri.parse(url)); final data = json.decode(resp.body); return [ @@ -60,68 +39,112 @@ class _SpeakersState extends State { @override Iterable build(BuildContext context) sync* { - yield FutureBuilder>( - initialData: [], - future: _futureSpeakerItems, - builder: (BuildContext context, - AsyncSnapshot> snapshot) sync* { - for (final (index, item) in snapshot.requireData.indexed) { - yield div( - id: 'item-$index', - styles: Styles.box( - border: Border.all( - BorderSide( - style: BorderStyle.solid, - color: Colors.purple, - width: Unit.pixels(1), - ), - ), - padding: EdgeInsets.all(Unit.em(0.5)), - margin: EdgeInsets.only(bottom: Unit.em(1.0)), - ), - [ - p( - [ - text('Name: '), - // text(item.toString()), - strong([text(item.name)]), - br(), - // text('Bio: '), - // strong([text(item.bio)]), - br(), - text(item.professional_role), - br(), - // text( - // 'at: ' - // //'${DateFormat.yMMMd().format(item.time)} at ' - // '${DateFormat.Hms().format(item.time)} UTC', - // ), - ], - ), - ], - ); - } - }, - ); + yield Grid(columns: 4, gap: Unit.pixels(30), spread: true, children: [ + FutureBuilder>( + initialData: [], + future: _futureSpeakerItems, + builder: (BuildContext context, + AsyncSnapshot> snapshot) sync* { + for (final (index, item) in snapshot.requireData.indexed) { + Map socialIcons = item.socialMedia; + + yield div( + classes: ['SpeakerItem'], + id: 'item-$index', + [ + img(classes: ['speaker-photo'], src: item.photoLink), + h3(classes: [ + 'speaker-name' + ], [ + text(item.name), + div(classes: [ + 'speaker-bio' + ], [ + p([text(item.bio)]) + ]) + ]), + p(classes: ['speaker-role'], [text(item.professionalRole)]), + div(classes: [ + 'social-bar' + ], [ + for (var item in socialIcons.entries) social_icon(item), + // a( + // href: socialIcons['x'] as String, + // target: Target.blank, + // [img(src: '/images/x-logo.svg', width: 16)]), + ]), + // p(classes: ['speaker-bio'], [text(item.bio)]), + ], + ); + } + }, + ) + ]); + } + + Component social_icon(MapEntry item) { + if (item.value != null) { + switch (item.key as String) { + case 'x': + return a( + href: item.value as String, + target: Target.blank, + [img(src: '/images/x-logo.svg')]); + + case 'linkedin': + return a( + href: item.value as String, + target: Target.blank, + [img(src: '/images/Linkedin.svg')]); + case 'github': + return a( + href: item.value as String, + target: Target.blank, + [img(src: '/images/github-color-svgrepo-com.svg')]); + case 'medium': + return a( + href: item.value as String, + target: Target.blank, + [img(src: '/images/medium-svgrepo-com.svg')]); + case 'youtube': + return a( + href: item.value as String, + target: Target.blank, + [img(src: '/images/youtube.svg')]); + default: + return span([]); + } + } else { + throw Exception('no social value found'); + } } } class SpeakerItem { - const SpeakerItem({ - // required this.conference_year, - required this.name, - required this.professional_role, - }); + const SpeakerItem( + { + // required this.conference_year, + required this.name, + required this.bio, + required this.photoLink, + required this.professionalRole, + required this.socialMedia}); // final String conference_year; final String name; - final String professional_role; + final String bio; + final String photoLink; + final String professionalRole; + final Map socialMedia; static SpeakerItem fromJson(Map json) { return SpeakerItem( // conference_year: json['conference_year'] as String, name: json['name'] as String, - professional_role: json['professional_role'] as String, + bio: json['bio'] as String, + photoLink: json['photo'] as String, + socialMedia: json['socialmedia'] as Map, + professionalRole: json['professional_role'] as String, ); } diff --git a/lib/pages/welcome_page.dart b/lib/pages/welcome_page.dart index 6d45036..2407f9d 100644 --- a/lib/pages/welcome_page.dart +++ b/lib/pages/welcome_page.dart @@ -1,5 +1,5 @@ import 'dart:html'; - +// import 'package:flutteristas/pages/code_of_conduct.dart'; import 'package:flutteristas/pages/code_of_conduct.dart'; import 'package:jaspr/components.dart'; import 'package:jaspr/html.dart'; @@ -147,8 +147,16 @@ class WelcomePage extends StatelessComponent { 'Do you agree with the Flutteristas ', ), a( - href: '#', - [text('Code of Conduct')], + href: CodeOfConduct.route.path, + events: { + 'click': (event) { + Router.of(context).push(CodeOfConduct.route.path); + (event as Event).preventDefault(); + }, + }, + [ + text('Code of conduct'), + ], ), text( '?', diff --git a/web/images/facebook-svgrepo-com.svg b/web/images/facebook-svgrepo-com.svg new file mode 100644 index 0000000..f343167 --- /dev/null +++ b/web/images/facebook-svgrepo-com.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/images/medium-svgrepo-com.svg b/web/images/medium-svgrepo-com.svg new file mode 100644 index 0000000..281f38b --- /dev/null +++ b/web/images/medium-svgrepo-com.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/web/styles.css b/web/styles.css index c201795..458929a 100644 --- a/web/styles.css +++ b/web/styles.css @@ -41,6 +41,12 @@ header>div { .social-icon { width: 20px; height: 20px; + padding:1px; + border-radius: 4px; +} + +.social-icon:hover { + background-color: #B672EC; } .social-media { @@ -141,6 +147,15 @@ footer .follow-us { gap: 20px; } +footer .privacy-section{ + display:flex; + gap:20px; +} + +footer .privacy-section a{ + color: #e8ddff; +} + /************************************************************* * nav *************************************************************/ @@ -316,6 +331,11 @@ h2 { padding-left: 5px; } +h3 { + font-size: 1.375em; + font-family: "Montserrat", serif; +} + p.hero-text { font-size: 2.25em; letter-spacing: -1px; @@ -738,4 +758,139 @@ div #welcome-items { .conference-title h2 { font-size: 2.25em; } +} + + +.speakers-container { + background: linear-gradient(94deg, rgba(205, 189, 255, 1) 24%, rgba(225, 182, 255, 1) 70%); + border: 1px solid #ccc; + border-radius: 14px; + padding: 10px; + margin: 30px auto; +} + +.speakers-container>div { + align-content: center; + justify-content: space-around; + justify-items: center; + align-items: start +} + +.SpeakerItem { + width: 250px; + /* border: 1px solid #cdbdff; + border-radius: 10px; */ + padding: 1em; +} + +.speaker-photo { + display: block; + width: 200px; + height: 200px; + object-fit: cover; + text-align: center; + border: 1px solid #CAC4CF; + border-radius: 100px; + margin: 10px auto; + text-align: center; + box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.30), 0px 4px 8px 3px rgba(0, 0, 0, 0.15); +} + +.SpeakerItem .speaker-name { + font-weight: 700; + text-align: center; +} + +.SpeakerItem .speaker-role { + font-style: italic; + text-align: center; +} + +.SpeakerItem .social-bar { + display: flex; + flex-direction: row; + justify-content: center; + align-content: center; + align-items: center; + border-radius: 100px; + background-color: #8060E2; + padding: 1em; + gap: 10px; +} + +/* .SpeakerItem .social-bar a{ + font-size: 20px; + color:#6745C7; +} */ + +.SpeakerItem .social-bar img { + width: 20px; + fill: #000; +} + +.SpeakerItem .speaker-bio { + width: 250px; + background-color: #F2DAFF; + text-align: center; + font-size: 12px; + font-weight: 300; + padding: 5px; + border: 1px solid #CAC4CF; + border-radius: 14px; + /* visibility: hidden; */ + display: none; + -webkit-transition: visibility 0s linear 0.3s, opacity 0.3s linear; + -moz-transition: visibility 0s linear 0.3s, opacity 0.3s linear; + -o-transition: visibility 0s linear 0.3s, opacity 0.3s linear; + transition: visibility 0s linear 0.3s, opacity 0.3s linear; + -webkit-transition: all 0.4s ease; + -moz-transition: all 0.4s ease; + -o-transition: all 0.4s ease; + transition: all 0.4s ease; + -webkit-transition-delay: 1s; + -moz-transition-delay: 1s; + -o-transition-delay: 1s; + transition-delay: 1s; + +} + +h3.speaker-name:hover { + cursor: pointer; +} + +h3.speaker-name:hover .speaker-bio { + display: block; + position: absolute; + z-index: 100; + -webkit-transition: all 0.3s ease; + -moz-transition: all 0.3s ease; + -o-transition: all 0.3s ease; + transition: all 0.3s ease; + -webkit-transition-delay: 0s; + -moz-transition-delay: 0s; + -o-transition-delay: 0s; + transition-delay: 0s; + z-index: 1000; +} + +.SpeakerItem { + padding: 10px; +} + +@media only screen and (max-width:80em) { + .speakers-container>div { + grid-template-columns: repeat(3, 1fr) !important; + } +} + +@media only screen and (max-width:52.5em) { + .speakers-container>div { + grid-template-columns: repeat(2, 1fr) !important; + } +} + +@media only screen and (max-width:30em) { + .speakers-container>div { + grid-template-columns: repeat(1, 1fr) !important; + } } \ No newline at end of file