Skip to content

Commit

Permalink
Add redux actions and form for creating lendable objects
Browse files Browse the repository at this point in the history
  • Loading branch information
eikhr committed Nov 7, 2023
1 parent 3ce0bd0 commit 2db4fe2
Show file tree
Hide file tree
Showing 15 changed files with 439 additions and 49 deletions.
10 changes: 10 additions & 0 deletions app/actions/ActionTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -408,3 +408,13 @@ export const Reaction = {
ADD: generateStatuses('Reaction.ADD') as AAT,
DELETE: generateStatuses('Reaction.DELETE') as AAT,
};

/**
*
*/
export const LendableObject = {
FETCH: generateStatuses('LendableObject.FETCH') as AAT,
CREATE: generateStatuses('LendableObject.CREATE') as AAT,
EDIT: generateStatuses('LendableObject.EDIT') as AAT,
DELETE: generateStatuses('LendableObject.DELETE') as AAT,
};
74 changes: 74 additions & 0 deletions app/actions/LendableObjectActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import callAPI from 'app/actions/callAPI';
import { lendableObjectSchema } from 'app/reducers';
import type {
EntityType,
NormalizedEntityPayload,
} from 'app/store/models/entities';
import type { Thunk } from 'app/types';
import { LendableObject } from './ActionTypes';

export function fetchAllLendableObjects(): Thunk<
Promise<NormalizedEntityPayload<EntityType.LendableObjects>>
> {
return callAPI({
types: LendableObject.FETCH,
endpoint: '/lendableobject/',
schema: [lendableObjectSchema],
meta: {
errorMessage: 'Henting av utlånsobjekter failet',
},
propagateError: true,
});
}

export function fetchLendableObject(id: number): Thunk<any> {
return callAPI({
types: LendableObject.FETCH,
endpoint: `/lendableobject/${id}/`,
schema: lendableObjectSchema,
meta: {
errorMessage: 'Henting av utlånsobjekt feilet',
},
propagateError: true,
});
}

export function deleteLendableObject(id: number): Thunk<any> {
return callAPI({
types: LendableObject.DELETE,
endpoint: `/lendableobject/${id}/`,
method: 'DELETE',
meta: {
id,
errorMessage: 'Sletting av utlånsobjekt feilet',
},
});
}

export function createLendableObject(data: any): Thunk<any> {
return callAPI({
types: LendableObject.CREATE,
endpoint: '/lendableobject/',
method: 'POST',
body: data,
schema: lendableObjectSchema,
meta: {
errorMessage: 'Opprettelse av utlånsobjekt feilet',
},
});
}

export function editLendableObject({
id,
...data
}: Record<string, any>): Thunk<any> {
return callAPI({
types: LendableObject.EDIT,
endpoint: `/lendableobject/${id}/`,
method: 'PUT',
body: data,
meta: {
errorMessage: 'Endring av utlånsobjekt feilet',
},
});
}
3 changes: 3 additions & 0 deletions app/reducers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,6 @@ export const followersCompanySchema = new schema.Entity(
export const followersUserSchema = new schema.Entity(followersKeyGen('user'), {
follower: userSchema,
});
export const lendableObjectSchema = new schema.Entity('lendableObjects', {
responsibleGroups: [groupSchema],
});
26 changes: 26 additions & 0 deletions app/reducers/lendableObjects.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { createSelector } from '@reduxjs/toolkit';
import { LendableObject } from 'app/actions/ActionTypes';
import type { RootState } from 'app/store/createRootReducer';
import createEntityReducer from 'app/utils/createEntityReducer';
import type { EntityId } from '@reduxjs/toolkit';

export default createEntityReducer({
key: 'lendableObjects',
types: {
fetch: LendableObject.FETCH,
mutate: LendableObject.CREATE,
delete: LendableObject.DELETE,
},
});
export const selectLendableObjects = createSelector(
(state: RootState) => state.lendableObjects.byId,
(state: RootState) => state.lendableObjects.items,
(lendableObjectsById, lendableObjectIds) =>
lendableObjectIds.map((id) => lendableObjectsById[id])
);
export const selectLendableObjectById = createSelector(
(state: RootState) => state.lendableObjects.byId,
(_: RootState, id: EntityId) => id,
(lendableObjectsById, lendableObjectId) =>
lendableObjectsById[lendableObjectId]
);
125 changes: 125 additions & 0 deletions app/routes/lending/LendableObjectAdminDetail.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import dayGridPlugin from '@fullcalendar/daygrid';
import interactionPlugin from '@fullcalendar/interaction';
import FullCalendar from '@fullcalendar/react';
import timeGridPlugin from '@fullcalendar/timegrid';
import moment from 'moment-timezone';
import { Helmet } from 'react-helmet-async';
import { useParams, Link } from 'react-router-dom';
import { Content } from 'app/components/Content';
import NavigationTab from 'app/components/NavigationTab';
import type { DetailedLendableObject } from 'app/store/models/LendableObject';

type Params = {
lendableObjectId: string;
};

const LendableObjectAdminDetail = () => {
const { lendableObjectId } = useParams<Params>();

const lendingRequest = {
id: 1,
user: {
id: 1,
username: 'Eik',
fullName: 'Test Testesen',
},
message: 'Jeg vil gjerne låne Soundboks til hyttetur:)',
startTime: moment().subtract({ hours: 2 }),
endTime: moment(),
approved: false,
};

const lendableObject: DetailedLendableObject = {
id: lendableObjectId,
title: 'Soundbox',
description: 'En soundbox som kan brukes til å spille av lyder',
lendingCommentPrompt: 'Hvorfor ønsker du å låne soundboks',
image:
'https://www.tntpyro.no/wp-content/uploads/2021/08/141_1283224098.jpg',
};

const otherLoans = [
{
id: 2,
startTime: moment().subtract({ days: 1, hours: 2 }),
endTime: moment().subtract({ hours: 8 }),
},
{
id: 3,
startTime: moment().subtract({ hours: 6 }),
endTime: moment().subtract({ hours: 2 }),
},
];

const requestEvent = {
id: String(lendingRequest.id),
title: lendingRequest.user.fullName,
start: lendingRequest.startTime.toISOString(),
end: lendingRequest.endTime.toISOString(),
backgroundColor: '#e11617',
borderColor: '#e11617',
};

const otherLoanEvents = otherLoans.map((loan) => ({
id: String(loan.id),
title: 'Test',
start: loan.startTime.toISOString(),
end: loan.endTime.toISOString(),
backgroundColor: '#999999',
borderColor: '#999999',
}));

const otherLoanRequests = [
{
id: 5,
startTime: moment().subtract({ hours: 2 }),
endTime: moment().add({ hours: 2 }),
},
];

const otherLoanRequestEvents = otherLoanRequests.map((loan) => ({
id: String(loan.id),
title: 'Test',
start: loan.startTime.toISOString(),
end: loan.endTime.toISOString(),
backgroundColor: '#f57676',
borderColor: '#f57676',
}));

return (
<Content banner={lendableObject.image}>
<Helmet title={`Godkjenn utlån av ${lendableObject.title}`} />
<NavigationTab
title={`Godkjenn utlån av ${lendableObject.title}`}
></NavigationTab>
<p>
{lendingRequest.message} -{' '}
<Link to={`/users/${lendingRequest.user.username}`}>
{lendingRequest.user.fullName}
</Link>{' '}
</p>
<FullCalendar
plugins={[interactionPlugin, timeGridPlugin, dayGridPlugin]}
initialView="timeGridWeek"
slotDuration={'01:00:00'}
nowIndicator
expandRows
slotLabelInterval={'02:00:00'}
slotLabelFormat={{
timeStyle: 'short',
}}
allDaySlot={false}
locale="nb"
firstDay={1}
headerToolbar={{
left: 'prev,today,next',
center: 'title',
right: 'timeGridWeek,dayGridMonth',
}}
events={[requestEvent, ...otherLoanEvents, ...otherLoanRequestEvents]}
/>
</Content>
);
};

export default LendableObjectAdminDetail;
Empty file.
16 changes: 7 additions & 9 deletions app/routes/lending/LendableObjectDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ import dayGridPlugin from '@fullcalendar/daygrid';
import interactionPlugin from '@fullcalendar/interaction';
import FullCalendar from '@fullcalendar/react';
import timeGridPlugin from '@fullcalendar/timegrid';
import moment from 'moment-timezone';
import { useState } from 'react';
import { Field } from 'react-final-form';
import { Helmet } from 'react-helmet-async';
import { useParams } from 'react-router-dom';
import { Content } from 'app/components/Content';
import { Button, TextArea, TextInput } from 'app/components/Form';
import LegoFinalForm from 'app/components/Form/LegoFinalForm';
import Modal from 'app/components/Modal';
import NavigationTab, { NavigationLink } from 'app/components/NavigationTab';
import type { DetailedLendableObject } from 'app/store/models/LendableObject';
import { useState } from 'react';
import LegoFinalForm from 'app/components/Form/LegoFinalForm';
import { createValidator, required } from 'app/utils/validation';
import { Field } from 'react-final-form';
import { Button, DatePicker, TextArea, TextInput } from 'app/components/Form';
import Modal from 'app/components/Modal';
import moment from 'moment-timezone';

type Params = {
lendableObjectId: string;
Expand Down Expand Up @@ -91,9 +91,7 @@ const LendableObjectDetail = () => {
<Field
label="Slutt for utlån"
name="endTime"
defaultValue={moment(startString).format(
'DD-MM-YYYY hh:mm:ss'
)}
defaultValue={moment(endString).format('DD-MM-YYYY hh:mm:ss')}
component={TextInput.Field}
disabled
/>
Expand Down
94 changes: 94 additions & 0 deletions app/routes/lending/LendableObjectEdit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Field, FormSpy } from 'react-final-form';
import { useParams } from 'react-router-dom';
import { createLendableObject } from 'app/actions/LendableObjectActions';
import { Content } from 'app/components/Content';
import {
Button,
EditorField,
Form,
SelectInput,
TextInput,
} from 'app/components/Form';
import LegoFinalForm from 'app/components/Form/LegoFinalForm';
import SubmissionError from 'app/components/Form/SubmissionError';
import { useAppDispatch } from 'app/store/hooks';
import { roleOptions } from 'app/utils/constants';
import { spySubmittable } from 'app/utils/formSpyUtils';

type Params = {
lendableObjectId: string | undefined;
};

const LendableObjectEdit = () => {
const { lendableObjectId } = useParams<Params>();
const isNew = lendableObjectId === undefined;

const dispatch = useAppDispatch();

const onSubmit = (values) =>
dispatch(
createLendableObject({
...values,
responsibleGroups: values.responsibleGroups.map((group) => group.id),
responsibleRoles: values.responsibleRoles.map((role) => role.value),
})
);

return (
<Content>
<LegoFinalForm onSubmit={onSubmit} subscription={{}}>
{({ handleSubmit }) => (
<Form onSubmit={handleSubmit}>
<FormSpy>
{(form) => {
return <pre>{JSON.stringify(form.values, undefined, 2)}</pre>;
}}
</FormSpy>
<Field
name="title"
label="Navn"
placeholder="Navn på utleieobjekt"
component={TextInput.Field}
/>
<Field
name="description"
label="Beskrivelse"
component={EditorField.Field}
initialized={true}
/>
<Field
name="responsibleGroups"
filter={['users.abakusgroup']}
label="Ansvarlige grupper"
placeholder="Skriv inn gruppene som skal kunne administrere objektet"
component={SelectInput.AutocompleteField}
isMulti
/>
<Field
label="Ansvarlige roller (hvis du lar denne stå tom inkluderer du alle i gruppen!)"
name="responsibleRoles"
isMulti
placeholder="Velg rolle"
options={roleOptions}
component={SelectInput.Field}
/>
<Field
name="location"
label="Posisjon"
placeholder="Hvor befinner objektet seg?"
component={TextInput.Field}
/>
<SubmissionError />
{spySubmittable((submittable) => (
<Button disabled={!submittable} submit>
{isNew ? 'Opprett utlåndsobjekt' : 'Lagre endringer'}
</Button>
))}
</Form>
)}
</LegoFinalForm>
</Content>
);
};

export default LendableObjectEdit;
Loading

0 comments on commit 2db4fe2

Please sign in to comment.