Add new seminar-series page to qiskit.org (#1293)

* Create "what is this event" section for Seminar Series (#1301)

* Create seminar series data table component (#1305)

* Creates helpful resources section (#1310)

* Fetch Seminar Series events from Airtable (#1320)

* Create Seminar Series header (#1339)

* Adds solution when there are no upcoming events (#1363)

* Instrument seminar series page (#1400)

* Add Olivia as co-host (#1401)
This commit is contained in:
Yaiza 2021-02-10 10:33:08 +01:00 committed by GitHub
parent 4d6f232fc8
commit 922a431aa5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 909 additions and 176 deletions

View File

@ -5,6 +5,7 @@
@include type-style('expressive-heading-05');
margin-bottom: $layout-03;
color: $cool-gray-80;
max-width: 8 * $column-size-large;
@include mq($from: medium, $until: large) {
@include type-style('expressive-heading-03');

View File

@ -42,7 +42,7 @@ body {
}
&_theme_light {
background-color: $ui-background;
background-color: $white;
}
&_theme_dark {

View File

@ -6,16 +6,25 @@
:tags="types"
:to="to"
cta-label="Join the event"
:segment="segment"
:vertical-layout="verticalLayout"
>
<div class="event-card__description">
<slot v-if="this.$slots.default" />
</div>
<div>
<p class="event-card__location">
<p class="event-card__detail">
<Map20 class="event-card__icon" />
{{ location }}
</p>
<p class="event-card__date">
<p class="event-card__detail">
<Calendar20 class="event-card__icon" />
<time>{{ date }}</time>
</p>
<p v-if="institution" class="event-card__detail">
<Education20 class="event-card__icon" />
{{ institution }}
</p>
</div>
</AppCard>
</template>
@ -23,15 +32,19 @@
<script lang="ts">
import Vue from 'vue'
import { Component, Prop } from 'vue-property-decorator'
import { SegmentData } from '~/constants/appLinks'
@Component
export default class EventCard extends Vue {
@Prop({ type: Array, default: () => [] }) types!: string[]
@Prop(String) title!: string
@Prop(String) image!: string
@Prop({ type: String, default: '' }) institution!: string
@Prop(String) location!: string
@Prop(String) date!: string
@Prop(String) to!: string
@Prop({ type: Object, required: false }) segment: SegmentData | undefined
@Prop({ type: Boolean, default: false }) verticalLayout!: Boolean
}
</script>
@ -45,14 +58,19 @@ export default class EventCard extends Vue {
margin-bottom: $layout-01;
}
&__location, &__date {
&__description {
margin-bottom: $spacing-06;
}
&__detail {
@include type-style('body-long-01');
display: flex;
align-items: center;
}
&__location {
margin-bottom: $spacing-03;
&:last-child {
margin-bottom: initial;
}
}
&__icon {

View File

@ -0,0 +1,46 @@
<template>
<section class="helpful-resources-section">
<h2 class="copy__title" v-text="title" />
<div class="helpful-resources-section__resources">
<AppDescriptionCard
v-for="resource in resources"
:key="resource.title"
v-bind="resource"
/>
</div>
</section>
</template>
<script lang="ts">
import Vue from 'vue'
import { Component, Prop } from 'vue-property-decorator'
import { DescriptionCard } from '~/components/ui/AppDescriptionCard.vue'
@Component
export default class HelpfulResourcesSection extends Vue {
@Prop(Array) resources!: DescriptionCard[]
title = 'Helpful Resources';
}
</script>
<style lang="scss" scoped>
@import "~/assets/scss/blocks/copy.scss";
.helpful-resources-section {
&__resources {
display: grid;
grid-template-columns: repeat(4, 1fr);
column-gap: 2rem;
row-gap: 2rem;
@include mq($from: medium, $until: large) {
grid-template-columns: repeat(2, 1fr);
}
@include mq($until: medium) {
grid-template-columns: repeat(1, 1fr);
}
}
}
</style>

View File

@ -0,0 +1,37 @@
<template>
<section class="past-seminar-series-section">
<h2 class="copy__title">
Past Quantum Seminars
</h2>
<SeminarSeriesDataTable :events="events" events-section="past-events-section" />
<AppCta
class="past-seminar-series-section__cta"
kind="ghost"
v-bind="showMoreCta"
/>
</section>
</template>
<script lang="ts">
import Vue from 'vue'
import { Component, Prop } from 'vue-property-decorator'
import { SEMINAR_SERIES_FULL_ARCHIVE_CTA } from '~/constants/appLinks.ts'
import { SeminarSeriesEvent } from '~/hooks/event-conversion-utils.ts'
@Component
export default class PastSeminarSeriesSection extends Vue {
@Prop({ type: Array, required: true }) events!: SeminarSeriesEvent[]
showMoreCta = SEMINAR_SERIES_FULL_ARCHIVE_CTA
}
</script>
<style lang="scss" scoped>
@import "~/assets/scss/blocks/copy.scss";
.past-seminar-series-section {
&__cta {
padding-bottom: 0;
}
}
</style>

View File

@ -0,0 +1,101 @@
<template>
<cv-data-table
class="seminar-series-data-table"
:columns="columns"
>
<template slot="data">
<cv-data-table-row v-for="(row, rowIndex) in dataPerRow" :key="`${rowIndex}`">
<cv-data-table-cell v-for="({ component, styles, data}, elementIndex) in row" :key="`${elementIndex}`">
<AppCta v-if="isAppCtaComponent(component)" kind="ghost" v-bind="data" :style="styles" />
<component
:is="component"
v-else
:style="styles"
>
{{ data }}
</component>
</cv-data-table-cell>
</cv-data-table-row>
</template>
</cv-data-table>
</template>
<script lang="ts">
import Vue from 'vue'
import { Component, Prop } from 'vue-property-decorator'
import { GeneralLink } from '~/constants/appLinks'
import { SeminarSeriesEvent } from '~/hooks/event-conversion-utils'
interface tableRowElement {
component: string,
styles: string,
data: string | GeneralLink,
}
@Component
export default class SeminarSeriesDataTable extends Vue {
@Prop({ type: Array, default: () => [] }) events!: SeminarSeriesEvent[]
@Prop({ type: String, required: true }) eventsSection!: string
dataPerRow: tableRowElement[][] = this.events.map(event => ([
{
component: 'span',
styles: 'min-width: 9rem; display: inline-block;',
data: event.speaker
},
{
component: 'span',
styles: 'min-width: 9rem; display: inline-block;',
data: event.institution
},
{
component: 'span',
styles: 'min-width: 19rem; display: inline-block;',
data: event.title
},
{
component: 'span',
styles: 'min-width: 8rem; display: inline-block;',
data: event.date
},
{
component: 'AppCta',
styles: 'min-width: 5rem;',
data: {
url: event.to,
label: 'Join event',
segment: {
action: `seminar-series > ${this.eventsSection} > talk-on-youtube`
}
}
}
]))
columns = ['Speaker', 'Institution', 'Name of talk', 'Date of talk', 'Link to talk']
isAppCtaComponent (component: string) : boolean {
return component === 'AppCta'
}
}
</script>
<style lang="scss" scoped>
.seminar-series-data-table {
overflow-x: scroll;
}
</style>
<style lang="scss">
.bx--data-table th {
color: $black-100;
background-color: $cool-gray-20;
}
.bx--data-table tbody tr td, .bx--data-table tbody tr:hover td {
color: $cool-gray-80;
background-color: $white;
border-top: none;
border-bottom: 1px solid $cool-gray-20;
}
</style>

View File

@ -0,0 +1,127 @@
<template>
<header class="seminar-series-header">
<div class="seminar-series-header__container">
<div class="seminar-series-header__main">
<div>
<AppPageHeaderTitle>
Quantum Information Science Seminar Series
</AppPageHeaderTitle>
<div class="seminar-series-header__description">
<p>The Qiskit Quantum Information Science Seminar Series is dedicated to the research and academic communities as a broad and deep dive into the latest cutting edge quantum research.</p>
<p>The seminar is live and interactive, you can discuss and ask questions as you watch, and is streamed on YouTube.</p>
<p>Join us live every Friday at 12:00 PM ET.</p>
</div>
</div>
<AppCta v-bind="cta" class="seminar-series-header__cta" />
</div>
<div class="seminar-series-header__aside">
<div class="seminar-series-header__card-up-title-wrapper">
<div class="seminar-series-header__card-up-title copy__subtitle">
{{ cardTitle }}
</div>
</div>
<EventCard v-bind="cardContent" :title="cardContent.speaker" :segment="segment" vertical-layout>
{{ cardContent.title }}
</EventCard>
</div>
</div>
</header>
</template>
<script lang="ts">
import Vue from 'vue'
import { Component, Prop } from 'vue-property-decorator'
import { SEMINAR_SERIES_ALL_EPISODES_CTA } from '~/constants/appLinks.ts'
import { SeminarSeriesEvent } from '~/hooks/event-conversion-utils.ts'
@Component
export default class SeminarSeriesHeader extends Vue {
@Prop({ type: Object, required: false }) nextEvent!: SeminarSeriesEvent|null
@Prop({ type: Array, required: true }) pastEvents!: SeminarSeriesEvent[]
cta = SEMINAR_SERIES_ALL_EPISODES_CTA
hasNextEvent = this.nextEvent
cardTitle = this.hasNextEvent ? 'Up next:' : 'Featured seminar:'
randomNumber = Math.random()
randomIndex = Math.floor(this.randomNumber * this.pastEvents.length)
cardContent = this.hasNextEvent ? this.nextEvent : this.pastEvents[this.randomIndex]
segment = { action: 'seminar-series > header > talk-on-youtube' }
}
</script>
<style lang="scss" scoped>
@import '~carbon-components/scss/globals/scss/typography';
.seminar-series-header {
@include responsive-grid-bg-strip('/images/grid/grid-hero-learn.svg', auto, 28rem);
&__container {
@include contained();
display: grid;
column-gap: $spacing-07;
grid-template-columns: 3fr 4fr 3fr;
grid-template-areas: 'main main aside';
row-gap: $spacing-07;
@include mq($until: large) {
grid-template-columns: 1fr 1fr;
grid-template-areas: 'main aside';
}
@include mq($until: medium) {
grid-template-columns: 1fr;
grid-template-areas:
'main'
'aside';
}
}
&__aside {
grid-area: aside;
}
&__cta {
margin: 0 0 $layout-02;
@include mq($until: medium) {
margin-top: $layout-03;
}
}
&__description {
margin: $layout-05 0 0;
max-width: 6 * $column-size-large;
> p {
@include type-style('body-long-01');
&:not(:last-child) {
margin-bottom: $spacing-06;
}
}
@include mq($from: medium, $until: large) {
margin-top: $layout-03;
}
}
&__main {
display: flex;
flex-flow: column;
grid-area: main;
justify-content: space-between;
margin-top: $spacing-07;
}
&__card-up-title {
border-bottom: 4px solid $purple-60;
display: inline;
padding-bottom: $spacing-02;
padding-right: $spacing-03;
&-wrapper {
margin-bottom: $spacing-06;
}
}
}
</style>

View File

@ -0,0 +1,29 @@
<template>
<section class="upcoming-seminar-series-section">
<h2 class="copy__title">
Upcoming Quantum Seminar Schedule
</h2>
<SeminarSeriesDataTable :events="events" events-section="upcoming-events-section" />
</section>
</template>
<script lang="ts">
import Vue from 'vue'
import { Component, Prop } from 'vue-property-decorator'
import { SeminarSeriesEvent } from '~/hooks/event-conversion-utils'
@Component
export default class UpcomingSeminarSeriesSection extends Vue {
@Prop({ type: Array, default: () => [] }) events!: SeminarSeriesEvent[]
}
</script>
<style lang="scss" scoped>
@import "~/assets/scss/blocks/copy.scss";
.upcoming-seminar-series-section {
.copy__title {
max-width: initial;
}
}
</style>

View File

@ -0,0 +1,52 @@
<template>
<section>
<h2 class="copy__title" v-text="title" />
<AppMosaic :mosaic-elements="mosaicElements" />
</section>
</template>
<script lang="ts">
import Vue from 'vue'
import { Component } from 'vue-property-decorator'
import { MosaicElement } from '~/components/ui/AppMosaic.vue'
@Component
export default class WhatIsThisEventSection extends Vue {
title = 'What is the Quantum Information Science Seminar Series?'
mosaicElements: MosaicElement[] = [
{
position: 'first',
title: 'Learn from experts',
description:
'Learn from & interact directly with world-leading experts in quantum, from across the globe.',
image: '/images/events/seminar-series/mosaic-experts.png'
},
{
position: 'second',
title: 'About the hosts',
description:
'Dr. Zlatko K. Minev, research staff member at IBM Quantum and recipient of MIT Tech Reviews 35 under 35 Global Innovator award, is our lead host. He is joined by Dr. Olivia Lanes, an experimental researcher and education developer at IBM, working to bridge the gap between the hardware and software communities.',
image: '/images/events/seminar-series/mosaic-hosts.png'
},
{
position: 'third',
title: 'The latest in quantum computing',
description:
'This series will discuss all the most current research and new developements across the field of quantum computing.',
image: '/images/events/seminar-series/mosaic-team.png'
},
{
position: 'fourth',
title: 'Real time questions & discussion',
description:
'Discuss in real time with other researchers, students, and folks in quantum, while having the ability to ask questions of the speaker in real time via the comment chat box on YouTube.',
image: '/images/events/seminar-series/mosaic-interactivity.png'
}
]
}
</script>
<style lang="scss" scoped>
@import "~/assets/scss/blocks/copy.scss";
</style>

View File

@ -10,15 +10,15 @@
<AppLink
v-for="element in elements"
:key="element.url"
:class="
iconsOnly
? `footer-section__icon-link footer-section__icon-link_theme_${theme}`
: `footer-section__link footer-section__link_theme_${theme}`
"
:class="`footer-section__link footer-section__link_theme_${theme}`"
v-bind="element"
kind="secondary"
>
<component :is="element.icon" v-if="iconsOnly" />
<component
:is="element.icon"
v-if="iconsOnly"
:class="`footer-section__icon-link footer-section__icon-link_theme_${theme}`"
/>
<span v-else>{{ element.label }}</span>
</AppLink>
</nav>

View File

@ -121,8 +121,6 @@ export default class TheMenu extends Mixins(MenuMixin) {
@import '~carbon-components/scss/globals/scss/typography';
.menu {
background-color: $white;
&__main-level {
--link-color: #{$gray-80};
}

View File

@ -1,12 +1,7 @@
<template>
<CarefulExplanation v-bind="$attrs">
<template #summary>
<h2 class="copy__subtitle">
A Carefully Worded Summary of Quantum Computing for Beginners
</h2>
<p class="copy__paragraph">
Physics is an attempt to describe the behaviour of the world around us. Around the early 20th century, through delicate experiments in laboratories, physicists saw strange behaviours which could not be explained by their current understanding of physics. These discoveries led to the development of the more complete quantum physics, which was able to describe this behaviour very successfully.
</p>
<AppDescriptionCard :title="title" :description="preliminaryDescription" />
</template>
<p class="copy__paragraph">
Quantum physics explained behaviour previously thought impossible, and it was later discovered that this could be exploited to solve specific computational problems that were also previously thought impossible. Since this behaviour occurs only in delicate laboratory experiments, performing these new computations require delicate machines known as quantum computers. We are still learning how to build quantum computers, and it will be a while before they are large and stable enough to be useful, but progress is promising and the science is fascinating.
@ -22,7 +17,10 @@ import Vue from 'vue'
import { Component } from 'vue-property-decorator'
@Component
export default class TheCarefulExplanationForBeginners extends Vue {}
export default class TheCarefulExplanationForBeginners extends Vue {
title = 'A Carefully Worded Summary of Quantum Computing for Beginners'
preliminaryDescription = 'Physics is an attempt to describe the behaviour of the world around us. Around the early 20th century, through delicate experiments in laboratories, physicists saw strange behaviours which could not be explained by their current understanding of physics. These discoveries led to the development of the more complete quantum physics, which was able to describe this behaviour very successfully.'
}
</script>
<style lang="scss" scoped>

View File

@ -1,12 +1,7 @@
<template>
<CarefulExplanation v-bind="$attrs">
<template #summary>
<h2 class="copy__subtitle">
A Carefully Worded Summary of Quantum Computing for Experts
</h2>
<p class="copy__paragraph">
The ability to control and manipulate matter on an atomic scale was a dream first shared by Richard Feynman in his speech, Theres Plenty of Room at the Bottom, in 1959. By the 80s, this idea really began to pick up speed, and a new subject called Quantum Computing had begun to emerge.
</p>
<AppDescriptionCard :title="title" :description="preliminaryDescription" />
</template>
<p class="copy__paragraph">
In the 90s, Peter Shor presented, for the first time, an algorithm that if run on a quantum processor, could provide a substantial speedup to the fundamental computational limits set by classical physics. The possibilities of a quantum computer, from being able to speed up algorithms, to their ability to more reliably simulate quantum systems themselves, was enough of an incentive for scientists and engineers across multiple disciplines to begin to dedicate their professional lives to this end goal.
@ -28,5 +23,8 @@ import Vue from 'vue'
import { Component } from 'vue-property-decorator'
@Component
export default class TheCarefulExplanationForExperts extends Vue { }
export default class TheCarefulExplanationForExperts extends Vue {
title = 'A Carefully Worded Summary of Quantum Computing for Experts'
preliminaryDescription = 'The ability to control and manipulate matter on an atomic scale was a dream first shared by Richard Feynman in his speech, “Theres Plenty of Room at the Bottom,” in 1959. By the 80s, this idea really began to pick up speed, and a new subject called Quantum Computing had begun to emerge.'
}
</script>

View File

@ -1,5 +1,8 @@
<template>
<article class="app-card">
<article
class="app-card"
:class="{'app-card_vertical': verticalLayout}"
>
<div
class="app-card__image"
:lazy-background="image"
@ -23,6 +26,7 @@
</div>
<AppCta
v-if="to"
class="app-card__cta"
v-bind="ctaLink"
kind="ghost"
/>
@ -33,6 +37,7 @@
<script lang="ts">
import Vue from 'vue'
import { Component, Prop } from 'vue-property-decorator'
import { SegmentData } from '~/constants/appLinks'
@Component
export default class AppCard extends Vue {
@ -41,11 +46,14 @@ export default class AppCard extends Vue {
@Prop({ type: Array, default: () => [] }) tags!: string[]
@Prop({ type: String, default: '' }) to!: string
@Prop({ type: String, default: '' }) ctaLabel!: string
@Prop({ type: Object, required: false }) segment: SegmentData | undefined
@Prop({ type: Boolean, default: false }) verticalLayout!: Boolean
get ctaLink () {
return {
url: this.to,
label: this.ctaLabel
label: this.ctaLabel,
segment: this.segment
}
}
@ -139,4 +147,35 @@ export default class AppCard extends Vue {
margin-top: $layout-02;
}
}
.app-card_vertical {
flex-direction: column;
.app-card {
&__content {
padding: $spacing-05;
}
&__cta {
padding-bottom: 0;
}
&__description {
margin-bottom: $spacing-03;
margin-top: $spacing-03;
}
&__image {
min-height: 4 * $column-size-large;
@include mq($until: large) {
min-height: 5 * $column-size-large;
}
@include mq($from: small, $until: medium) {
min-height: 6 * $column-size-large;
}
}
}
}
</style>

View File

@ -0,0 +1,51 @@
<template>
<article class="app-description-card">
<div>
<h2 class="copy__subtitle">
{{ title }}
</h2>
<p class="copy__paragraph">
{{ description }}
</p>
</div>
<AppCta
v-if="cta && cta.url"
v-bind="cta"
kind="ghost"
class="app-description-card__cta"
/>
</article>
</template>
<script lang="ts">
import Vue from 'vue'
import { Component, Prop } from 'vue-property-decorator'
import { GeneralLink } from '~/constants/appLinks'
export type DescriptionCard = {
title: string,
description: string,
cta: GeneralLink
}
@Component
export default class AppDescriptionCard extends Vue {
@Prop(String) title!: string
@Prop(String) description!: string
@Prop(Object) cta!: GeneralLink
}
</script>
<style lang="scss" scoped>
@import '~/assets/scss/blocks/copy.scss';
.app-description-card {
display: flex;
flex-direction: column;
justify-content: space-between;
&__cta {
width: 100%;
}
}
</style>

View File

@ -7,7 +7,10 @@
class="app-mosaic__element"
:class="`app-mosaic__element_${position}`"
>
<div class="app-mosaic__element-copy">
<div
class="app-mosaic__element-copy"
:class="`app-mosaic__element-copy_${position}`"
>
<dt class="copy__subtitle">
{{ title }}
</dt>
@ -32,7 +35,7 @@
import Vue from 'vue'
import { Component, Prop } from 'vue-property-decorator'
type MosaicElement = {
export type MosaicElement = {
position: string,
title: string,
description: string,
@ -53,7 +56,8 @@ export default class AppMosaic extends Vue {
&__layout {
display: grid;
gap: $spacing-07;
grid-template-columns: 3fr 4fr 3fr;
grid-template-columns: 2.5fr 4fr 3fr;
grid-template-rows: 29.5rem 16rem;
grid-template-areas:
"a b c"
"d d c"
@ -61,8 +65,8 @@ export default class AppMosaic extends Vue {
justify-items: stretch;
@include mq($from: medium, $until: large) {
grid-template-columns: 2fr 3fr;
grid-template-rows: repeat(3, minmax(10rem, auto));
grid-template-columns: 1fr 1fr;
grid-template-rows: 26rem 13rem 12rem;
grid-template-areas:
"a b"
"c c"
@ -71,22 +75,24 @@ export default class AppMosaic extends Vue {
}
@include mq($until: medium) {
display: flex;
flex-direction: column;
grid-template-columns: 1fr;
grid-template-rows: repeat(4, minmax(18.75rem, auto));
grid-template-areas:
"a"
"b"
"c"
"d"
;
}
}
&__element {
background-color: $cool-gray-10;
display: flex;
flex-direction: column;
@include mq($until: medium) {
height: 18.75rem;
}
&_first {
grid-area: a;
flex-direction: column;
}
&_second {
@ -96,15 +102,13 @@ export default class AppMosaic extends Vue {
&_third {
grid-area: c;
flex-direction: column;
@include mq($from: medium, $until: large) {
display: grid;
grid-template-columns: 2fr 3fr;
gap: $spacing-07;
flex-direction: row-reverse;
}
@include mq($until: medium) {
display: flex;
flex-direction: column-reverse;
}
}
@ -131,40 +135,26 @@ export default class AppMosaic extends Vue {
background-position: center top;
background-size: cover;
background-repeat: no-repeat;
min-height: 15rem;
@include mq($from: medium, $until: large) {
min-height: 10rem;
}
@include mq($until: medium) {
min-height: 4rem;
@include mq($until: large) {
min-height: 12rem;
}
}
&_third {
background-position: center bottom;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
height: 25rem;
@include mq($from: medium, $until: large) {
height: auto;
}
@include mq($until: medium) {
min-height: 4rem;
min-height: 12rem;
}
}
&_fourth {
background-position: right bottom;
background-size: 12rem auto;
background-size: contain;
background-repeat: no-repeat;
@include mq($until: large) {
background-size: 10rem auto;
}
}
}
@ -175,6 +165,10 @@ export default class AppMosaic extends Vue {
padding: $spacing-05;
}
&_fourth {
flex: 1 0 0;
}
&-description {
@include type-style('body-long-01');
color: $cool-gray-80;
@ -187,14 +181,6 @@ export default class AppMosaic extends Vue {
@include mq($until: medium) {
max-width: 100%;
}
&_fourth {
padding-bottom: $spacing-09;
@include mq($until: medium) {
padding-bottom: 0;
}
}
}
}
}

View File

@ -0,0 +1,31 @@
<template>
<h1 class="app-page-header-title">
<slot />
</h1>
</template>
<script lang="ts">
import Vue from 'vue'
import { Component } from 'vue-property-decorator'
@Component
export default class AppPageHeaderTitle extends Vue {}
</script>
<style lang="scss" scoped>
@import '~carbon-components/scss/globals/scss/typography';
.app-page-header-title {
@include type-style('expressive-heading-05', true);
color: $white-text-01;
max-width: 10 * $column-size-large;
@include mq($from: medium, $until: large) {
max-width: 6 * $column-size-medium;
}
@include mq($until: medium) {
max-width: 5 * $column-size-medium;
}
}
</style>

View File

@ -1,9 +1,9 @@
<template>
<header class="page-header">
<div class="page-header__container">
<h1 class="page-header__title">
<AppPageHeaderTitle>
<slot />
</h1>
</AppPageHeaderTitle>
</div>
</header>
</template>
@ -17,8 +17,6 @@ export default class ThePageHeader extends Vue {}
</script>
<style lang="scss" scoped>
@import '~carbon-components/scss/globals/scss/typography';
.page-header {
@include responsive-grid-bg-strip('/images/grid/grid-hero-learn.svg', auto, 28rem);
min-height: 28rem;
@ -38,19 +36,5 @@ export default class ThePageHeader extends Vue {}
height: 28rem * 40 / 64;
}
}
&__title {
@include type-style('expressive-heading-05', true);
color: $white-text-01;
max-width: 10 * $column-size-large;
@include mq($from: medium, $until: large) {
max-width: 6 * $column-size-medium;
}
@include mq($until: medium) {
max-width: 5 * $column-size-medium;
}
}
}
</style>

View File

@ -41,6 +41,24 @@ const YOUTUBE_ALL_EPISODES_CTA: GeneralLink = {
label: 'View all episodes'
}
const seminarSeriesPlaylistUrl = 'https://www.youtube.com/playlist?list=PLOFEBzvs-Vvr0uEoGFo08n4-WrM_8fft2'
const SEMINAR_SERIES_ALL_EPISODES_CTA: GeneralLink = {
url: seminarSeriesPlaylistUrl,
label: 'Go to YouTube playlist',
segment: {
action: 'seminar-series > header > youtube-playlist'
}
}
const SEMINAR_SERIES_FULL_ARCHIVE_CTA: GeneralLink = {
url: seminarSeriesPlaylistUrl,
label: 'Explore Full Seminar Archive',
segment: {
action: 'seminar-series > past-events-section > youtube-playlist'
}
}
const DISCOVER_TEXTBOOK_CTA: GeneralLink = {
url: 'https://qiskit.org/textbook',
label: 'Discover more'
@ -57,11 +75,14 @@ const IBM_Q_EXPERIENCE: GeneralLink = {
}
export {
GeneralLink,
SegmentData,
ACCESS_IBM_Q_SYSTEMS,
EVENT_REQUEST_LINK,
YOUTUBE_ALL_EPISODES_CTA,
DISCOVER_TEXTBOOK_CTA,
REQUEST_AN_EVENT_CTA,
SEMINAR_SERIES_ALL_EPISODES_CTA,
SEMINAR_SERIES_FULL_ARCHIVE_CTA,
IBM_Q_EXPERIENCE
}

View File

@ -1,46 +1,24 @@
[
{
"title": "Qiskit Community Summer Jam - Midwest ",
"title": "Boson Sampling and Quantum Simulations in Circuit QED",
"types": [
"Hackathon"
"Talks"
],
"image": "https://dl.airtable.com/.attachmentThumbnails/5fb60efc02b56ae0f4b336ef4f656682/382f1148",
"location": "HackerEarth.com",
"region": "Americas",
"date": "June 24 - July 1, 2020",
"to": "https://www.hackerearth.com/challenges/hackathon/qiskit-community-summer-jam-mid-west/"
"image": "https://dl.airtable.com/.attachmentThumbnails/1e1b501e69679ad462f7634891255817/f394afe9",
"location": "YouTube",
"region": "Online",
"date": "January 15, 2021",
"to": "https://youtu.be/miK5y8BYlwQ"
},
{
"title": "Qiskit Community Summer Jam - New England",
"title": "Quantum Engineering of Superconducting Qubits | Seminar Series with Will Oliver",
"types": [
"Hackathon"
"Talks"
],
"image": "https://dl.airtable.com/.attachmentThumbnails/5fb60efc02b56ae0f4b336ef4f656682/382f1148",
"location": "HackerEarth.com",
"region": "Americas",
"date": "June 24 - July 1, 2020",
"to": "https://qiskit-community-summer-jam-new-england.hackerearth.com/"
},
{
"title": "Qiskit Community Summer Jam - California",
"types": [
"Hackathon"
],
"image": "https://dl.airtable.com/.attachmentThumbnails/5fb60efc02b56ae0f4b336ef4f656682/382f1148",
"location": "HackerEarth.com",
"region": "Americas",
"date": "June 24 - July 1, 2020",
"to": "https://www.hackerearth.com/challenges/hackathon/qiskit-community-summer-jam-california/"
},
{
"title": "Qiskit Community Summer Jam - North Carolina",
"types": [
"Hackathon"
],
"image": "https://dl.airtable.com/.attachmentThumbnails/5fb60efc02b56ae0f4b336ef4f656682/382f1148",
"location": "HackerEarth.com",
"region": "Americas",
"date": "June 24 - July 1, 2020",
"to": "https://www.hackerearth.com/challenges/hackathon/qiskit-community-summer-jam-north-carolina/"
"image": "https://dl.airtable.com/.attachmentThumbnails/f0daa554e731da5f87d13511546f3c77/6caa60ee",
"location": "YouTube",
"region": "Online",
"date": "December 18, 2020",
"to": "https://youtu.be/aGAb-GbrvMU"
}
]

View File

@ -0,0 +1,11 @@
[
{
"date": "January 15, 2021",
"image": "https://dl.airtable.com/.attachmentThumbnails/1e1b501e69679ad462f7634891255817/f394afe9",
"institution": "Yale University",
"location": "YouTube",
"speaker": "Steve Girvin\n\t",
"title": "Boson Sampling and Quantum Simulations in Circuit QED",
"to": "https://youtu.be/miK5y8BYlwQ"
}
]

View File

@ -13,7 +13,7 @@ nav:
goTo: '#code-of-conduct'
routeName: events
---
[//]: # "TODO: Remove this file once the new Seminar Series page is done."
## What is the Quantum Information Science Seminar Series?
The Quantum Information Science Seminar Series is a deep dive into various academic and research topics within the quantum community. This series is hosted by [Zlatko Minev, Ph.D.](https://twitter.com/zlatko_minev). **Subscribe to our YouTube channel and set a reminder to watch [Quantum Information Science Seminar Series](https://www.youtube.com/watch?v=7dfw8k2p1to&feature=youtu.be)**

View File

@ -1,13 +1,64 @@
[
{
"title": "Qiskit Global Summer School",
"title": "What to do with a near-term quantum computer?",
"types": [
"Online"
"Talks"
],
"image": "https://dl.airtable.com/.attachmentThumbnails/7de6fa69d8061d8f9e49e4505691338a/c067c96f",
"location": "Online",
"image": "https://dl.airtable.com/.attachmentThumbnails/49a702d8b04f71c43f0d4e08ac5cb1db/634f7076",
"location": "YouTube",
"region": "Online",
"date": "July 20-30, 2020",
"to": "https://qiskit.org/events/summer-school/"
"date": "January 22, 2021",
"to": "https://youtu.be/I6_tHLAeBsI"
},
{
"title": "Compilation for Quantum Computing: Gap Analysis and Optimal Solution",
"types": [
"Talks"
],
"image": "https://dl.airtable.com/.attachmentThumbnails/b81d53d95b7a02f156dedfa6b00e5c7a/3c652699",
"location": "YouTube",
"region": "Online",
"date": "January 29, 2021",
"to": "https://youtu.be/Z9R9a3hku9Y"
},
{
"title": "Qiskit Hackathon @ MIT",
"types": [
"Hackathon"
],
"image": "https://dl.airtable.com/.attachmentThumbnails/22ce9a8acb5781f8991d908fb7c247d2/b666a5e3",
"location": "MIT",
"region": "Americas",
"date": "January 30-31, 2021"
},
{
"title": "Qiskit Hackathon @ McMaster University",
"types": [
"Hackathon"
],
"image": "https://dl.airtable.com/.attachmentThumbnails/da755eac58a003a1ce054470b19bccf8/209a594e",
"location": "McMaster University",
"region": "Americas",
"date": "February 5-7, 2021"
},
{
"title": "Qiskit Hackathon @ NYU",
"types": [
"Hackathon"
],
"image": "https://dl.airtable.com/.attachmentThumbnails/61994381e16351e2a3c5f0e293390b59/9dae0f09",
"location": "NYU",
"region": "Americas",
"date": "February 12-13, 2021"
},
{
"title": "Qiskit Hackathon Korea",
"types": [
"Hackathon"
],
"image": "/images/events/no-picture.jpg",
"location": "Korea",
"region": "Asia Pacific",
"date": "February 18-19, 2021"
}
]

View File

@ -0,0 +1,20 @@
[
{
"date": "January 22, 2021",
"image": "https://dl.airtable.com/.attachmentThumbnails/49a702d8b04f71c43f0d4e08ac5cb1db/634f7076",
"institution": "Harvard University",
"location": "YouTube",
"speaker": "Alán Aspuru-Guzik",
"title": "What to do with a near-term quantum computer?",
"to": "https://youtu.be/I6_tHLAeBsI"
},
{
"date": "January 29, 2021",
"image": "https://dl.airtable.com/.attachmentThumbnails/b81d53d95b7a02f156dedfa6b00e5c7a/3c652699",
"institution": "UCLA",
"location": "YouTube",
"speaker": "Jason Cong",
"title": "Compilation for Quantum Computing: Gap Analysis and Optimal Solution",
"to": "https://youtu.be/Z9R9a3hku9Y"
}
]

View File

@ -15,6 +15,16 @@ import {
findImageAttachment
} from './airtable-conversion-utils'
type SeminarSeriesEvent = {
date: string,
image: string,
institution: string,
location: string,
speaker: string,
title: string,
to: string
}
const RECORD_FIELDS = Object.freeze({
name: 'Name',
startDate: 'Start Date',
@ -24,31 +34,61 @@ const RECORD_FIELDS = Object.freeze({
location: 'Event Location',
region: 'Region',
image: 'Picture?',
published: 'SUZIE - for website?'
institution: 'Institution',
showOnEventsPage: 'SUZIE - for website?',
showOnSeminarSeriesPage: 'PAUL - Seminar Site',
speaker: 'Speaker'
} as const)
async function fetchCommunityEvents (apiKey: string, { days }: { days: any }): Promise<CommunityEvent[]> {
const { startDate, published } = RECORD_FIELDS
const communityEvents: CommunityEvent[] = []
function getEventsQuery (apiKey: string, days: number, view: string, filters: string[] = []): Airtable.Query<{}> {
const { startDate } = RECORD_FIELDS
const base = new Airtable({ apiKey }).base('appkaaRF2QdwfusP1')
await base('Events Main View').select({
fields: Object.values(RECORD_FIELDS),
filterByFormula: `AND(
DATETIME_DIFF({${startDate}}, TODAY(), 'days') ${days > 0 ? '<=' : '>='} ${days},
DATETIME_DIFF({${startDate}}, TODAY(), 'days') ${days > 0 ? '>=' : '<'} 0,
{${published}}
)`,
sort: [{ field: startDate, direction: days > 0 ? 'asc' : 'desc' }]
}).eachPage((records, nextPage) => {
const formulaFilters = [
`DATETIME_DIFF({${startDate}}, TODAY(), 'days') ${days > 0 ? '<=' : '>='} ${days}`,
`DATETIME_DIFF({${startDate}}, TODAY(), 'days') ${days > 0 ? '>=' : '<'} 0`,
...filters
]
const filterByFormula = `AND(${formulaFilters.join(',')})`
return base('Events Main View').select({
filterByFormula,
sort: [{ field: startDate, direction: days > 0 ? 'asc' : 'desc' }],
view
})
}
async function fetchCommunityEvents (apiKey: string, { days }: { days: any }): Promise<CommunityEvent[]> {
const { showOnEventsPage } = RECORD_FIELDS
const communityEvents: CommunityEvent[] = []
await getEventsQuery(apiKey, days, 'Main List', [`{${showOnEventsPage}}`]).eachPage((records, nextPage) => {
for (const record of records) {
const communityEvent = convertToCommunityEvent(record)
communityEvents.push(communityEvent)
}
nextPage()
})
return Promise.resolve(communityEvents)
}
async function fetchSeminarSeriesEvents (apiKey: string, { days }: { days: any }): Promise<SeminarSeriesEvent[]> {
const { showOnSeminarSeriesPage } = RECORD_FIELDS
const seminarSeriesEvents: SeminarSeriesEvent[] = []
await getEventsQuery(apiKey, days, 'Seminar Series Website', [`{${showOnSeminarSeriesPage}}`]).eachPage((records, nextPage) => {
for (const record of records) {
const seminarSeriesEvent = convertToSeminarSeriesEvent(record)
seminarSeriesEvents.push(seminarSeriesEvent)
}
nextPage()
})
return Promise.resolve(seminarSeriesEvents)
}
function convertToCommunityEvent (record: any): CommunityEvent {
return {
title: getName(record),
@ -61,10 +101,30 @@ function convertToCommunityEvent (record: any): CommunityEvent {
}
}
function convertToSeminarSeriesEvent (record: any): SeminarSeriesEvent {
return {
date: formatDates(...getDates(record)),
image: getImage(record),
institution: getInstitution(record),
location: getLocation(record),
speaker: getSpeaker(record),
title: getName(record),
to: getWebsite(record)
}
}
function getInstitution (record: any): string {
return record.get(RECORD_FIELDS.institution)
}
function getName (record: any): string {
return record.get(RECORD_FIELDS.name)
}
function getSpeaker (record: any): string {
return record.get(RECORD_FIELDS.speaker)
}
function getTypes (record: any): CommunityEventType[] {
const value = record.get(RECORD_FIELDS.typeOfEvent) || []
const valueList = (Array.isArray(value) ? value : [value]) as string[]
@ -137,6 +197,7 @@ function getWebsite (record: any): string {
export {
RECORD_FIELDS,
fetchCommunityEvents,
fetchSeminarSeriesEvents,
convertToCommunityEvent,
getName,
getTypes,
@ -146,5 +207,6 @@ export {
getWebsite,
getDates,
formatDates,
filterWithWhitelist
filterWithWhitelist,
SeminarSeriesEvent
}

View File

@ -1,15 +1,23 @@
import fs from 'fs'
import util from 'util'
import { fetchCommunityEvents } from './event-conversion-utils'
import { fetchCommunityEvents, fetchSeminarSeriesEvents } from './event-conversion-utils'
export default async function (apiKey: any, outputFolder: string) {
const upcomingCommunityEvents = await fetchCommunityEvents(apiKey, { days: 31 })
const pastCommunityEvents = await fetchCommunityEvents(apiKey, { days: -31 })
const writeFile = util.promisify(fs.writeFile)
const writeUpcomingEvents = writeFile(`${outputFolder}/upcoming-community-events.json`, JSON.stringify(upcomingCommunityEvents, null, 2))
const writePastEvents = writeFile(`${outputFolder}/past-community-events.json`, JSON.stringify(pastCommunityEvents, null, 2))
const upcomingSeminarSeriesEvents = await fetchSeminarSeriesEvents(apiKey, { days: 31 })
const pastSeminarSeriesEvents = await fetchSeminarSeriesEvents(apiKey, { days: -62 })
await Promise.all([writeUpcomingEvents, writePastEvents])
const writeFile = util.promisify(fs.writeFile)
const eventsAndOutputFilename = [
{ events: upcomingCommunityEvents, outputFilename: 'upcoming-community-events.json' },
{ events: pastCommunityEvents, outputFilename: 'past-community-events.json' },
{ events: upcomingSeminarSeriesEvents, outputFilename: 'upcoming-seminar-series-events.json' },
{ events: pastSeminarSeriesEvents, outputFilename: 'past-seminar-series-events.json' }
]
await Promise.all(eventsAndOutputFilename.map(curr => writeFile(`${outputFolder}/${curr.outputFilename}`, JSON.stringify(curr.events, null, 2))))
}

View File

@ -1,6 +1,6 @@
<template>
<!-- tabindex is needed to allow hiding the menu in iOS Safari -->
<div class="default-layout content-root_theme_dark" tabindex="-1">
<div class="default-layout content-root_theme_light" tabindex="-1">
<header id="navigation">
<TheMenu @change-visibility="isMenuShown = $event === 'shown'" />
</header>

View File

@ -32,9 +32,3 @@ export default class AdvocatesPage extends QiskitPage {
routeName: string = 'advocates'
}
</script>
<style lang="scss">
.advocates-page {
background-color: $white;
}
</style>

View File

@ -184,7 +184,6 @@ export default class EventsPage extends QiskitPage {
@import '~carbon-components/scss/globals/scss/typography';
.event-page {
background-color: $white;
color: $white-text-01;
&__container {

View File

@ -0,0 +1,105 @@
<template>
<main class="event-page seminar-series-page">
<SeminarSeriesHeader class="seminar-series-page__header" :next-event="nextEvent" :past-events="pastEvents" />
<WhatIsThisEventSection class="seminar-series-page__section" />
<UpcomingSeminarSeriesSection v-if="hasUpcomingEvents" :events="upcomingEvents" class="seminar-series-page__section" />
<PastSeminarSeriesSection class="seminar-series-page__section" :events="pastEvents" />
<HelpfulResourcesSection class="seminar-series-page__section" :resources="helpfulResources" />
</main>
</template>
<script lang="ts">
import { Component } from 'vue-property-decorator'
import QiskitPage from '~/components/logic/QiskitPage.vue'
import { DescriptionCard } from '~/components/ui/AppDescriptionCard.vue'
import pastSeminarSeriesEvents from '~/content/events/past-seminar-series-events.json'
import upcomingSeminarSerieEvents from '~/content/events/upcoming-seminar-series-events.json'
@Component({
head () {
return {
title: 'Qiskit Seminar Series'
}
}
})
export default class SeminarSeriesPage extends QiskitPage {
routeName = 'seminar-series'
// Assign the imported variable to a local one to be able to use it on the template
upcomingEvents = upcomingSeminarSerieEvents
pastEvents = pastSeminarSeriesEvents
// When there are no upcoming events, the JSON file is filled with []
hasUpcomingEvents = JSON.stringify(this.upcomingEvents) !== '[]'
nextEvent = this.hasUpcomingEvents ? this.upcomingEvents[0] : null
helpfulResources: DescriptionCard[] = [
{
title: 'Stay informed',
description: 'Want to keep up to date with upcoming seminars? Click here to subscribe to our calendar for all upcoming events.',
cta: {
url: 'https://calendar.google.com/calendar/ical/c12g9fqo0mkvp9bo26dhm3u1rs%40group.calendar.google.com/public/basic.ics',
label: 'Get calendar updates',
segment: {
action: `${this.routeName} > helpful-resources > get-calendar`
}
}
},
{
title: 'Nominate',
description: 'If you or someone you know might be interested in speaking in a future seminar, we would love to include them. Please include your name, topic and available dates.',
cta: {
url: 'https://airtable.com/shrB5wy8SCaMMtKop',
label: 'Contact us',
segment: {
action: `${this.routeName} > helpful-resources > contact`
}
}
},
{
title: 'Get up to speed',
description: 'If the content of the seminar series is too dense or technical, we have a host of content to help you get up to speed.',
cta: {
url: 'https://qiskit.org/learn',
label: 'Start learning',
segment: {
action: `${this.routeName} > helpful-resources > qiskit-org-learn`
}
}
},
{
title: 'Code of conduct',
description: 'Qiskit is dedicated to providing an enjoyable and safe experience for all participants. We have a code of conduct that all events adhere to.',
cta: {
url: 'https://github.com/Qiskit/qiskit/blob/master/CODE_OF_CONDUCT.md',
label: 'See code of conduct',
segment: {
action: `${this.routeName} > helpful-resources > code-of-conduct`
}
}
}
]
}
</script>
<style lang="scss" scoped>
.seminar-series-page {
color: $white-text-01;
&__header {
padding-top: $layout-06;
@include mq($until: medium) {
padding-top: $layout-04;
}
}
&__section {
@include contained();
margin-top: $layout-05;
margin-bottom: $layout-03;
@include mq($until: large) {
margin-bottom: $layout-01;
}
}
}
</style>

View File

@ -32,8 +32,4 @@ export default class LandingPage extends QiskitPage {
<style lang="scss">
@import '~/assets/scss/blocks/copy.scss';
.landing-page {
background-color: white;
}
</style>

View File

@ -43,8 +43,6 @@ export default class LearnEntry extends QiskitPage {
@import '~carbon-components/scss/globals/scss/typography';
.learn-entry {
background-color: white;
&__header {
@include responsive-grid-bg-strip('/images/grid/grid-hero-learn.svg', auto, 28rem);
min-height: 28rem;

View File

@ -139,9 +139,3 @@ export default class LearnPage extends QiskitPage {
}
}
</script>
<style lang="scss" scoped>
.learn-page {
background-color: white;
}
</style>

View File

@ -96,8 +96,6 @@ export default class OverviewPage extends QiskitPage {
@import '~/assets/scss/blocks/copy.scss';
.overview-page {
background-color: white;
&__content-container {
@include contained();
display: flex;

View File

@ -3,6 +3,7 @@ import CarbonComponentsVue from '@carbon/vue'
import '~/assets/scss/carbon/components.scss'
import { CarbonIconsVue } from '@carbon/icons-vue'
import Calendar20 from '@carbon/icons-vue/lib/calendar/20'
import Education20 from '@carbon/icons-vue/lib/education/20'
import Map20 from '@carbon/icons-vue/lib/map/20'
import ArrowRight20 from '@carbon/icons-vue/lib/arrow--right/20'
import ArrowRight16 from '@carbon/icons-vue/lib/arrow--right/16'
@ -21,6 +22,7 @@ Vue.use(CarbonComponentsVue)
Vue.use(CarbonIconsVue, {
components: {
Calendar20,
Education20,
Map20,
ArrowRight20,
ArrowRight16,

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 797 KiB