Last active
February 9, 2023 21:18
-
-
Save symbioquine/5593a2000dbd4762c090449ad89fb231 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script setup> | |
import { inject, ref, watch } from 'vue'; | |
const props = defineProps({ | |
asset: { | |
type: Object, | |
required: false, | |
}, | |
}); | |
import { formatRFC3339, uuidv4 } from "assetlink-plugin-api"; | |
const assetLink = inject('assetLink'); | |
const showQuickObservationMenu = ref(false); | |
const showQuickPhotoObservationDialog = ref(undefined); | |
const capturedPhotos = ref([]); | |
const observationNotes = ref(""); | |
const photoCaptureModel = ref(null); | |
const carouselPosition = ref("capture-photo"); | |
watch(photoCaptureModel, async () => { | |
const file = photoCaptureModel.value; | |
if (!file) { | |
return; | |
} | |
const fileToArrayBuffer = data => new Promise((resolve, reject) => { | |
const reader = new FileReader(); | |
reader.onload = () => resolve(reader.result); | |
reader.onerror = error => reject(error); | |
reader.readAsArrayBuffer(data); | |
}); | |
const fileName = file.name; | |
const fileType = file.type; | |
const fileData = await fileToArrayBuffer(file); | |
const fileStringData = new Uint8Array(fileData).reduce((data, byte) => { | |
return data + String.fromCharCode(byte); | |
}, ''); | |
const fileDataUrl = `data:${fileType};base64, ` + btoa(fileStringData); | |
const photoId = uuidv4() | |
capturedPhotos.value.push({ | |
id: photoId, | |
fileName, | |
fileDataUrl, | |
}); | |
carouselPosition.value = photoId; | |
}); | |
const resetQuickPhotoObservationForm = () => { | |
capturedPhotos.value = []; | |
observationNotes.value = ""; | |
photoCaptureModel.value = null; | |
carouselPosition.value = "capture-photo"; | |
}; | |
const saveQuickPhotoObservation = async (photos, notes) => { | |
const timestamp = formatRFC3339(new Date()) | |
const observationLog = { | |
type: 'log--observation', | |
attributes: { | |
name: `Quick photo observation at ${timestamp}`, | |
timestamp, | |
notes: { value: notes }, | |
status: "done", | |
}, | |
relationships: { | |
image: { | |
data: photos.map(({ id, fileName, fileDataUrl }) => | |
({ | |
type: 'file--file', | |
id, | |
'$upload': { | |
fileName, | |
fileDataUrl, | |
} | |
}) | |
), | |
}, | |
}, | |
}; | |
if (props.asset) { | |
observationLog.relationships.asset = { | |
data: [{ type: props.asset.type, id: props.asset.id }], | |
}; | |
} | |
await assetLink.entitySource.update( | |
(t) => t.addRecord(observationLog), | |
{label: observationLog.attributes.name}); | |
resetQuickPhotoObservationForm(); | |
} | |
</script> | |
<template> | |
<q-dialog ref="dialogRef"> | |
<q-card class="q-dialog-plugin q-gutter-md" style="width: 500px; max-width: 80vw;"> | |
<h4 class="q-mb-sm">Quick Photo Observation</h4> | |
<q-card-section> | |
<div class="q-pa-md"> | |
<q-carousel | |
swipeable | |
animated | |
navigation | |
navigation-icon="mdi-radiobox-marked" | |
control-type="flat" | |
control-color="orange" | |
:arrows="false" | |
height="200px" | |
v-model="carouselPosition" | |
> | |
<q-carousel-slide | |
v-for="(capturedPhoto, capturedPhotoIdx) in capturedPhotos" | |
:key="capturedPhoto.id" | |
:name="capturedPhoto.id"> | |
<q-img | |
:src="capturedPhoto.fileDataUrl" | |
class="rounded-borders full-height" | |
fit="contain" | |
></q-img> | |
</q-carousel-slide> | |
<q-carousel-slide name="capture-photo"> | |
<photo-input class="q-pb-xl" v-model="photoCaptureModel"></photo-input> | |
</q-carousel-slide> | |
</q-carousel> | |
</div> | |
</q-card-section> | |
<q-card-section> | |
<q-input | |
label="Notes" | |
v-model="observationNotes" | |
filled | |
autogrow | |
stack-label | |
/> | |
</q-card-section> | |
<q-card-actions align="right"> | |
<q-btn flat label="Cancel" color="primary" @click="() => resetQuickPhotoObservationForm()" v-close-popup /> | |
<q-btn flat label="Save" color="primary" @click="() => saveQuickPhotoObservation(capturedPhotos, observationNotes)" v-close-popup /> | |
</q-card-actions> | |
</q-card> | |
</q-dialog> | |
</template> | |
<script> | |
import { h } from 'vue'; | |
import { QBtn, QFabAction } from 'quasar'; | |
export default { | |
onLoad(handle, assetLink) { | |
handle.defineSlot('net.symbioquine.farmos_asset_link.slots.v0.quick_asset_photo_observation', actionsSlot => { | |
actionsSlot.type('asset-action'); | |
const doActionWorkflow = async (asset) => { | |
await assetLink.ui.dialog.custom(handle.thisPlugin, { asset }); | |
}; | |
actionsSlot.showIf(({ asset }) => asset.attributes.status !== 'archived'); | |
actionsSlot.component(({ asset }) => | |
h(QBtn, { block: true, color: 'secondary', onClick: () => doActionWorkflow(asset), 'no-caps': true }, () => "Make Quick Photo Observation" )); | |
}); | |
handle.defineSlot('net.symbioquine.farmos_asset_link.slots.v0.quick_record_menu_photo_observation', actionsSlot => { | |
actionsSlot.type('quick-menu-action'); | |
const doActionWorkflow = async () => { | |
await assetLink.ui.dialog.custom(handle.thisPlugin, {}); | |
}; | |
actionsSlot.component(({ asset }) => | |
h(QFabAction, { icon: "mdi-camera", color: 'green', onClick: () => doActionWorkflow() }, () => "" )); | |
}); | |
} | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
quick_photo_observation_example_alink_plugin.webm