You've already forked reloading-manager
Some checks failed
🧪 ✨ Unit Tests Workflow / 🧪 📜 JavaScript Tests (push) Successful in 1m23s
🧪 ✨ Unit Tests Workflow / 🧪 🐹 GolangCI-Lint (push) Successful in 1m32s
🧪 ✨ Unit Tests Workflow / 🔍 🐹 Go Tests (push) Successful in 3m35s
🏗️ ✨ Build Workflow / 🖼️ 🔨 Build Frontend (push) Successful in 6m11s
🏗️ ✨ Build Workflow / 🖥️ 🔨 Build Backend (push) Successful in 6m47s
🏗️ ✨ Build Workflow / 🚀 ✨ Deploy Application (push) Failing after 53s
Reviewed-on: Siteworxpro/reloading-manager#9 Co-authored-by: Ron Rise <ron@siteworxpro.com> Co-committed-by: Ron Rise <ron@siteworxpro.com>
408 lines
12 KiB
Vue
408 lines
12 KiB
Vue
<template>
|
|
<Card class="md:w-2/3 w-full">
|
|
<template #title>
|
|
<span v-if="route.params.id">Edit Load</span>
|
|
<span v-else>Add New Load</span>
|
|
</template>
|
|
<template #content>
|
|
<div class="grid grid-cols-1">
|
|
<div>
|
|
Cartridge
|
|
</div>
|
|
<div class="flex flex-row items-center">
|
|
<div class="grow mr-3">
|
|
<Select v-model="load.cartridge" :options="cartridges" optionLabel="label" optionValue="value"
|
|
class="w-full" />
|
|
<Message v-if="v$.$dirty && v$.cartridge.$invalid" :value="false" size="small" severity="error"
|
|
variant="simple">Cartridge Required
|
|
</Message>
|
|
</div>
|
|
<div class="cursor-pointer" @click="addCartridgeDialog = true">
|
|
<i :class="icons.add" class="fa-2x" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-3 mr-3">
|
|
Bullet
|
|
</div>
|
|
<div class="w-full mt-3">
|
|
<Select v-model="load.bullet" :invalid="v$.$dirty && v$.bullet.$invalid" :options="bullets"
|
|
optionLabel="label" optionValue="value" class="w-full" />
|
|
<Message v-if="v$.$dirty && v$.bullet.$invalid" :value="false" size="small" severity="error" variant="simple">
|
|
Bullet Required
|
|
</Message>
|
|
</div>
|
|
|
|
<div class="mt-3 mr-3">
|
|
Powder
|
|
</div>
|
|
<div class="flex flex-row">
|
|
<div class="grow mr-3">
|
|
<Select v-model="load.powder" :options="powders" optionLabel="label" optionValue="value" class="w-full" />
|
|
<Message v-if="v$.$dirty && v$.powder.$invalid" :value="false" size="small" severity="error"
|
|
variant="simple">Powder Required
|
|
</Message>
|
|
</div>
|
|
<div>
|
|
<InputNumber v-model="load.powderGrs" placeholder="00.0 gr" suffix=" gr" :minFractionDigits="1"
|
|
:maxFractionDigits="1" :min="0"
|
|
:max="1000" class="w-full" />
|
|
<Message v-if="v$.$dirty && v$.powderGrs.$invalid" :value="false" size="small" severity="error"
|
|
variant="simple">Grains Required
|
|
</Message>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-3 mr-3">
|
|
Primer
|
|
</div>
|
|
<div class="w-full mt-3">
|
|
<Select v-model="load.primer" :options="primers" optionLabel="label" optionValue="value" class="w-full" />
|
|
<Message v-if="v$.$dirty && v$.primer.$invalid" :value="false" size="small" severity="error"
|
|
variant="simple">Primer Required
|
|
</Message>
|
|
</div>
|
|
|
|
<div class="mt-3 mr-3">
|
|
COL
|
|
</div>
|
|
<div class="w-full mt-3">
|
|
<InputMask :unmask="loading" :value="load.col" placeholder="0.000" v-model="load.col" mask="9.999" class="w-full" />
|
|
<Message v-if="v$.$dirty && v$.col.$invalid" :value="false" size="small" severity="error"
|
|
variant="simple">COL Required
|
|
</Message>
|
|
</div>
|
|
|
|
<div class="w-full mt-5">
|
|
<label>Picture</label>
|
|
<Message v-if="v$.$dirty && (!route.params.id && !file)" :value="false" size="small" severity="error"
|
|
variant="simple">Picture Required
|
|
</Message>
|
|
<div class="w-1/2">
|
|
<img v-if="pictureUrl && route.params.id && !loading" :src="pictureUrl" alt="picture" />
|
|
</div>
|
|
<FileUpload v-model="file" mode="basic" @select="fileSelected" customUpload />
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<template #footer>
|
|
<Button v-if="!route.params.id" label="Add" :icon="icons.add" @click="add" />
|
|
<Button v-else label="Save" :icon="icons.edit" @click="add" />
|
|
<Button class="ml-3" severity="danger" v-if="route.params.id" label="Delete" :icon="icons.delete" @click="deleteLoad" />
|
|
</template>
|
|
</Card>
|
|
|
|
<Dialog @update:visible="addCartridgeDialog = false" modal :visible="addCartridgeDialog" :style="{ width: '35rem' }">
|
|
<template #header>
|
|
Add Cartridge
|
|
</template>
|
|
<div class="grid grid-cols-1">
|
|
<div>
|
|
Name
|
|
</div>
|
|
<div class="w-full">
|
|
<InputText :invalid="cartridgeName === ''" v-model="cartridgeName" class="w-full" />
|
|
</div>
|
|
</div>
|
|
<template #footer>
|
|
<Button :disabled="cartridgeName === ''" label="Add" :icon="icons.add" @click="addCartridgeName" />
|
|
</template>
|
|
</Dialog>
|
|
|
|
<FullScreenLoader :loading="loading" />
|
|
</template>
|
|
<script lang="ts">
|
|
interface Select {
|
|
label: string
|
|
value: string
|
|
}
|
|
|
|
interface Cartridge {
|
|
id: string
|
|
name: string
|
|
}
|
|
</script>
|
|
<script lang="ts" setup>
|
|
import { Bullet } from '../../types/bullet'
|
|
import { defineAsyncComponent, onMounted, ref } from 'vue'
|
|
import { Primers } from '../../types/primers'
|
|
import { Powder } from '../../types/powder'
|
|
import axios from 'axios'
|
|
import { Load, Response } from '../../types/Response'
|
|
import { icons } from '../../lib/icons.ts'
|
|
import { FileUploadSelectEvent } from 'primevue/fileupload'
|
|
import useVuelidate from '@vuelidate/core'
|
|
import { required } from '@vuelidate/validators'
|
|
import { useToast } from 'primevue/usetoast'
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
|
|
const router = useRouter()
|
|
const route = useRoute()
|
|
|
|
const Card = defineAsyncComponent(() => import('primevue/card'))
|
|
const Select = defineAsyncComponent(() => import('primevue/select'))
|
|
const Button = defineAsyncComponent(() => import('primevue/button'))
|
|
const InputNumber = defineAsyncComponent(() => import('primevue/inputnumber'))
|
|
const InputText = defineAsyncComponent(() => import('primevue/inputtext'))
|
|
const InputMask = defineAsyncComponent(() => import('primevue/inputmask'))
|
|
const FileUpload = defineAsyncComponent(() => import('primevue/fileupload'))
|
|
const Dialog = defineAsyncComponent(() => import('primevue/dialog'))
|
|
const Message = defineAsyncComponent(() => import('primevue/message'))
|
|
const FullScreenLoader = defineAsyncComponent(() => import('../../components/FullScreenLoader.vue'))
|
|
|
|
const toast = useToast()
|
|
|
|
const bullets = ref<Select[]>([])
|
|
const primers = ref<Select[]>([])
|
|
const powders = ref<Select[]>([])
|
|
const cartridges = ref<Select[]>([])
|
|
const loading = ref(false)
|
|
|
|
const cartridgeName = ref('')
|
|
const addCartridgeDialog = ref(false)
|
|
|
|
const file = ref<File | null>(null)
|
|
const fileSelected = (e: FileUploadSelectEvent) => {
|
|
file.value = e.files[0]
|
|
}
|
|
|
|
const pictureUrl = ref<string | null>(null)
|
|
const calcUrl = () => {
|
|
const cache = new Date().getMilliseconds()
|
|
pictureUrl.value = import.meta.env.VITE_API + `/load/${route.params.id}/photo?cache=${cache}`
|
|
}
|
|
|
|
const load = ref({
|
|
bullet: '',
|
|
cartridge: '',
|
|
powder: '',
|
|
powderGrs: 0,
|
|
primer: '',
|
|
col: '',
|
|
})
|
|
|
|
const v$ = useVuelidate({
|
|
bullet: { required },
|
|
cartridge: { required },
|
|
powder: { required },
|
|
powderGrs: {
|
|
required, min: function (val: number | null) {
|
|
if (!val) return false
|
|
|
|
return val > 0
|
|
},
|
|
},
|
|
primer: { required },
|
|
col: {
|
|
required, min: function (val: string | null) {
|
|
if (!val) return false
|
|
|
|
// regex to validate COL does not container _
|
|
return !val.includes('_')
|
|
},
|
|
},
|
|
}, load.value)
|
|
|
|
const fetchBullets = async () => {
|
|
const response = await axios.get<any, Response<Bullet[]>>(`${import.meta.env.VITE_API}/bullet`)
|
|
response.data.payload.forEach((bullet: Bullet) => {
|
|
bullets.value.push({ label: `${bullet.manufacturer.name} ${bullet.weight}gr ${bullet.name}`, value: bullet.id })
|
|
})
|
|
}
|
|
const fetchPrimers = async () => {
|
|
const response = await axios.get<any, Response<Primers[]>>(`${import.meta.env.VITE_API}/primer`)
|
|
response.data.payload.forEach((primer: Primers) => {
|
|
primers.value.push({ label: `${primer.manufacturer.name} ${primer.name}`, value: primer.id })
|
|
})
|
|
}
|
|
const fetchPowders = async () => {
|
|
const response = await axios.get<any, Response<Powder[]>>(`${import.meta.env.VITE_API}/powder`)
|
|
response.data.payload.forEach((powder: Powder) => {
|
|
powders.value.push({ label: `${powder.manufacturer.name} ${powder.name}`, value: powder.id })
|
|
})
|
|
}
|
|
const fetchCartridges = async () => {
|
|
cartridges.value = []
|
|
|
|
const response = await axios.get<any, Response<Cartridge[]>>(`${import.meta.env.VITE_API}/cartridge`)
|
|
response.data.payload.forEach((cartridge: Cartridge) => {
|
|
cartridges.value.push({ label: `${cartridge.name}`, value: cartridge.id })
|
|
})
|
|
}
|
|
const addCartridgeName = async () => {
|
|
if (cartridgeName.value === '') {
|
|
return
|
|
}
|
|
|
|
try {
|
|
await axios.post<any, Response<Cartridge>>(`${import.meta.env.VITE_API}/cartridge`, {
|
|
name: cartridgeName.value,
|
|
})
|
|
} catch (error) {
|
|
toast.add({
|
|
severity: 'error',
|
|
summary: 'Error',
|
|
detail: 'Error adding cartridge',
|
|
})
|
|
}
|
|
|
|
await fetchCartridges()
|
|
|
|
cartridgeName.value = ''
|
|
addCartridgeDialog.value = false
|
|
}
|
|
const add = async () => {
|
|
v$.value.$touch()
|
|
|
|
if (v$.value.$invalid || (!route.params.id && !file.value)) {
|
|
toast.add({
|
|
severity: 'error',
|
|
summary: 'Error',
|
|
detail: 'Please correct all fields',
|
|
life: 3000,
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
let id = ""
|
|
|
|
if (route.params.id) {
|
|
id = "/" + route.params.id.toString()
|
|
}
|
|
|
|
const formData = new FormData()
|
|
formData.append('bullet_id', load.value.bullet)
|
|
formData.append('cartridge_id', load.value.cartridge)
|
|
formData.append('powder_id', load.value.powder)
|
|
formData.append('powder_gr', load.value.powderGrs.toString())
|
|
formData.append('primer_id', load.value.primer)
|
|
formData.append('col', load.value.col)
|
|
if (file.value) {
|
|
formData.append('photo', file.value)
|
|
}
|
|
|
|
try {
|
|
const response = await axios.post<any, Response<string>>(`${import.meta.env.VITE_API}/load${id}`, formData, {
|
|
headers: {
|
|
'Content-Type': 'multipart/form-data',
|
|
},
|
|
})
|
|
|
|
const message = route.params.id ? 'Load updated' : 'Load added'
|
|
|
|
toast.add({
|
|
severity: 'success',
|
|
summary: 'Success',
|
|
detail: message,
|
|
life: 3000,
|
|
})
|
|
|
|
if (route.params.id) {
|
|
load.value = {
|
|
bullet: '',
|
|
cartridge: '',
|
|
powder: '',
|
|
powderGrs: 0,
|
|
primer: '',
|
|
col: '',
|
|
}
|
|
file.value = null
|
|
|
|
v$.value.$reset()
|
|
await fetchLoad()
|
|
|
|
return
|
|
}
|
|
|
|
await router.push(`/loads/edit/${response.data.payload}`)
|
|
} catch (error) {
|
|
toast.add({
|
|
severity: 'error',
|
|
summary: 'Error',
|
|
detail: 'Error adding load',
|
|
life: 3000,
|
|
})
|
|
}
|
|
}
|
|
const deleteLoad = async () => {
|
|
if (!route.params.id) {
|
|
return
|
|
}
|
|
|
|
try {
|
|
await axios.delete(`${import.meta.env.VITE_API}/load/${route.params.id}`)
|
|
|
|
toast.add({
|
|
severity: 'success',
|
|
summary: 'Success',
|
|
detail: 'Load deleted',
|
|
life: 3000,
|
|
})
|
|
|
|
await router.push('/loads/search')
|
|
} catch (error) {
|
|
toast.add({
|
|
severity: 'error',
|
|
summary: 'Error',
|
|
detail: 'Error deleting load',
|
|
life: 3000,
|
|
})
|
|
}
|
|
}
|
|
|
|
const fetchLoad = async () => {
|
|
if (!route.params.id) {
|
|
return
|
|
}
|
|
|
|
loading.value = true
|
|
const response = await axios.get<any, Response<Load>>(`${import.meta.env.VITE_API}/load/${route.params.id}`)
|
|
|
|
if (response.data.payload) {
|
|
load.value.cartridge = response.data.payload.cartridge_id
|
|
load.value.bullet = response.data.payload.bullet.id
|
|
load.value.powder = response.data.payload.powder.id
|
|
load.value.powderGrs = response.data.payload.powder_gr
|
|
load.value.primer = response.data.payload.primer.id
|
|
load.value.col = response.data.payload.col.toString()
|
|
|
|
if (!load.value.col.includes('.')) {
|
|
load.value.col = `${load.value.col}.000`
|
|
} else {
|
|
const parts = load.value.col.split('.')
|
|
if (parts[1].length < 3) {
|
|
load.value.col = `${parts[0]}.${parts[1].padEnd(3, '0')}`
|
|
}
|
|
}
|
|
|
|
calcUrl()
|
|
} else {
|
|
toast.add({
|
|
severity: 'error',
|
|
summary: 'Error',
|
|
detail: 'Load not found',
|
|
life: 3000,
|
|
})
|
|
|
|
await router.push('/loads')
|
|
}
|
|
|
|
loading.value = false
|
|
}
|
|
|
|
|
|
onMounted(() => {
|
|
fetchBullets()
|
|
fetchPrimers()
|
|
fetchPowders()
|
|
fetchCartridges()
|
|
|
|
if (route.params.id) {
|
|
setTimeout(() => {
|
|
fetchLoad()
|
|
}, 100)
|
|
}
|
|
})
|
|
|
|
</script> |