From f3dc770bde94eea3c39409af18e4851d3b171b33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20Engstr=C3=B6m?= Date: Wed, 12 Jun 2024 11:43:48 +0200 Subject: [PATCH 01/19] Create component --- .../[fnurgel=fnurgel]/+page.svelte | 48 +++++++++++-------- .../[fnurgel=fnurgel]/HoldingStatus.svelte | 16 +++++++ 2 files changed, 45 insertions(+), 19 deletions(-) create mode 100644 lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte diff --git a/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/+page.svelte b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/+page.svelte index 035ccaf2d..3862a4308 100644 --- a/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/+page.svelte +++ b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/+page.svelte @@ -10,8 +10,9 @@ import Modal from '$lib/components/Modal.svelte'; import ResourceImage from '$lib/components/ResourceImage.svelte'; import DecoratedData from '$lib/components/DecoratedData.svelte'; - import InstancesList from './InstancesList.svelte'; import SearchResult from '$lib/components/find/SearchResult.svelte'; + import InstancesList from './InstancesList.svelte'; + import HoldingStatus from './HoldingStatus.svelte'; export let data; @@ -213,33 +214,42 @@ : data.t('holdings.libraries')} {/if} - + - - - +
  • + +
    + + {console.log(holdingItem)} + {holdingItem?.heldBy?.name} + + {holdingItem?.heldBy?.sigel ? `(${holdingItem?.heldBy?.sigel})` : ''} + + +
    +
    +
  • {/each} {/if} {:else if data.holdersByType?.[latestHoldingUrl]} {#each data.holdersByType[latestHoldingUrl] as holderItem} - - - - +
  • +
    + + {holderItem?.name} + {holderItem?.sigel ? `(${holderItem?.sigel})` : ''} + + + +
    +
  • {/each} {/if} -
    - {holdingItem?.heldBy?.name} - - {holdingItem?.heldBy?.sigel ? `(${holdingItem?.heldBy?.sigel})` : ''} -
    - {holderItem?.name} - - {holderItem?.sigel ? `(${holderItem?.sigel})` : ''} -
    + diff --git a/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte new file mode 100644 index 000000000..b36e87bd4 --- /dev/null +++ b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte @@ -0,0 +1,16 @@ + + + +
    Hello
    From a3a47e1e835a20c4bbc51fc258ec96a936c7a768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20Engstr=C3=B6m?= Date: Thu, 13 Jun 2024 12:25:50 +0200 Subject: [PATCH 02/19] Get holding status working --- lxl-web/.env.example | 1 + lxl-web/src/lib/types/api.ts | 22 ++++++ lxl-web/src/lib/utils/holdings.ts | 14 ++++ .../[fnurgel=fnurgel]/+page.server.ts | 8 +- .../[fnurgel=fnurgel]/+page.svelte | 20 +++-- .../[fnurgel=fnurgel]/HoldingStatus.svelte | 75 ++++++++++++++++--- .../src/routes/api/holdingstatus/+server.ts | 16 ++++ 7 files changed, 134 insertions(+), 22 deletions(-) create mode 100644 lxl-web/src/routes/api/holdingstatus/+server.ts diff --git a/lxl-web/.env.example b/lxl-web/.env.example index 11dc0ccd1..60fc6b1e4 100644 --- a/lxl-web/.env.example +++ b/lxl-web/.env.example @@ -3,3 +3,4 @@ API_URL= ID_URL= AUXD_SECRET= USE_LOCAL_DISPLAY_JSONLD=false +HOLDING_STATUS_URL= diff --git a/lxl-web/src/lib/types/api.ts b/lxl-web/src/lib/types/api.ts index 055953b7f..c6c1820f1 100644 --- a/lxl-web/src/lib/types/api.ts +++ b/lxl-web/src/lib/types/api.ts @@ -5,3 +5,25 @@ export type ApiError = { status_code: NumericRange<400, 599>; status: string; }; + +export interface HoldingStatus { + item_information: ItemInformation; +} + +interface ItemInformation { + library_code: string; + count: number; + error?: string; + items: HoldingItem[] | []; +} + +interface HoldingItem { + Item_No: string; + UniqueItemId: string; + Location: string; + Call_No: string; + Status: string; + Status_Date_Description: string; + Status_Date: string; + Loan_Policy: string; +} diff --git a/lxl-web/src/lib/utils/holdings.ts b/lxl-web/src/lib/utils/holdings.ts index 36a8796a8..ec024cb56 100644 --- a/lxl-web/src/lib/utils/holdings.ts +++ b/lxl-web/src/lib/utils/holdings.ts @@ -53,6 +53,20 @@ export function getHoldingsByInstanceId(mainEntity) { }, {}); } +export function getBibIdsByInstanceId(mainEntity) { + return mainEntity['@reverse']?.instanceOf?.reduce((acc, instanceOfItem) => { + const id = instanceOfItem['@id']?.replace('#it', ''); + const bibId = instanceOfItem.sameAs?.[0]?.['@id']; + if (!id || !bibId) { + return acc; + } + return { + ...acc, + [id]: bibId + }; + }, {}); +} + export function getHoldingsByType(mainEntity: FramedData) { const holdingsByType = mainEntity['@reverse']?.instanceOf?.reduce((acc, instanceOfItem) => { const type = instanceOfItem['@type']; diff --git a/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/+page.server.ts b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/+page.server.ts index 7d152e1e9..e85953f9c 100644 --- a/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/+page.server.ts +++ b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/+page.server.ts @@ -14,7 +14,11 @@ import addDefaultSearchParams from '$lib/utils/addDefaultSearchParams.js'; import getSortedSearchParams from '$lib/utils/getSortedSearchParams.js'; import { asResult, displayPredicates } from '$lib/utils/search'; import getAtPath from '$lib/utils/getAtPath'; -import { getHoldingsByInstanceId, getHoldingsByType } from '$lib/utils/holdings.js'; +import { + getHoldingsByInstanceId, + getHoldingsByType, + getBibIdsByInstanceId +} from '$lib/utils/holdings.js'; export const load = async ({ params, url, locals, fetch }) => { const displayUtil: DisplayUtil = locals.display; @@ -57,6 +61,7 @@ export const load = async ({ params, url, locals, fetch }) => { const images = getImages(mainEntity, locale).map((i) => toSecure(i, env.AUXD_SECRET)); const holdingsByInstanceId = getHoldingsByInstanceId(mainEntity); + const bibIdsByInstanceId = getBibIdsByInstanceId(mainEntity); const holdingsByType = getHoldingsByType(mainEntity); const holdersByType = Object.entries(holdingsByType).reduce((acc, [type, holdings]) => { const heldBys = holdings.map((holdingItem) => holdingItem.heldBy); @@ -74,6 +79,7 @@ export const load = async ({ params, url, locals, fetch }) => { details: displayUtil.lensAndFormat(mainEntity, LxlLens.PageDetails, locale), instances: sortedInstances, holdingsByInstanceId, + bibIdsByInstanceId, holdersByType, full: overview, images, diff --git a/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/+page.svelte b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/+page.svelte index 3862a4308..3565d1156 100644 --- a/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/+page.svelte +++ b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/+page.svelte @@ -147,6 +147,7 @@ {/if} {#if holdingUrl && selectedHoldingInstance} + {@const selectedBibId = data.bibIdsByInstanceId[selectedHoldingInstance['@id']]} {data.t('holdings.findAtYourNearestLibrary')}
    @@ -219,16 +220,13 @@ {#if data.holdingsByInstanceId[selectedHolding]} {#each data.holdingsByInstanceId[selectedHolding] as holdingItem}
  • - -
    - - {console.log(holdingItem)} - {holdingItem?.heldBy?.name} - - {holdingItem?.heldBy?.sigel ? `(${holdingItem?.heldBy?.sigel})` : ''} - - -
    + + + {holdingItem?.heldBy?.name} + + {holdingItem?.heldBy?.sigel ? `(${holdingItem?.heldBy?.sigel})` : ''} + +
  • {/each} @@ -243,7 +241,7 @@ >{holderItem?.sigel ? `(${holderItem?.sigel})` : ''} - + diff --git a/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte index b36e87bd4..2c9fb5d88 100644 --- a/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte +++ b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte @@ -1,16 +1,71 @@ - -
    Hello
    +
    + +
    + {#if loading} + Laddar.... + {/if} + {#if error} + {error} + {/if} + {#if statusData && statusData.item_information.items.length > 0} + + + {#each statusData.item_information.items as item} + + + + + + + + + + + + + + + + + + + + + + + +
    Placering{item.Location}
    Hylla{item.Call_No}
    Lånepolitik{item.Loan_Policy}
    Status{item.Status}
    Datum{item.Status_Date}
    + {/each} + {/if} +
    +
    diff --git a/lxl-web/src/routes/api/holdingstatus/+server.ts b/lxl-web/src/routes/api/holdingstatus/+server.ts new file mode 100644 index 000000000..7075533a4 --- /dev/null +++ b/lxl-web/src/routes/api/holdingstatus/+server.ts @@ -0,0 +1,16 @@ +import { env } from '$env/dynamic/private'; +import type { HoldingStatus } from '$lib/types/api'; +import { json } from '@sveltejs/kit'; + +export async function GET({ url }) { + const sigel = url.searchParams.get('sigel'); + const bib_id = url.searchParams.get('bib_id'); + const res = await fetch(`${env.HOLDING_STATUS_URL}?output=json&sigel=${sigel}&bib_id=${bib_id}`); + + // TODO error handling, map statuses + // see https://github.com/libris/search4/blob/09180a39077619b2e4f4ba0887f9474679b40489/src/views/ProductPage/Work/Holding.vue#L64 + + const data = (await res.json()) as HoldingStatus; + + return json(data); +} From 9daf75129838356fea593ad33908c6e3e5f979bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20Engstr=C3=B6m?= Date: Wed, 3 Jul 2024 12:27:26 +0200 Subject: [PATCH 03/19] Holding status for multiple instances etc --- lxl-web/package-lock.json | 8 +- lxl-web/package.json | 2 +- lxl-web/src/app.css | 21 +- lxl-web/src/lib/i18n/locales/en.js | 11 +- lxl-web/src/lib/i18n/locales/sv.js | 11 +- lxl-web/src/lib/types/api.ts | 4 +- lxl-web/src/lib/utils/holdings.test.ts | 8 + lxl-web/src/lib/utils/holdings.ts | 17 +- .../[fnurgel=fnurgel]/+page.svelte | 46 +++-- .../[fnurgel=fnurgel]/HoldingStatus.svelte | 188 +++++++++++++----- .../[fnurgel=fnurgel]/InstancesList.svelte | 4 - 11 files changed, 233 insertions(+), 87 deletions(-) create mode 100644 lxl-web/src/lib/utils/holdings.test.ts diff --git a/lxl-web/package-lock.json b/lxl-web/package-lock.json index 191271fa9..4711bd99b 100644 --- a/lxl-web/package-lock.json +++ b/lxl-web/package-lock.json @@ -28,7 +28,7 @@ "globals": "^15.4.0", "husky": "^9.0.11", "jmespath": "^0.16.0", - "lint-staged": "^15.2.6", + "lint-staged": "^15.2.7", "lxljs": "file:../lxljs", "magic-string": "^0.30.7", "mdsvex": "^0.11.2", @@ -3642,9 +3642,9 @@ "dev": true }, "node_modules/lint-staged": { - "version": "15.2.6", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.6.tgz", - "integrity": "sha512-M/3PdijFXT/A5lnbSK3EQNLbIIrkE00JZaD39r7t4kfFOqT1Ly9LgSZSMMtvQ3p2/C8Nyj/ou0vkNHmEwqoB8g==", + "version": "15.2.7", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.7.tgz", + "integrity": "sha512-+FdVbbCZ+yoh7E/RosSdqKJyUM2OEjTciH0TFNkawKgvFp1zbGlEC39RADg+xKBG1R4mhoH2j85myBQZ5wR+lw==", "dev": true, "dependencies": { "chalk": "~5.3.0", diff --git a/lxl-web/package.json b/lxl-web/package.json index e359e0a65..31020cd03 100644 --- a/lxl-web/package.json +++ b/lxl-web/package.json @@ -42,7 +42,7 @@ "globals": "^15.4.0", "husky": "^9.0.11", "jmespath": "^0.16.0", - "lint-staged": "^15.2.6", + "lint-staged": "^15.2.7", "lxljs": "file:../lxljs", "magic-string": "^0.30.7", "mdsvex": "^0.11.2", diff --git a/lxl-web/src/app.css b/lxl-web/src/app.css index 600715b13..75f893f61 100644 --- a/lxl-web/src/app.css +++ b/lxl-web/src/app.css @@ -48,19 +48,25 @@ @apply bg-transparent text-primary text-3-cond-bold; } + summary { + @apply list-none; + } + + summary::-webkit-details-marker { + display: none; + } + .container-fluid { @apply mx-auto px-4 sm:px-8; } /* primary btn */ .button-primary { - @apply relative z-[1] flex h-10 items-center justify-center gap-2 rounded-md px-4 py-2 text-primary-inv - no-underline shadow-btn-primary text-3-cond-bold gradient-primary; + @apply relative z-[1] flex h-10 items-center justify-center gap-2 rounded-md px-4 py-2 text-primary-inv no-underline shadow-btn-primary text-3-cond-bold gradient-primary; @apply hover:text-primary-inv hover:outline hover:outline-4 hover:outline-accent-dark/8; /* enable transition to background gradient */ - @apply before:absolute before:left-0 before:top-0 before:-z-[1] before:h-full before:w-full - before:rounded-[inherit] before:opacity-0 before:shadow-btn-primary before:transition-opacity before:content-[''] before:gradient-secondary; + @apply before:absolute before:left-0 before:top-0 before:-z-[1] before:h-full before:w-full before:rounded-[inherit] before:opacity-0 before:shadow-btn-primary before:transition-opacity before:content-[''] before:gradient-secondary; @apply before:hover:opacity-100 before:focus:opacity-100; } @@ -76,12 +82,9 @@ /* ghost btn */ .button-ghost { - @apply flex h-10 items-center justify-center gap-2 whitespace-nowrap rounded-md border-2 border-primary/16 - px-4 py-2 text-secondary no-underline transition-colors text-3-cond-bold; + @apply flex h-10 items-center justify-center gap-2 whitespace-nowrap rounded-md border-2 border-primary/16 px-4 py-2 text-secondary no-underline transition-colors text-3-cond-bold; - @apply hover:border-accent-dark/32 hover:bg-positive/32 hover:text-positive - hover:outline hover:outline-4 hover:outline-accent-dark/8 - focus:border-accent-dark/32 focus:bg-positive/32 focus:text-positive; + @apply hover:border-accent-dark/32 hover:bg-positive/32 hover:text-positive hover:outline hover:outline-4 hover:outline-accent-dark/8 focus:border-accent-dark/32 focus:bg-positive/32 focus:text-positive; &:not(:hover):not(:focus) svg { @apply text-primary/40; diff --git a/lxl-web/src/lib/i18n/locales/en.js b/lxl-web/src/lib/i18n/locales/en.js index d81ac62d2..d79974a77 100644 --- a/lxl-web/src/lib/i18n/locales/en.js +++ b/lxl-web/src/lib/i18n/locales/en.js @@ -131,6 +131,15 @@ export default { availableAt: 'Available at', library: 'library', libraries: 'libraries', - findAtYourNearestLibrary: 'Find it at your nearest library' + findAtYourNearestLibrary: 'Find it at your nearest library', + location: 'Location', + shelf: 'Shelf', + status: 'Status', + date: 'Date', + loanPolicy: 'Loan policy', + loanStatusNotAvailable: 'Loan status is not available', + loanStatusFailed: 'Failed to get loan status', + available: 'Available', + unavailable: 'Not available' } }; diff --git a/lxl-web/src/lib/i18n/locales/sv.js b/lxl-web/src/lib/i18n/locales/sv.js index 49970c407..2dd730b86 100644 --- a/lxl-web/src/lib/i18n/locales/sv.js +++ b/lxl-web/src/lib/i18n/locales/sv.js @@ -130,6 +130,15 @@ export default { availableAt: 'Finns på', library: 'bibliotek', libraries: 'bibliotek', - findAtYourNearestLibrary: 'Hitta på ditt närmaste bibliotek' + findAtYourNearestLibrary: 'Hitta på ditt närmaste bibliotek', + location: 'Placering', + shelf: 'Hylla', + loanPolicy: 'Lånepolitik', + status: 'Status', + date: 'Datum', + loanStatusNotAvailable: 'Lånestatus är ej tillgänglig', + loanStatusFailed: 'Misslyckades att hämta lånestatus', + available: 'Tillgänglig', + unavailable: 'Utlånad' } }; diff --git a/lxl-web/src/lib/types/api.ts b/lxl-web/src/lib/types/api.ts index c6c1820f1..20b97814e 100644 --- a/lxl-web/src/lib/types/api.ts +++ b/lxl-web/src/lib/types/api.ts @@ -23,7 +23,7 @@ interface HoldingItem { Location: string; Call_No: string; Status: string; - Status_Date_Description: string; - Status_Date: string; + Status_Date_Description?: string; + Status_Date?: string; Loan_Policy: string; } diff --git a/lxl-web/src/lib/utils/holdings.test.ts b/lxl-web/src/lib/utils/holdings.test.ts new file mode 100644 index 000000000..6388a699f --- /dev/null +++ b/lxl-web/src/lib/utils/holdings.test.ts @@ -0,0 +1,8 @@ +import { describe, it } from 'vitest'; + +describe('getBibIdsByInstanceId', () => { + it('extracts id, type and holders for an entity', () => { + // TODO + // expect(); + }); +}); diff --git a/lxl-web/src/lib/utils/holdings.ts b/lxl-web/src/lib/utils/holdings.ts index ec024cb56..0e610871b 100644 --- a/lxl-web/src/lib/utils/holdings.ts +++ b/lxl-web/src/lib/utils/holdings.ts @@ -55,14 +55,23 @@ export function getHoldingsByInstanceId(mainEntity) { export function getBibIdsByInstanceId(mainEntity) { return mainEntity['@reverse']?.instanceOf?.reduce((acc, instanceOfItem) => { - const id = instanceOfItem['@id']?.replace('#it', ''); - const bibId = instanceOfItem.sameAs?.[0]?.['@id']; - if (!id || !bibId) { + const id = relativizeUrl(instanceOfItem['@id'])?.replace('#it', ''); + + // TODO better (is there an existing lxljs util for getting bib-ids?) + const bibId = + relativizeUrl(instanceOfItem.sameAs?.[0]?.['@id'])?.replace('resourcebib', '') || id; + const type = instanceOfItem['@type']; + const holders = instanceOfItem['@reverse']?.itemOf?.map((i) => i?.heldBy?.sigel); + if (!id) { return acc; } return { ...acc, - [id]: bibId + [id]: { + bibId, + '@type': type, + holders + } }; }, {}); } diff --git a/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/+page.svelte b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/+page.svelte index 3565d1156..e64399a8b 100644 --- a/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/+page.svelte +++ b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/+page.svelte @@ -6,6 +6,7 @@ import getPageTitle from '$lib/utils/getPageTitle'; import isFnurgel from '$lib/utils/isFnurgel'; import { getHoldingsLink, handleClickHoldings } from '$lib/utils/holdings'; + import BiChevronRight from '~icons/bi/chevron-right'; import Modal from '$lib/components/Modal.svelte'; import ResourceImage from '$lib/components/ResourceImage.svelte'; @@ -57,7 +58,7 @@ }); function handleCloseHoldings() { - if (!previousURL.searchParams.has('holdings')) { + if (!previousURL?.searchParams.has('holdings')) { history.back(); } else { const newSearchParams = new URLSearchParams([ @@ -147,7 +148,6 @@
    {/if} {#if holdingUrl && selectedHoldingInstance} - {@const selectedBibId = data.bibIdsByInstanceId[selectedHoldingInstance['@id']]} {data.t('holdings.findAtYourNearestLibrary')}
    @@ -217,13 +217,17 @@
      {#if isFnurgel(latestHoldingUrl)} + {#if data.holdingsByInstanceId[selectedHolding]} {#each data.holdingsByInstanceId[selectedHolding] as holdingItem} -
    • - - - {holdingItem?.heldBy?.name} - +
    • + + + + + + {holdingItem?.heldBy?.name} + {holdingItem?.heldBy?.sigel ? `(${holdingItem?.heldBy?.sigel})` : ''} @@ -231,19 +235,22 @@
    • {/each} {/if} + {:else if data.holdersByType?.[latestHoldingUrl]} {#each data.holdersByType[latestHoldingUrl] as holderItem} -
    • -
      - - {holderItem?.name} - + + + + + + + {holderItem?.name} + {holderItem?.sigel ? `(${holderItem?.sigel})` : ''} - - -
      +
    • {/each} {/if} @@ -325,4 +332,13 @@ :global([data-property='_script']) { display: block; } + + .arrow { + transform-origin: center; + @apply rotate-0 transition-transform; + } + + :global(details[open] .arrow) { + @apply rotate-90; + } diff --git a/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte index 2c9fb5d88..f15cd95c7 100644 --- a/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte +++ b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte @@ -1,71 +1,167 @@
      -
      +
      {#if loading} - Laddar.... +

      {$page.data.t('search.loading')}

      {/if} {#if error} - {error} +
      + +
      {/if} - {#if statusData && statusData.item_information.items.length > 0} - - - {#each statusData.item_information.items as item} - - - - - - - - - - - - - - - - - - - - - - - -
      Placering{item.Location}
      Hylla{item.Call_No}
      Lånepolitik{item.Loan_Policy}
      Status{item.Status}
      Datum{item.Status_Date}
      + {#if statusData && statusData.length > 0} + {#each statusData as instance} + {#if instance && instance.item_information} +
      + {#if instance.item_information.error || instance.item_information.count === 0} + + {/if} + {#each instance.item_information.items as item} + {@const indicator = getIndicator(item.Status)} + + + + + + + + + + + + + + + + + + + {#if item.Status_Date} + + + + + {/if} + +
      {$page.data.t('holdings.location')}{item.Location}
      {$page.data.t('holdings.shelf')}{item.Call_No}
      {$page.data.t('holdings.loanPolicy')}{item.Loan_Policy}
      {$page.data.t('holdings.status')} + {#if indicator} + + {$page.data.t(`holdings.${indicator}`)} + {:else} + {item.Status} + {/if} +
      {$page.data.t('holdings.date')}{item?.Status_Date_Description} {item.Status_Date}
      + {/each} +
      + {/if} {/each} {/if}
      + + diff --git a/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/InstancesList.svelte b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/InstancesList.svelte index f2320a874..674cfdd2f 100644 --- a/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/InstancesList.svelte +++ b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/InstancesList.svelte @@ -172,8 +172,4 @@ transform-origin: center; @apply rotate-0 transition-transform; } - - details summary::-webkit-details-marker { - display: none; - } From 7b1b8891e7ba0580be7603da04e1c361b0b3ea5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20Engstr=C3=B6m?= Date: Wed, 3 Jul 2024 15:03:39 +0200 Subject: [PATCH 04/19] Refactor a bit --- .../[fnurgel=fnurgel]/+page.svelte | 43 +---- .../[fnurgel=fnurgel]/HoldingStatus.svelte | 147 ++++++++++-------- 2 files changed, 89 insertions(+), 101 deletions(-) diff --git a/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/+page.svelte b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/+page.svelte index e64399a8b..209e947f3 100644 --- a/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/+page.svelte +++ b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/+page.svelte @@ -6,7 +6,6 @@ import getPageTitle from '$lib/utils/getPageTitle'; import isFnurgel from '$lib/utils/isFnurgel'; import { getHoldingsLink, handleClickHoldings } from '$lib/utils/holdings'; - import BiChevronRight from '~icons/bi/chevron-right'; import Modal from '$lib/components/Modal.svelte'; import ResourceImage from '$lib/components/ResourceImage.svelte'; @@ -220,38 +219,17 @@ {#if data.holdingsByInstanceId[selectedHolding]} {#each data.holdingsByInstanceId[selectedHolding] as holdingItem} -
    • - - - - - - {holdingItem?.heldBy?.name} - - {holdingItem?.heldBy?.sigel ? `(${holdingItem?.heldBy?.sigel})` : ''} - - - -
    • + + {holdingItem?.heldBy?.name} + {/each} {/if} {:else if data.holdersByType?.[latestHoldingUrl]} {#each data.holdersByType[latestHoldingUrl] as holderItem} -
    • - - - - - - - {holderItem?.name} - {holderItem?.sigel ? `(${holderItem?.sigel})` : ''} - - -
    • + + {holderItem?.name} + {/each} {/if}
    @@ -332,13 +310,4 @@ :global([data-property='_script']) { display: block; } - - .arrow { - transform-origin: center; - @apply rotate-0 transition-transform; - } - - :global(details[open] .arrow) { - @apply rotate-90; - } diff --git a/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte index f15cd95c7..f52a09301 100644 --- a/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte +++ b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte @@ -3,6 +3,7 @@ import { page } from '$app/stores'; export let sigel: string; export let holdingUrl: string; + import BiChevronRight from '~icons/bi/chevron-right'; let loading = false; let statusData: HoldingStatus[] | void[] | undefined; @@ -73,78 +74,95 @@ } -
    - -
    - {#if loading} -

    {$page.data.t('search.loading')}

    - {/if} - {#if error} -
    - -
    - {/if} - {#if statusData && statusData.length > 0} - {#each statusData as instance} - {#if instance && instance.item_information} -
    - {#if instance.item_information.error || instance.item_information.count === 0} - - {/if} - {#each instance.item_information.items as item} - {@const indicator = getIndicator(item.Status)} - - - - - - - - - - - - - - - - - - - {#if item.Status_Date} +
  • +
    + + + + + + {sigel ? `(${sigel})` : ''} + +
    + {#if loading} +

    {$page.data.t('search.loading')}

    + {/if} + {#if error} +
    + +
    + {/if} + {#if statusData && statusData.length > 0} + {#each statusData as instance} + {#if instance && instance.item_information} +
    + {#if instance.item_information.error || instance.item_information.count === 0} + + {/if} + {#each instance.item_information.items as item} + {@const indicator = getIndicator(item.Status)} +
  • {$page.data.t('holdings.location')}{item.Location}
    {$page.data.t('holdings.shelf')}{item.Call_No}
    {$page.data.t('holdings.loanPolicy')}{item.Loan_Policy}
    {$page.data.t('holdings.status')} - {#if indicator} - - {$page.data.t(`holdings.${indicator}`)} - {:else} - {item.Status} - {/if} -
    + - - + + - {/if} - -
    {$page.data.t('holdings.date')}{item?.Status_Date_Description} {item.Status_Date}{$page.data.t('holdings.location')}{item.Location}
    - {/each} -
    - {/if} - {/each} - {/if} -
    -
    - - diff --git a/lxl-web/src/lib/i18n/locales/en.js b/lxl-web/src/lib/i18n/locales/en.js index d79974a77..867ccd9ac 100644 --- a/lxl-web/src/lib/i18n/locales/en.js +++ b/lxl-web/src/lib/i18n/locales/en.js @@ -140,6 +140,7 @@ export default { loanStatusNotAvailable: 'Loan status is not available', loanStatusFailed: 'Failed to get loan status', available: 'Available', - unavailable: 'Not available' + unavailable: 'Not available', + map: 'map' } }; diff --git a/lxl-web/src/lib/i18n/locales/sv.js b/lxl-web/src/lib/i18n/locales/sv.js index 2dd730b86..cc656abfe 100644 --- a/lxl-web/src/lib/i18n/locales/sv.js +++ b/lxl-web/src/lib/i18n/locales/sv.js @@ -139,6 +139,7 @@ export default { loanStatusNotAvailable: 'Lånestatus är ej tillgänglig', loanStatusFailed: 'Misslyckades att hämta lånestatus', available: 'Tillgänglig', - unavailable: 'Utlånad' + unavailable: 'Utlånad', + map: 'karta' } }; diff --git a/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte index f38e01d6a..470cba503 100644 --- a/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte +++ b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte @@ -124,7 +124,15 @@ {$page.data.t('holdings.shelf')} - {item.Call_No} + + {item.Call_No} + + {#if item.Map && (item.Map.startsWith('http://') || item.Map.startsWith('https://'))} + ( + {$page.data.t('holdings.map')}) + {/if} + {$page.data.t('holdings.loanPolicy')} From 8a76d1b4154f892cd15be4432ffb68bc9e6c57e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20Engstr=C3=B6m?= Date: Wed, 7 Aug 2024 14:42:39 +0200 Subject: [PATCH 15/19] Improve error messaging --- lxl-web/src/lib/i18n/locales/en.js | 2 +- lxl-web/src/lib/i18n/locales/sv.js | 4 +-- .../[fnurgel=fnurgel]/HoldingStatus.svelte | 30 +++++++++++++++---- 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/lxl-web/src/lib/i18n/locales/en.js b/lxl-web/src/lib/i18n/locales/en.js index 867ccd9ac..ec723de48 100644 --- a/lxl-web/src/lib/i18n/locales/en.js +++ b/lxl-web/src/lib/i18n/locales/en.js @@ -137,7 +137,7 @@ export default { status: 'Status', date: 'Date', loanPolicy: 'Loan policy', - loanStatusNotAvailable: 'Loan status is not available', + libraryUnvaliable: 'Loan status is not available for this library', loanStatusFailed: 'Failed to get loan status', available: 'Available', unavailable: 'Not available', diff --git a/lxl-web/src/lib/i18n/locales/sv.js b/lxl-web/src/lib/i18n/locales/sv.js index cc656abfe..2e9697153 100644 --- a/lxl-web/src/lib/i18n/locales/sv.js +++ b/lxl-web/src/lib/i18n/locales/sv.js @@ -136,8 +136,8 @@ export default { loanPolicy: 'Lånepolitik', status: 'Status', date: 'Datum', - loanStatusNotAvailable: 'Lånestatus är ej tillgänglig', - loanStatusFailed: 'Misslyckades att hämta lånestatus', + libraryUnvaliable: 'Lånestatus är inte tillgänglig för detta bibliotek', + loanStatusFailed: 'Lånestatus kunde inte hämtas', available: 'Tillgänglig', unavailable: 'Utlånad', map: 'karta' diff --git a/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte index 470cba503..4048d3b43 100644 --- a/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte +++ b/lxl-web/src/routes/(app)/[[lang=lang]]/[fnurgel=fnurgel]/HoldingStatus.svelte @@ -44,7 +44,7 @@ if (response && response.ok) { return response.json(); } else { - error = $page.data.t('holdings.loanStatusFailed'); + error = `${$page.data.t('holdings.loanStatusFailed')}: ${response?.status} ${response?.statusText}`; } }) ); @@ -53,7 +53,7 @@ async function getHoldingStatus() { if (!sigel || !bibIds || bibIds.length < 1) { - error = $page.data.t('holdings.loanStatusNotAvailable'); + error = $page.data.t('holdings.loanStatusFailed'); } else if (!statusData) { loading = true; @@ -87,6 +87,16 @@ } return false; } + + function urlNotDefinedError(err: string | undefined) { + if ( + err && + err === `URL till lånestatus för ${sigel} ej definierad i LIBRIS biblioteksdatabas!` + ) { + return true; + } + return false; + }
  • @@ -110,11 +120,16 @@ {#if statusData && statusData.length > 0} {#each statusData as instance} {#if instance && instance.item_information} + {@const items = instance.item_information}
    - {#if instance.item_information.error || instance.item_information.count === 0} - + {#if items.error || items.count === 0} + {#if urlNotDefinedError(items.error)} +

    {$page.data.t('holdings.libraryUnvaliable')}

    + {:else} + + {/if} {/if} - {#each instance.item_information.items as item} + {#each items.items as item} {@const indicator = getIndicator(item.Status)} @@ -182,6 +197,11 @@ &:has(p.error) { @apply bg-negative; } + + /* don't repeat the library-unavailable message for every instance */ + &:has(p.library-unavailable):not(:first-of-type) { + @apply hidden; + } } table th { From 037241301b5254d8618ce3697c9736774e4abbdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20Engstr=C3=B6m?= Date: Wed, 7 Aug 2024 15:03:24 +0200 Subject: [PATCH 16/19] Simplify api GET method --- lxl-web/src/routes/api/holdingstatus/+server.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lxl-web/src/routes/api/holdingstatus/+server.ts b/lxl-web/src/routes/api/holdingstatus/+server.ts index 90bfb8632..d63e34b91 100644 --- a/lxl-web/src/routes/api/holdingstatus/+server.ts +++ b/lxl-web/src/routes/api/holdingstatus/+server.ts @@ -1,10 +1,6 @@ -import { json } from '@sveltejs/kit'; import { env } from '$env/dynamic/private'; -import type { HoldingStatus } from '$lib/types/api'; export async function GET({ url }) { const res = await fetch(`${env.HOLDING_STATUS_URL}?${url.searchParams.toString()}&output=json`); - const data = (await res.json()) as HoldingStatus; - - return json(data); + return res; } From 585a6d6a538363d2a64e44316359a1925d59e5d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20Engstr=C3=B6m?= Date: Thu, 8 Aug 2024 11:16:58 +0200 Subject: [PATCH 17/19] Replace ISO-encoded chars --- .../src/routes/api/holdingstatus/+server.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lxl-web/src/routes/api/holdingstatus/+server.ts b/lxl-web/src/routes/api/holdingstatus/+server.ts index d63e34b91..cb1f0b18a 100644 --- a/lxl-web/src/routes/api/holdingstatus/+server.ts +++ b/lxl-web/src/routes/api/holdingstatus/+server.ts @@ -1,6 +1,26 @@ +import { json } from '@sveltejs/kit'; import { env } from '$env/dynamic/private'; export async function GET({ url }) { const res = await fetch(`${env.HOLDING_STATUS_URL}?${url.searchParams.toString()}&output=json`); + if (res.ok) { + const data = await res.json(); + const cleanData = replaceChars(data); + return json(cleanData); + } return res; } + +// Replace ISO-8859-1 encoded chars returned by some libraries +function replaceChars(data: Response) { + const d = JSON.stringify(data) + .replaceAll('Ã¥', 'å') + .replaceAll('Ã…', 'Å') + .replaceAll('ä', 'ä') + .replaceAll('Ã\\?', 'Ö') + .replaceAll('Ä', 'Ä') + .replaceAll('ö', 'ö') + .replaceAll('é', 'é'); + + return JSON.parse(d); +} From 4898c9e363c74a8098c517b15d1d617b8ec646d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesper=20Engstr=C3=B6m?= Date: Thu, 8 Aug 2024 11:20:44 +0200 Subject: [PATCH 18/19] Translation tweak --- lxl-web/src/lib/i18n/locales/sv.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxl-web/src/lib/i18n/locales/sv.js b/lxl-web/src/lib/i18n/locales/sv.js index 2e9697153..9d5fdf935 100644 --- a/lxl-web/src/lib/i18n/locales/sv.js +++ b/lxl-web/src/lib/i18n/locales/sv.js @@ -139,7 +139,7 @@ export default { libraryUnvaliable: 'Lånestatus är inte tillgänglig för detta bibliotek', loanStatusFailed: 'Lånestatus kunde inte hämtas', available: 'Tillgänglig', - unavailable: 'Utlånad', + unavailable: 'Ej tillgänglig', map: 'karta' } }; From ccb175b5947c74d83bc696954560e2b9e219d27c Mon Sep 17 00:00:00 2001 From: Johan Bisse Mattsson Date: Wed, 14 Aug 2024 11:07:21 +0200 Subject: [PATCH 19/19] Fix breaking test --- lxl-web/tests/resource.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lxl-web/tests/resource.spec.ts b/lxl-web/tests/resource.spec.ts index bd1ef74e0..d4371307a 100644 --- a/lxl-web/tests/resource.spec.ts +++ b/lxl-web/tests/resource.spec.ts @@ -15,7 +15,7 @@ test('initially opened holdings modals are closable', async ({ page }) => { await expect(page.getByTestId('modal')).toBeHidden(); await page.getByTestId('holding-link').first().click(); await expect(page.getByTestId('modal')).toBeVisible(); - await expect(page).toHaveURL('/h08ndxddfg5v2pjf?holdings=Electronic'); + await expect(page).toHaveURL(new RegExp(/h08ndxddfg5v2pjf\?holdings=\w+/)); }); test('decorated data in holdings modal is not duplicated while closing modal', async ({ page }) => {