Skip to content
Snippets Groups Projects
HomePage.vue 13.2 KiB
Newer Older
<template>
ivelov-vm's avatar
ivelov-vm committed
  <div>
    <HeaderComponent
      @onButtonClick="scrollToContacts"
      current-page="home"
      :transparent="scroll < 100"
      :with-shadow="scroll > 100"
      :text-white="scroll < 100"
    ></HeaderComponent>
      class="relative mt-[-80px] z-10 text-white h-[700px] sm:h-[900px] rounded-b-lg bg-cover bg-no-repeat bg-center bg-blend-multiply bg-gray-300 bg-[url('../public/img/home-header.jfif')]"
ivelov-vm's avatar
ivelov-vm committed
      &nbsp;
        class="text-[54px] font-bold leading-[4.5rem] max-w-lg ml-10 min-[500px]:ml-24 mt-16 sm:mt-[173px]"
        {{ pageInfo?pageInfo.header_heading:'Loading...' }}
ivelov-vm's avatar
ivelov-vm committed
      <p
        class="text-2xl ml-10 min-[500px]:ml-24 mt-[10px] max-w-[434px] font-gilroy"
      >
      {{ pageInfo?pageInfo.header_subheading:'Loading...' }}
        class="rounded bg-blue text-white ml-10 min-[500px]:ml-24 mt-10 py-3.5 px-10 w-[270px] font-medium text-lg hover:bg-blue-700 transition-colors"
        @click="scrollToContacts"
        Help to emigrate
      </button>
    <section class="mt-16 relative">
      <div
        class="absolute bg-gray-bg blur-[58px] rounded-[190px] w-[380px] h-[380px] z-0 -top-[134px] -left-[87px]"
      ></div>
        class="z-10 w-[85.4%] mx-auto flex justify-between items-center h-36 relative"
        <button @click="reviewLeft" class="mr-3">
          <LeftArrow class="text-blue"></LeftArrow>
        </button>
        <div
          class="flex gap-[38px] items-center relative"
          :class="{ slideLeft: slideLeft, slideRight: slideRight }"
        >
          <ReviewComponent
ivelov-vm's avatar
ivelov-vm committed
            v-for="review in reviews"
            :key="review.id"
            :data="review.attributes"
            :class="[
              { hidden: review.hidden, '-left-[373px]': review.isLeft },
              review.absolute ? 'absolute' : 'relative',
              review.isRight ? '-right-[373px]' : '',
              review.fade ? 'fade' : 'unfade',
            ]"
          ></ReviewComponent>
        </div>
        <button @click="reviewRight" class="ml-3">
          <RightArrow class="text-blue"></RightArrow>
        </button>
    </section>

    <!-- Services -->
    <section class="mt-28 pt-4 relative overflow-hidden">
      <div
        class="absolute bg-gray-bg blur-[58px] rounded-[190px] w-[255px] h-[255px] z-0 -right-[65px] top-[42%]"
      ></div>
      <div class="relative z-10">
        <h1
          class="text-center font-medium text-[54px] leading-[4.5rem] relative"
        >
          <span class="text-dark">Our </span>
          <span class="text-blue-text"
            >Services
            <FeatureSvg
              class="inline absolute -top-3 -translate-x-[3px]"
            ></FeatureSvg>
          </span>
        </h1>
        <h2
          class="text-dark text-center font-medium text-[2rem] leading-[42px] mt-4"
        >
        {{ pageInfo?pageInfo.services_subheading:'Loading...' }}
        </h2>
        <p class="text-[15px] leading-[18px] mt-8 w-[92.5%] mx-auto">
          {{ pageInfo?pageInfo.services_description:'Loading...' }}
        <div
          class="grid w-max mx-auto gap-[31px] mt-4 justify-center grid-cols-1 min-[700px]:grid-cols-2 min-[1000px]:grid-cols-3 min-[1400px]:grid-cols-4 min-[1800px]:grid-cols-5"
        >
          <ServiceCard
ivelov-vm's avatar
ivelov-vm committed
            v-for="service in $store.state.services"
            :key="service.id"
            :data="service.attributes"
          ></ServiceCard>
ivelov-vm's avatar
ivelov-vm committed
          <ServiceQuestionCard
            @onButtonClick="scrollToContacts"
          ></ServiceQuestionCard>
      </div>
    <section
      class="mt-12 sm:mt-[100px] text-white bg-cover bg-no-repeat bg-center bg-blend-multiply bg-dark-400 bg-[url('../public/img/home-bg-2.png')]"
    >
ivelov-vm's avatar
ivelov-vm committed
      &nbsp;
        class="mt-9 sm:mt-[87px] text-[3.375rem] leading-[4.5rem] text-center relative"
      >
        Our Story
        <FeatureSvg
          class="inline absolute -top-3 -translate-x-[3px]"
        ></FeatureSvg>
      </h1>
ivelov-vm's avatar
ivelov-vm committed
      <p
        class="mt-10 mb-9 sm:mb-[89px] text-lg leading-[1.625rem] text-center mx-auto w-[66%]"
        v-html="pageInfo?pageInfo.story:'Loading...'"
ivelov-vm's avatar
ivelov-vm committed
    <section
      class="bg-blue-200 rounded-2xl w-[92.5%] mx-auto mt-[100px] relative overflow-hidden"
    >
      <div
        class="absolute bg-blue-400 blur-[170px] rounded-[190px] w-[190px] h-[190px] z-0 -left-[72px] -top-[47px]"
      ></div>
      <div class="relative z-10">
        &nbsp;
ivelov-vm's avatar
ivelov-vm committed
        <h1
          class="relative text-[54px] leading-[4.5rem] w-min mx-auto mt-[71px]"
        >
          <FeatureLeftSvg
            class="inline absolute -top-7 -translate-x-[93%] text-blue-text h-[51px] w-[51px]"
          ></FeatureLeftSvg>
          Team
        </h1>
ivelov-vm's avatar
ivelov-vm committed
        <h2
          class="text-center text-[2rem] leading-[2.625rem] max-w-[385px] mx-auto"
          v-html="pageInfo?pageInfo.team_subheading:'Loading...'"
ivelov-vm's avatar
ivelov-vm committed
        <div
          class="mt-16 mx-auto mb-[53px] w-max grid grid-cols-1 lg:grid-cols-2 gap-x-[90px] gap-y-16"
        >
          <TeamMember
ivelov-vm's avatar
ivelov-vm committed
            v-for="member in $store.state.team"
            :key="member.id"
            :data="member.attributes"
ivelov-vm's avatar
ivelov-vm committed
          ></TeamMember>
        </div>
        &nbsp;
      </div>
      <div
        class="absolute bg-blue-400 blur-[170px] rounded-[190px] w-[190px] h-[190px] z-0 -right-[133px] -bottom-[136px]"
      ></div>
ivelov-vm's avatar
ivelov-vm committed

    <FooterComponent ref="contacts" class="mt-[92px]"></FooterComponent>
  </div>
</template>
<script>
import HeaderComponent from "../components/HeaderComponent.vue";
import ReviewComponent from "../components/ReviewComponent.vue";
import ServiceCard from "../components/ServiceCard.vue";
import ServiceQuestionCard from "../components/ServiceQuestionCard.vue";
import TeamMember from "../components/TeamMember.vue";
ivelov-vm's avatar
ivelov-vm committed
import FooterComponent from "../components/FooterComponent.vue";
import TeamMixin from "../components/mixins/TeamMixin.vue";

import RightArrow from "../components/svg/RightArrow.vue";
import LeftArrow from "../components/svg/LeftArrow.vue";
import FeatureSvg from "../components/svg/FeatureSvg.vue";
import FeatureLeftSvg from "../components/svg/FeatureLeftSvg.vue";
export default {
  name: "HomePage",
  data() {
      slideLeft: false,
      slideRight: false,
      firstReview: 0,
      lastReview: 2,
      sliderRunning: false,
      windowWidth: window.innerWidth,
ivelov-vm's avatar
ivelov-vm committed
      scroll: window.scrollY,
ivelov-vm's avatar
ivelov-vm committed
      pageInfo: null,
      reviews: []
  mounted() {
    window.addEventListener("resize", this.onWidthChange);
ivelov-vm's avatar
ivelov-vm committed
    window.addEventListener("scroll", this.onScroll);
ivelov-vm's avatar
ivelov-vm committed
    if (this.$route.query.contacts) {
      this.$router.replace({ query: undefined });
      this.scrollToContacts();
    }
    this.$store.dispatch("getHomePage").then(()=>{
      this.pageInfo = this.$store.state.homePage;
    });
    
ivelov-vm's avatar
ivelov-vm committed
    this.$store.dispatch("getServices");
ivelov-vm's avatar
ivelov-vm committed
    this.$store.dispatch("getReviews").then(()=>{
      this.reviews = [...this.$store.state.reviews];
      this.onWidthChange();
      this.updateReviews();
    });
ivelov-vm's avatar
ivelov-vm committed
    this.$store.dispatch("getTeam");
  },
  beforeDestroy() {
    window.removeEventListener("resize", this.onWidthChange);
    window.removeEventListener("scroll", this.onScroll);
ivelov-vm's avatar
ivelov-vm committed
    onScroll() {
      this.scroll = window.scrollY;
    },
    onWidthChange() {
      this.windowWidth = window.innerWidth;
      let newLast = this.lastReview;
      let newFirst = this.firstReview;
      let elementsDisplayed = this.lastReview - this.firstReview + 1;

      if (newLast >= this.reviews.length) {
        newLast = this.reviews.length - 1;
      }

      //Small screens, max 1 element
      if (this.windowWidth < 900) {
        if (elementsDisplayed > 1) {
          newLast = this.firstReview;
        }

        //Medium screens, max 2 elements
      } else if (this.windowWidth < 1300) {
        if (elementsDisplayed > 2) {
          newLast = this.firstReview + 1;
        } else if (elementsDisplayed < 2) {
          if (this.reviews.length > 3) {
            let nextOutOfBounds = newLast + 1 >= this.reviews.length;
            if (nextOutOfBounds) {
              if (newFirst > 0) {
                newFirst--;
              }
            } else {
              newLast++;
            }
          } else {
            newFirst = 0;
            newLast = 1;
          }
        }

        //Large screens, max 3 elements
      } else {
        if (elementsDisplayed > 3) {
          newLast = this.firstReview + 1;
          this.updateReviews();
        } else if (elementsDisplayed < 3) {
          let mustDisplay =
            this.reviews.length > 3 ? 3 : this.reviews.length - 1;
          let nextOutOfBounds = newLast + 1 >= this.reviews.length;
          while (!nextOutOfBounds && elementsDisplayed < mustDisplay) {
            newLast++;
            nextOutOfBounds = newLast + 1 >= this.reviews.length;
            elementsDisplayed = newLast - newFirst + 1;
          }
          let prevOutOfBounds = newFirst - 1 < 0;
          while (!prevOutOfBounds && elementsDisplayed < mustDisplay) {
            newFirst--;
            prevOutOfBounds = newFirst - 1 < 0;
            elementsDisplayed = newLast - newFirst + 1;
          }
        }
      }

      if (newFirst !== this.firstReview || newLast !== this.lastReview) {
        this.firstReview = newFirst;
        this.lastReview = newLast;
        this.updateReviews();
      }
    },
    updateReviews() {
      let newReviews = [...this.reviews];
      for (let i = 0; i < this.reviews.length; i++) {
        if (i >= this.firstReview && i <= this.lastReview) {
          newReviews[i].absolute = false;
          newReviews[i].fade = false;
          newReviews[i].hidden = false;
        } else {
          newReviews[i].absolute = true;
          newReviews[i].fade = true;
          newReviews[i].hidden = true;
        }
      }
      this.reviews = newReviews;
    },
    reviewLeft() {
      let reviewsCount = this.reviews.length;
      if (this.sliderRunning || reviewsCount <= 1) {
        return;
      }
      this.sliderRunning = true;
      this.slideLeft = true;

      let elementToFadeIndex = this.firstReview;
      if (this.lastReview + 1 >= reviewsCount) {
        //Move first element to end
        this.reviews.push(this.reviews.splice(0, 1)[0]);
        if (elementToFadeIndex > 0) {
          elementToFadeIndex--;
        }
      } else {
        this.lastReview++;
        this.firstReview++;
      }

      this.$set(this.reviews[elementToFadeIndex], "fade", true);
      this.$set(this.reviews[this.lastReview], "hidden", false);
      this.$set(this.reviews[this.lastReview], "fade", false);
      this.$set(this.reviews[this.lastReview], "isRight", true);

      setTimeout(() => {
        this.$set(this.reviews[elementToFadeIndex], "hidden", true);
        this.$set(this.reviews[elementToFadeIndex], "absolute", true);
        this.$set(this.reviews[this.lastReview], "absolute", false);
        this.$set(this.reviews[this.lastReview], "isRight", false);

        this.slideLeft = false;
        this.sliderRunning = false;
      }, 150);
    },
    reviewRight() {
      let reviewsCount = this.reviews.length;
      if (this.sliderRunning || reviewsCount <= 1) {
        return;
      }
      this.sliderRunning = true;
      this.slideRight = true;

      let lastReview = this.lastReview;
      if (this.firstReview - 1 < 0) {
        //Move last element to start
        this.reviews.unshift(this.reviews.splice(reviewsCount - 1, 1)[0]);
        if (this.lastReview + 1 < reviewsCount) {
          lastReview++;
        }
      } else {
        this.firstReview--;
        this.lastReview--;
      }

      this.$set(this.reviews[lastReview], "fade", true);
      this.$set(this.reviews[this.firstReview], "hidden", false);
      this.$set(this.reviews[this.firstReview], "fade", false);
      this.$set(this.reviews[this.firstReview], "isLeft", true);

      setTimeout(() => {
        this.$set(this.reviews[lastReview], "hidden", true);
        this.$set(this.reviews[lastReview], "absolute", true);
        this.$set(this.reviews[this.firstReview], "absolute", false);
        this.$set(this.reviews[this.firstReview], "isLeft", false);

        this.slideRight = false;
        this.sliderRunning = false;
      }, 150);
    },
ivelov-vm's avatar
ivelov-vm committed
    scrollToContacts() {
      this.$refs.contacts.$el.scrollIntoView({ behavior: "smooth" });
    },
  components: {
    HeaderComponent,
    ReviewComponent,
    LeftArrow,
    RightArrow,
    FeatureSvg,
    ServiceCard,
    ServiceQuestionCard,
    FeatureLeftSvg,
    TeamMember,
ivelov-vm's avatar
ivelov-vm committed
    FooterComponent,
ivelov-vm's avatar
ivelov-vm committed
  mixins: [TeamMixin],
</script>

<style scoped>
.slideRight {
  left: 0px;
  animation: slideRight 150ms 1 ease-in-out;
}

@keyframes slideRight {
  0% {
    left: 0px;
  }
  100% {
    left: 373px;
  }
}

.slideLeft {
  right: 0px;
  animation: slideLeft 150ms 1 ease-in-out;
}

@keyframes slideLeft {
  0% {
    right: 0px;
  }
  100% {
    right: 373px;
  }
}

.fade {
  opacity: 0;
  animation: fade 150ms 1 ease-in;
}

@keyframes fade {
  0% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}

.unfade {
  opacity: 1;
  animation: unfade 150ms 1 ease-out;
}

@keyframes unfade {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}
</style>