ode> 2779
       "dev": true
2769
     },
2780
     },
2770
     "node_modules/@stencil/core": {
2781
     "node_modules/@stencil/core": {
2771
-      "version": "4.6.0",
2772
-      "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.6.0.tgz",
2773
-      "integrity": "sha512-5Y6/fP28aspPDRB+Tz5GuB1jRVGuUEk5/rnyE8ACGcuzEOG+zR0A/IHRJSU3XmCxUlVDKfKoO6St5W84oUCVMA==",
2782
+      "version": "4.7.1",
2783
+      "resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.7.1.tgz",
2784
+      "integrity": "sha512-KDWA/3qDiABA5LqtHCmTVwORuzgu/YdC0FpSBwVmwlw6K8jUjbTA5JB6Q03da2F+EQlDHOVgbW0TNtHCm54uXQ==",
2774
       "bin": {
2785
       "bin": {
2775
         "stencil": "bin/stencil"
2786
         "stencil": "bin/stencil"
2776
       },
2787
       },
7261
         "node": ">=12"
7272
         "node": ">=12"
7262
       }
7273
       }
7263
     },
7274
     },
7275
+    "node_modules/marked": {
7276
+      "version": "10.0.0",
7277
+      "resolved": "https://registry.npmjs.org/marked/-/marked-10.0.0.tgz",
7278
+      "integrity": "sha512-YiGcYcWj50YrwBgNzFoYhQ1hT6GmQbFG8SksnYJX1z4BXTHSOrz1GB5/Jm2yQvMg4nN1FHP4M6r03R10KrVUiA==",
7279
+      "bin": {
7280
+        "marked": "bin/marked.js"
7281
+      },
7282
+      "engines": {
7283
+        "node": ">= 18"
7284
+      }
7285
+    },
7264
     "node_modules/merge-stream": {
7286
     "node_modules/merge-stream": {
7265
       "version": "2.0.0",
7287
       "version": "2.0.0",
7266
       "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
7288
       "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
8767
         "url": "https://github.com/sponsors/ljharb"
8789
         "url": "https://github.com/sponsors/ljharb"
8768
       }
8790
       }
8769
     },
8791
     },
8792
+    "node_modules/swiper": {
8793
+      "version": "11.0.3",
8794
+      "resolved": "https://registry.npmjs.org/swiper/-/swiper-11.0.3.tgz",
8795
+      "integrity": "sha512-MyV9ooQsriAe2EibeamqewLjgCfSvl2xoyratl6S3ln5BXDL4BzlO6mxcbLMCzQL6Z60b/u0AS/nKrepL0+TAg==",
8796
+      "funding": [
8797
+        {
8798
+          "type": "patreon",
8799
+          "url": "https://www.patreon.com/swiperjs"
8800
+        },
8801
+        {
8802
+          "type": "open_collective",
8803
+          "url": "http://opencollective.com/swiper"
8804
+        }
8805
+      ],
8806
+      "engines": {
8807
+        "node": ">= 4.7.0"
8808
+      }
8809
+    },
8770
     "node_modules/symbol-tree": {
8810
     "node_modules/symbol-tree": {
8771
       "version": "3.2.4",
8811
       "version": "3.2.4",
8772
       "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
8812
       "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",

+ 5 - 2
package.json

20
     "@capacitor/keyboard": "5.0.6",
20
     "@capacitor/keyboard": "5.0.6",
21
     "@capacitor/preferences": "^5.0.6",
21
     "@capacitor/preferences": "^5.0.6",
22
     "@capacitor/push-notifications": "^5.1.0",
22
     "@capacitor/push-notifications": "^5.1.0",
23
+    "@capacitor/share": "^5.0.6",
23
     "@capacitor/status-bar": "5.0.6",
24
     "@capacitor/status-bar": "5.0.6",
24
     "@codetrix-studio/capacitor-google-auth": "^3.3.4",
25
     "@codetrix-studio/capacitor-google-auth": "^3.3.4",
25
-    "@ionic/vue": "^7.0.0",
26
-    "@ionic/vue-router": "^7.0.0",
26
+    "@ionic/vue": "^7.5.4",
27
+    "@ionic/vue-router": "^7.5.4",
27
     "@vimeo/player": "^2.20.1",
28
     "@vimeo/player": "^2.20.1",
28
     "axios": "^1.6.0",
29
     "axios": "^1.6.0",
29
     "ionicons": "^7.0.0",
30
     "ionicons": "^7.0.0",
31
+    "marked": "^10.0.0",
30
     "moment": "^2.29.4",
32
     "moment": "^2.29.4",
33
+    "swiper": "^11.0.3",
31
     "vue": "^3.2.45",
34
     "vue": "^3.2.45",
32
     "vue-core-video-player": "^0.2.0",
35
     "vue-core-video-player": "^0.2.0",
33
     "vue-router": "^4.1.6",
36
     "vue-router": "^4.1.6",

+ 12 - 8
src/components/Course.vue

1
 <template>
1
 <template>
2
   <ion-card :router-link="'/tabs/course_detail/'+courseObj.id">
2
   <ion-card :router-link="'/tabs/course_detail/'+courseObj.id">
3
-    <img alt="Silhouette of mountains" :src="courseObj.feature_img"  v-if="courseObj.feature_img" />
3
+    <img alt="Silhouette of mountains" :src="feature_img"  v-if="feature_img" />
4
     <ion-card-header>
4
     <ion-card-header>
5
       <ion-card-title>{{ courseObj.name }}</ion-card-title>
5
       <ion-card-title>{{ courseObj.name }}</ion-card-title>
6
-      <ion-card-subtitle>{{ courseObj.description }}</ion-card-subtitle>
7
     </ion-card-header>
6
     </ion-card-header>
8
 
7
 
9
-    <ion-card-content class='ion-text-wrap'>
10
-      {{ courseObj.description }}
11
-    </ion-card-content>
12
   </ion-card>
8
   </ion-card>
13
 </template>
9
 </template>
14
 
10
 
15
 <script setup lang="ts">
11
 <script setup lang="ts">
16
   import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent, IonIcon, onIonViewWillEnter, IonCard, IonCardHeader, IonCardTitle, IonCardSubtitle, IonCardContent } from '@ionic/vue';
12
   import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent, IonIcon, onIonViewWillEnter, IonCard, IonCardHeader, IonCardTitle, IonCardSubtitle, IonCardContent } from '@ionic/vue';
17
 
13
 
18
-import { getTrainers, listCourses } from '@/settings';
14
+import { getTrainers, listCourses, BASE_URL } from '@/composable/settings';
19
   import { ref, onMounted } from 'vue'
15
   import { ref, onMounted } from 'vue'
20
   const props = defineProps({
16
   const props = defineProps({
21
     courseObj: Object,
17
     courseObj: Object,
22
   })
18
   })
23
-
19
+  const feature_img  = ref()
24
   onMounted(() => {
20
   onMounted(() => {
25
     console.log("--- onmounted ---")
21
     console.log("--- onmounted ---")
26
     console.log("name === ", props.courseObj)
22
     console.log("name === ", props.courseObj)
23
+    if( props.courseObj.feature_img && !props.courseObj.feature_img.startsWith('http') ) {
24
+      feature_img.value = BASE_URL + props.courseObj.feature_img
25
+    }else {
26
+      feature_img.value = props.courseObj.feature_img
27
+    }
27
   })
28
   })
29
+  const truncate = (str) => {
30
+    return "T "+str
31
+  }
28
 </script>
32
 </script>
29
 
33
 
30
 <style scoped>
34
 <style scoped>
32
   background-color:#f00;
36
   background-color:#f00;
33
 }
37
 }
34
 ion-card-title {
38
 ion-card-title {
35
-  font-size:1.5em;
39
+  font-size:1em;
36
 }
40
 }
37
 </style>
41
 </style>

+ 1 - 1
src/components/CourseMat.vue

14
 <script setup lang="ts">
14
 <script setup lang="ts">
15
   import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent, IonIcon, onIonViewWillEnter, IonCard, IonCardHeader, IonCardTitle, IonCardSubtitle, IonCardContent } from '@ionic/vue';
15
   import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent, IonIcon, onIonViewWillEnter, IonCard, IonCardHeader, IonCardTitle, IonCardSubtitle, IonCardContent } from '@ionic/vue';
16
   import moment from 'moment'
16
   import moment from 'moment'
17
-import { getTrainers, listCourses } from '@/settings';
17
+import { getTrainers, listCourses } from '@/composable/settings';
18
   import { ref, onMounted  } from 'vue'
18
   import { ref, onMounted  } from 'vue'
19
 
19
 
20
   const props = defineProps({
20
   const props = defineProps({

+ 47 - 0
src/components/CourseSchedule.vue

1
+<template>
2
+  <ion-card :router-link="'/tabs/course_detail/'+courseObj.id">
3
+    <img alt="Silhouette of mountains" :src="feature_img"  v-if="feature_img" />
4
+    <ion-card-header>
5
+      <ion-card-title>{{ courseObj.name }}</ion-card-title>
6
+    </ion-card-header>
7
+
8
+     <ion-card-content>
9
+       {{ fromTime.substring(0, 5) }} - {{ toTime.substring(0,5) }}
10
+    </ion-card-content>
11
+
12
+  </ion-card>
13
+</template>
14
+
15
+<script setup lang="ts">
16
+  import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent, IonIcon, onIonViewWillEnter, IonCard, IonCardHeader, IonCardTitle, IonCardSubtitle, IonCardContent } from '@ionic/vue';
17
+
18
+import { getTrainers, listCourses, BASE_URL } from '@/composable/settings';
19
+  import { ref, onMounted } from 'vue'
20
+  const props = defineProps({
21
+    courseObj: Object,
22
+    fromTime: String, 
23
+    toTime: String,
24
+  })
25
+  const feature_img  = ref()
26
+  onMounted(() => {
27
+    console.log("--- onmounted ---")
28
+    console.log("name === ", props.courseObj)
29
+    if( props.courseObj.feature_img && !props.courseObj.feature_img.startsWith('http') ) {
30
+      feature_img.value = BASE_URL + props.courseObj.feature_img
31
+    }else {
32
+      feature_img.value = props.courseObj.feature_img
33
+    }
34
+  })
35
+  const truncate = (str) => {
36
+    return "T "+str
37
+  }
38
+</script>
39
+
40
+<style scoped>
41
+#container {
42
+  background-color:#f00;
43
+}
44
+ion-card-title {
45
+  font-size:1em;
46
+}
47
+</style>

+ 40 - 0
src/components/Post.vue

1
+<template>
2
+  <ion-card :router-link="'/tabs/post_detail/'+obj.id">
3
+    <img alt="Silhouette of mountains" :src="feature_img"  v-if="feature_img" />
4
+    <ion-card-header>
5
+      <ion-card-title>{{ obj.name }}</ion-card-title>
6
+    </ion-card-header>
7
+
8
+  </ion-card>
9
+</template>
10
+
11
+<script setup lang="ts">
12
+  import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent, IonIcon, onIonViewWillEnter, IonCard, IonCardHeader, IonCardTitle, IonCardSubtitle, IonCardContent } from '@ionic/vue';
13
+
14
+import { getTrainers, listCourses, BASE_URL } from '@/composable/settings';
15
+  import { ref, onMounted } from 'vue'
16
+  const props = defineProps({
17
+    obj: Object,
18
+  })
19
+  const feature_img  = ref()
20
+  onMounted(() => {
21
+    console.log("--- onmounted ---")
22
+    if( props.obj.feature_img && !props.obj.feature_img.startsWith('http') ) {
23
+      feature_img.value = BASE_URL + props.obj.feature_img
24
+    }else {
25
+      feature_img.value = props.obj.feature_img
26
+    }
27
+  })
28
+  const truncate = (str) => {
29
+    return "T "+str
30
+  }
31
+</script>
32
+
33
+<style scoped>
34
+#container {
35
+  background-color:#f00;
36
+}
37
+ion-card-title {
38
+  font-size:1em;
39
+}
40
+</style>

+ 42 - 0
src/components/SearchEngine.vue

1
+<template>
2
+  <ion-searchbar :debounce="1000" @ionInput="handleInput($event)"></ion-searchbar>
3
+  <ion-list>
4
+    <template v-for="result in results">
5
+      <SearchItem :obj="result" />
6
+    </template>
7
+  </ion-list>
8
+
9
+</template>
10
+<script setup>
11
+import { IonItem, IonList, IonSearchbar,IonLabel } from '@ionic/vue';
12
+import { ref } from 'vue';
13
+import { searchMat } from '@/composable/settings';
14
+import SearchItem from '@/components/SearchItem.vue';
15
+
16
+ const data = [
17
+        'Amsterdam',
18
+        'Buenos Aires',
19
+        'Cairo',
20
+        'Geneva',
21
+        'Hong Kong',
22
+        'Istanbul',
23
+        'London',
24
+        'Madrid',
25
+        'New York',
26
+        'Panama City',
27
+];
28
+const results = ref();
29
+const handleInput = async (event) => {
30
+  console.log("handleInput")
31
+  console.log(event)
32
+  const query = event.target.value.toLowerCase();
33
+  console.log("query = ", query)
34
+  if(query == "") {
35
+    results.value = []
36
+  }else {
37
+    //results.value = data.filter((d) => d.toLowerCase().indexOf(query) > -1);
38
+    const r = await searchMat(query)
39
+    results.value = r.results
40
+  }
41
+}
42
+</script>

+ 36 - 0
src/components/SearchItem.vue

1
+<template>
2
+  <ion-item :router-link="'/tabs/mat_detail/'+obj.id">
3
+    <ion-thumbnail slot="start">
4
+      <img alt="Silhouette of mountains" :src="thmb"  v-if="thmb" />
5
+    </ion-thumbnail>
6
+    <ion-label>{{ name }} {{ dt }}</ion-label>
7
+  </ion-item>
8
+</template>
9
+
10
+<script setup>
11
+  import { IonItem, IonLabel, IonThumbnail } from '@ionic/vue';
12
+  import { ref, onMounted } from 'vue';
13
+  import moment from 'moment';
14
+  
15
+  const dt = ref()
16
+  const thmb = ref()
17
+  const name = ref()
18
+  
19
+  const props = defineProps({
20
+    obj: Object
21
+  })
22
+
23
+  onMounted(() => {
24
+    const obj = props.obj
25
+    let temp  = obj.display_datetime || obj.vimeo_created || obj.created_at 
26
+    dt.value = moment(temp).format('LLL'); 
27
+
28
+    if( obj.gifs && obj.gifs.length > 0 ) {
29
+      thmb.value = obj.gifs[0].sizes[2].link
30
+    }else {
31
+      thmb.value = obj.course_image
32
+    }
33
+    name.value = obj.vimeo_name || props.courseName
34
+  })
35
+
36
+</script>

+ 71 - 8
src/settings.ts

51
   console.log(data)
51
   console.log(data)
52
   return data.results
52
   return data.results
53
 }
53
 }
54
-export const listMats = async  () => {
54
+export const listMats = async  (ids=[]) => {
55
 
55
 
56
   const token = await Preferences.get({ key: 'token' });
56
   const token = await Preferences.get({ key: 'token' });
57
   console.log("token = ", token)
57
   console.log("token = ", token)
58
-  const { data } = await axios.get(BASE_URL + "backend/api/mats/?ordering=-id", {
59
-    headers: { 
60
-    'Authorization': `Token ${token.value}`
61
-    }
62
-  })
63
-  console.log(data)
64
-  return data.results
58
+  console.log("ids ", ids)
59
+  if( ids.length == 0 ) {
60
+    const { data } = await axios.get(BASE_URL + "backend/api/mats/?ordering=-id", {
61
+      headers: { 
62
+      'Authorization': `Token ${token.value}`
63
+      }
64
+    })
65
+    
66
+    return data.results
67
+  }else {
68
+    const { data } = await axios.get(BASE_URL + "backend/api/mats/?ids="+ids.join(), {
69
+      headers: { 
70
+      'Authorization': `Token ${token.value}`
71
+      }
72
+    })
73
+    console.log("results = ", data.results)
74
+    return data.results
75
+  }
65
 }
76
 }
66
 export const listCourses = async  () => {
77
 export const listCourses = async  () => {
67
 
78
 
126
   console.log(data)
137
   console.log(data)
127
   return data
138
   return data
128
 }
139
 }
140
+export const getTodayProgs = async  () => {
141
+  const token = await Preferences.get({ key: 'token' });
142
+  console.log("token = ", token)
143
+  const { data } = await axios.get(BASE_URL + "backend/api/courses/today_progs/", {
144
+    headers: { 
145
+    'Authorization': `Token ${token.value}`
146
+    }
147
+  })
148
+  console.log(data)
149
+  return data
150
+}
151
+export const getLive = async  () => {
152
+  const token = await Preferences.get({ key: 'token' });
153
+  console.log("token = ", token)
154
+  const { data } = await axios.get(BASE_URL + "backend/api/scheds/get_live/", {
155
+    headers: { 
156
+    'Authorization': `Token ${token.value}`
157
+    }
158
+  })
159
+  console.log(data)
160
+  return data
161
+}
162
+export const getPosts = async (cat) => {
163
+  const token = await Preferences.get({ key: 'token' });
164
+  console.log("token = ", token)
165
+  const { data } = await axios.get(BASE_URL + `backend/api/posts/?category__slug=${cat}&status=active&ordering=-created_at`, {
166
+    headers: { 
167
+    'Authorization': `Token ${token.value}`
168
+    }
169
+  })
170
+  return data.results
171
+}
172
+export const getPost = async (pid) => {
173
+  const token = await Preferences.get({ key: 'token' });
174
+  console.log("token = ", token)
175
+  const { data } = await axios.get(BASE_URL + `backend/api/posts/${pid}/`, {
176
+    headers: { 
177
+    'Authorization': `Token ${token.value}`
178
+    }
179
+  })
180
+  return data
181
+}
182
+export const searchMat = async (s) => {
183
+  const token = await Preferences.get({ key: 'token' });
184
+  console.log("token = ", token)
185
+  const { data } = await axios.get(BASE_URL + `backend/api/mats/?search=${s}`, {
186
+    headers: { 
187
+    'Authorization': `Token ${token.value}`
188
+    }
189
+  })
190
+  return data
191
+}

+ 10 - 0
src/router/index.ts

45
         path: 'mat_detail/:id/',
45
         path: 'mat_detail/:id/',
46
         name: 'mat_detail', 
46
         name: 'mat_detail', 
47
         component: () => import('@/views/CourseMatDetailPage.vue')
47
         component: () => import('@/views/CourseMatDetailPage.vue')
48
+      },
49
+      {
50
+        path: 'search_page/',
51
+        name: 'search_page', 
52
+        component: () => import('@/views/SearchPage.vue')
53
+      },
54
+      {
55
+        path: 'post_detail/:id/',
56
+        name: 'post_detail', 
57
+        component: () => import('@/views/PostDetailPage.vue')
48
       }
58
       }
49
     ]
59
     ]
50
   }
60
   }

+ 2 - 2
src/views/CourseDetailPage.vue

18
       <ion-grid>
18
       <ion-grid>
19
         <ion-row>
19
         <ion-row>
20
           <ion-col size="12" size-md="4" size-lg="2" v-for="c in mats">
20
           <ion-col size="12" size-md="4" size-lg="2" v-for="c in mats">
21
-            <CourseMat :obj="c" :course-name="course.name" />
21
+            <CourseMat :obj="c" :course-name="course.name" class='ion-no-margin' />
22
           </ion-col>
22
           </ion-col>
23
         </ion-row>
23
         </ion-row>
24
       </ion-grid> 
24
       </ion-grid> 
32
     IonButtons,
32
     IonButtons,
33
     IonBackButton, onIonViewWillEnter, IonRow, IonGrid, IonCol } from '@ionic/vue';
33
     IonBackButton, onIonViewWillEnter, IonRow, IonGrid, IonCol } from '@ionic/vue';
34
 import { useRoute } from 'vue-router';
34
 import { useRoute } from 'vue-router';
35
-import { listCourseMats, getCourse } from '@/settings';
35
+import { listCourseMats, getCourse } from '@/composable/settings';
36
 import { ref } from 'vue';
36
 import { ref } from 'vue';
37
 import CourseMat from '@/components/CourseMat.vue'
37
 import CourseMat from '@/components/CourseMat.vue'
38
 
38
 

+ 46 - 4
src/views/CourseMatDetailPage.vue

13
       <div style="padding:56.25% 0 0 0;position:relative;" v-html="mat.embed" v-if='mat'></div>
13
       <div style="padding:56.25% 0 0 0;position:relative;" v-html="mat.embed" v-if='mat'></div>
14
       <div v-if="mat" class='ion-padding ion-text-wrap'>
14
       <div v-if="mat" class='ion-padding ion-text-wrap'>
15
         <h2 v-if='name'>{{ name }}</h2>
15
         <h2 v-if='name'>{{ name }}</h2>
16
-        {{ mat }}
17
-      </div> 
16
+      </div>
17
+      <template v-if="mat">
18
+        <ion-item-divider>
19
+          <ion-label>Trainners</ion-label>
20
+        </ion-item-divider> 
21
+        <ion-list>
22
+          <ion-item v-for="m in mat.trainers">
23
+            <ion-avatar aria-hidden="true" slot="start">
24
+              <img  :src="BASE_URL+m.photo"  v-if="m.photo"/>
25
+            </ion-avatar>
26
+            <ion-label>{{ m.name }}</ion-label>
27
+          </ion-item>
28
+        </ion-list>
29
+        <ion-item-divider class='ion-margin-vertical'>
30
+          <ion-label>Related Trainings</ion-label>
31
+        </ion-item-divider> 
32
+      <swiper :slides-per-view="2" :loop="true" v-if="mats" class='ion-no-margin'>
33
+
34
+      <swiper-slide v-for="m in mats">
35
+        <CourseMat :obj="m" :course-name="mat.course_name" />
36
+      </swiper-slide>
37
+      </swiper>
38
+      </template>
39
+
18
     </ion-content>
40
     </ion-content>
19
   </ion-page>
41
   </ion-page>
20
 </template>
42
 </template>
23
 import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent,   IonNavLink,
45
 import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent,   IonNavLink,
24
     IonButton,
46
     IonButton,
25
     IonButtons,
47
     IonButtons,
26
-    IonBackButton, onIonViewWillEnter, IonRow, IonGrid, IonCol } from '@ionic/vue';
48
+    IonBackButton, onIonViewWillEnter, IonRow, IonGrid, IonCol, IonItem, IonList, IonAvatar, IonLabel, IonItemDivider} from '@ionic/vue';
49
+
50
+import { Autoplay, Keyboard, Pagination, Scrollbar, Zoom } from 'swiper/modules';
51
+import { Swiper, SwiperSlide } from 'swiper/vue';
52
+
53
+
54
+import 'swiper/css';
55
+import 'swiper/css/autoplay';
56
+import 'swiper/css/keyboard';
57
+import 'swiper/css/pagination';
58
+import 'swiper/css/scrollbar';
59
+import 'swiper/css/zoom';
60
+import '@ionic/vue/css/ionic-swiper.css';
61
+
27
 import { useRoute } from 'vue-router';
62
 import { useRoute } from 'vue-router';
28
-import { getMat } from '@/settings';
63
+import { getMat, BASE_URL, listMats } from '@/composable/settings';
29
 import { ref } from 'vue';
64
 import { ref } from 'vue';
30
 import CourseMat from '@/components/CourseMat.vue'
65
 import CourseMat from '@/components/CourseMat.vue'
31
 
66
 
32
 const route = useRoute();
67
 const route = useRoute();
33
 const { id } = route.params;
68
 const { id } = route.params;
34
 
69
 
70
+const modules =  [Autoplay, Keyboard, Pagination, Scrollbar, Zoom]
71
+
35
 const mat = ref()
72
 const mat = ref()
36
 const name = ref()
73
 const name = ref()
74
+const mats = ref()
75
+
76
+
37
 onIonViewWillEnter(async () => {
77
 onIonViewWillEnter(async () => {
38
   mat.value = await getMat(id)
78
   mat.value = await getMat(id)
79
+  console.log(mat.value)
39
   name.value = mat.value.vimeo_name || mat.value.course_name
80
   name.value = mat.value.vimeo_name || mat.value.course_name
81
+  mats.value  = await listMats(mat.value.related_mats)
40
 })
82
 })
41
 </script>
83
 </script>

+ 77 - 0
src/views/PostDetailPage.vue

1
+
2
+<template>
3
+  <ion-page>
4
+    <ion-header>
5
+      <ion-toolbar>
6
+        <ion-buttons slot="start">
7
+          <ion-back-button></ion-back-button>
8
+          </ion-buttons>
9
+          <ion-title v-if="obj">{{ obj.name }}</ion-title>
10
+      </ion-toolbar>
11
+    </ion-header>
12
+    <ion-content :fullscreen="true">
13
+      <ion-header collapse="condense">
14
+        <ion-toolbar>
15
+          <ion-title size="large" v-if="obj">{{ obj.name }}</ion-title>
16
+        </ion-toolbar>
17
+      </ion-header>
18
+      <div v-html="render_body" class='ion-padding'></div>
19
+
20
+       <ion-fab slot="fixed" vertical="bottom" horizontal="end">
21
+      <ion-fab-button>
22
+        <ion-icon :icon="shareSocial" @click="socShare"></ion-icon>
23
+      </ion-fab-button>
24
+    </ion-fab>
25
+    </ion-content>
26
+  </ion-page>
27
+</template>
28
+
29
+<script setup lang="ts">
30
+
31
+import { marked } from 'marked';
32
+import { Share } from '@capacitor/share';
33
+
34
+import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent,   IonNavLink,
35
+    IonButton,
36
+    IonButtons,
37
+    IonIcon,
38
+    IonBackButton, onIonViewWillEnter, IonRow, IonGrid, IonCol,
39
+    IonFab, IonFabButton } from '@ionic/vue';
40
+import {
41
+    chevronDownCircle,
42
+    chevronForwardCircle,
43
+    chevronUpCircle,
44
+    colorPalette,
45
+    shareSocial, 
46
+    document,
47
+    globe,
48
+  } from 'ionicons/icons';
49
+
50
+import { useRoute } from 'vue-router';
51
+import { listCourseMats, getCourse, getPost} from '@/composable/settings';
52
+import { ref } from 'vue';
53
+import CourseMat from '@/components/CourseMat.vue'
54
+
55
+const route = useRoute();
56
+const { id } = route.params;
57
+
58
+const obj = ref()
59
+const render_body = ref()
60
+
61
+onIonViewWillEnter(async () => {
62
+  const post  = await getPost(id)
63
+  console.log(" post ", post)
64
+  obj.value = post
65
+  render_body.value = marked(post.body)
66
+
67
+})
68
+const socShare = async () => {
69
+  console.log('share social')
70
+  await Share.share({
71
+    title: 'See cool stuff',
72
+    text: 'Really awesome thing you need to see right meow',
73
+    url: 'http://ionicframework.com/',
74
+    dialogTitle: 'Share with buddies',
75
+  });
76
+}
77
+</script>

+ 68 - 0
src/views/SearchPage.vue

1
+<template>
2
+  <ion-page>
3
+    <ion-header>
4
+      <ion-toolbar>
5
+        <ion-buttons slot="start">
6
+          <ion-back-button></ion-back-button>
7
+        </ion-buttons>
8
+        <ion-title>
9
+          Search
10
+        </ion-title>
11
+      </ion-toolbar>
12
+    </ion-header>
13
+    <ion-content class='ion-padding'>
14
+      <ion-searchbar :debounce="1000" @ionInput="handleInput($event)"></ion-searchbar>
15
+      <ion-list>
16
+        <template v-for="result in results">
17
+          <SearchItem :obj="result" />
18
+        </template>
19
+        <template v-if="processing">
20
+            <ion-progress-bar type="indeterminate"></ion-progress-bar>
21
+
22
+        </template>
23
+        <template v-if="is_empty">
24
+          <h1 class='ion-text-center'>Search not found</h1>
25
+        </template>
26
+      </ion-list>
27
+    </ion-content>
28
+  </ion-page>
29
+</template>
30
+
31
+<script setup lang="ts">
32
+import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent, IonButton, onIonViewWillEnter, IonList, IonSearchbar, IonLabel,
33
+IonItem, IonBackButton, IonProgressBar } from '@ionic/vue';
34
+import ExploreContainer from '@/components/ExploreContainer.vue';
35
+import { defineComponent, onMounted } from 'vue';
36
+import { GoogleAuth } from '@codetrix-studio/capacitor-google-auth';
37
+import { searchMat } from '@/composable/settings';
38
+
39
+import { ref } from 'vue';
40
+import SearchItem from '@/components/SearchItem.vue';
41
+
42
+const results = ref();
43
+const is_empty = ref(false);
44
+const processing = ref(false)
45
+const handleInput = async (event) => {
46
+  console.log("handleInput")
47
+  console.log(event)
48
+  const query = event.target.value.toLowerCase();
49
+  console.log("query = ", query)
50
+  is_empty.value = false;
51
+  if(query == "") {
52
+    results.value = []
53
+  }else {
54
+    //results.value = data.filter((d) => d.toLowerCase().indexOf(query) > -1);
55
+    processing.value = true;
56
+    const r = await searchMat(query)
57
+    processing.value = false;
58
+
59
+    results.value = r.results
60
+    if(r.count == 0 ) {
61
+      is_empty.value = true;
62
+    }else {
63
+      is_empty.value = false;
64
+    }
65
+  }
66
+  //test
67
+}
68
+</script>

+ 104 - 66
src/views/Tab1Page.vue

2
   <ion-page>
2
   <ion-page>
3
     <ion-header>
3
     <ion-header>
4
       <ion-toolbar>
4
       <ion-toolbar>
5
-        <ion-title>Tab 1</ion-title>
5
+        <ion-title>TMTLive</ion-title>
6
+       <ion-buttons slot="end">
7
+          <ion-button :router-link="'/tabs/search_page/'">
8
+            <ion-icon slot="icon-only" :icon="search"></ion-icon>
9
+          </ion-button>
10
+        </ion-buttons>
6
       </ion-toolbar>
11
       </ion-toolbar>
7
     </ion-header>
12
     </ion-header>
8
     <ion-content>
13
     <ion-content>
9
       <ion-header collapse="condense">
14
       <ion-header collapse="condense">
10
         <ion-toolbar>
15
         <ion-toolbar>
11
-          <ion-title size="large">Tab 1</ion-title>
16
+          <ion-title size="large">TMTLive</ion-title>
12
         </ion-toolbar>
17
         </ion-toolbar>
13
       </ion-header>
18
       </ion-header>
19
+      <template v-if="liveObj && liveObj.result">
20
+        <div v-html="liveObj.result.embed_html"></div>
21
+      </template>
22
+      <div class='ion-padding'>
23
+      <h2>Today Programs</h2>
24
+      <swiper :slides-per-view="2" :loop="true" v-if="todays" class='ion-no-margin'>
25
+      <swiper-slide v-for="m in todays">
26
+        <CourseSchedule :course-obj="m.course_level.course" :from-time="m.from_time" :to-time='m.to_time'  />
27
+      </swiper-slide>
28
+      </swiper>
29
+      </div>
30
+      <template v-if="posts">
31
+        <div class='ion-padding'>
32
+          <h2>Muaythai Techniques</h2>
33
+          <swiper :slides-per-view="2"  :loop="true"  class='ion-no-margin'>
34
+          <swiper-slide v-for="m in posts">
35
+            <Post :obj="m" />
36
+          </swiper-slide>
37
+          </swiper>
38
+        </div>
39
+      </template>
40
+      <template v-if="mats">
41
+        <h2>Trainings</h2>
14
       <ion-grid>
42
       <ion-grid>
43
+
15
         <ion-row>
44
         <ion-row>
16
-          <ion-col size="12" size-md="4" size-lg="2">
17
-            <ion-card>
18
-              <img alt="Silhouette of mountains" src="https://ionicframework.com/docs/img/demos/card-media.png" />
19
-              <ion-card-header>
20
-                <ion-card-title>Card Title</ion-card-title>
21
-                <ion-card-subtitle>Card Subtitle</ion-card-subtitle>
22
-              </ion-card-header>
23
-
24
-              <ion-card-content>
25
-                Here's a small text description for the card content. Nothing more, nothing less.
26
-              </ion-card-content>
27
-            </ion-card>
28
-          </ion-col>
29
-          <ion-col size="12" size-md="4" size-lg="2">
30
-            <ion-card>
31
-              <img alt="Silhouette of mountains" src="https://ionicframework.com/docs/img/demos/card-media.png" />
32
-              <ion-card-header>
33
-                <ion-card-title>Card Title</ion-card-title>
34
-                <ion-card-subtitle>Card Subtitle</ion-card-subtitle>
35
-              </ion-card-header>
36
-
37
-              <ion-card-content>
38
-                Here's a small text description for the card content. Nothing more, nothing less.
39
-              </ion-card-content>
40
-            </ion-card>
45
+          <ion-col size="12" size-md="4" size-lg="2" v-for="c in mats">
46
+            <CourseMat :obj="c" :course-name="c.course_name" class='ion-no-margin' />
41
           </ion-col>
47
           </ion-col>
42
         </ion-row>
48
         </ion-row>
43
       </ion-grid>
49
       </ion-grid>
44
-      {{ token }}
50
+      </template>
45
       <ion-button router-link="/tabs/detail/3/" router-direction="forward">Click Me</ion-button>
51
       <ion-button router-link="/tabs/detail/3/" router-direction="forward">Click Me</ion-button>
46
-      {{ safeUrl }}
47
-      
52
+
48
       <div style="padding:56.25% 0 0 0;position:relative;"><iframe src="https://player.vimeo.com/video/880005259?badge=0&amp;autopause=0&amp;quality_selector=1&amp;player_id=0&amp;app_id=58479" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" style="position:absolute;top:0;left:0;width:100%;height:100%;" title="Muay Thai Advance"></iframe></div> 
53
       <div style="padding:56.25% 0 0 0;position:relative;"><iframe src="https://player.vimeo.com/video/880005259?badge=0&amp;autopause=0&amp;quality_selector=1&amp;player_id=0&amp;app_id=58479" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" style="position:absolute;top:0;left:0;width:100%;height:100%;" title="Muay Thai Advance"></iframe></div> 
49
-<!--
50
-<vue-core-video-player src="https://player.vimeo.com/progressive_redirect/playback/880005259/rendition/720p/file.mp4?loc=external&signature=5ddad1430211b33f65824444a3137fc0ec6fe61183d39997036d2dd3b78654f9"></vue-core-video-player> -->
51
-
52
-      Video
53
-
54
-
55
-<div id="made-in-ny" ref='video'></div>
56
-
57
-      <ul>
58
-        <li v-for="(item, index) in items">
59
-          {{ item.msg }}
60
-        </li>
61
-      </ul>
62
-      <ion-item v-for="t in trainers" :router-link="'/detail/'+t.id">
63
-        <ion-thumbnail slot="start" v-if="t.photo">
64
-          <img alt="Silhouette of mountains" :src="t.photo" />
65
-        </ion-thumbnail>
66
-        <ion-label class='ion-text-wrap'>{{ t.name }}</ion-label>
67
-      </ion-item>
68
-      <ion-icon icon="heart"></ion-icon>
69
-      <ion-button @click="scrollToBottom">Scroll to Bottom</ion-button>
70
-      {{ mats }}
54
+      <!--
55
+        <vue-core-video-player src="https://player.vimeo.com/progressive_redirect/playback/880005259/rendition/720p/file.mp4?loc=external&signature=5ddad1430211b33f65824444a3137fc0ec6fe61183d39997036d2dd3b78654f9"></vue-core-video-player> -->
56
+
57
+        Video
58
+
59
+
60
+        <div id="made-in-ny" ref='video'></div>
61
+
62
+        <ul>
63
+          <li v-for="(item, index) in items">
64
+            {{ item.msg }}
65
+          </li>
66
+        </ul>
67
+        <ion-item v-for="t in trainers" :router-link="'/detail/'+t.id">
68
+          <ion-thumbnail slot="start" v-if="t.photo">
69
+            <img alt="Silhouette of mountains" :src="t.photo" />
70
+          </ion-thumbnail>
71
+          <ion-label class='ion-text-wrap'>{{ t.name }}</ion-label>
72
+        </ion-item>
73
+        <ion-icon icon="heart"></ion-icon>
74
+        <ion-button @click="scrollToBottom">Scroll to Bottom</ion-button>
71
     </ion-content>
75
     </ion-content>
72
   </ion-page>
76
   </ion-page>
73
 </template>
77
 </template>
74
 
78
 
75
 <script setup lang="ts">
79
 <script setup lang="ts">
76
 
80
 
77
-import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent, IonIcon, onIonViewWillEnter, IonButton, 
78
-  onIonViewWillLeave, IonItem, IonLabel, IonThumbnail, IonCol, IonGrid, IonRow, IonCard, IonCardContent, IonCardHeader, IonCardSubtitle, IonCardTitle  } from '@ionic/vue';
81
+  import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent, IonIcon, onIonViewWillEnter, IonButton, 
82
+  onIonViewWillLeave, IonItem, IonLabel, IonThumbnail, IonCol, IonGrid, IonRow, IonCard, IonCardContent, IonCardHeader, IonCardSubtitle, IonCardTitle, onIonViewDidEnter,  IonButtons } from '@ionic/vue';
83
+
79
 import ExploreContainer  from '@/components/ExploreContainer.vue';
84
 import ExploreContainer  from '@/components/ExploreContainer.vue';
85
+import CourseSchedule from '@/components/CourseSchedule.vue'
86
+import Post from '@/components/Post.vue'
87
+import CourseMat from '@/components/CourseMat.vue'
88
+import SearchEngine from '@/components/SearchEngine.vue'
89
+
80
 import { CapacitorHttp } from '@capacitor/core';
90
 import { CapacitorHttp } from '@capacitor/core';
81
 import { ref } from 'vue';
91
 import { ref } from 'vue';
82
-import { TOKEN, getProducts, setToken, getObject, getToken, getTrainers, listMats, storeAPNToken } from '@/settings';
92
+import { TOKEN, getProducts, setToken, getObject, getToken, getTrainers, listMats, storeAPNToken,
93
+getTodayProgs, getLive, getPosts} from '@/composable/settings';
83
 //import VueCoreVideoPlayer from 'vue-core-video-player'
94
 //import VueCoreVideoPlayer from 'vue-core-video-player'
84
 import { vueVimeoPlayer } from 'vue-vimeo-player'
95
 import { vueVimeoPlayer } from 'vue-vimeo-player'
85
 
96
 
89
 
100
 
90
 import Player from '@vimeo/player';
101
 import Player from '@vimeo/player';
91
 
102
 
103
+import { Autoplay, Keyboard, Pagination, Scrollbar, Zoom } from 'swiper/modules';
104
+import { Swiper, SwiperSlide } from 'swiper/vue';
105
+import 'swiper/css';
106
+import 'swiper/css/autoplay';
107
+import 'swiper/css/keyboard';
108
+import 'swiper/css/pagination';
109
+import 'swiper/css/scrollbar';
110
+import 'swiper/css/zoom';
111
+import '@ionic/vue/css/ionic-swiper.css';
112
+import { search } from 'ionicons/icons';
113
+
92
 const safeUrl = "https://vimeo.com/880005259?share=copy"
114
 const safeUrl = "https://vimeo.com/880005259?share=copy"
93
 const token = ref()
115
 const token = ref()
94
 const trainers = ref([])
116
 const trainers = ref([])
95
 const mats = ref()
117
 const mats = ref()
96
- const scrollToBottom = () => {
97
-    console.log("scroll")
98
-    //content.value.$el.scrollToBottom(300);
118
+const todays = ref()
119
+const posts = ref()
120
+const scrollToBottom = () => {
121
+  console.log("scroll")
122
+  //content.value.$el.scrollToBottom(300);
123
+};
124
+const items = ref([{msg: 'foo'}, {msg: 'bar2'}])
125
+
126
+const options = {
127
+      id: 59777392,
128
+      width: 640,
129
+      loop: true
99
   };
130
   };
100
-  const items = ref([{msg: 'foo'}, {msg: 'bar2'}])
101
-
102
-  const options = {
103
-        id: 59777392,
104
-        width: 640,
105
-        loop: true
106
-    };
107
  //const video = ref(null)
131
  //const video = ref(null)
108
 
132
 
109
   //const player = new Player(video, options);
133
   //const player = new Player(video, options);
110
 
134
 
135
+const modules =  [Autoplay, Keyboard, Pagination, Scrollbar, Zoom]
111
   onIonViewWillEnter(async () => {
136
   onIonViewWillEnter(async () => {
112
     //console.log("video " ,video)
137
     //console.log("video " ,video)
113
     //console.log("video id", video.value.id)
138
     //console.log("video id", video.value.id)
128
     token.value = await getToken()
153
     token.value = await getToken()
129
     trainers.value = await getTrainers()
154
     trainers.value = await getTrainers()
130
     mats.value = await listMats()
155
     mats.value = await listMats()
156
+    posts.value = await getPosts("muaythai-techniques")
131
     console.log(" trainers => ", trainers)
157
     console.log(" trainers => ", trainers)
132
 
158
 
133
     GoogleAuth.initialize();
159
     GoogleAuth.initialize();
134
     registerNotifications()
160
     registerNotifications()
135
     addListeners()
161
     addListeners()
162
+
163
+    todays.value = await getTodayProgs()
136
   })
164
   })
137
 
165
 
138
   onIonViewWillLeave(() => {
166
   onIonViewWillLeave(() => {
139
     console.log("view will leave")
167
     console.log("view will leave")
140
-  
168
+
169
+  })
170
+
171
+const liveObj = ref()
172
+  onIonViewDidEnter(async() => {
173
+    setInterval( async () => {
174
+      console.log("did enter")
175
+      if( liveObj.value == null ||  liveObj.value.result == false ) {
176
+        liveObj.value = await getLive()
177
+      }
178
+    }, 1000)
141
   })
179
   })
142
 
180
 
143
   const doGet = async () => {
181
   const doGet = async () => {

+ 3 - 3
src/views/Tab2Page.vue

13
       </ion-header>
13
       </ion-header>
14
       <ion-grid>
14
       <ion-grid>
15
         <ion-row>
15
         <ion-row>
16
-          <ion-col size="12" size-md="4" size-lg="2" v-for="c in courses">
17
-            <Course :course-obj="c" />
16
+          <ion-col size="6" size-md="4" size-lg="2" v-for="c in courses">
17
+            <Course :course-obj="c" class="ion-no-margin" />
18
           </ion-col>
18
           </ion-col>
19
         </ion-row>
19
         </ion-row>
20
       </ion-grid>
20
       </ion-grid>
27
 import Course from '@/components/Course.vue';
27
 import Course from '@/components/Course.vue';
28
 import { ref } from 'vue';
28
 import { ref } from 'vue';
29
 
29
 
30
-import {listCourses} from '@/settings';
30
+import {listCourses} from '@/composable/settings';
31
 const courses = ref([])
31
 const courses = ref([])
32
 
32
 
33
 onIonViewWillEnter(async () => {
33
 onIonViewWillEnter(async () => {

Вход - Gogs: Simplico Git Service

Вход