Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quantum-kittens
GitHub Repository: quantum-kittens/platypus
Path: blob/main/frontend/vue/components/Syllabus/SyllabusView.vue
3375 views
<template>
  <section class="syllabus-view">
    <bx-btn
      class="syllabus-view__duplicate"
      :disabled="!userIsLoggedIn"
      kind="ghost"
      @click="copySyllabus"
    >
      <span class="syllabus-view__duplicate__label">Duplicate syllabus</span>
      <bx-tooltip-icon
        v-if="!userIsLoggedIn"
        alignment="end"
        direction="bottom"
        body-text="Please log in to duplicate this syllabus"
      >
        <Download16 />
      </bx-tooltip-icon>
      <Download16 v-else class="syllabus-view__duplicate__icon" />
    </bx-btn>
    <h2 class="syllabus-view__general-info-title">
      General Information
    </h2>
    <SyllabusGeneralInformation class="syllabus-view__general-information" :syllabus="syllabus" />
    <div
      v-for="(course, idx) in syllabus.courseList"
      :key="`${idx}-${course.title}`"
      class="syllabus-view__course"
    >
      <div class="syllabus-view__course__title">
        {{ course.title }}
      </div>
      <!-- eslint-disable-next-line vue/no-v-html -->
      <div class="syllabus-view__course__description" v-html="course.description" />
      <div v-if="course.unitList.length > 0" class="syllabus-view__course__units-title">
        <!-- TODO replace with translation on final design -->
        Chapters
      </div>
      <ColumnFlowGrid
        tag="ul"
        class="syllabus-view__course__unit-list"
        :elements="course.unitList"
      >
        <template #default="slotProps">
          <li class="syllabus-view__course__unit-list__item">
            <BasicLink class="syllabus-view__course__unit-list__unit" :url="getUrlById(slotProps.element)">
              {{ getNameById(slotProps.element) }}
            </BasicLink>
          </li>
        </template>
      </ColumnFlowGrid>
    </div>
  </section>
</template>

<script lang="ts">
import { defineComponent } from 'vue-demi'
import Download16 from '@carbon/icons-vue/es/download/16'
import 'carbon-web-components/es/components/tooltip/tooltip-icon'
import BasicLink from '../common/BasicLink.vue'
import ColumnFlowGrid from '../common/ColumnFlowGrid.vue'
import { Syllabus } from '../../../ts/syllabus'
import { getCourseList, Section, Course } from '../../../ts/courses'
import SyllabusGeneralInformation from './SyllabusGeneralInformation.vue'

declare global {
  interface Window {
    textbook: any
  }
}

export default defineComponent({
  name: 'SyllabusView',
  components: {
    SyllabusGeneralInformation,
    BasicLink,
    ColumnFlowGrid,
    Download16
  },
  props: {
    syllabus: {
      type: Object,
      default: () => ({} as Syllabus),
      required: true
    },
    userIsLoggedIn: {
      type: Boolean,
      default: false,
      required: true
    }
  },
  data () {
    return {
      sectionList: [] as Section[]
    }
  },
  mounted () {
    getCourseList().then((courses) => {
      const availableCourses = courses.filter(course => course.type !== 'docs')
      this.sectionList = availableCourses.reduce((sectionList: Section[], course: Course) => {
        return sectionList.concat(course.sections)
      }, [] as Section[])
    })
  },
  methods: {
    getUrlById (id: string) {
      return this.sectionList.find((section: Section) => section.uuid === id)?.pageUrl
    },
    getNameById (id: string) {
      return this.sectionList.find((section: Section) => section.uuid === id)?.title
    },
    copySyllabus () {
      const duplicatedSyllabus = this.syllabus
      duplicatedSyllabus.id = ''
      duplicatedSyllabus.code = ''
      duplicatedSyllabus.name = `Copy of ${this.syllabus.name}`

      window.textbook.trackClickEvent('syllabus', `Duplicate syllabus: ${this.syllabus.name}`)

      const url = '/syllabus'
      const csrfToken = { _csrf: window.csrfToken }
      fetch(url, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          ...csrfToken,
          ...duplicatedSyllabus
        })
      }).then((res) => {
        if (res.status === 200) {
          res.json().then((jsonResult: Syllabus) => {
            window.location.href = `/syllabus/edit/${jsonResult.code}`
          })
        } else {
          // TODO: Manage this error (and improve backend feedback)
          console.error(res)
        }
      })
    }
  }
})
</script>

<style lang="scss" scoped>
@import 'carbon-components/scss/globals/scss/typography';
@import '~/../scss/variables/mq.scss';
@import '~/../scss/variables/colors.scss';

.syllabus-view {
  position: relative;
  background-color: $background-color-lighter;
  padding: $spacing-05;
  user-select: text;
  cursor: auto;
  @include mq($until: medium) {
    padding: $spacing-10 $spacing-05 $spacing-06 $spacing-05;
  }

  &__duplicate {
    position: absolute;
    top: 0;
    right: 0;

    @include mq($until: medium) {
      right: initial;
      left: 0;
    }

    &::part(button) {
      --cds-link-01: #{$purple-70};
      --cds-hover-primary-text: #{$purple-70};
      &:focus,
      &:hover {
        background-color: $cool-gray-20;
      }
    }

    &__label {
      display: block;
      padding-right: $spacing-05;
    }
  }

  &__general-info-title {
    @include type-style('expressive-heading-03', $fluid: true);
    margin-bottom: $spacing-05;
  }

  &__section-title {
    @include type-style('expressive-heading-03', $fluid: true);
    margin: $spacing-05 0;
  }

  &__general-information {
    padding-bottom: $spacing-06;
  }

  &__course {
    border-top: 2px solid $border-color-light-2;
    padding-bottom: $spacing-05;

    &__units-title,
    &__title {
      @include type-style('expressive-heading-03', $fluid: true);
      margin: $spacing-05 0;
    }
    &__description {
      margin-bottom: $spacing-07;
      :deep(p, a) {
        @include type-style("body-long-01");
      }
      :deep(i) {
        font-style: italic;
      }
      :deep(ol) {
        list-style: decimal;
        margin: 0 0 $spacing-05 $spacing-05;
        li {
          @include type-style("body-long-01");
          margin-bottom: $spacing-03;
        }
      }
      :deep(ul) {
        list-style: disc;
        margin: 0 0 $spacing-05 $spacing-05;
        li {
          @include type-style("body-long-01");
          margin-bottom: $spacing-03;
        }
      }
      :deep(ul li) {
        list-style-position: outside;
      }
    }
    &__unit-list {
      &__item {
        list-style-position: outside;
        list-style-type: disc;
        margin-left: $spacing-06;

        &::marker {
          color: $text-color-lighter;
          font-size: 1.25rem;
        }
      }
      &__unit {
        color: $block-link-color-tertiary;
      }
    }
  }
}
</style>