upload image
This commit is contained in:
parent
2c94f9cf30
commit
b36c54eb60
8
package-lock.json
generated
8
package-lock.json
generated
@ -9,7 +9,7 @@
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"axios": "^1.12.2",
|
||||
"core-js": "^3.8.3",
|
||||
"core-js": "^3.46.0",
|
||||
"pinia": "^3.0.3",
|
||||
"primeicons": "^7.0.0",
|
||||
"vue": "^3.2.13",
|
||||
@ -4973,9 +4973,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/core-js": {
|
||||
"version": "3.45.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.45.1.tgz",
|
||||
"integrity": "sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==",
|
||||
"version": "3.46.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.46.0.tgz",
|
||||
"integrity": "sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.12.2",
|
||||
"core-js": "^3.8.3",
|
||||
"core-js": "^3.46.0",
|
||||
"pinia": "^3.0.3",
|
||||
"primeicons": "^7.0.0",
|
||||
"vue": "^3.2.13",
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
class="flex flex-col items-center"
|
||||
>
|
||||
<img
|
||||
:src="page.image"
|
||||
:src="page.media"
|
||||
:alt="'Halaman ' + (index + 1)"
|
||||
class="w-full rounded-xl shadow-lg border border-white/40 object-contain"
|
||||
/>
|
||||
@ -68,6 +68,7 @@
|
||||
<script>
|
||||
import router from '@/router'
|
||||
import api from '@/util/api'
|
||||
// import { forEach } from 'core-js/core/array';
|
||||
|
||||
export default {
|
||||
name: "MangaPageScrollReader",
|
||||
@ -112,7 +113,11 @@ export default {
|
||||
if (this.pages.length > 0 && this.pages[0].chapter?.manga?.title) {
|
||||
this.mangaTitle = this.pages[0].chapter.manga.title
|
||||
}
|
||||
console.log(this.pages)
|
||||
|
||||
this.pages.forEach((page) => {
|
||||
console.log("Pages :" + JSON.stringify(page))
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
console.error("Gagal memuat halaman:", err)
|
||||
} finally {
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
<!-- Manga Cover -->
|
||||
<div class="relative" @click="toPage(chapter.id,chapter.chapter)">
|
||||
<img
|
||||
:src="images.find(image => image.id === chapter.id).image"
|
||||
:src="chapter.manga?.featured_image"
|
||||
:alt="chapter.status"
|
||||
class="w-full h-48 object-cover rounded-xl group-hover:scale-105 transition-transform duration-500"
|
||||
/>
|
||||
@ -95,7 +95,7 @@ export default {
|
||||
router.back();
|
||||
},
|
||||
toPage(manga_id,chapter_id) {
|
||||
router.push(`/entertainment/manga/${manga_id}/chapters/${chapter_id}/`);
|
||||
router.push(`/entertainment/manga/${manga_id}/chapters/${chapter_id}/pages`);
|
||||
},
|
||||
async loadContent() {
|
||||
const res = await getChapters(this.id);
|
||||
|
||||
@ -183,7 +183,6 @@
|
||||
>
|
||||
Status: {{ mission.userStatus }}
|
||||
</p>
|
||||
|
||||
<div class="flex items-center gap-4 text-xs text-gray-700 pt-2">
|
||||
<span class="flex items-center gap-1">
|
||||
<svg class="w-4 h-4 text-gray-500" fill="currentColor" viewBox="0 0 20 20">
|
||||
@ -191,7 +190,7 @@
|
||||
d="M10 2a8 8 0 100 16 8 8 0 000-16zM9 11V5a1 1 0 012 0v6a1 1 0 01-2 0zM10 13a1 1 0 100 2 1 1 0 000-2z"
|
||||
/>
|
||||
</svg>
|
||||
{{ timeLeft(mission.date_valid, mission.time_to_valid) }}
|
||||
{{ timeLeft(mission.date_from,mission.date_to, mission.time_from_valid, mission.time_to_valid) }}
|
||||
</span>
|
||||
|
||||
<span class="flex items-center gap-1">
|
||||
@ -205,7 +204,6 @@
|
||||
</svg>
|
||||
+{{ mission.coin }} koin
|
||||
</span>
|
||||
|
||||
<span class="flex items-center gap-1">
|
||||
<svg
|
||||
class="w-4 h-4 text-green-600 transform rotate-180"
|
||||
@ -224,6 +222,7 @@
|
||||
<div class="flex items-center gap-4 text-xs text-gray-700">
|
||||
<button
|
||||
v-if="mission.userStatus !== 'completed'"
|
||||
:disabled="!isMissionActive(mission.date_from, mission.time_from_valid)"
|
||||
class="w-full mt-4 py-2 px-4 rounded-lg bg-red-600 text-white font-bold hover:bg-green-700 transition"
|
||||
@click="handleMissionClick(mission)"
|
||||
>
|
||||
@ -330,8 +329,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import { computed } from "vue"
|
||||
import { getContent } from '@/services/content';
|
||||
import { getMissions,getMissionLogs, createMissionLog } from '@/services/missions';
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
data() {
|
||||
@ -363,7 +365,9 @@ export default {
|
||||
now : new Date(),
|
||||
timer: null,
|
||||
misi:[],
|
||||
logs:[]
|
||||
logs:[],
|
||||
authStore : useAuthStore(),
|
||||
// currentUser : computed(() => this.authStore.currentUser)
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
@ -379,6 +383,12 @@ export default {
|
||||
this.selectedProfile = profile;
|
||||
this.showModal = false;
|
||||
},
|
||||
isMissionActive(dateFrom, timeFrom) {
|
||||
if (!dateFrom || !timeFrom) return false;
|
||||
const start = new Date(`${dateFrom}T${timeFrom}`);
|
||||
const now = this.now ? new Date(this.now) : new Date();
|
||||
return now >= start;
|
||||
},
|
||||
async getMission() {
|
||||
const res = await getMissions();
|
||||
this.missions = res.results;
|
||||
@ -404,13 +414,14 @@ export default {
|
||||
return "—";
|
||||
}
|
||||
},
|
||||
timeLeft(dateValid, timeToValid) {
|
||||
if (!dateValid || !timeToValid) return "—";
|
||||
|
||||
const now = new Date(this.now);
|
||||
const target = new Date(`${dateValid}T${timeToValid}`);
|
||||
const diffMs = target - now;
|
||||
timeLeft3(dateValidFrom,dateValidTo, timeValidFrom, timeToValid) {
|
||||
console.log(dateValidFrom,timeValidFrom,timeValidFrom,timeToValid)
|
||||
if (!dateValidFrom || !timeValidFrom || !dateValidFrom || !timeToValid) return "—";
|
||||
|
||||
const validDate = new Date(`${dateValidFrom}T${timeValidFrom}`);
|
||||
const target = new Date(`${dateValidTo}T${timeToValid}`);
|
||||
const diffMs = target - validDate;
|
||||
console.log("Sisa Waktu:" + diffMs)
|
||||
if (diffMs <= 0) return "Waktu habis";
|
||||
|
||||
const totalSeconds = Math.floor(diffMs / 1000);
|
||||
@ -422,6 +433,39 @@ export default {
|
||||
|
||||
return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
|
||||
},
|
||||
timeLeft(dateFrom, dateTo, timeFrom, timeTo) {
|
||||
if (!dateFrom || !timeFrom || !dateTo || !timeTo) return "—";
|
||||
|
||||
// const now = new Date();
|
||||
const start = new Date(`${dateFrom}T${timeFrom}`);
|
||||
const end = new Date(`${dateTo}T${timeTo}`);
|
||||
const current = this.now;
|
||||
|
||||
const pad = (num) => String(num).padStart(2, "0");
|
||||
const diffToStart = start - current;
|
||||
const diffToEnd = end - current;
|
||||
|
||||
// 🕰️ CASE 1: Before start
|
||||
if (diffToStart > 0) {
|
||||
const totalSeconds = Math.floor(diffToStart / 1000);
|
||||
const hours = Math.floor(totalSeconds / 3600);
|
||||
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
||||
const seconds = totalSeconds % 60;
|
||||
|
||||
return `⏳ ${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
|
||||
}
|
||||
if (diffToEnd > 0) {
|
||||
const totalSeconds = Math.floor(diffToEnd / 1000);
|
||||
const hours = Math.floor(totalSeconds / 3600);
|
||||
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
||||
const seconds = totalSeconds % 60;
|
||||
|
||||
return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
|
||||
}
|
||||
|
||||
// ⛔ CASE 3: After end
|
||||
return "❌ Waktu habis";
|
||||
},
|
||||
downtimeHours(from, to) {
|
||||
if (!from || !to) return '-';
|
||||
const [fh, fm] = from.split(':').map(Number);
|
||||
@ -464,7 +508,7 @@ export default {
|
||||
}
|
||||
if (mission.userStatus === 'completed') return;
|
||||
if (mission.userStatus === 'not_started') {
|
||||
this.startMission(mission);
|
||||
// this.startMission(mission);
|
||||
}
|
||||
if (mission.task === 'scan-qr') {
|
||||
console.log(mission)
|
||||
@ -483,9 +527,13 @@ export default {
|
||||
},
|
||||
|
||||
async startMission(mission) {
|
||||
if (!mission || !mission.id) {
|
||||
console.error("Invalid mission", mission);
|
||||
return;
|
||||
}
|
||||
await createMissionLog({
|
||||
mission: mission.id,
|
||||
user_id: this.currentUser.id,
|
||||
user_id: this.currentUser?.value?.id,
|
||||
status: 'in_progress',
|
||||
coin: mission.coin,
|
||||
point: mission.point
|
||||
@ -505,10 +553,15 @@ export default {
|
||||
beforeUnmount() {
|
||||
clearInterval(this.timer);
|
||||
},
|
||||
computed: {
|
||||
currentUser() {
|
||||
return this.authStore.currentUser;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<script setup>
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
// import { useAuthStore } from '@/stores/auth'
|
||||
import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
const auth = useAuthStore()
|
||||
|
||||
@ -1,4 +1,17 @@
|
||||
<template>
|
||||
<!-- Header Section -->
|
||||
<div class="w-full px-6 py-4 flex items-center justify-between backdrop-blur-md bg-white/30 border-b border-emerald-100 z-20">
|
||||
<!-- Back Button -->
|
||||
<button
|
||||
@click="goBack"
|
||||
class="flex items-center gap-2 text-emerald-600 hover:text-emerald-800 transition-colors"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
<span class="font-semibold">Kembali</span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="min-h-screen flex flex-col items-center justify-center bg-gradient-to-b from-teal-100 to-lime-200 p-6"
|
||||
>
|
||||
@ -111,8 +124,8 @@
|
||||
import { QrcodeStream } from "vue-qrcode-reader"
|
||||
import api from "@/util/api"
|
||||
import { useRoute } from "vue-router"
|
||||
import { createMissionLog } from '@/services/missions';
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
// import { createMissionLog } from '@/services/missions';
|
||||
// import { useAuthStore } from '@/stores/auth'
|
||||
|
||||
const mission = ref({
|
||||
id: 1,
|
||||
@ -133,8 +146,12 @@
|
||||
const scanning = ref(false)
|
||||
const detectedOnce = ref(false)
|
||||
const router = useRoute();
|
||||
const authStore = useAuthStore()
|
||||
const currentUser = computed(() => authStore.currentUser)
|
||||
// const authStore = useAuthStore()
|
||||
// const currentUser = computed(() => authStore.currentUser)
|
||||
|
||||
function goBack() {
|
||||
window.history.back('/mission/missions');
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if(router.query.mission){
|
||||
@ -224,7 +241,7 @@
|
||||
// data: result,
|
||||
// completed_at: new Date().toISOString(),
|
||||
// })
|
||||
this.startMission(data={
|
||||
this.startMission({
|
||||
mission: mission.value.id,
|
||||
user_id: user_id,
|
||||
type: "scan-qr",
|
||||
@ -281,18 +298,22 @@
|
||||
alert("🎉 Misi selesai! Semua task telah disimpan.")
|
||||
}
|
||||
|
||||
async function startMission(mission) {
|
||||
console.log("mission: ", mission)
|
||||
await createMissionLog({
|
||||
mission: mission.id,
|
||||
user_id: currentUser.id,
|
||||
status: 'in_progress',
|
||||
coin: mission.coin,
|
||||
point: mission.point
|
||||
});
|
||||
// async function startMission(mission) {
|
||||
// if (!mission || !mission.id) {
|
||||
// console.error("⚠️ Invalid mission:", mission);
|
||||
// return;
|
||||
// }
|
||||
// console.log("mission: ", mission)
|
||||
// await createMissionLog({
|
||||
// mission: mission.id,
|
||||
// user_id: currentUser.id,
|
||||
// status: 'in_progress',
|
||||
// coin: mission.coin,
|
||||
// point: mission.point
|
||||
// });
|
||||
|
||||
mission.userStatus = 'in_progress';
|
||||
}
|
||||
// mission.userStatus = 'in_progress';
|
||||
// }
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@ -28,7 +28,7 @@ const routes = [
|
||||
})
|
||||
},
|
||||
{ path: '/entertainment/manga/:manga_id/chapters/:chapter_id/', name:'manga-list', component:ChapterListPage, props:true},
|
||||
|
||||
{ path: '/entertainment/manga/:manga_id/chapters/:chapter_id/pages', name:'manga-read', component:ChapterListPage, props:true},
|
||||
{ path:'/mission/quest/:id/missions', name: 'quest-missions', component:MissionPage}
|
||||
]
|
||||
|
||||
|
||||
@ -10,3 +10,7 @@ export const getChapters = async(id) => {
|
||||
export const getMangaByChapter = async(manga_id, chapter_id) => {
|
||||
return await api.get(`/entertainment/manga/${manga_id}/chapters/${chapter_id}/`).then((res) => res.data);
|
||||
};
|
||||
|
||||
export const getMangaByChapterPages = async (manga_id, chapter_id) =>{
|
||||
return await api.get(`/entertainment/manga/${manga_id}/chapters/${chapter_id}/pages`).then((res) => res.data);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user