Commit a4444e7b authored by David Björkheim's avatar David Björkheim
Browse files

DEICH-5558: Add other publication section

parent 88a5ab5c
......@@ -4,16 +4,18 @@ import { translations as TRANSLATIONS } from "../../../constants/translations";
import { Flex, Block, Text } from "@digibib/deichman-ui";
const Availability = ({ languages, items }) => {
const Availability = ({ languages = [], items }) => {
const availableItems = items.reduce((sum, item) => sum + item.available, 0);
const totalItems = items.reduce((sum, item) => sum + item.total, 0);
return (
<Flex>
<Block right={2}>
<Text bold>{languages.map(l => TRANSLATIONS[l]).join(", ")}</Text>
</Block>
<Text gray>
{languages.length > 0 && (
<Block right={2}>
<Text bold>{languages.map(l => TRANSLATIONS[l]).join(", ")}</Text>
</Block>
)}
<Text>
{availableItems} av {totalItems} ledig
</Text>
</Flex>
......
import React from "react";
import PropTypes from "prop-types";
import { translations as TRANSLATIONS } from "../../../constants/translations";
import { Text } from "@digibib/deichman-ui";
const Format = ({ formats }) => {
if (formats.length === 0) {
return null;
}
return <Text gray>{formats.map(f => TRANSLATIONS[f]).join(",")}</Text>;
};
Format.propTypes = {
formats: PropTypes.array.isRequired
};
export default Format;
import Format from "./Format";
export default Format;
......@@ -3,12 +3,8 @@ import PropTypes from "prop-types";
import Link from "next/link";
import Availability from "./Availability";
import ReserveButton from "./ReserveButton";
import Format from "./Format";
import PublicationTitle from "../PublicationTitle";
import WorkMeta from "../WorkMeta";
import PublicationImage from "../PublicationImage";
import WorkContributors from "../WorkContributors";
import WorkEmployeeEdit from "../WorkEmployeeEdit";
import { translations as TRANSLATIONS } from "../../constants/translations";
......@@ -16,43 +12,52 @@ import { Block, Text, Flex } from "@digibib/deichman-ui";
import "./styles.css";
import PublicationDetailsButton from "../PublicationDetailsButton/PublicationDetailsButton";
import {
AUDIO_BOOK,
FILM,
GAME,
MUSIC_RECORDING
} from "../../constants/mediaTypes";
import { matchMedia } from "../../utilities/media";
import { READER } from "../../constants/roles";
const metaSelectors = {
default: p => TRANSLATIONS[p.mediaType],
[AUDIO_BOOK]: p => p.formats.map(f => TRANSLATIONS[f]).join(""),
[MUSIC_RECORDING]: () => "",
[FILM]: p => p.formats.map(f => TRANSLATIONS[f]).join(""),
[GAME]: p => p.platforms.map(f => TRANSLATIONS[f]).join("")
};
const Publicationcard = ({
data,
flags,
parent,
export default function PublicationCard({
publication,
copies,
isFavourited,
onFavourite,
onReserve,
ReserveComponent,
userCategory
}) => {
}) {
const {
id,
formats = [],
mainTitle,
recordId,
items = [],
languages = [],
flags,
mediaType,
sellingPoint,
publicationYear = "",
imageObj,
numHolds = ""
} = data;
const getRecordId = (recId, userCat) => {
if (userCat === "ANS") {
return (
<a
href={`https://intra.deichman.no/cgi-bin/koha/catalogue/detail.pl?biblionumber=${recId}`}
>
{recId}
</a>
);
}
return recId;
};
publishers = []
} = publication;
const copyInfo = copies[recordId] || {};
const meta = generateMeta(mediaType, publication);
const reader = publication.contributors
.filter(c => c.role === READER)
.map(c => c.agent.name)
.join(" ");
const publisher = publishers.map(p => p.name).join(" ");
const languages =
(mediaType !== MUSIC_RECORDING && publication.languages) || [];
return (
<article className="publication-card">
<div className="publication-card__image">
......@@ -62,7 +67,7 @@ const Publicationcard = ({
sellingPoint={sellingPoint}
mediaType={TRANSLATIONS[mediaType]}
images={imageObj}
publication={data}
publication={publication}
onFavourite={onFavourite}
size="thumb"
isBookmarked={isFavourited}
......@@ -71,82 +76,87 @@ const Publicationcard = ({
{userCategory === "ANS" && <WorkEmployeeEdit id={id} />}
</div>
<div className="publication-card__contents">
<div className="publication-card__meta">
<WorkMeta data={{ mediaType: TRANSLATIONS[mediaType], work: {} }} />
</div>
{meta && (
<div className="publication-card__meta">
<div className="work-meta">
<p className="overline">
<span>{meta}</span>
</p>
</div>
</div>
)}
<Link href="/utgivelse/[publicationId]" as={`/utgivelse/${id}`}>
<a
className="publication-card__title"
data-cy="publication-card-title"
>
<h4>
<PublicationTitle data={data} />
<PublicationTitle data={publication} />
</h4>
</a>
</Link>
<Block top={1}>
<Format formats={formats} />
</Block>
{publicationYear && (
<div
className="publication-card__year"
data-cy="publication-card-year"
>
Utgitt {publicationYear}
<span className="publication-card__term">Utgitt</span>{" "}
{publicationYear}
</div>
)}
<Block top={2}>
<WorkContributors
onlyMainAuthor
contributors={parent.groupedContributors}
/>
</Block>
{mediaType === AUDIO_BOOK && reader && (
<Block top={2}>
<span className="publication-card__term">Innleser</span>
<span>{reader}</span>
</Block>
)}
{mediaType === MUSIC_RECORDING && publisher && (
<Block top={2}>{publisher}</Block>
)}
<Block top={4}>
<Availability languages={languages} items={items} />
<Availability languages={languages} items={copyInfo.items} />
</Block>
<div className="publication-card__record-id">
<Text gray>{getRecordId(recordId, userCategory)}</Text>
<Text>{getRecordId(recordId, userCategory)}</Text>
</div>
<Block top={4}>
<Flex align="center">
<ReserveButton
items={items}
formats={formats}
onReserve={onReserve}
numHolds={numHolds}
/>
<ReserveComponent publication={publication} copies={copies} />
</Flex>
</Block>
<Block top={2}>
<PublicationDetailsButton publication={data} className="link">
<PublicationDetailsButton publication={publication} className="link">
Mer info og hylleplassering
</PublicationDetailsButton>
</Block>
</div>
</article>
);
};
Publicationcard.defaultProps = {
flags: []
};
}
Publicationcard.propTypes = {
data: PropTypes.object.isRequired,
parent: PropTypes.object.isRequired,
flags: PropTypes.array,
PublicationCard.propTypes = {
publication: PropTypes.object.isRequired,
isFavourited: PropTypes.bool.isRequired,
onReserve: PropTypes.func.isRequired,
ReserveComponent: PropTypes.func.isRequired,
onFavourite: PropTypes.func.isRequired,
tjenesteKatalogUrl: PropTypes.string.isRequired,
userCategory: PropTypes.string.isRequired
};
export default Publicationcard;
function generateMeta(mediaType, publication) {
const metaGenerator = matchMedia(metaSelectors, mediaType);
return metaGenerator(publication);
}
function getRecordId(recId, userCat) {
if (userCat === "ANS") {
return (
<a
href={`https://intra.deichman.no/cgi-bin/koha/catalogue/detail.pl?biblionumber=${recId}`}
>
{recId}
</a>
);
}
return recId;
}
......@@ -38,9 +38,14 @@
}
}
&__term {
/* color: var(--col-gray-6); */
&::after {
content: " ";
}
}
&__year {
margin-top: var(--spacing-1);
color: var(--col-gray-6);
font-size: 14px;
}
......@@ -81,10 +86,6 @@
margin-top: var(--spacing-1);
}
&__num-holds {
color: var(--col-gray-6);
}
&__add-recommendation {
display: block;
margin-top: var(--spacing-2);
......
......@@ -14,30 +14,34 @@ import { translations as TRANSLATIONS } from "../../constants/translations";
import "./styles.css";
export default function PublicationList({
parent,
onFavourite,
favourites,
onReserve,
ReserveComponent,
tjenesteKatalogUrl,
userCategory,
query,
publications,
copies,
publicationId
}) {
const dispItems = filterAndSortItems({ query, publications, publicationId });
const dispPublications = filterAndSortItems({
query,
publications,
publicationId
});
return (
<section className="publication-list">
<ul className="publication-list__items">
{dispItems.map(item => (
<li className="publication-list__item" key={item.id}>
{dispPublications.map(publication => (
<li className="publication-list__item" key={publication.id}>
<PublicationCard
data={item}
parent={parent}
flags={item.flags}
isFavourited={favourites.includes(item.recordId)}
onFavourite={() => onFavourite(item.recordId)}
onReserve={() => onReserve(item.recordId)}
publication={publication}
copies={copies}
isFavourited={favourites.includes(publication.recordId)}
onFavourite={() => onFavourite(publication.recordId)}
ReserveComponent={ReserveComponent}
onReserve={() => onReserve(publication.recordId)}
tjenesteKatalogUrl={tjenesteKatalogUrl}
userCategory={userCategory}
/>
......@@ -52,18 +56,17 @@ PublicationList.defaultProps = {
items: [],
query: {},
favourites: [],
parent: {},
limited: false,
userCategory: ""
};
PublicationList.propTypes = {
publications: PropTypes.arrayOf(PropTypes.object),
copies: PropTypes.object,
query: PropTypes.object,
parent: PropTypes.object,
favourites: PropTypes.array,
onFavourite: PropTypes.func.isRequired,
onReserve: PropTypes.func.isRequired,
ReserveComponent: PropTypes.func.isRequired,
userCategory: PropTypes.string,
tjenesteKatalogUrl: PropTypes.string.isRequired,
publicationId: PropTypes.string.isRequired
......
import React, { useEffect, useState } from "react";
import { Accordion, Block, Loader } from "@digibib/deichman-ui";
import { Accordion, Block, Button, Loader } from "@digibib/deichman-ui";
import { useDispatch, useSelector } from "react-redux";
import ActiveFilters from "../ActiveFilters";
import PublicationList from "./PublicationList";
import { useRouter } from "next/router";
import ReservationContainer from "../ReservationsContainer/ReservationContainer";
import LoginButton from "../LoginButton/LoginButton";
export default function PublicationsAccordionContainer({ publication }) {
const [isLoading, setLoading] = useState(true);
......@@ -19,24 +21,15 @@ export default function PublicationsAccordionContainer({ publication }) {
const { query } = useRouter();
const getCopies = id => {
return (
copies[publication.id] &&
copies[publication.id][id] &&
copies[publication.id][id].items
);
return (copies[publication.id] && copies[publication.id][id]) || {};
};
const decoratedPublications = publication.work.publications
.map(p => ({
...p,
items: getCopies(p.recordId)
}))
.filter(
p => p.mediaType === publication.mediaType && p.id !== publication.id
);
const publications = publication.work.publications.filter(
p => p.mediaType === publication.mediaType && p.id !== publication.id
);
return (
<Block top={8}>
<Accordion text="Andre utgivelser" closedByDefault large showDividers>
<Accordion text="Andre utgaver" closedByDefault large showDividers>
<>
<LoadDataTrigger
publication={publication}
......@@ -49,24 +42,23 @@ export default function PublicationsAccordionContainer({ publication }) {
<Loader />
</Block>
)}
{!isLoading && decoratedPublications.length > 0 && (
{!isLoading && publications.length > 0 && (
<Block top={5}>
<ActiveFilters type="publication" />
<PublicationList
query={query}
publications={decoratedPublications}
publications={publications}
copies={copies[publication.id] || {}}
favourites={favourites}
parent={publication.work}
onReserve={Function.prototype} //TODO fix this post DEICH-5554 merge
ReserveComponent={ReserveComponent}
onFavourite={onFavourite}
onShowDetails={Function.prototype} //TODO fix this post DEICH-5554 merge
userCategory={userCategory}
tjenesteKatalogUrl={tjenesteKatalogUrl}
publicationId={publication.id}
/>
</Block>
)}
{!isLoading && decoratedPublications.length === 0 && (
{!isLoading && publications.length === 0 && (
<Block top={5}> Her fantes det ingen utgaver å vise</Block>
)}
</>
......@@ -111,3 +103,41 @@ function LoadDataTrigger(props) {
);
return null;
}
function ReserveComponent({ publication, copies }) {
const [reservationPublications, setReservationPublications] = useState([]);
const isLoggedIn = useSelector(state => state.auth.isLoggedIn);
const { numHolds } = copies[publication.recordId] || {};
return (
<>
{!isLoggedIn && (
<div>
<LoginButton primary>Logg inn og bestill</LoginButton>
</div>
)}
{isLoggedIn && (
<>
<Button
primary
onClick={() => setReservationPublications([publication])}
>
Bestill
</Button>
{numHolds && (
<span className="publication-card__num-holds">
{numHolds} venteliste
</span>
)}
{!!reservationPublications.length && (
<ReservationContainer
publications={reservationPublications}
copies={copies}
onClose={() => setReservationPublications([])}
/>
)}
</>
)}
</>
);
}
......@@ -158,7 +158,7 @@ class ReservationContainer extends React.Component {
render() {
const { borrowerId, reservationCompleted, selectedBranchId } = this.state;
const {
work,
work = {},
branches,
copies,
publications,
......@@ -210,7 +210,7 @@ class ReservationContainer extends React.Component {
tabIndex="-1"
>
<Fragment>
{!publication && (
{!publication && publications.length > 1 && (
<ReservationSelector
onClick={this.setPublication}
copies={copies}
......
......@@ -22,6 +22,18 @@ export default function ReservationSelector({
const copyInfo = copies[pub.recordId] || {};
return { ...pub, ...copyInfo };
});
const ReserveComponent = ({ publication, copies }) => {
const { formats, recordId } = publication;
const copiesInfo = copies[publication.recordId] || {};
return (
<ReserveButton
items={copiesInfo.items}
formats={formats}
numHolds={copiesInfo.numHolds}
onClick={() => onClick(recordId)}
/>
);
};
return (
<Block top={4}>
<h2>Velg en utgave</h2>
......@@ -30,6 +42,7 @@ export default function ReservationSelector({
favourites={favourites}
parent={currentPublication.work}
onReserve={onClick}
ReserveComponent={ReserveComponent}
onFavourite={onFavourite}
userCategory={userCategory}
tjenesteKatalogUrl={tjenesteKatalogUrl}
......
import React from "react";
import PropTypes from "prop-types";
import Link from "next/link";
import { fullName } from "../../utilities/name";
import { translations as TRANSLATIONS } from "../../constants/translations";
import "./styles.css";
const WorkContributors = ({ contributors, onlyMainAuthor, query }) => {
const authors = contributors.find(
c => c.role === "http://data.deichman.no/role#author"
);
const mainAuthor =
authors && authors.items ? authors.items.find(a => a.mainEntry) : null;
if (!mainAuthor && onlyMainAuthor) {
return null;
}
return (
<div className="work-contributors">
{onlyMainAuthor && (
<span className="work-contributors__item" key={mainAuthor.agent.uri}>
<Link
href="/sok/[searchQuery]"
as={`/sok/aktør: "${mainAuthor.agent.name}"`}
>
<a>{fullName(mainAuthor.agent)}</a>
</Link>
</span>
)}
{!onlyMainAuthor &&
contributors.map(group => (
<div key={group.role}>
<span className="work-contributors__title">
{TRANSLATIONS[group.role]}{" "}
</span>
{group.items.map(item => (
<span className="work-contributors__item" key={item.agent.uri}>
<Link
href="/sok/[searchQuery]"
as={`/sok/aktør: "${item.agent.name}"`}
>
<a>{fullName(item.agent)}</a>
</Link>