fix/feat: primary photo auto set and auto-set primary photo (#651)

* fix error when item doesn't have photo attachment

* automatically detect image types and set primary photo if first

* remove charts.js from libs

Former-commit-id: 321a83b634
This commit is contained in:
Hayden 2023-12-01 11:57:29 -06:00 committed by GitHub
parent bd1a241be1
commit 81e76d9dd4
13 changed files with 41 additions and 424 deletions

View file

@ -1,94 +0,0 @@
<template>
<DoughnutChart
:chart-options="chartOptions"
:chart-data="chartData"
:chart-id="chartId"
:dataset-id-key="datasetIdKey"
:css-classes="cssClasses"
:styles="styles"
:width="width"
:height="height"
/>
</template>
<script lang="ts">
import { Doughnut as DoughnutChart } from "vue-chartjs";
import { Chart as ChartJS, Title, Tooltip, Legend, CategoryScale, LinearScale, ArcElement } from "chart.js";
import { TChartData } from "vue-chartjs/dist/types";
ChartJS.register(Title, Tooltip, Legend, CategoryScale, LinearScale, ArcElement);
export default defineComponent({
name: "BarChart",
components: {
DoughnutChart,
},
props: {
chartId: {
type: String,
default: "bar-chart",
},
datasetIdKey: {
type: String,
default: "label",
},
width: {
type: Number,
default: 400,
},
height: {
type: Number,
default: 400,
},
cssClasses: {
default: "",
type: String,
},
styles: {
type: Object,
default: () => {
return {};
},
},
chartData: {
type: Object as () => TChartData<"doughnut", number[], unknown>,
default: () => {
return {
labels: ["Red", "Blue", "Yellow"],
datasets: [
{
label: "My First Dataset",
data: [300, 50, 100],
backgroundColor: ["rgb(255, 99, 132)", "rgb(54, 162, 235)", "rgb(255, 205, 86)"],
hoverOffset: 4,
},
],
};
},
},
},
data() {
return {
chartOptions: {
responsive: false,
// Legend on the left
plugins: {
legend: {
position: "bottom",
},
// Display percentage
// tooltip: {
// callbacks: {
// label: context => {
// const label = context.dataset?.label || "";
// const value = context.parsed.y;
// return `${label}: ${value}%`;
// },
// },
// },
},
},
};
},
});
</script>

View file

@ -1,113 +0,0 @@
<template>
<div ref="el" class="min-h-full flex flex-col">
{{ styles }}
<LineChart :chart-options="options" :chart-data="chartData" :styles="styles" />
</div>
</template>
<script lang="ts">
import { Line as LineChart } from "vue-chartjs";
import {
Chart as ChartJS,
PointElement,
Title,
Tooltip,
Legend,
CategoryScale,
LinearScale,
LineElement,
} from "chart.js";
import { TChartData } from "vue-chartjs/dist/types";
ChartJS.register(Title, Tooltip, Legend, CategoryScale, LinearScale, PointElement, LineElement);
export default defineComponent({
name: "BarChart",
components: {
LineChart,
},
props: {
chartId: {
type: String,
default: "bar-chart",
},
datasetIdKey: {
type: String,
default: "label",
},
cssClasses: {
default: "",
type: String,
},
chartData: {
type: Object as () => TChartData<"line", number[], unknown>,
default: () => {
return {
labels: ["January", "February", "March"],
datasets: [{ data: [40, 20, 12] }],
};
},
},
},
setup() {
const el = ref<HTMLElement | null>(null);
const calcHeight = ref(0);
const calcWidth = ref(0);
function resize() {
calcHeight.value = el.value?.offsetHeight || 0;
calcWidth.value = el.value?.offsetWidth || 0;
}
onMounted(() => {
resize();
window.addEventListener("resize", resize);
});
onUnmounted(() => {
window.removeEventListener("resize", resize);
});
const styles = computed(() => {
return {
height: `${calcHeight.value}px`,
width: `${calcWidth.value}px`,
position: "relative",
};
});
return {
el,
parentHeight: calcHeight,
styles,
};
},
data() {
return {
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
display: false,
},
y: {
display: true,
},
},
elements: {
line: {
borderWidth: 5,
},
point: {
radius: 4,
},
},
},
};
},
});
</script>
<style></style>

View file

@ -28,10 +28,12 @@ export type ItemsQuery = {
};
export class AttachmentsAPI extends BaseAPI {
add(id: string, file: File | Blob, filename: string, type: AttachmentTypes) {
add(id: string, file: File | Blob, filename: string, type: AttachmentTypes | null = null) {
const formData = new FormData();
formData.append("file", file);
formData.append("type", type);
if (type) {
formData.append("type", type);
}
formData.append("name", filename);
return this.http.post<FormData, ItemOut>({

View file

@ -44,7 +44,6 @@
"@vueuse/nuxt": "^10.0.0",
"@vueuse/router": "^10.0.0",
"autoprefixer": "^10.4.8",
"chart.js": "^4.0.1",
"daisyui": "^2.24.0",
"dompurify": "^3.0.0",
"h3": "^1.7.1",
@ -54,7 +53,6 @@
"postcss": "^8.4.16",
"tailwindcss": "^3.1.8",
"vue": "^3.3.1",
"vue-chartjs": "^4.1.2",
"vue-router": "4"
}
}

View file

@ -1,71 +0,0 @@
import { TChartData } from "vue-chartjs/dist/types";
import { UserClient } from "~~/lib/api/user";
export function purchasePriceOverTimeChart(api: UserClient) {
const { data: timeseries } = useAsyncData(async () => {
const { data } = await api.stats.totalPriceOverTime();
return data;
});
const primary = useCssVar("--p");
return computed(() => {
if (!timeseries.value) {
return {
labels: ["Purchase Price"],
datasets: [
{
label: "Purchase Price",
data: [],
backgroundColor: primary.value,
borderColor: primary.value,
},
],
} as TChartData<"line", number[], unknown>;
}
let start = timeseries.value?.valueAtStart;
return {
labels: timeseries?.value.entries.map(t => new Date(t.date).toDateString()) || [],
datasets: [
{
label: "Purchase Price",
data:
timeseries.value?.entries.map(t => {
start += t.value;
return start;
}) || [],
backgroundColor: primary.value,
borderColor: primary.value,
},
],
} as TChartData<"line", number[], unknown>;
});
}
export function inventoryByLocationChart(api: UserClient) {
const { data: donutSeries } = useAsyncData(async () => {
const { data } = await api.stats.locations();
return data;
});
const primary = useCssVar("--p");
const secondary = useCssVar("--s");
const neutral = useCssVar("--n");
return computed(() => {
return {
labels: donutSeries.value?.map(l => l.name) || [],
datasets: [
{
label: "Value",
data: donutSeries.value?.map(l => l.total) || [],
backgroundColor: [primary.value, secondary.value, neutral.value],
borderColor: [primary.value, secondary.value, neutral.value],
hoverOffset: 4,
},
],
};
});
}

View file

@ -22,55 +22,11 @@
const itemTable = itemsTable(api);
const stats = statCardData(api);
// const purchasePriceOverTime = purchasePriceOverTimeChart(api);
// const inventoryByLocation = inventoryByLocationChart(api);
// const refDonutEl = ref<HTMLDivElement>();
// const donutElWidth = computed(() => {
// return refDonutEl.value?.clientWidth || 0;
// });
</script>
<template>
<div>
<BaseContainer class="flex flex-col gap-12 pb-16">
<!-- <section class="grid grid-cols-6 gap-6">
<article class="col-span-4">
<Subtitle> Inventory Value Over Time </Subtitle>
<BaseCard>
<div class="p-10 h-[300px]">
<ClientOnly>
<ChartLine :chart-data="purchasePriceOverTime" />
</ClientOnly>
</div>
</BaseCard>
</article>
<article class="col-span-2">
<Subtitle>
Inventory By
<span class="btn-group">
<button class="btn btn-xs btn-active text-no-transform">Locations</button>
<button class="btn btn-xs text-no-transform">Labels</button>
</span>
</Subtitle>
<BaseCard class="h-[300px]">
<div ref="refDonutEl" class="grid place-content-center h-full">
<ClientOnly>
<ChartDonut
chart-id="donut"
:width="donutElWidth - 50"
:height="265"
:chart-data="inventoryByLocation"
/>
</ClientOnly>
</div>
</BaseCard>
</article>
</section> -->
<section>
<Subtitle> Quick Statistics </Subtitle>
<div class="grid grid-cols-2 gap-2 md:grid-cols-4 md:gap-6">

View file

@ -253,7 +253,7 @@
return;
}
uploadAttachment([first], AttachmentTypes.Attachment);
uploadAttachment([first], null);
}
const dropPhoto = (files: File[] | null) => uploadAttachment(files, AttachmentTypes.Photo);
@ -262,7 +262,7 @@
const dropManual = (files: File[] | null) => uploadAttachment(files, AttachmentTypes.Manual);
const dropReceipt = (files: File[] | null) => uploadAttachment(files, AttachmentTypes.Receipt);
async function uploadAttachment(files: File[] | null, type: AttachmentTypes) {
async function uploadAttachment(files: File[] | null, type: AttachmentTypes | null) {
if (!files || files.length === 0) {
return;
}

View file

@ -35,9 +35,6 @@ dependencies:
autoprefixer:
specifier: ^10.4.8
version: 10.4.13(postcss@8.4.19)
chart.js:
specifier: ^4.0.1
version: 4.0.1
daisyui:
specifier: ^2.24.0
version: 2.43.0(autoprefixer@10.4.13)(postcss@8.4.19)
@ -65,9 +62,6 @@ dependencies:
vue:
specifier: ^3.3.1
version: 3.3.4
vue-chartjs:
specifier: ^4.1.2
version: 4.1.2(chart.js@4.0.1)(vue@3.3.4)
vue-router:
specifier: '4'
version: 4.1.6(vue@3.3.4)
@ -3770,11 +3764,6 @@ packages:
resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==}
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
/chart.js@4.0.1:
resolution: {integrity: sha512-5/8/9eBivwBZK81mKvmIwTb2Pmw4D/5h1RK9fBWZLLZ8mCJ+kfYNmV9rMrGoa5Hgy2/wVDBMLSUDudul2/9ihA==}
engines: {pnpm: ^7.0.0}
dev: false
/check-error@1.0.2:
resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==}
dev: true
@ -9107,16 +9096,6 @@ packages:
dependencies:
ufo: 1.1.2
/vue-chartjs@4.1.2(chart.js@4.0.1)(vue@3.3.4):
resolution: {integrity: sha512-QSggYjeFv/L4jFSBQpX8NzrAvX0B+Ha6nDgxkTG8tEXxYOOTwKI4phRLe+B4f+REnkmg7hgPY24R0cixZJyXBg==}
peerDependencies:
chart.js: ^3.7.0
vue: ^3.0.0-0 || ^2.6.0
dependencies:
chart.js: 4.0.1
vue: 3.3.4
dev: false
/vue-demi@0.13.11(vue@3.3.4):
resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==}
engines: {node: '>=12'}