Selaa lähdekoodia

fix merge conflicts

Olivier Massot 3 kuukautta sitten
vanhempi
commit
0548a7673f

+ 3 - 3
assets/css/theme.scss

@@ -47,9 +47,9 @@
   color: rgb(var(--v-theme-on-info)) !important;
 }
 
-.theme-x-create-btn {
-  background-color: rgb(var(--v-theme-x-create-btn)) !important;
-  color: rgb(var(--v-theme-on-x-create-btn)) !important;
+.theme-standout {
+  background-color: rgb(var(--v-theme-standout)) !important;
+  color: rgb(var(--v-theme-on-standout)) !important;
 }
 
 .theme-artist {

+ 13 - 7
assets/css/vue-date-picker.scss

@@ -1,28 +1,34 @@
 // @see https://vue3datepicker.com/customization/theming/
 // [!] Sass variables overriding does not work in scoped mode
-.dp__theme_light,
-.dp__theme_dark {
+
+.dp__theme_light, .dp__theme_dark {
+  --dp-primary-color: rgb(var(--v-theme-primary)) !important;
+  --dp-primary-text-color: rgb(var(--v-theme-on-primary)) !important;
+  --dp-secondary-color: rgb(var(--v-theme-neutral-strong)) !important;
+  --dp-success-color: rgb(var(--v-theme-success)) !important;
+  --dp-success-color-disabled: rgb(var(--v-theme-neutral-strong)) !important;
+}
+
+.dp__theme_light {
   --dp-background-color: #ffffff;
   --dp-text-color: #212121;
   --dp-hover-color: #f3f3f3;
   --dp-hover-text-color: #212121;
   --dp-hover-icon-color: #959595;
-  --dp-primary-color: rgb(var(--v-theme-primary)) !important;
-  --dp-primary-text-color: rgb(var(--v-theme-on-primary)) !important;
-  --dp-secondary-color: rgb(var(--v-theme-neutral-strong)) !important;
   --dp-border-color: #ddd;
   --dp-menu-border-color: #ddd;
   --dp-border-color-hover: #aaaeb7;
   --dp-disabled-color: #f6f6f6;
   --dp-scroll-bar-background: #f3f3f3;
   --dp-scroll-bar-color: #959595;
-  --dp-success-color: rgb(var(--v-theme-success)) !important;
-  --dp-success-color-disabled: rgb(var(--v-theme-neutral-strong)) !important;
   --dp-icon-color: #959595;
   --dp-danger-color: #ff6f60;
   --dp-highlight-color: rgba(25, 118, 210, 0.1);
 }
 
+.dp__theme_dark {
+}
+
 :root {
   --dp-action-button-height: 35px;
 }

+ 1 - 1
components/Layout/Header.vue

@@ -19,7 +19,7 @@ Contient entre autres le nom de l'organisation, l'accès à l'aide et aux préf
       </LayoutHeaderTitle>
     </v-toolbar-title>
 
-    <LayoutThemeSwitcher v-if="false" />
+    <LayoutThemeSwitcher v-if="true" />
     <!-- En attente validation PO -->
 
     <LayoutHeaderUniversalCreationCreateButton

+ 1 - 1
components/Layout/Header/UniversalCreation/CreateButton.vue

@@ -19,7 +19,7 @@
       v-else
       :elevation="2"
       height="30"
-      class="theme-x-create-btn"
+      class="theme-standout"
       @click="show"
     >
       <span>{{ $t('create') }}</span>

+ 1 - 1
components/Layout/UpgradePremiumButton.vue

@@ -75,7 +75,7 @@ const trialAction = async () => {
 
 <style scoped lang="scss">
 .btn_trial {
-  background-color: rgb(var(--v-theme-x-create-btn));
+  background-color: rgb(var(--v-theme-standout));
   border-radius: 5px;
   border: 1px solid #fff;
   margin-left: 15px;

+ 42 - 0
components/Ui/DatePicker.vue

@@ -21,6 +21,7 @@ Sélecteur de dates
     :readonly="readonly"
     position="left"
     :teleport="true"
+    :dark="isDark"
     :select-text="$t('select')"
     :cancel-text="$t('cancel')"
     input-class-name="date-picker-input"
@@ -34,6 +35,7 @@ import type { PropType } from 'vue'
 import type { Locale } from 'date-fns'
 import type { supportedLocales } from '~/services/utils/dateUtils'
 import DateUtils from '~/services/utils/dateUtils'
+import { useTheme } from 'vuetify'
 
 const emit = defineEmits(['update:modelValue'])
 
@@ -65,6 +67,10 @@ const i18n = useI18n()
 
 const locale: Ref<string> = i18n.locale
 
+const theme = useTheme()
+
+const isDark = computed(() => theme.global.current.value.dark)
+
 const fnsLocale: ComputedRef<Locale> = computed(() =>
   DateUtils.getFnsLocale(locale.value as supportedLocales),
 )
@@ -91,4 +97,40 @@ const today = new Date()
 :deep(.dp__action_button) {
   height: 32px;
 }
+
+:deep(.date-picker-input) {
+  background-color: rgb(var(--v-theme-surface)) !important;
+  color: rgb(var(--v-theme-on-surface)) !important;
+  border: 1px solid rgb(var(--v-theme-neutral)) !important;
+  border-radius: 4px !important;
+
+  &:hover {
+    border-color: rgb(var(--v-theme-on-surface)) !important;
+  }
+
+  &:focus {
+    border: 2px solid rgb(var(--v-theme-primary)) !important;
+    outline: none !important;
+  }
+}
+
+:deep(.dp__menu) {
+  background-color: rgb(var(--v-theme-surface)) !important;
+  color: rgb(var(--v-theme-on-surface)) !important;
+  border: 1px solid rgb(var(--v-theme-neutral)) !important;
+}
+
+:deep(.dp__calendar_header) {
+  background-color: rgb(var(--v-theme-surface)) !important;
+  color: rgb(var(--v-theme-on-surface)) !important;
+}
+
+:deep(.dp__cell_inner) {
+  color: rgb(var(--v-theme-on-surface)) !important;
+}
+
+:deep(.dp__action_cancel) {
+  background-color: rgb(var(--v-theme-neutral)) !important;
+  color: rgb(var(--v-theme-on-neutral)) !important;
+}
 </style>

+ 14 - 17
components/Ui/Input/DatePicker.vue

@@ -144,10 +144,10 @@ onBeforeUnmount(() => {
 
 .label {
   position: absolute;
-  color: #8e8e8e;
+  color: rgb(var(--v-theme-on-neutral));
   top: -0.7rem;
   left: 0.75rem;
-  background-color: rgb(var(--v-theme-surface));
+  background-color: rgb(var(--v-theme-neutral-very-soft));
   padding: 0 0.3rem;
   font-size: 0.8rem;
   z-index: 1;
@@ -157,23 +157,20 @@ onBeforeUnmount(() => {
     top 0.2s;
 }
 
-.date-picker:hover {
-  :deep(.dp__input) {
-    border-color: #333333;
-  }
+:deep(.dp__input) {
+  background-color: rgb(var(--v-theme-neutral-very-soft));
+  color: rgb(var(--v-theme-on-neutral-very-soft));
+  border: 1px solid rgba(var(--v-theme-on-neutral-very-soft), 0.4);
+  border-radius: 4px;
+  font-family: inherit;
+  font-size: 14px;
+  min-height: 40px;
+  box-sizing: border-box;
 }
 
-.container:focus-within {
-  .label {
-    color: #333333;
-  }
-
-  :deep(.dp__input_focus) {
-    border: solid 2px #333333;
-  }
-
-  :deep(.dp__input_icon) {
-    color: #333333;
+.date-picker:hover {
+  :deep(.dp__input) {
+    border: 0.5px solid rgb(var(--v-theme-on-neutral-very-soft));
   }
 }
 

+ 6 - 9
config/theme.ts

@@ -36,11 +36,8 @@ interface Theme {
     'on-info': string
     artist: string
     school: string
-
-    // Special cases
-    // TODO: voir ceux dont on peut se passer
-    'x-create-btn': string
-    'on-x-create-btn': string
+    'standout': string
+    'on-standout': string
   }
 }
 
@@ -93,8 +90,8 @@ export const lightTheme: Theme = {
     info: '#3c8dbc',
     'on-info': '#ffffff',
 
-    'x-create-btn': '#f39c12',
-    'on-x-create-btn': '#ffffff',
+    'standout': '#f39c12',
+    'on-standout': '#ffffff',
 
     artist: '#fac20a',
     school: '#1893bf',
@@ -149,8 +146,8 @@ export const darkTheme: Theme = {
     info: '#3c8dbc',
     'on-info': '#ffffff',
 
-    'x-create-btn': '#f39c12',
-    'on-x-create-btn': '#ffffff',
+    'standout': '#f39c12',
+    'on-standout': '#ffffff',
 
     artist: '#fac20a',
     school: '#1893bf',

+ 251 - 0
pages/freemium/dashboard.vue

@@ -0,0 +1,251 @@
+<template>
+  <v-container fluid class="inner-container">
+    <v-row>
+      <!-- Bloc événements -->
+      <v-col cols="12" md="7">
+        <v-card>
+          <v-tabs v-model="tab" class="tabs-title">
+            <v-tab value="future">{{$t('futur_event')}}</v-tab>
+            <v-tab value="past">{{$t('past_event')}}</v-tab>
+          </v-tabs>
+
+          <v-btn color="primary" to="events/new" class="ml-5 mt-5">{{$t('add_event')}}</v-btn>
+
+          <v-tabs-window v-model="tab">
+            <v-tabs-window-item value="future">
+              <UiLoadingPanel v-if="statusUpcomingEvents == FETCHING_STATUS.PENDING" />
+
+              <UiEventList
+                v-if="statusUpcomingEvents == FETCHING_STATUS.SUCCESS && upcomingEvents?.items"
+                :events="upcomingEvents.items"
+                :pagination="upcomingEvents.pagination"
+                @load="loadUpcomingEvents"
+                @edit="editEvent"
+              />
+              <span v-if="upcomingEvents.items.length == 0" class="no_event">
+                {{$t('no_future_event')}}
+              </span>
+            </v-tabs-window-item>
+            <v-tabs-window-item value="past">
+              <UiLoadingPanel v-if="statusPastEvents == FETCHING_STATUS.PENDING" />
+
+              <UiEventList
+                v-if="statusPastEvents == FETCHING_STATUS.SUCCESS && pastEvents?.items"
+                :events="pastEvents.items"
+                :pagination="pastEvents.pagination"
+                @load="loadPastEvents"
+                @edit="editEvent"
+              />
+              <span v-if="pastEvents.items.length == 0" class="no_event">
+                {{$t('no_past_event')}}
+              </span>
+            </v-tabs-window-item>
+          </v-tabs-window>
+
+        </v-card>
+      </v-col>
+
+      <!-- Bloc structure -->
+      <v-col cols="12" md="5">
+        <v-card v-if="statusOrganization == FETCHING_STATUS.SUCCESS" class="pa-5">
+          <v-card-title class="text-h6" >
+            <v-icon icon="fa fa-hotel" class="text-button icon-hotel"  />
+            <span class="organization_title">{{$t('my_organization')}}</span>
+          </v-card-title>
+          <v-card-text>
+            <div><strong>{{$t('name')}} :</strong> {{ organization?.name }}</div>
+            <div><strong>{{$t('email')}} :</strong> {{ organization?.email }}</div>
+          </v-card-text>
+        </v-card>
+
+        <v-btn block class="mb-2 btn btn_edit_orga" to="organization">
+          <i class="fa fa-pen mr-2" />{{$t('edit_organization')}}
+        </v-btn>
+
+        <v-btn block class="text-black btn btn_trial" @click="startTrial">
+          <span><v-icon icon="fa fa-ticket" /> {{$t('try_premium_light')}}<br /> {{$t('30_days_free')}}</span>
+        </v-btn>
+
+      </v-col>
+    </v-row>
+
+  </v-container>
+
+  <LayoutDialogTrialAlreadyDid
+    :show="showDialogTrialAlReadyDid"
+    @close-dialog="showDialogTrialAlReadyDid = false"
+  />
+
+</template>
+
+<script setup lang="ts">
+
+import Query from "~/services/data/Query";
+
+definePageMeta({
+  name: 'freemium_dashboard_page',
+})
+
+import {type Ref, ref} from 'vue'
+import {useEntityFetch} from "~/composables/data/useEntityFetch";
+import Organization from "~/models/Freemium/Organization";
+import Event from "~/models/Freemium/Event";
+import type {AsyncData} from "#app";
+import OrderBy from "~/services/data/Filters/OrderBy";
+import {FETCHING_STATUS, ORDER_BY_DIRECTION, TIME_STRATEGY} from "~/types/enum/data";
+import PageFilter from "~/services/data/Filters/PageFilter";
+import TimeFilter from "~/services/data/Filters/TimeFilter";
+import Country from "~/models/Core/Country";
+import DateUtils from "~/services/utils/dateUtils";
+import UrlUtils from "~/services/utils/urlUtils";
+import {useApiLegacyRequestService} from "~/composables/data/useApiLegacyRequestService";
+import {useAdminUrl} from "~/composables/utils/useAdminUrl";
+
+//Ref Définition
+const runtimeConfig = useRuntimeConfig()
+const { fetch, fetchCollection } = useEntityFetch()
+const { apiRequestService } = useApiLegacyRequestService()
+const {makeAdminUrl} = useAdminUrl()
+const tab = ref(null)
+const upcomingPage = ref(1)
+const pastPage = ref(1)
+const showDialogTrialAlReadyDid: Ref<boolean> = ref(false)
+
+//Fetch
+const { data: organization, status:statusOrganization } = fetch(Organization)
+const { data: upcomingEvents, status: statusUpcomingEvents, refresh: refreshUpcomingEvents } = fetchEvents()
+const { data: pastEvents, status: statusPastEvents, refresh: refreshPastEvents } = fetchEvents(true)
+
+/**
+ * Charge une page des événements à venir
+ * @param pageNumber
+ */
+function loadUpcomingEvents(pageNumber: number) {
+  upcomingPage.value = pageNumber
+  refreshPastEvents()
+}
+
+/**
+ * Cahrge une page des événements passées
+ * @param pageNumber
+ */
+function loadPastEvents(pageNumber: number) {
+  pastPage.value = pageNumber
+  refreshPastEvents()
+}
+
+/**
+ * Redirige vers la page d'édition d'un événement
+ * @param eventId
+ */
+function editEvent(eventId: number) {
+  navigateTo(UrlUtils.join('events', eventId))
+}
+
+/**
+ * Récupère la liste des événements
+ * @param past
+ */
+function fetchEvents(past:boolean = false){
+  const today = computed(() => DateUtils.formatIsoShortDate(new Date()))
+  const query =
+    new Query(
+      new OrderBy('datetimeStart', past ? ORDER_BY_DIRECTION.DESC : ORDER_BY_DIRECTION.ASC),
+      new PageFilter(past ? pastPage : upcomingPage, ref(5)),
+      new TimeFilter('datetimeStart', today, past ? TIME_STRATEGY.BEFORE : TIME_STRATEGY.AFTER)
+    )
+
+  return fetchCollection(Event, null, query)
+}
+
+/**
+ * Action lorsque l'on souhaite démarrer l'essai
+ */
+async function startTrial() {
+  try {
+    await apiRequestService.get('/trial/is_available')
+    await navigateTo(makeAdminUrl('trial'), {
+      external: true,
+    })
+  } catch (error) {
+    showDialogTrialAlReadyDid.value = true
+  }
+}
+
+/**
+ * Nettoyage du store
+ */
+onUnmounted(() => {
+  useRepo(Organization).flush()
+  useRepo(Event).flush()
+  useRepo(Country).flush()
+})
+
+</script>
+
+<style scoped lang="scss">
+
+.tabs-title{
+  margin-top: 20px;
+  padding-left: 20px;
+  background-color: rgb(var(--v-theme-neutral));
+  .v-tab--selected{
+    color: rgb(var(--v-theme-on-neutral--clickable));
+  }
+}
+
+
+.v-card {
+  margin-bottom: 16px;
+  color: rgb(var(--v-theme-on-neutral));
+}
+
+.v-card-text{
+  div{
+    line-height: 2;
+  }
+}
+
+.organization_title{
+  font-weight: 500;
+}
+
+.icon-hotel{
+  margin: 0 5px 4px 0;
+}
+
+.btn {
+  border: 1px solid;
+  cursor: pointer;
+}
+
+.inner-container {
+  margin: 0 auto;
+  padding: 30px;
+}
+.btn_trial {
+  height: 55px;
+  background-color: rgb(var(--v-theme-standout));
+  color: #000;
+
+  span {
+    text-align: center;
+    line-height: 1.2; /* optionnel : pour resserrer ou espacer */
+  }
+  .v-icon {
+    transform: rotate(135deg); /* angle en degrés */
+    font-size: 16px;
+    padding-right: 5px;
+    margin: 0 5px 4px 0;
+  }
+}
+
+.btn_edit_orga {
+  color: rgb(var(--v-theme-on-neutral)) !important;
+}
+
+.no_event{
+  padding: 25px;
+  font-size: 16px;
+}
+</style>