Skip to content

Commit

Permalink
feat(proof add): new page to add a proof (#639)
Browse files Browse the repository at this point in the history
  • Loading branch information
raphodn authored Jun 20, 2024
1 parent 8355f56 commit f9086a0
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 7 deletions.
6 changes: 5 additions & 1 deletion src/components/ProofInputRow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<span class="d-sm-none">{{ $t('AddPriceSingle.PriceDetails.Gallery') }}</span>
<span class="d-none d-sm-inline-flex">{{ $t('AddPriceSingle.PriceDetails.SelectFromGallery') }}</span>
</v-btn>
<v-btn class="mb-2" size="small" prepend-icon="mdi-receipt-text-clock" @click="showUserRecentProofsDialog">
<v-btn v-if="!hideRecentProofOption" class="mb-2" size="small" prepend-icon="mdi-receipt-text-clock" @click="showUserRecentProofsDialog">
<span class="d-sm-none">{{ $t('AddPriceSingle.PriceDetails.RecentProof') }}</span>
<span class="d-none d-sm-inline-flex">{{ $t('AddPriceSingle.PriceDetails.SelectRecentProof') }}</span>
</v-btn>
Expand Down Expand Up @@ -104,6 +104,10 @@ export default {
type: Object,
default: () => ({ proof_id: null, date: utils.currentDate() })
},
hideRecentProofOption: {
type: Boolean,
default: false
},
},
data() {
return {
Expand Down
10 changes: 10 additions & 0 deletions src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,14 @@
"Language": "Languages",
"Locations": "Locations",
"Date": "Date",
"Details": "Details",
"Delete": "Delete",
"Discount": "Discount",
"Edit": "Edit",
"Filter": "Filter",
"FilterProductWithPriceCountHide": "Hide products with prices",
"FilterPriceMoreThan30DaysHide": "Hide prices older than 30 days",
"Image": "Image",
"Order": "Order",
"OrderProductUniqueScansDESC": "Number of scans",
"OrderProductPriceCountDESC": "Number of prices",
Expand All @@ -127,6 +129,7 @@
"OrderPriceDateDESC": "Price date",
"SignInOFFAccount": "Sign in with your {url} account",
"Source": "Source",
"Type": "Type",
"Yes": "Yes",
"No": "No",
"Food": "Food",
Expand Down Expand Up @@ -158,6 +161,7 @@
"Home": {
"AddPrice": "Add a price",
"LatestPrices": "Latest prices",
"ProofAdd": "Add a proof",
"SearchProduct": "Search for a product",
"SettingsUpdated": "Settings updated!",
"TodayPriceStat": "No prices added today | {todayPriceNumber} new prices added today | {todayPriceNumber} new prices added today!",
Expand Down Expand Up @@ -278,6 +282,9 @@
"RECEIPT": "Receipt",
"GDPR_REQUEST": "GDPR request"
},
"ProofCreate": {
"Success": "Proof created!"
},
"ProofDelete": {
"Confirmation": "Are you sure you want to delete this proof?",
"Delete": "Delete",
Expand Down Expand Up @@ -307,6 +314,9 @@
"LatestPrices": {
"Title": "Latest prices"
},
"ProofAddSingle": {
"Title": "Add a proof"
},
"Search": {
"Title": "Search"
},
Expand Down
3 changes: 2 additions & 1 deletion src/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const routes = [
{ path: '/settings', name: 'settings', component: () => import('./views/UserSettings.vue'), meta: { title: 'Settings', requiresAuth: true } },
{ path: '/search', name: 'search', component: () => import('./views/Search.vue'), meta: { title: 'Search', icon: 'mdi-magnify', drawerMenu: true }},
{ path: '/prices', name: 'prices', component: () => import('./views/PriceList.vue'), meta: { title: 'LatestPrices', icon: 'mdi-tag-multiple-outline', drawerMenu: true }},
{ path: '/prices/add', name: 'add-price', component: () => import('./views/AddPriceHome.vue'), meta: { title: 'AddPrice', icon: 'mdi-plus', drawerMenu: true, color: 'primary', requiresAuth: true }},
{ path: '/prices/add', name: 'price-add', component: () => import('./views/AddPriceHome.vue'), meta: { title: 'AddPrice', icon: 'mdi-plus', drawerMenu: true, color: 'primary', requiresAuth: true }},
{ path: '/prices/add/single', name: 'add-price-single', component: () => import('./views/AddPriceSingle.vue'), meta: { title: 'Add a single price (price tag)', requiresAuth: true }},
{ path: '/prices/add/multiple/price-tag', name: 'add-price-multiple-price-tag', component: () => import('./views/AddPriceMultiple.vue'), meta: { title: 'Add multiple prices (price tags)', requiresAuth: true }},
{ path: '/prices/add/multiple/receipt', name: 'add-price-multiple-receipt', component: () => import('./views/AddPriceMultiple.vue'), meta: { title: 'Add multiple prices (receipt)', requiresAuth: true }},
Expand All @@ -22,6 +22,7 @@ const routes = [
{ path: '/locations/:id', name: 'location-detail', component: () => import('./views/LocationDetail.vue'), meta: { title: 'Location detail' }},
{ path: '/brands/:id', name: 'brand-detail', component: () => import('./views/BrandDetail.vue'), meta: { title: 'Brand detail' }},
{ path: '/categories/:id', name: 'category-detail', component: () => import('./views/CategoryDetail.vue'), meta: { title: 'Category detail' }},
{ path: '/proofs/add/single', name: 'proof-add', component: () => import('./views/ProofAddSingle.vue'), meta: { title: 'ProofAddSingle', icon: 'mdi-plus', color: 'primary', requiresAuth: true }},
{ path: '/proofs/:id', name: 'proof-detail', component: () => import('./views/ProofDetail.vue'), meta: { title: 'Proof detail', requiresAuth: true }},
{ path: '/users', name: 'users', component: () => import('./views/UserList.vue'), meta: { title: 'TopContributors', icon: 'mdi-account-star-outline', drawerMenu: true }},
{ path: '/users/:username', name: 'user-detail', component: () => import('./views/UserDetail.vue'), meta: { title: 'User detail' }},
Expand Down
13 changes: 12 additions & 1 deletion src/services/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ function buildURLParams(params = {}) {
return new URLSearchParams({...DEFAULT_PARAMS, ...params})
}

function filterParamsWithAllowedKeys(params, allowedKeys) {
const filteredParams = {}
for (const key in params) {
if (allowedKeys.includes(key)) {
filteredParams[key] = params[key]
}
}
return filteredParams
}


export default {
signIn(username, password) {
Expand Down Expand Up @@ -84,14 +94,15 @@ export default {
},

updateProof(proofId, params = {}) {
const PROOF_UPDATABLE_FIELDS = ['type', 'date', 'currency']
const store = useAppStore()
const url = `${import.meta.env.VITE_OPEN_PRICES_API_URL}/proofs/${proofId}?${buildURLParams()}`
return fetch(url, {
method: 'PATCH',
headers: Object.assign({}, DEFAULT_HEADERS, {
'Authorization': `Bearer ${store.user.token}`,
}),
body: JSON.stringify(params),
body: JSON.stringify(filterParamsWithAllowedKeys(params, PROOF_UPDATABLE_FIELDS)),
})
.then((response) => response.json())
},
Expand Down
4 changes: 2 additions & 2 deletions src/views/AddPriceMultiple.vue
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,10 @@ export default {
origins_tags: '',
labels_tags: [],
price: null,
price_per: null, // see initPriceSingleForm
price_per: null, // see PriceInputRow
price_is_discounted: false,
price_without_discount: null,
currency: null, // see initPriceMultipleForm
currency: null, // see PriceInputRow
},
categoryPricePerList: [
{key: 'KILOGRAM', value: this.$t('AddPriceSingle.CategoryPricePer.PerKg'), icon: 'mdi-weight-kilogram'},
Expand Down
16 changes: 14 additions & 2 deletions src/views/Home.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@
</v-col>
</v-row>

<v-snackbar
v-model="proofSingleSuccessMessage"
color="success"
:timeout="2000"
>
{{ $t('ProofCreate.Success') }}
</v-snackbar>
<v-snackbar
v-model="settingsSuccessMessage"
color="success"
Expand All @@ -81,9 +88,11 @@ export default {
data() {
return {
APP_NAME: constants.APP_NAME,
settingsSuccessMessage: false,
todayPriceCount: null,
loading: false
loading: false,
// success messages
proofSingleSuccessMessage: false,
settingsSuccessMessage: false,
}
},
computed: {
Expand All @@ -93,6 +102,9 @@ export default {
},
},
mounted() {
if (this.$route.query.proofSingleSuccess === 'true') {
this.proofSingleSuccessMessage = true
}
if (this.$route.query.settingsSuccess === 'true') {
this.settingsSuccessMessage = true
}
Expand Down
170 changes: 170 additions & 0 deletions src/views/ProofAddSingle.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
<template>
<h1 class="text-h5 mb-1">
{{ $t('Home.ProofAdd') }}
</h1>

<v-form @submit.prevent="createProof">
<v-row>
<!-- Step 1: proof type -->
<v-col cols="12" md="6" lg="4">
<v-card
:title="$t('Common.Type')"
:prepend-icon="(addProofSingleForm.type === 'RECEIPT') ? 'mdi-receipt-text-outline' : 'mdi-library-shelves'"
height="100%"
:style="proofTypeFormFilled ? 'border: 1px solid #4CAF50' : 'border: 1px solid transparent'"
>
<template v-if="proofTypeFormFilled" #append>
<v-icon icon="mdi-checkbox-marked-circle" color="success" />
</template>
<v-divider />
<v-card-text>
<v-item-group v-model="addProofSingleForm.type" class="d-inline" mandatory>
<v-item v-for="pt in proofTypeList" :key="pt.key" v-slot="{ isSelected, toggle }" :value="pt.key">
<v-chip class="mr-1" :style="isSelected ? 'border: 1px solid #9E9E9E' : 'border: 1px solid transparent'" @click="toggle">
<v-icon start :icon="isSelected ? 'mdi-checkbox-marked-circle' : 'mdi-circle-outline'" />
{{ pt.value }}
</v-chip>
</v-item>
</v-item-group>
</v-card-text>
<v-overlay v-model="disableProofTypeForm" scrim="#E8F5E9" contained persistent />
</v-card>
</v-col>

<!-- Step 2: proof image -->
<v-col cols="12" md="6" lg="4">
<v-card
:title="$t('Common.Image')"
prepend-icon="mdi-image"
height="100%"
:style="proofImageFormFilled ? 'border: 1px solid #4CAF50' : 'border: 1px solid transparent'"
>
<template v-if="proofImageFormFilled" #append>
<v-icon icon="mdi-checkbox-marked-circle" color="success" />
</template>
<v-divider />
<v-card-text>
<ProofInputRow :proofType="addProofSingleForm.type" :proofForm="addProofSingleForm" :hideRecentProofOption="true" />
</v-card-text>
<v-overlay v-model="disableProofImageForm" scrim="#E8F5E9" contained persistent />
</v-card>
</v-col>

<!-- Step 3: date & currency -->
<v-col cols="12" md="6" lg="4">
<v-card
:title="$t('Common.Details')"
prepend-icon="mdi-cog-outline"
height="100%"
:style="proofDetailsFormSuccess ? 'border: 1px solid #4CAF50' : 'border: 1px solid transparent'"
>
<template v-if="proofDetailsFormSuccess" #append>
<v-icon icon="mdi-checkbox-marked-circle" color="success" />
</template>
<v-divider />
<v-card-text>
<h3 class="mb-1">
{{ $t('AddPriceSingle.WhereWhen.Date') }}
</h3>
<v-row>
<v-col cols="12" sm="6">
<v-text-field
v-model="addProofSingleForm.date"
:label="$t('AddPriceSingle.WhereWhen.DateLabel')"
type="date"
/>
</v-col>
</v-row>
</v-card-text>
<v-overlay v-model="disableProofDetailsForm" scrim="#E8F5E9" contained persistent />
</v-card>
</v-col>
</v-row>

<v-row>
<v-col>
<v-btn
type="submit"
:color="formFilled ? 'success' : ''"
:loading="loading"
:disabled="!formFilled"
>
{{ $t('AddPriceSingle.Create') }}
</v-btn>
</v-col>
</v-row>
</v-form>
</template>

<script>
import { defineAsyncComponent } from 'vue'
import { mapStores } from 'pinia'
import { useAppStore } from '../store'
import api from '../services/api'
import utils from '../utils.js'
export default {
components: {
ProofInputRow: defineAsyncComponent(() => import('../components/ProofInputRow.vue')),
},
data() {
return {
proofTypeList: [
{key: 'PRICE_TAG', value: this.$t('AddPriceHome.MultipleProductMode.Title'), icon: 'mdi-library-shelves'},
{key: 'RECEIPT', value: this.$t('AddPriceHome.ReceiptMode.Title'), icon: 'mdi-receipt-text-outline'}
],
addProofSingleForm: {
type: 'PRICE_TAG',
proof_id: null,
date: utils.currentDate(),
},
loading: false,
}
},
computed: {
...mapStores(useAppStore),
proofTypeFormFilled() {
let keys = ['type']
return Object.keys(this.addProofSingleForm).filter(k => keys.includes(k)).every(k => !!this.addProofSingleForm[k])
},
proofImageFormFilled() {
let keys = ['proof_id']
return Object.keys(this.addProofSingleForm).filter(k => keys.includes(k)).every(k => !!this.addProofSingleForm[k])
},
proofDetailsFormFilled() {
let keys = ['date']
return Object.keys(this.addProofSingleForm).filter(k => keys.includes(k)).every(k => !!this.addProofSingleForm[k])
},
formFilled() {
return this.proofTypeFormFilled && this.proofImageFormFilled && this.proofDetailsFormFilled
},
proofDetailsFormSuccess() {
return this.proofDetailsFormFilled && this.proofImageFormFilled
},
disableProofTypeForm() {
return this.proofImageFormFilled
},
disableProofImageForm() {
return !this.proofTypeFormFilled
},
disableProofDetailsForm() {
return !this.proofTypeFormFilled || !this.proofImageFormFilled
}
},
methods: {
createProof() {
this.loading = true
api.updateProof(this.addProofSingleForm.proof_id, this.addProofSingleForm)
.then(() => {
this.$router.push({ path: '/', query: { proofSingleSuccess: 'true' } })
})
.catch(err => {
this.$store.app.showError(err)
})
.finally(() => {
this.loading = false
})
}
}
}
</script>

0 comments on commit f9086a0

Please sign in to comment.