Skip to content

Commit

Permalink
Flesh out remove-owner form #85
Browse files Browse the repository at this point in the history
  • Loading branch information
Daniel Haarhoff committed Nov 3, 2024
1 parent 23c7952 commit f2779a5
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 43 deletions.
23 changes: 23 additions & 0 deletions src/commands/area/get-area-name.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {eq} from 'drizzle-orm';
import {pipe} from 'fp-ts/lib/function';
import {StatusCodes} from 'http-status-codes';
import {SharedReadModel} from '../../read-models/shared-state';
import {areasTable} from '../../read-models/shared-state/state';
import {failureWithStatus} from '../../types/failure-with-status';
import * as E from 'fp-ts/Either';

export const getAreaName = (db: SharedReadModel['db'], areaId: string) =>
pipe(
db
.select({areaName: areasTable.name})
.from(areasTable)
.where(eq(areasTable.id, areaId))
.get(),
result => result?.areaName,
E.fromNullable(
failureWithStatus(
'The requested area does not exist',
StatusCodes.NOT_FOUND
)()
)
);
20 changes: 1 addition & 19 deletions src/commands/area/remove-area-form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@ import {User} from '../../types';
import {Form} from '../../types/form';
import {flow, pipe} from 'fp-ts/lib/function';
import {html, safe, sanitizeString} from '../../types/html';
import {eq} from 'drizzle-orm';
import {StatusCodes} from 'http-status-codes';
import {SharedReadModel} from '../../read-models/shared-state';
import {areasTable} from '../../read-models/shared-state/state';
import {failureWithStatus} from '../../types/failure-with-status';
import {formatValidationErrors} from 'io-ts-reporters';
import {getAreaName} from './get-area-name';

type ViewModel = {
user: User;
Expand All @@ -33,22 +31,6 @@ const renderForm = (viewModel: ViewModel) =>
pageTemplate(safe('Remove Area'), viewModel.user)
);

const getAreaName = (db: SharedReadModel['db'], areaId: string) =>
pipe(
db
.select({areaName: areasTable.name})
.from(areasTable)
.where(eq(areasTable.id, areaId))
.get(),
result => result?.areaName,
E.fromNullable(
failureWithStatus(
'The requested area does not exist',
StatusCodes.NOT_FOUND
)()
)
);

const paramsCodec = t.strict({
area: t.string,
});
Expand Down
102 changes: 78 additions & 24 deletions src/commands/area/remove-owner-form.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,63 @@
/* eslint-disable unused-imports/no-unused-vars */
import * as tt from 'io-ts-types';
import * as t from 'io-ts';
import * as E from 'fp-ts/Either';
import {pageTemplate} from '../../templates';
import {User} from '../../types';
import {EmailAddress, User} from '../../types';
import {Form} from '../../types/form';
import {pipe} from 'fp-ts/lib/function';
import {flow, pipe} from 'fp-ts/lib/function';
import {html, safe, sanitizeString} from '../../types/html';
import {eq} from 'drizzle-orm';
import {StatusCodes} from 'http-status-codes';
import {SharedReadModel} from '../../read-models/shared-state';
import {areasTable} from '../../read-models/shared-state/state';
import {failureWithStatus} from '../../types/failure-with-status';
import {formatValidationErrors} from 'io-ts-reporters';
import {getAreaName} from './get-area-name';
import {membersTable} from '../../read-models/shared-state/state';
import {eq} from 'drizzle-orm';
import {SharedReadModel} from '../../read-models/shared-state';
import * as O from 'fp-ts/Option';

type ViewModel = {
user: User;
areaId: string;
areaName: string;
ownerName: string;
ownerMemberNumber: number;
owner: {
memberNumber: number;
name: O.Option<string>;
email: EmailAddress;
};
};

const renderOwner = (owner: ViewModel['owner']) =>
pipe(
owner.name,
O.match(
() =>
html`<a href="/member/${owner.memberNumber}"
>${sanitizeString(owner.email)} (${owner.memberNumber})
</a>`,
name =>
html`<a href="/member/${owner.memberNumber}"
>${sanitizeString(name)} (${owner.memberNumber})</a
>`
)
);

const renderForm = (viewModel: ViewModel) =>
pipe(
viewModel,
() => html`
<div class="stack-large">
<h1>Remove owner?</h1>
<p>
This will remove <b>${sanitizeString(viewModel.ownerName)}</b> from
the owners of the <b>${sanitizeString(viewModel.areaName)}</b> area.
</p>
<p>
They will also no longer be a trainer for any of the red equipment in
the area.
</p>
<div>
<p>
This will remove ${renderOwner(viewModel.owner)} from the owners of
the <b>${sanitizeString(viewModel.areaName)}</b> area.
</p>
<p>
They will also no longer be a trainer for any of the red equipment
in the area.
</p>
</div>
<form action="#" method="post">
<input
type="hidden"
Expand All @@ -42,7 +67,7 @@ const renderForm = (viewModel: ViewModel) =>
<input
type="hidden"
name="memberNumber"
value="${viewModel.ownerMemberNumber}"
value="${viewModel.owner.memberNumber}"
/>
<button type="submit">Confirm and send</button>
</form>
Expand All @@ -51,28 +76,57 @@ const renderForm = (viewModel: ViewModel) =>
pageTemplate(safe('Remove Owner'), viewModel.user)
);

const getAreaName = (db: SharedReadModel['db'], areaId: string) =>
const getOwner = (db: SharedReadModel['db'], memberNumber: number) =>
pipe(
db
.select({areaName: areasTable.name})
.from(areasTable)
.where(eq(areasTable.id, areaId))
.select({
name: membersTable.name,
memberNumber: membersTable.memberNumber,
email: membersTable.emailAddress,
})
.from(membersTable)
.where(eq(membersTable.memberNumber, memberNumber))
.get(),
result => result?.areaName,
E.fromNullable(
failureWithStatus(
'The requested area does not exist',
'The requested member does not exist',
StatusCodes.NOT_FOUND
)()
)
);

const paramsCodec = t.strict({
memberNumber: tt.NumberFromString,
areaId: tt.UUID,
});

const decodeParams = (input: unknown) =>
pipe(
input,
paramsCodec.decode,
E.mapLeft(
flow(
formatValidationErrors,
failureWithStatus(
'Parameters submitted to the form were invalid',
StatusCodes.BAD_REQUEST
)
)
)
);

export const removeOwnerForm: Form<ViewModel> = {
renderForm,
constructForm:
input =>
({user, readModel}) =>
E.left(
failureWithStatus('not implemented', StatusCodes.NOT_IMPLEMENTED)()
pipe(
input,
decodeParams,
E.bind('user', () => E.right(user)),
E.bind('areaName', ({areaId}) => getAreaName(readModel.db, areaId)),
E.bind('owner', ({memberNumber}) =>
getOwner(readModel.db, memberNumber)
)
),
};

0 comments on commit f2779a5

Please sign in to comment.