From 99e9b557838b8fa866bc9f261ef55a52a9680096 Mon Sep 17 00:00:00 2001 From: VaiTon Date: Tue, 5 Sep 2023 20:43:00 +0200 Subject: [PATCH] Create error page for product not found --- src/app.d.ts | 8 +- src/lib/api/product.ts | 45 ++++- src/lib/const.ts | 2 - src/lib/utils.ts | 4 +- src/routes/+error.svelte | 38 ++++ src/routes/products/[barcode]/+page.svelte | 188 +++++++++--------- src/routes/products/[barcode]/+page.ts | 30 ++- .../products/[barcode]/edit/+page.svelte | 11 +- src/routes/products/[barcode]/edit/+page.ts | 10 +- 9 files changed, 213 insertions(+), 123 deletions(-) create mode 100644 src/routes/+error.svelte diff --git a/src/app.d.ts b/src/app.d.ts index f59b884..2cf38f8 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -2,7 +2,13 @@ // for information about these interfaces declare global { namespace App { - // interface Error {} + interface Error { + errors?: { + field: { id: string; value: string }; + impact: { lc_name: string; name: string; id: string }; + message: { lc_name: string; name: string; id: string }; + }[]; + } // interface Locals {} // interface PageData {} // interface Platform {} diff --git a/src/lib/api/product.ts b/src/lib/api/product.ts index 7a746eb..0ff2310 100644 --- a/src/lib/api/product.ts +++ b/src/lib/api/product.ts @@ -5,19 +5,47 @@ import type { Nutriments } from './nutriments'; import { preferences } from '$lib/settings'; import { wrapFetch } from '$lib/utils'; -export type ProductState = { - status: 'success' | 'success_with_warnings' | 'success_with_errors' | 'failure'; +export type ProductStateBase = { result: { id: string; name: string; lc_name: string; }; - errors: object[]; - warnings: object[]; +}; + +export type ProductStateFailure = ProductStateBase & { + status: 'failure'; + errors: { + field: { id: string; value: string }; + impact: { lc_name: string; name: string; id: string }; + message: { lc_name: string; name: string; id: string }; + }[]; +}; +export type ProductStateFound = ProductStateBase & { product: T; }; +export type ProductStateSuccess = ProductStateFound & { + status: 'success'; +}; + +export type ProductStateSuccessWithWarnings = ProductStateFound & { + status: 'success_with_warnings'; + warnings: object[]; +}; + +export type ProductStateSuccessWithErrors = ProductStateFound & { + status: 'success_with_errors'; + errors: object[]; +}; + +export type ProductState = + | ProductStateSuccess + | ProductStateSuccessWithWarnings + | ProductStateSuccessWithErrors + | ProductStateFailure; + export type ProductSearch = { count: number; page: number; @@ -99,7 +127,7 @@ export async function getProduct( export async function getProductReducedForCard( barcode: string, fetch: (url: string) => Promise -): Promise { +): Promise> { const url = PRODUCT_URL(barcode) + '?' + @@ -110,13 +138,13 @@ export async function getProductReducedForCard( const res = await wrapFetch(fetch)(url); const productState = (await res.json()) as ProductState; - return productState.product; + return productState; } export async function getProductName( fetch: (url: string) => Promise, barcode: string -): Promise> { +): Promise | null> { const url = PRODUCT_URL(barcode) + '?' + @@ -128,7 +156,8 @@ export async function getProductName( const res = await wrapFetch(fetch)(url); const productState = (await res.json()) as ProductState>; - return productState.product; + if (productState.status !== 'success') return null; + else return productState.product; } export async function addOrEditProductV2( diff --git a/src/lib/const.ts b/src/lib/const.ts index aa05adf..436fb60 100644 --- a/src/lib/const.ts +++ b/src/lib/const.ts @@ -8,5 +8,3 @@ export const PRODUCT_URL = (barcode: string) => `${API_HOST}/api/v3/product/${ba export const SEARCH_URL = `${API_HOST}/api/v2/search`; export const USER_AGENT = `Open Food Facts Explorer (${import.meta.env.PACKAGE_VERSION})`; - -console.debug('User agent', USER_AGENT); diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 30c710c..22c442c 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,6 +1,4 @@ export const wrapFetch = (fetch: (url: string, options?: RequestInit) => Promise) => (url: string, options?: RequestInit) => - fetch(url, { - ...options - }); + fetch(url, { ...options }); diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte new file mode 100644 index 0000000..97cc1c0 --- /dev/null +++ b/src/routes/+error.svelte @@ -0,0 +1,38 @@ + + +
+
+

+ {$page.error?.message} +

+ + {#if $page.error?.errors != null} + {@const errors = $page.error.errors} + +

+ {errors.length} error{#if errors.length > 1}s{/if} occurred: +

+ +
    + {#each errors as error} +
  • +

    + + {error['impact']['lc_name']}: + + {error['message']['lc_name']} + (id: {error['message']['id']}) +

    +

    + {#if error.field} + Caused by field `{error.field.id}` with value `{error.field.value}` + {/if} +

    +
  • + {/each} +
+ {/if} +
+
diff --git a/src/routes/products/[barcode]/+page.svelte b/src/routes/products/[barcode]/+page.svelte index 7a60630..92d60e3 100644 --- a/src/routes/products/[barcode]/+page.svelte +++ b/src/routes/products/[barcode]/+page.svelte @@ -15,108 +15,110 @@ $: lang = $preferences.lang; -{#if data.state.status === 'success'} - -
-

- {product.product_name ?? product.code} -

+ +
+

+ {product.product_name ?? product.code} +

- - See on OpenFoodFacts - - - Edit - -
+ + See on OpenFoodFacts + + + Edit + +
-
-
-
- Quantity: - {product.quantity} +
+
+
+ Quantity: + {product.quantity} - Brands: - - {#await data.taxo.brands} - Loading... - {:then brands} - {#each product.brands_tags as tag, i} - {#if i > 0}, {/if} - {brands[tag] != null ? getOrDefault(brands[tag].name, lang) : tag} - {/each} - {/await} - + Brands: + + {#await data.taxo.brands} + Loading... + {:then brands} + {#each product.brands_tags as tag, i} + {#if i > 0}, + {/if} + {brands[tag] != null ? getOrDefault(brands[tag].name, lang) : tag} + {/each} + {/await} + - Categories: - - {#await data.taxo.categories} - Loading... - {:then categories} - {#each product.categories_tags as tag, i} - {#if i > 0}, {/if} - {categories[tag] != null ? getOrDefault(categories[tag].name, lang) : tag} - {/each} - {/await} - + Categories: + + {#await data.taxo.categories} + Loading... + {:then categories} + {#each product.categories_tags as tag, i} + {#if i > 0}, + {/if} + {categories[tag] != null ? getOrDefault(categories[tag].name, lang) : tag} + {/each} + {/await} + - Stores: - - {#await data.taxo.stores} - Loading... - {:then stores} - {#each product.stores_tags as tag, i} - {#if i > 0}, {/if} - {stores[tag] != null ? getOrDefault(stores[tag].name, lang) : tag} - {/each} - {/await} - + Stores: + + {#await data.taxo.stores} + Loading... + {:then stores} + {#each product.stores_tags as tag, i} + {#if i > 0}, + {/if} + {stores[tag] != null ? getOrDefault(stores[tag].name, lang) : tag} + {/each} + {/await} + - Labels: - - {#await data.taxo.labels} - Loading... - {:then labels} - {#each product.labels_tags as tag, i} - {#if i > 0}, {/if} - {labels[tag] != null ? getOrDefault(labels[tag].name, lang) : tag} - {/each} - {/await} - -
-
-
- {product.product_name} + Labels: + + {#await data.taxo.labels} + Loading... + {:then labels} + {#each product.labels_tags as tag, i} + {#if i > 0}, + {/if} + {labels[tag] != null ? getOrDefault(labels[tag].name, lang) : tag} + {/each} + {/await} +
- - -
- - - +
+ {product.product_name} +
+ - +
+ + + +
- -

- Folksonomy Engine (alpha) -

+ + + +

+ Folksonomy Engine (alpha) +

- it.k)} barcode={product.code} /> -
-{/if} + it.k)} barcode={product.code} /> +
diff --git a/src/routes/products/[barcode]/+page.ts b/src/routes/products/[barcode]/+page.ts index 4fbcb59..2be2dbf 100644 --- a/src/routes/products/[barcode]/+page.ts +++ b/src/routes/products/[barcode]/+page.ts @@ -1,12 +1,26 @@ import type { PageLoad } from './$types'; -import { getKeys, getProduct, getProductFolksonomy, getTaxo } from '$lib/api'; +import { + type Brand, + type Label, + getKeys, + getProduct, + getProductFolksonomy, + getTaxo, + type Store, + type Category +} from '$lib/api'; +import { error } from '@sveltejs/kit'; -export const load = (async ({ params, fetch }) => { - const state = getProduct(params.barcode, fetch); - const categories = getTaxo('categories', fetch); - const labels = getTaxo('labels', fetch); - const stores = getTaxo('stores', fetch); - const brands = getTaxo('brands', fetch); +export const load: PageLoad = async ({ params, fetch }) => { + const state = await getProduct(params.barcode, fetch); + if (state.status === 'failure') { + throw error(404, { message: 'Failure to load product', errors: state.errors }); + } + + const categories = getTaxo('categories', fetch); + const labels = getTaxo