iNeoO / vue-meeting-selector

This component is inspired from the meeting selector from doctolib with the power of Vuejs components.
https://vue-meeting-selector.tuturu.io
96 stars 19 forks source link

Nuxt3 css is not loaded #39

Closed urko-b closed 12 months ago

urko-b commented 1 year ago

Hi, I've been following documentation example and I found some trouble while installing component, I'm able now to make it build but looks like css is not loaded. Could you please give me any clue how can I finally use this component.

I have no error on console neither network when I inspect browser tab with F12.

This is my component

<template>
    <div class="simple-multi-example">
      <vue-meeting-selector
        class="simple-multi-example__meeting-selector"
        v-model="meeting"
        :date="date"
        :loading="loading"
        :class-names="classNames"
        :meetings-days="meetingsDays"
        :multi="true"
        @next-date="nextDate"
        @previous-date="previousDate"
      />
      <p>meeting Selected: {{ meeting.length ? meeting : 'No Meeting selected' }}</p>
    </div>
  </template>

  <script lang="ts">
  import {
    defineComponent,
    onMounted,
    ref,
    computed,
  } from 'vue';

  import VueMeetingSelector from 'vue-meeting-selector';
  // Function used to generate slots, use your own function
  import slotsGenerator from '@/helpers/slotsGenerator';

  import type MeetingsDay from 'vue-meeting-selector/src/interfaces/MeetingsDay.interface';
  import type MeetingSlot from 'vue-meeting-selector/src/interfaces/MeetingSlot.interface';
  import type Time from 'vue-meeting-selector/src/interfaces/Time.interface';

  export default defineComponent({
    name: 'SimpleMultiExample',
    components: {
      VueMeetingSelector,
    },
    setup() {
      const date = ref(new Date());
      const meetingsDays = ref<MeetingsDay[]>([]);
      const meeting = ref<MeetingSlot[]>([]);
      const loading = ref(true);

      const nbDaysToDisplay = computed(() => 5);

      // because of line-height, font-type you might need to change top value
      const classNames = computed(() => ({
        tabLoading: 'loading-div',
      }));

      // juste set async the gettings of meeting to display loading
      const slotsGeneratorAsync = (
        d: Date, // date
        n: number, // nbDaysToDisplay
        start: Time,
        end: Time,
        timesBetween: number,
      ):Promise<MeetingsDay[]> => new Promise((resolve) => {
        setTimeout(() => {
          resolve(slotsGenerator(d, n, start, end, timesBetween));
        }, 1000);
      });

      const nextDate = async () => {
        loading.value = true;
        const start: Time = {
          hours: 8,
          minutes: 0,
        };
        const end: Time = {
          hours: 16,
          minutes: 0,
        };
        const dateCopy = new Date(date.value);
        const newDate = new Date(dateCopy.setDate(dateCopy.getDate() + 7));
        date.value = newDate;
        meetingsDays.value = await slotsGeneratorAsync(
          newDate,
          nbDaysToDisplay.value,
          start,
          end,
          30,
        );
        loading.value = false;
      };

      const previousDate = async () => {
        loading.value = true;
        const start: Time = {
          hours: 8,
          minutes: 0,
        };
        const end: Time = {
          hours: 16,
          minutes: 0,
        };
        const dateCopy = new Date(date.value);
        dateCopy.setDate(dateCopy.getDate() - 7);
        const formatingDate = (dateToFormat: Date): String => {
          const d = new Date(dateToFormat);
          const day = d.getDate() < 10 ? `0${d.getDate()}` : d.getDate();
          const month = d.getMonth() + 1 < 10 ? `0${d.getMonth() + 1}` : d.getMonth() + 1;
          const year = d.getFullYear();
          return `${year}-${month}-${day}`;
        };
        const newDate = formatingDate(new Date()) >= formatingDate(dateCopy)
          ? new Date()
          : new Date(dateCopy);
        date.value = newDate;
        meetingsDays.value = await slotsGeneratorAsync(
          newDate,
          nbDaysToDisplay.value,
          start,
          end,
          30,
        );
        loading.value = false;
      };

      onMounted(async () => {
        const start: Time = {
          hours: 8,
          minutes: 0,
        };
        const end: Time = {
          hours: 16,
          minutes: 0,
        };
        meetingsDays.value = await slotsGeneratorAsync(
          date.value,
          nbDaysToDisplay.value,
          start,
          end,
          30,
        );
        loading.value = false;
      });

      return {
        date,
        meetingsDays,
        meeting,
        loading,
        nbDaysToDisplay,
        classNames,
        nextDate,
        previousDate,
      };
    },
  });
  </script>

  <style scoped lang="scss">
  .simple-multi-example {
    &__meeting-selector {
      max-width: 542px;
    }
  }
  // since our scss is scoped we need to use ::v-deep
  :deep(.loading-div) {
    top: 58px!important;
  }
  </style>

This my nuxt configuration

export default defineNuxtConfig({
    ssr: false,
    app: {
        head: {
            title: "test",
        },
    },
    css: [ "~/assets/css/main.css"],
    postcss: {
        plugins: {
            tailwindcss: {},
            autoprefixer: {},
        },
    },
    devServer: {
        port: 12345,
    },
    modules: [],
    build: {
    }
})

image

urko-b commented 1 year ago

ok I've added this line and looks like css now is working: import 'vue-meeting-selector/dist/style.css'; It would be great if both could work together to fix this problem. I'm around if you need feedback from my side image

urko-b commented 1 year ago

I achieve same task using this code as my calendar component Please share feedback :wink:

<template>
  <div class="simple-multi-example">
    <vue-meeting-selector class="simple-multi-example__meeting-selector" v-model="selectedMeetings" :date="date" :loading="loading"
      :class-names="classNames" :meetings-days="meetingsDays" :multi="true" @next-date="nextDate"
      @previous-date="previousDate" />
    <p>meeting Selected: {{ selectedMeetings.length ? selectedMeetings : 'No Meeting selected' }}</p>
  </div>
</template>

<script lang="ts">
import VueMeetingSelector from 'vue-meeting-selector';
import 'vue-meeting-selector/dist/style.css';
import slotsGenerator from '@/helpers/slotsGenerator';

import type MeetingsDay from 'vue-meeting-selector/src/interfaces/MeetingsDay.interface';
import type MeetingSlot from 'vue-meeting-selector/src/interfaces/MeetingSlot.interface';
import type Time from 'vue-meeting-selector/src/interfaces/Time.interface';

export default {
  name: 'SimpleMultiExample',
  components: {
    VueMeetingSelector,
  },
  data() {
    return {
      date: new Date(),
      meetingsDays: ([] as MeetingsDay[]),
      selectedMeetings: ([] as MeetingSlot[]),
      loading: true,
      nbDaysToDisplay: 7,
      classNames: { tabLoading: 'loading-div' },
      start: {
        hours: 0,
        minutes: 0,
      },
      end: {
        hours: 23,
        minutes: 30,
      }
    };
  },

  methods: {
    slotsGeneratorAsync(
      d: Date,
      daysToDisplay: number,
      start: Time,
      end: Time,
      timesBetween: number,
      ): MeetingsDay[] {
      return slotsGenerator(d, daysToDisplay, start, end, timesBetween)     
    },
    nextDate() {
      this.loading = true;
      const dateCopy = new Date(this.date);
      const newDate = new Date(dateCopy.setDate(dateCopy.getDate() + 7));
      this.date = newDate;
      this.meetingsDays = this.slotsGeneratorAsync(
        newDate,
        this.nbDaysToDisplay,
        this.start,
        this.end,
        30,
      );
      this.loading = false;
    },
    previousDate() {
      this.loading = true;
      const dateCopy = new Date(this.date);
      dateCopy.setDate(dateCopy.getDate() - 7);
      const formatingDate = (dateToFormat: Date): String => {
        const d = new Date(dateToFormat);
        const day = d.getDate() < 10 ? `0${d.getDate()}` : d.getDate();
        const month = d.getMonth() + 1 < 10 ? `0${d.getMonth() + 1}` : d.getMonth() + 1;
        const year = d.getFullYear();
        return `${year}-${month}-${day}`;
      };
      const newDate = formatingDate(new Date()) >= formatingDate(dateCopy)
        ? new Date()
        : new Date(dateCopy);
      this.date = newDate;
      this.meetingsDays = this.slotsGeneratorAsync(
        newDate,
        this.nbDaysToDisplay,
        this.start,
        this.end,
        30,
      );
      this.loading = false;
    }
  },  
  mounted() {
    this.meetingsDays = this.slotsGeneratorAsync(
      this.date,
      this.nbDaysToDisplay,
      this.start,
      this.end,
      30,
    );    
    this.loading = false;
  }

};
</script>

<style scoped lang="scss">
.simple-multi-example {
  &__meeting-selector {
    max-width: 542px;
  }
}

// since our scss is scoped we need to use ::v-deep
:deep(.loading-div) {
  top: 58px !important;
}
</style>

And this is my helper, which is a copy of yours basically haha :smile:

import type MeetingsDay from 'vue-meeting-selector/src/interfaces/MeetingsDay.interface';
import type MeetingSlot from 'vue-meeting-selector/src/interfaces/MeetingSlot.interface';
import type Time from 'vue-meeting-selector/src/interfaces/Time.interface';

function formatingDate(date: Date | string):string {
  const d = new Date(date);
  const day = d.getDate() < 10 ? `0${d.getDate()}` : d.getDate();
  const month = d.getMonth() + 1 < 10 ? `0${d.getMonth() + 1}` : d.getMonth() + 1;
  const year = d.getFullYear();
  return `${year}-${month}-${day}`;
}

function randomNumber(min: number, max: number): number {
  return Math.floor(Math.random() * (max - min) + min);
}

function setTime(date: Date, time: Time): Date {
  const d: Date = new Date(date);
  d.setHours(time.hours);
  d.setMinutes(time.minutes);
  d.setSeconds(0);
  d.setMilliseconds(0);
  return d;
}

function roundToClosestTime(date: Date, interval: number): Date {
  const d: Date = new Date(date);
  const minutes: number = d.getMinutes();
  const minutesToAdd: number = minutes % interval;
  d.setMinutes(minutes + (interval - minutesToAdd));
  d.setSeconds(0);
  d.setMilliseconds(0);
  return d;
}

// function roundDate(date: Date): Date {
//   const tz = -date.getTimezoneOffset();
//   const time: Time = {
//     hours: Math.floor(tz / 60),
//     minutes: tz % 60,
//   };
//   return setTime(date, time);
// }

function generateSlots(
  start: Date,
  end: Date,
  interval: number,
  randomSlotsToDelete = 0,
): MeetingSlot[] {
  let startStamp: number = start.getTime();
  const endStamp: number = end.getTime();
  const slots: MeetingSlot[] = [];
  for (;startStamp <= endStamp; startStamp += interval * 60000) {
    const slot: MeetingSlot = {
      date: new Date(startStamp),
    };
    slots.push(slot);
  }
  for (let i = 0; i < randomSlotsToDelete; i += 1) {
    const indexToDelete = randomNumber(0, slots.length);
    slots.splice(indexToDelete, 1);
  }
  return slots;
}

function generateFirstDate(
  date: Date,
  interval: number,
  startTime: Time,
  endTime: Time,
):MeetingsDay {
  let start: Date;
  if (formatingDate(date) <= formatingDate(new Date())) {
    start = roundToClosestTime(date, interval);
  } else {
    start = setTime(date, startTime);
  }
  const end: Date = setTime(date, endTime);
  const slots: MeetingSlot[] = generateSlots(start, end, interval);
  return {
    date,
    slots,
  };
}

function generateDays(
  initialDate: Date,
  daysToDisplay: number,
  startTime: Time,
  endTime: Time,
  interval: number,
  randomSlotsToDelete = 0,
): MeetingsDay[] {
  const days: MeetingsDay[] = [];
  days.push(generateFirstDate(initialDate, interval, startTime, endTime));
  // Set to second Day
  const startingDay: Date = new Date(initialDate);
  for (let i = 1; i < daysToDisplay; i += 1) {
    const slotsDate: Date = new Date(startingDay.setDate(startingDay.getDate() + 1));
      const startDate: Date = setTime(slotsDate, startTime);
      const endDate: Date = setTime(slotsDate, endTime);
      const slots: MeetingSlot[] = generateSlots(
        startDate,
        endDate,
        interval,
        randomSlotsToDelete,
      );
      const meetingsDay: MeetingsDay = {
        date: new Date(startingDay),
        slots,
      };
      days.push(meetingsDay);
  }
  return days;
}

export default generateDays;

And here is a screenshot on how calendar is displayed: ![Uploading image.png…]()

iNeoO commented 1 year ago

HI, i will update the doc

fallingsky2023 commented 1 year ago

Hi, I'm a beginner in coding, and I'm trying to use this plugin in my project that I find the result great.

I'm also using Nuxt3 and trying to create a component based on the simple example here, but I'm running into an issue. I'm getting the following error:

Missing "./src/helpers/slotsGenerator" specifier in "vue-meeting-selector" package.

Here is my package.json file:

{
  "name": "nuxt-app",
  "private": true,
  "scripts": {
    "build": "nuxt build",
    "dev": "nuxt dev",
    "generate": "nuxt generate",
    "preview": "nuxt preview",
    "postinstall": "nuxt prepare"
  },
  "devDependencies": {
    "@nuxtjs/tailwindcss": "^6.6.7",
    "@types/node": "^18",
    "nuxt": "^3.4.3",
    "vue": "^3.2.36"
  },
  "dependencies": {
    "axios": "^1.4.0",
    "sass": "^1.62.1",
    "vue-meeting-selector": "^3.0.2"
  }
}

Here is my nuxt.config.js file:

export default ({
    modules: [
      '@nuxtjs/tailwindcss',
    ],
})

My component is from the Simple example here .

Here is my page code:

<template>
<div>
    <Calendar />
</div>
</template>

And when I comment out this line:

// import slotsGenerator from 'vue-meeting-selector/src/helpers/slotsGenerator';

, I get the error:

[Vue warn]: Property "meeting" was accessed during render but is not defined on instance.              07:29:46
[07:29:46] [Vue warn]: Invalid prop: type check failed for prop "modelValue". Expected Array | Object | Null, got Undefined
[Vue warn]: Property "meeting" was accessed during render but is not defined on instance.              07:29:46  
[Vue warn]: Property "meeting" was accessed during render but is not defined on instance.              07:30:03
[07:30:03] [Vue warn]: Invalid prop: type check failed for prop "modelValue". Expected Array | Object | Null, got Undefined
[Vue warn]: Property "meeting" was accessed during render but is not defined on instance.   
iNeoO commented 12 months ago

@fallingsky2023 Can you create this issue i m closing this one (doc has been updated)