Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 38 additions & 14 deletions lego-webapp/pages/lending/+Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import { HeartHandshake } from 'lucide-react';
import { useEffect, useState, useRef } from 'react';
import { Helmet } from 'react-helmet-async';
import { fetchAllLendableObjects } from '~/redux/actions/LendableObjectActions';
import { fetchLendingRequests } from '~/redux/actions/LendingRequestActions';
import {
editLendingRequest,
fetchLendingRequests,
} from '~/redux/actions/LendingRequestActions';
import { useAppDispatch, useAppSelector } from '~/redux/hooks';
import { EntityType } from '~/redux/models/entities';
import { selectLendableObjectsForIndex } from '~/redux/slices/lendableObjects';
Expand All @@ -16,26 +19,35 @@ import useQuery from '~/utils/useQuery';
import FilterSearch from './FilterSearch';
import ItemIndex from './ItemIndex';
import styles from './LendingPage.module.css';
import RequestInbox, { type LendingRequestOrdering } from './RequestInbox';
import RequestInbox, {
type LendingRequestArchivedFilter,
} from './RequestInbox';
import {
REQUEST_INBOX_PAGE_SIZE,
getNextVisibleCount,
getVisibleRequestCount,
shouldFetchMoreRequests,
} from './requestInboxPagination';
import type { TransformedLendingRequest } from '~/redux/models/LendingRequest';

const defaultLendingQuery = {
search: '',
lendingCategories: [] as FilterLendingCategory[],
ordering: '-created_at' as LendingRequestOrdering,
archived: 'false' as LendingRequestArchivedFilter,
};

const LendableObjectList = () => {
const { query, setQueryValue } = useQuery(defaultLendingQuery);
const requestOrdering: LendingRequestOrdering =
query.ordering === 'created_at' ? 'created_at' : '-created_at';
const requestArchived: LendingRequestArchivedFilter =
query.archived === 'true' ? 'true' : 'false';
const requestQuery = {
ordering: requestOrdering,
archived: requestArchived,
};
const activeRequestQuery = {
archived: 'false' as const,
};
const archivedRequestQuery = {
archived: 'true' as const,
};

const dispatch = useAppDispatch();
Expand All @@ -56,7 +68,7 @@ const LendableObjectList = () => {
query: requestQuery,
}),
),
[requestOrdering],
[requestArchived],
);

const { pagination: requestsPagination } = useAppSelector((state) =>
Expand All @@ -81,11 +93,11 @@ const LendableObjectList = () => {
selectTransformedLendingRequests(state, { pagination: requestsPagination }),
);
const [visibleCount, setVisibleCount] = useState(REQUEST_INBOX_PAGE_SIZE);
const previousRequestOrderingRef = useRef(requestOrdering);
const previousRequestArchivedRef = useRef(requestArchived);
const visibleRequestCount = getVisibleRequestCount({
visibleCount,
currentOrdering: requestOrdering,
previousOrdering: previousRequestOrderingRef.current,
currentArchived: requestArchived,
previousArchived: previousRequestArchivedRef.current,
});

const lendingRequests = originalLendingRequests.slice(0, visibleRequestCount);
Expand All @@ -106,6 +118,17 @@ const LendableObjectList = () => {
setVisibleCount(nextVisibleCount);
};

const handleArchiveRequest = async (
requestId: TransformedLendingRequest['id'],
archived: boolean,
) => {
await dispatch(editLendingRequest({ id: requestId, archived }));
await Promise.all([
dispatch(fetchLendingRequests({ query: activeRequestQuery })),
dispatch(fetchLendingRequests({ query: archivedRequestQuery })),
]);
};

const objectsActionGrant = useAppSelector(
(state) => state.lendableObjects.actionGrant,
);
Expand Down Expand Up @@ -137,9 +160,9 @@ const LendableObjectList = () => {
};

useEffect(() => {
previousRequestOrderingRef.current = requestOrdering;
previousRequestArchivedRef.current = requestArchived;
setVisibleCount(REQUEST_INBOX_PAGE_SIZE);
}, [requestOrdering]);
}, [requestArchived]);

useEffect(() => {
if (!heartRef.current) return;
Expand Down Expand Up @@ -233,8 +256,9 @@ const LendableObjectList = () => {
isFetching={requestsPagination.fetching}
hasMore={requestsPagination.hasMore}
onLoadMore={handleLoadMore}
ordering={requestOrdering}
onOrderingChange={setQueryValue('ordering')}
onArchive={handleArchiveRequest}
archived={requestArchived}
onArchivedChange={setQueryValue('archived')}
className={styles.requestInbox}
/>
<ItemIndex
Expand Down
95 changes: 61 additions & 34 deletions lego-webapp/pages/lending/LendingRequestCard.tsx
Original file line number Diff line number Diff line change
@@ -1,60 +1,87 @@
import { Card, Flex, Icon, Image } from '@webkom/lego-bricks';
import { MoveRight } from 'lucide-react';
import { Button, Card, Flex, Icon, Image } from '@webkom/lego-bricks';
import { Archive, MoveRight } from 'lucide-react';
import Time from '~/components/Time';
import LendingStatusTag from '~/pages/lending/LendingStatusTag';
import { TransformedLendingRequest } from '~/redux/models/LendingRequest';
import truncateString from '~/utils/truncateString';
import styles from './RequestInbox.module.css';
import type { MouseEvent } from 'react';

const LendingRequestCard = ({
lendingRequest,
isFromAdmin,
onArchive,
}: {
lendingRequest: TransformedLendingRequest;
isFromAdmin?: boolean;
onArchive?: (
requestId: TransformedLendingRequest['id'],
archived: boolean,
) => void;
}) => {
if (!lendingRequest.lendableObject) {
return null;
}

const preventCardNavigation = (event: MouseEvent<HTMLDivElement>) => {
event.preventDefault();
event.stopPropagation();
};

return (
<a
href={`/lending/${lendingRequest.lendableObject.id}/request/${lendingRequest.id} ${
isFromAdmin ? '?fromAdmin=true' : ''
}
<Card className={styles.lendingRequestCard}>
<Flex column width="100%" gap="var(--spacing-sm)">
<a
href={`/lending/${lendingRequest.lendableObject.id}/request/${lendingRequest.id} ${
isFromAdmin ? '?fromAdmin=true' : ''
}
`}
>
<Card className={styles.lendingRequestCard}>
<Flex width="100%">
<Flex>
<Flex column gap="var(--spacing-sm)">
<Flex column gap="var(--spacing-xs)">
<h4>
{truncateString(lendingRequest.lendableObject.title, 30)}
</h4>
<Flex gap="var(--spacing-sm)">
<Time time={lendingRequest.startDate} format="DD. MMM" />
<Icon iconNode={<MoveRight />} size={19} />
<Time time={lendingRequest.endDate} format="DD. MMM" />
>
<Flex width="100%">
<Flex width="100%">
<Flex>
<Flex column gap="var(--spacing-sm)">
<Flex column gap="var(--spacing-xs)">
<h4>
{truncateString(lendingRequest.lendableObject.title, 30)}
</h4>
<Flex gap="var(--spacing-sm)">
<Time time={lendingRequest.startDate} format="DD. MMM" />
<Icon iconNode={<MoveRight />} size={19} />
<Time time={lendingRequest.endDate} format="DD. MMM" />
</Flex>
</Flex>
<div className={styles.tagContainer}>
<LendingStatusTag
lendingRequestStatus={lendingRequest.status}
/>
</div>
</Flex>
</Flex>
<div className={styles.tagContainer}>
<LendingStatusTag
lendingRequestStatus={lendingRequest.status}
/>
</div>
</Flex>
<Image
className={styles.lendingRequestImage}
height={80}
width={80}
src={lendingRequest.lendableObject.image || '/icon-192x192.png'}
alt={lendingRequest.lendableObject.title}
/>
</Flex>
</Flex>
<Image
className={styles.lendingRequestImage}
height={80}
width={80}
src={lendingRequest.lendableObject.image || '/icon-192x192.png'}
alt={lendingRequest.lendableObject.title}
/>
</Card>
</a>
</a>
{!isFromAdmin && (
<div onClick={preventCardNavigation}>
<Button
onPress={() =>
onArchive?.(lendingRequest.id, !lendingRequest.archived)
}
>
{lendingRequest.archived ? 'Fjern arkivering' : 'Arkiver'}
<Icon iconNode={<Archive />} size={19} />
</Button>
</div>
)}
</Flex>
</Card>
);
};

Expand Down
5 changes: 4 additions & 1 deletion lego-webapp/pages/lending/RequestInbox.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,15 @@
flex-direction: row;
align-items: center;
padding: var(--spacing-md);
color: var(--lego-font-color);
transition:
box-shadow var(--easing-fast),
background-color var(--easing-fast);
}

.lendingRequestCard > div > a {
color: var(--lego-font-color);
}

.lendingRequestCard:hover {
background-color: var(--additive-background);
}
Expand Down
35 changes: 22 additions & 13 deletions lego-webapp/pages/lending/RequestInbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ import { canLoadMoreRequests } from './requestInboxPagination';
import useAnimateRequestInbox from './useAnimateRequestInbox';
import type { TransformedLendingRequest } from '~/redux/models/LendingRequest';

export type LendingRequestOrdering = 'created_at' | '-created_at';
export type LendingRequestArchivedFilter = 'true' | 'false';

const orderingOptions: PillSwitchOption<LendingRequestOrdering>[] = [
{ label: 'Nyeste', value: '-created_at' },
{ label: 'Eldste', value: 'created_at' },
const archivedOptions: PillSwitchOption<LendingRequestArchivedFilter>[] = [
{ label: 'Aktiv', value: 'false' },
{ label: 'Arkivert', value: 'true' },
];

type Props = {
Expand All @@ -23,8 +23,12 @@ type Props = {
isFetching: boolean;
hasMore: boolean;
onLoadMore: () => void;
ordering: LendingRequestOrdering;
onOrderingChange: (ordering: LendingRequestOrdering) => void;
onArchive: (
requestId: TransformedLendingRequest['id'],
archived: boolean,
) => void;
archived: LendingRequestArchivedFilter;
onArchivedChange: (archived: LendingRequestArchivedFilter) => void;
className?: string;
};

Expand All @@ -34,8 +38,9 @@ const RequestInbox = ({
isFetching,
hasMore,
onLoadMore,
ordering,
onOrderingChange,
onArchive,
archived,
onArchivedChange,
className,
}: Props) => {
const listRef = useRef<HTMLDivElement>(null);
Expand All @@ -60,9 +65,11 @@ const RequestInbox = ({
<div className={styles.headerRow}>
<h3 className={styles.header}>Din innboks</h3>
<PillSwitch
options={orderingOptions}
value={ordering}
onChange={onOrderingChange}
options={archivedOptions}
value={archived}
onChange={(value) =>
onArchivedChange(value === 'true' ? 'true' : 'false')
}
ariaLabel="Sorter utlånsforespørsler"
/>
</div>
Expand All @@ -73,13 +80,15 @@ const RequestInbox = ({
)}
{hasRequests && (
<div ref={listRef} className={styles.lendingRequestsContainer}>
{lendingRequests.map((req) => (
{lendingRequests
.filter((req) => req.archived === (archived === 'true'))
.map((req) => (
<div
key={req.id}
data-request-id={String(req.id)}
className={styles.requestCardWrapper}
>
<LendingRequestCard lendingRequest={req} />
<LendingRequestCard lendingRequest={req} onArchive={onArchive} />
</div>
))}
</div>
Expand Down
10 changes: 5 additions & 5 deletions lego-webapp/pages/lending/requestInboxPagination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ export const getNextVisibleCount = (visibleCount: number) =>

export const getVisibleRequestCount = ({
visibleCount,
currentOrdering,
previousOrdering,
currentArchived,
previousArchived,
}: {
visibleCount: number;
currentOrdering: string;
previousOrdering: string;
currentArchived: string;
previousArchived: string;
}) =>
currentOrdering === previousOrdering ? visibleCount : REQUEST_INBOX_PAGE_SIZE;
currentArchived === previousArchived ? visibleCount : REQUEST_INBOX_PAGE_SIZE;

export const canLoadMoreRequests = ({
hasMore,
Expand Down
7 changes: 6 additions & 1 deletion lego-webapp/redux/models/LendingRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ interface LendingRequest {
updatedBy: EntityId;
createdAt: Dateish;
lendableObject: EntityId;
archived: boolean;
status: LendingRequestStatus;
startDate: Dateish;
endDate: Dateish;
Expand All @@ -31,6 +32,7 @@ export type ListLendingRequest = Pick<
| 'id'
| 'createdBy'
| 'lendableObject'
| 'archived'
| 'status'
| 'startDate'
| 'endDate'
Expand All @@ -45,6 +47,7 @@ export type DetailLendingRequest = Pick<
| 'createdAt'
| 'updatedBy'
| 'lendableObject'
| 'archived'
| 'status'
| 'startDate'
| 'endDate'
Expand All @@ -65,7 +68,9 @@ export type CreateLendingRequest = Pick<
'lendableObject' | 'startDate' | 'endDate'
> & { comment: string };
export type EditLendingRequest = Required<Pick<LendingRequest, 'id'>> &
Partial<Pick<LendingRequest, 'status' | 'startDate' | 'endDate'>>;
Partial<
Pick<LendingRequest, 'status' | 'startDate' | 'endDate' | 'archived'>
>;

export type TimelineEntry = {
id: EntityId;
Expand Down
1 change: 1 addition & 0 deletions lego-webapp/redux/slices/__tests__/lendingRequests.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const createLendingRequest = (
id,
createdBy: 1,
lendableObject: lendableObjectId,
archived: false,
status: LendingRequestStatus.Created,
startDate: '2026-01-01',
endDate: '2026-01-02',
Expand Down
Loading