New project view
Below is the template and script for the component. Each project is created with a title, description and cover image:
<template>
<div class="row">
<form @submit.prevent="handleSubmit" class="col-12" :class="{light: light}">
<h4 :class="{light: light}">Create new project</h4>
<input type="text" placeholder="Project title" v-model="title" :class="{light: light}" required>
<textarea placeholder="Description" v-model="description" :class="{light: light}" required></textarea>
<label :class="{light: light}" for="file-upload" class="file-upload-button">Upload project image</label>
<input id="file-upload" type="file" @change="handleChange">
<div class="error">{{ fileError }}</div>
<button :class="{light: light}" v-if="!isPending">Add new project</button>
<button :class="{light: light}" v-if="isPending" class="loading">Uploading...</button>
</form>
</div>
</template>
<script>
import { ref } from '@vue/reactivity'
import useStorage from '../composables/useStorage'
import useCollection from '../composables/useCollection'
import getUser from '../composables/getUser'
import { timestamp } from '../firebase/config'
import { useRouter } from 'vue-router'
export default {
props: ['light'],
setup(){
const title = ref('')
const description = ref('')
const projectImage = ref(null)
const fileTypes = ['image/png', 'image/jpeg']
const fileError = ref(null)
const isPending = ref(false)
const { uploadImage, error, filePath, url } = useStorage()
const { addDoc } = useCollection('projects')
const { user } = getUser()
const router = useRouter()
const handleSubmit = async () => {
if(projectImage.value){
isPending.value = true
await uploadImage(projectImage.value)
const res = await addDoc({
title: title.value,
description: description.value,
userId: user.value.uid,
userName: user.value.displayName,
imageUrl: url.value,
filePath: filePath.value,
tasks: [],
createdAt: timestamp()
})
if(!error.value){
isPending.value = false
router.push({ name: 'SingleProject', params: { id: res.id } })
}
}
projectImage.value = null
title.value = ''
description.value = ''
fileError.value = null
}
const handleChange = (e) => {
const selected = e.target.files[0]
if(selected && fileTypes.includes(selected.type)){
projectImage.value = selected
fileError.value = null
} else{
projectImage.value = null
fileError.value = 'Please select an image file, jpeg or png'
}
}
return { title, description, projectImage, fileError, handleSubmit, handleChange, error, isPending }
}
}
</script>
Practically the user fills out the title and description then clicks on the file upload button, when an image is chosen the above handleChange
function is invoked, which checks if the image is either a jpeg or png file type and then if so assigns the image as the value of the projectImage
Then the user clicks the Add new project
button and invokes the handleSubmit
function (above) which if an image is chosen, uploads the image to firebase storage and once the promise is resolved the addDoc
function as invoked, also using the await keyword as the addDoc
is an asynchronous function within the useCollection
composable.
Once the response is returned from Firebase Firestore the user is pushed to the project view where they can add tasks.
Below is the uploadImage
function from the useStorage
composable and beneath that the addDoc
snippet from the useCollection
composable:
const uploadImage = async (file) => {
filePath.value = `projectImages/${user.value.uid}/${file.name}`
const storageRef = fStorage.ref(filePath.value)
try{
const res = await storageRef.put(file)
url.value = await res.ref.getDownloadURL()
}
catch(err){
console.log(err.message)
error.value = err.message
}
}
const addDoc = async (doc) => {
error.value = null
isPending.value = true
try{
const res = await fStore.collection(collection).add(doc)
isPending.value = false
return res
}
catch(err){
console.log(err.message)
isPending.value = false
error.value = 'Unable to send this message'
}
}