Spaces:
Configuration error
Configuration error
<script setup> | |
import { ref } from "vue"; | |
import axios from "axios"; | |
import Dropzone from "../components/Dropzone.vue"; | |
import DropzoneLoading from "../components/DropzoneLoading.vue"; | |
import Result from "../components/Result.vue"; | |
const apiUrl = import.meta.env.VITE_BASE_URL || "http://127.0.0.1:8000"; | |
const EXERCISES = ["squat", "plank", "bicep_curl", "lunge"]; | |
const submitData = ref({ | |
videoFile: null, | |
exerciseType: null, | |
}); | |
const processedData = ref(null); | |
const isProcessing = ref(false); | |
const uploadToServer = async () => { | |
if (!submitData.value.videoFile) { | |
alert("No video selected"); | |
return; | |
} | |
if (!submitData.value.exerciseType) { | |
alert("No exercise type selected"); | |
return; | |
} | |
processedData.value = null; | |
try { | |
isProcessing.value = true; | |
const { data } = await axios.post( | |
`${apiUrl}/api/video/upload?type=${submitData.value.exerciseType}`, | |
{ file: submitData.value.videoFile }, | |
{ | |
headers: { | |
"Content-Type": "multipart/form-data", | |
}, | |
} | |
); | |
processedData.value = data; | |
} catch (e) { | |
console.error("Error: ", e); | |
} finally { | |
isProcessing.value = false; | |
} | |
}; | |
</script> | |
<template> | |
<!-- Input section --> | |
<section class="input-section"> | |
<Dropzone | |
v-show="!isProcessing" | |
@file-uploaded="(file) => (submitData.videoFile = file)" | |
/> | |
<DropzoneLoading v-show="isProcessing" /> | |
<div class="right-container"> | |
<!-- exercises selection --> | |
<div class="exercises-container"> | |
<p | |
class="exercise" | |
v-for="exercise in EXERCISES" | |
:class="{ active: submitData.exerciseType == exercise }" | |
@click="submitData.exerciseType = exercise" | |
> | |
{{ exercise }} | |
</p> | |
</div> | |
<button class="process-btn" @click="uploadToServer"> | |
<span>Process!</span> | |
</button> | |
</div> | |
</section> | |
<!-- Results section --> | |
<Result v-if="processedData" :data="processedData" /> | |
</template> | |
<style lang="scss" scoped> | |
.input-section { | |
display: flex; | |
gap: 1rem; | |
* { | |
flex: 1; | |
} | |
.right-container { | |
display: flex; | |
flex-direction: column; | |
width: 100%; | |
.exercises-container { | |
display: flex; | |
flex-wrap: wrap; | |
gap: 1rem; | |
margin-bottom: 1rem; | |
.exercise { | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
padding: 1rem 0; | |
flex: 45%; | |
color: var(--secondary-color); | |
text-transform: uppercase; | |
border: 3px solid var(--primary-color); | |
border-radius: 0.3rem; | |
cursor: pointer; | |
transition: all 0.25s ease; | |
&:hover { | |
box-shadow: 0 6px 18px 0 rgba(#000, 0.1); | |
transform: translateY(-6px); | |
} | |
&.active { | |
background-color: var(--primary-color); | |
font-weight: 700; | |
} | |
} | |
} | |
.process-btn { | |
border: none; | |
background-color: var(--primary-color); | |
padding: 1.25rem 0; | |
color: whitesmoke; | |
font-size: 1.25rem; | |
font-weight: 700; | |
cursor: pointer; | |
border-radius: 8px; | |
transition: all 0.25s ease; | |
&:hover { | |
box-shadow: 0 6px 18px 0 rgba(#000, 0.1); | |
color: var(--primary-color); | |
border-color: transparent; | |
background-color: transparent; | |
} | |
} | |
} | |
} | |
</style> | |