diff --git a/src/components/CurrentlyOutEquipment.tsx b/src/components/CurrentlyOutEquipment.tsx
new file mode 100644
index 00000000..863d3738
--- /dev/null
+++ b/src/components/CurrentlyOutEquipment.tsx
@@ -0,0 +1,54 @@
+import React from 'react';
+import Skeleton from 'react-loading-skeleton';
+import useSwr from 'swr';
+import { getResponseContentOrError } from '../lib/utils';
+import { CurrentlyOutEquipmentInfo } from '../models/misc/CurrentlyOutEquipmentInfo';
+import { TableConfiguration, TableDisplay } from './TableDisplay';
+import { Card } from 'react-bootstrap';
+import TableStyleLink from './utils/TableStyleLink';
+
+const EquipmentNameDisplayFn = (x: CurrentlyOutEquipmentInfo) =>
+ x.equipmentId ? {x.name} : {x.name};
+
+const CurrentlyOutEquipment: React.FC = () => {
+ const { data: currentlyOutEquipmentInfos } = useSwr('/api/equipment/currentlyOut', (url) =>
+ fetch(url).then((response) => getResponseContentOrError(response)),
+ );
+
+ if (!currentlyOutEquipmentInfos) {
+ return ;
+ }
+
+ const tableSettings: TableConfiguration = {
+ entityTypeDisplayName: 'utrustning',
+ defaultSortPropertyName: 'name',
+ defaultSortAscending: true,
+ hideTableFilter: true,
+ hideTableCountControls: true,
+ columns: [
+ {
+ key: 'name',
+ displayName: 'Utrustning',
+ getValue: (x: CurrentlyOutEquipmentInfo) => x.name,
+ getContentOverride: EquipmentNameDisplayFn,
+ textTruncation: true,
+ columnWidth: 300,
+ },
+ {
+ key: 'numberOfUnits',
+ displayName: 'Antal',
+ getValue: (x: CurrentlyOutEquipmentInfo) => x.numberOfUnits,
+ columnWidth: 220,
+ },
+ ],
+ };
+
+ return (
+
+ Utlämnad utrustning
+
+
+ );
+};
+
+export default CurrentlyOutEquipment;
diff --git a/src/lib/db-access/equipmentList.ts b/src/lib/db-access/equipmentList.ts
index d01acf82..7303063f 100644
--- a/src/lib/db-access/equipmentList.ts
+++ b/src/lib/db-access/equipmentList.ts
@@ -8,6 +8,10 @@ import {
import { ensureDatabaseIsInitialized } from '../database';
import { validateEquipmentListEntryObjectionModel } from './equipmentListEntry';
import { compareLists, removeIdAndDates, withCreatedDate, withUpdatedDate } from './utils';
+import { RentalStatus } from '../../models/enums/RentalStatus';
+import { BookingType } from '../../models/enums/BookingType';
+import { formatDatetimeForForm } from '../datetimeUtils';
+import { Status } from '../../models/enums/Status';
export const fetchEquipmentList = async (
id: number,
@@ -41,6 +45,31 @@ export const fetchEquipmentListsForBooking = async (bookingId: number): Promise<
return EquipmentListObjectionModel.query().where('bookingId', bookingId).orderBy('id');
};
+export const fetchOutEquipmentLists = async (): Promise => {
+ ensureDatabaseIsInitialized();
+ const now = formatDatetimeForForm(new Date());
+
+ return EquipmentListObjectionModel.query()
+ .join('Booking', 'Booking.id', '=', 'EquipmentList.bookingId')
+ .where({ rentalStatus: RentalStatus.OUT, bookingType: BookingType.RENTAL })
+ .orWhere((x) =>
+ x
+ .where({ bookingType: BookingType.GIG })
+ .where('equipmentOutDatetime', '<=', now)
+ .where('equipmentInDatetime', '>=', now)
+ .whereNot({status: Status.CANCELED})
+ )
+ .orWhere((x) =>
+ x
+ .where({ bookingType: BookingType.GIG })
+ .where('usageStartDatetime', '<=', now)
+ .where('usageEndDatetime', '>=', now)
+ .whereNot({status: Status.CANCELED})
+ )
+ .withGraphFetched('listEntries.equipment')
+ .withGraphFetched('listHeadings.listEntries.equipment');
+};
+
export const updateEquipmentList = async (
id: number,
equipmentList: EquipmentListObjectionModel,
diff --git a/src/models/misc/CurrentlyOutEquipmentInfo.ts b/src/models/misc/CurrentlyOutEquipmentInfo.ts
new file mode 100644
index 00000000..4fd99422
--- /dev/null
+++ b/src/models/misc/CurrentlyOutEquipmentInfo.ts
@@ -0,0 +1,6 @@
+export interface CurrentlyOutEquipmentInfo {
+ id: number;
+ equipmentId?: number;
+ name: string;
+ numberOfUnits: number;
+}
diff --git a/src/pages/api/equipment/currentlyOut.ts b/src/pages/api/equipment/currentlyOut.ts
new file mode 100644
index 00000000..9866eab6
--- /dev/null
+++ b/src/pages/api/equipment/currentlyOut.ts
@@ -0,0 +1,52 @@
+import { NextApiRequest, NextApiResponse } from 'next';
+import { respondWithCustomErrorMessage, respondWithEntityNotFoundResponse } from '../../../lib/apiResponses';
+import { withSessionContext } from '../../../lib/sessionContext';
+import { fetchOutEquipmentLists } from '../../../lib/db-access/equipmentList';
+import { groupBy, reduceSumFn } from '../../../lib/utils';
+import { EquipmentListObjectionModel } from '../../../models/objection-models/BookingObjectionModel';
+
+const handler = withSessionContext(async (req: NextApiRequest, res: NextApiResponse): Promise | void> => {
+ switch (req.method) {
+ case 'GET':
+ await fetchOutEquipmentLists()
+ .then(getEquipmentFromLists)
+ .then((result) => res.status(200).json(result))
+ .catch((error) => respondWithCustomErrorMessage(res, error.message));
+
+ return;
+
+ default:
+ respondWithEntityNotFoundResponse(res);
+ }
+
+ return;
+});
+
+export default handler;
+
+const getEquipmentFromLists = (equipmentLists: EquipmentListObjectionModel[]) => {
+ const listEntries = equipmentLists.flatMap((list) => [
+ ...list.listEntries,
+ ...list.listHeadings.flatMap((heading) => heading.listEntries),
+ ]);
+
+ const equipmentGroupings = groupBy(
+ listEntries.filter((x) => x.equipmentId),
+ (x) => x.equipmentId?.toString() ?? '0',
+ );
+ const equipmentWithCount = Object.keys(equipmentGroupings).map((key) => {
+ const records = equipmentGroupings[key];
+ return {
+ equipmentId: key,
+ id: records[0].id,
+ name: records[0].equipment?.name,
+ numberOfUnits: records.map((x) => x.numberOfUnits).reduce(reduceSumFn, 0),
+ };
+ });
+
+ const customRows = listEntries
+ .filter((x) => !x.equipment)
+ .map((x) => ({ name: x.name, id: x.id, numberOfUnits: x.numberOfUnits }));
+
+ return [...equipmentWithCount, ...customRows];
+};
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index add251cf..01d60d4c 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -21,6 +21,7 @@ import Link from 'next/link';
import { IfNotReadonly } from '../components/utils/IfAdmin';
import TableStyleLink from '../components/utils/TableStyleLink';
import { KeyValue } from '../models/interfaces/KeyValue';
+import CurrentlyOutEquipment from '../components/CurrentlyOutEquipment';
// eslint-disable-next-line react-hooks/rules-of-hooks
export const getServerSideProps = useUserWithDefaultAccessAndWithSettings();
@@ -74,6 +75,7 @@ const IndexPage: React.FC = ({ user: currentUser, globalSettings }: Props
bookings={outBookings}
showDateHeadings={false}
>
+
Aktivitet