Skip to content

Commit

Permalink
Save constant presence selection in database
Browse files Browse the repository at this point in the history
  • Loading branch information
joonashak committed Jan 3, 2025
1 parent 9712b43 commit 72272ec
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 13 deletions.
1 change: 1 addition & 0 deletions app/src/bolt/enums/view-action.enum.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
enum ViewAction {
CREATE_OFFICE = "create_office",
EDIT_OFFICE = "edit_office",
SAVE_CONSTANT_PRESENCES = "save_constant_presences",
}

export default ViewAction;
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { ConstantPresence } from "./constant-presence.model";
import { ConstantPresenceService } from "./constant-presence.service";

@Module({ imports: [TypeOrmModule.forFeature([ConstantPresence])], exports: [TypeOrmModule] })
@Module({
imports: [TypeOrmModule.forFeature([ConstantPresence])],
providers: [ConstantPresenceService],
exports: [TypeOrmModule, ConstantPresenceService],
})
export class ConstantPresenceModule {}
54 changes: 54 additions & 0 deletions app/src/entities/constant-presence/constant-presence.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import dayjs from "../../common/dayjs";
import { ConstantPresence, ConstantPresenceRepository } from "./constant-presence.model";
import { CreateConstantPresence } from "./dto/create-constant-presence.dto";

@Injectable()
export class ConstantPresenceService {
constructor(
@InjectRepository(ConstantPresence)
private constantPresenceRepository: ConstantPresenceRepository,
) {}

// async findEffectiveByUserId(userId: string) {
// return this.constantPresenceRepository
// .createQueryBuilder("cp")
// .select()
// .where("cp.user_slack_id = :userId", { userId })
// .andWhere("in_effect @> NOW()::date")
// .execute();
// }

/**
* Create new constant presences for user.
*
* All currently effective constant presence entries will be closed as of the
* current date. New entries will be effective starting on the current date.
*/
async closeOldAndCreateMany(userId: string, cps: CreateConstantPresence[]) {
await this.closeEffectiveCpsByUserId(userId);

const user = { slackId: userId };
const inEffect = { start: dayjs(), end: undefined };

const inserts = cps.map(({ officeId, ...cp }) => ({
...cp,
user,
inEffect,
office: officeId ? { id: officeId } : null,
}));

await this.constantPresenceRepository.save(inserts);
}

private async closeEffectiveCpsByUserId(userId: string) {
return this.constantPresenceRepository
.createQueryBuilder()
.update()
.set({ inEffect: () => "daterange(lower(in_effect), NOW()::date)" })
.where("user_slack_id = :userId", { userId })
.andWhere("in_effect @> NOW()::date")
.execute();
}
}
11 changes: 5 additions & 6 deletions app/src/entities/constant-presence/daterange-transformer.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import dayjs, { Dayjs } from "dayjs";
import { ValueTransformer } from "typeorm";

export type DateRange = {
start: Dayjs;
end: Dayjs;
start: Dayjs | undefined;
end: Dayjs | undefined;
};

/**
Expand All @@ -27,11 +26,11 @@ const parseValidDate = (value: unknown): Dayjs | undefined => {
* Does not account for range inclusivity and exclusivity. Rather always assumes
* that range start is inclusive and end is exclusive.
*/
export const daterangeTransformer: ValueTransformer = {
export const daterangeTransformer = {
to: (value: DateRange) => {
const format = "YYYY-MM-DD";
const start = dayjs(value.start).format(format);
const end = dayjs(value.end).format(format);
const start = value.start ? dayjs(value.start).format(format) : "";
const end = value.end ? dayjs(value.end).format(format) : "";

return `[${start},${end})`;
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { ConstantPresence } from "../constant-presence.model";

export type CreateConstantPresence = Pick<ConstantPresence, "dayOfWeek" | "remote"> & {
officeId: string | undefined;
};
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import { Controller } from "@nestjs/common";
import { Controller, InternalServerErrorException } from "@nestjs/common";
import { get, range } from "lodash";
import BoltAction from "../../../../../bolt/decorators/bolt-action.decorator";
import BoltViewAction from "../../../../../bolt/decorators/bolt-view-action.decorator";
import Action from "../../../../../bolt/enums/action.enum";
import ViewAction from "../../../../../bolt/enums/view-action.enum";
import { BoltActionArgs } from "../../../../../bolt/types/bolt-action-args.type";
import { BoltViewActionArgs } from "../../../../../bolt/types/bolt-view-action-args.type";
import { ConstantPresenceService } from "../../../../../entities/constant-presence/constant-presence.service";
import { CreateConstantPresence } from "../../../../../entities/constant-presence/dto/create-constant-presence.dto";
import { ConstantPresenceManagementModal } from "./constant-presence-management.modal";

@Controller()
export class ConstantPresenceManagementController {
constructor(private modal: ConstantPresenceManagementModal) {}
constructor(
private modal: ConstantPresenceManagementModal,
private cpService: ConstantPresenceService,
) {}

@BoltAction(Action.OPEN_CONSTANT_PRESENCE_MANAGEMENT_MODAL)
async openModal({ client, body }: BoltActionArgs) {
Expand All @@ -15,4 +24,39 @@ export class ConstantPresenceManagementController {
view: await this.modal.build(),
});
}

@BoltViewAction(ViewAction.SAVE_CONSTANT_PRESENCES)
async saveConstantPresences({ body, view }: BoltViewActionArgs) {
const inserts = this.insertsFromModalStateValues(view.state.values);
await this.cpService.closeOldAndCreateMany(body.user.id, inserts);
}

private presencePropsFromStaticSelectValue(value: unknown) {
if (typeof value !== "string") {
throw new InternalServerErrorException();
}

if (["REMOTE", "OFFICE"].includes(value)) {
return {
remote: value === "REMOTE",
officeId: null,
};
}

return { remote: false, officeId: value };
}

private insertsFromModalStateValues(stateValues: unknown): CreateConstantPresence[] {
const valuesForAllDays = range(5).map((dayOfWeek) => ({
dayOfWeek,
value: get(stateValues, `day-${dayOfWeek}.presence.selected_option.value`),
}));

const selection = valuesForAllDays.filter((o) => o.value);

return selection.map((selected) => ({
dayOfWeek: selected.dayOfWeek,
...this.presencePropsFromStaticSelectValue(selected.value),
}));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Injectable } from "@nestjs/common";
import { capitalize, range } from "lodash";
import { Actions, Button, Header, Modal, Option, Section, StaticSelect } from "slack-block-builder";
import { SlackModalDto } from "slack-block-builder/dist/internal";
import ViewAction from "../../../../../bolt/enums/view-action.enum";
import dayjs from "../../../../../common/dayjs";
import { Office } from "../../../../../entities/office/office.model";
import { OfficeService } from "../../../../../entities/office/office.service";
Expand All @@ -16,7 +17,12 @@ export class ConstantPresenceManagementModal {
this.constantPresenceSelect(offices, dayOfWeek),
);

return Modal({ title: "Vakioilmoittautumiset", submit: "Tallenna", close: "Eiku" })
return Modal({
title: "Vakioilmoittautumiset",
submit: "Tallenna",
close: "Eiku",
callbackId: ViewAction.SAVE_CONSTANT_PRESENCES,
})
.blocks(
Section({
text:
Expand All @@ -41,8 +47,8 @@ export class ConstantPresenceManagementModal {

return [
Header({ text: capitalize(dayOfWeekName) }),
Actions().elements(
StaticSelect().options(options),
Actions({ blockId: `day-${dayOfWeek}` }).elements(
StaticSelect({ actionId: "presence" }).options(options),
Button({ text: "Poista ilmoittautuminen" }).danger(),
),
];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Module } from "@nestjs/common";
import { ConstantPresenceModule } from "../../../../../entities/constant-presence/constant-presence.module";
import { OfficeModule } from "../../../../../entities/office/office.module";
import { ConstantPresenceManagementController } from "./constant-presence-management.controller";
import { ConstantPresenceManagementModal } from "./constant-presence-management.modal";

@Module({
imports: [OfficeModule],
imports: [OfficeModule, ConstantPresenceModule],
providers: [ConstantPresenceManagementModal],
controllers: [ConstantPresenceManagementController],
})
Expand Down

0 comments on commit 72272ec

Please sign in to comment.