Skip to content

Commit

Permalink
Merge pull request #13 from icefoganalytics/main
Browse files Browse the repository at this point in the history
Bunch o Changes
  • Loading branch information
datajohnson authored Jul 3, 2024
2 parents 38932c6 + 015147d commit c6c1166
Show file tree
Hide file tree
Showing 22 changed files with 575 additions and 185 deletions.
3 changes: 2 additions & 1 deletion api/src/data/migrations/016.create-incidents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ export async function up(knex: knex.Knex) {
table.string("description", 4000).notNullable();
table.datetime("created_at").notNullable().defaultTo(knex.fn.now());
table.datetime("reported_at").nullable();
table.string("urgency_code").notNullable();
table.string("urgency_code",8).notNullable();
table.string("investigation_notes", 4000).nullable();

table.foreign("sensitivity_code").references("sensitivities.code");
table.foreign("status_code").references("incident_statuses.code");
Expand Down
5 changes: 3 additions & 2 deletions api/src/data/migrations/017.create-hazards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ export async function up(knex: knex.Knex) {
table.string("description", 4000).nullable();
table.string("location_detail", 1000).nullable();
table.datetime("created_at").nullable().defaultTo(knex.fn.now());
table.datetime("reported_at").nullable()
table.datetime("reported_at").nullable();
table.integer("reopen_count").notNullable().defaultTo(0);
table.string("urgency_code").notNullable();
table.string("urgency_code", 8).notNullable();
table.string("notes", 4000).nullable();

table.foreign("hazard_type_id").references("hazard_types.id");
table.foreign("location_code").references("locations.code");
Expand Down
2 changes: 2 additions & 0 deletions api/src/data/models/hazard-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export interface Hazard {
description?: string;
location_detail?: string;
created_at: Date | Knex.Raw<any>;
reported_at: Date | Knex.Raw<any>;
reopen_count: number;
urgency_code: string;
notes?: string;
}
2 changes: 2 additions & 0 deletions api/src/data/models/incident-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ export interface Incident {
proxy_user_id?: string;
description: string;
created_at: Date | Knex.Raw<any>;
reported_at: Date | Knex.Raw<any>;
urgency_code: string;
investigation_notes?: string;

attachments?: any[];
steps?: IncidentStep[];
Expand Down
2 changes: 2 additions & 0 deletions api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
userRouter,
departmentRouter,
actionRouter,
attachmentRouter,
} from "./routes";
import { checkJwt, loadUser } from "./middleware/authz.middleware";
import migrator from "./data/migrator";
Expand Down Expand Up @@ -64,6 +65,7 @@ app.use("/migrate", migrator.migrationRouter);
app.use("/api/directory", directoryRouter);
app.use("/api/department", departmentRouter);
app.use("/api/location", locationRouter);
app.use("/api/attachment", attachmentRouter);

app.use("/api/reports", checkJwt, loadUser, reportRouter);
app.use("/api/user", checkJwt, loadUser, userRouter);
Expand Down
19 changes: 19 additions & 0 deletions api/src/routes/attachment-router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import express, { Request, Response } from "express";
import { param } from "express-validator";

import { IncidentAttachment } from "../data/models";
import { db as knex } from "../data";

export const attachmentRouter = express.Router();

attachmentRouter.get("/incident/:incident_id/attachment/:id", async (req: Request, res: Response) => {
const { incident_id, id } = req.params;

let attach = await knex("incident_attachments").where({ incident_id, id }).first<IncidentAttachment>();

if (!attach) return res.status(404).send();

res.setHeader("Content-disposition", `attachment; filename="${attach.file_name}"`);
res.setHeader("Content-type", attach.file_type ?? "");
res.send(attach.file);
});
1 change: 1 addition & 0 deletions api/src/routes/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from "./action-router";
export * from "./attachment-router";
export * from "./department-router";
export * from "./directory-router";
export * from "./location-router";
Expand Down
33 changes: 27 additions & 6 deletions api/src/routes/report-router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,38 @@ reportRouter.get("/role/:role", async (req: Request, res: Response) => {

reportRouter.get("/:id", async (req: Request, res: Response) => {
const { id } = req.params;
const list = await db.getById(id);
return res.json({ data: list });
const data = await db.getById(id);

if (!data) return res.status(404).send();

return res.json({ data });
});

reportRouter.put("/:id", async (req: Request, res: Response) => {
const { id } = req.params;
const { description, investigation_notes } = req.body;

await knex("incidents").where({ id }).update({ description, investigation_notes });

return res.json({ data: {}, messages: [{ variant: "success", text: "Incident Saved" }] });
});

reportRouter.post("/", async (req: Request, res: Response) => {
const {} = req.body;
req.body.email = req.user.email;
req.body.status = "Initial Report";

let { date, eventType, description, location_code, location_detail, supervisor_email, on_behalf, on_behalf_email } =
req.body;
let {
date,
eventType,
description,
location_code,
location_detail,
supervisor_email,
on_behalf,
on_behalf_email,
urgency,
} = req.body;

const reporting_person_email = on_behalf == "Yes" ? on_behalf_email : req.user.email;

Expand Down Expand Up @@ -93,7 +114,7 @@ reportRouter.post("/", async (req: Request, res: Response) => {
reopen_count: 0,
created_at: InsertableDate(new Date().toISOString()),
reported_at: InsertableDate(date),
urgency_code: Urgencies.MEDIUM.code,
urgency_code: urgency,
} as Hazard;

const incident = {
Expand All @@ -107,7 +128,7 @@ reportRouter.post("/", async (req: Request, res: Response) => {
incident_type_id: defaultIncidentType.id,
reporting_person_email,
proxy_user_id: req.user.id,
urgency_code: Urgencies.MEDIUM.code,
urgency_code: urgency,
} as Incident;

const insertedIncidents = await trx("incidents").insert(incident).returning("*");
Expand Down
18 changes: 11 additions & 7 deletions api/src/services/incident-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,18 @@ export class IncidentService {

item.steps = await db("incident_steps").where({ incident_id: item.id }).orderBy("order");
item.actions = await db("actions").where({ incident_id: item.id }).orderBy("due_date");
item.hazards = await db("incident_hazards").where({ incident_id: item.id });

for (let hazard of item.hazards) {
hazard.hazard = await db("hazards").where({ id: hazard.hazard_id }).first();
}
item.hazards = await db("incident_hazards")
.where({ incident_id: item.id })
.innerJoin("incident_hazard_types", "incident_hazards.incident_hazard_type_code", "incident_hazard_types.code")
.select("incident_hazards.*", "incident_hazard_types.name as incident_hazard_type_name");

for (let hazard of item.hazards) {
hazard.hazard = await db("hazards").where({ id: hazard.hazard_id }).first();
for (let hazard of item.hazards ?? []) {
hazard.hazard = await db("hazards")
.where({ "hazards.id": hazard.hazard_id })
.innerJoin("hazard_types", "hazards.hazard_type_id", "hazard_types.id")
.innerJoin("locations", "hazards.location_code", "locations.code")
.select("hazards.*", "hazard_types.name as hazard_type_name", "locations.name as location_name")
.first();
}

for (let action of item.actions) {
Expand Down
64 changes: 54 additions & 10 deletions web/src/components/hazard/HazardList.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,46 @@
<template>
<div v-if="selectedReport">


<!-- {{ selectedReport.hazards }} -->


<div v-if="selectedReport.hazards && selectedReport.hazards.length > 0">
<v-list bg-color="#ffffff00" rounded class="py-0" style="border: 1px #00000066 solid">
<div v-for="(hazard, idx) of selectedReport.hazards">
<v-list-item
:title="hazard.hazard.description"
:subtitle="`&nbsp; Urgency: ${hazard.hazard.urgency_code}, Status: ${hazard.hazard.status_code}`"
style="background-color: #ffffff"
class="pt-3 pb-3">
<!-- <template #append>
<v-list-item style="background-color: #ffffff" class="pt-3 pb-3">
<!-- <template #append>
<v-btn fab class="my-0" color="success" size="x-small" icon="mdi-check"></v-btn>
</template> -->
<h6 class="text-body-1 mt-0 mb-2">Hazard {{ idx + 1 }} ({{ hazard.incident_hazard_type_name }})</h6>

<table class="info-table" cellpadding="0" cellspacing="0">
<tr>
<td style="width: 150px">Current Status</td>
<td>{{ hazard.hazard.status_code }}</td>
</tr>
<tr>
<td>General Location</td>
<td>{{ hazard.hazard.location_name }}</td>
</tr>
<tr>
<td>Specific Location</td>
<td>{{ hazard.hazard.location_detail }}</td>
</tr>
<tr>
<td>Urgency</td>
<td>{{ hazard.hazard.urgency_code }}</td>
</tr>
<tr>
<td>Hazard Type</td>
<td>{{ hazard.hazard.hazard_type_name }}</td>
</tr>
<tr>
<td>First Reported</td>
<td>{{ formatDate(hazard.hazard.reported_at) }}</td>
</tr>
<tr>
<td>Description</td>
<td>{{ hazard.hazard.description }}</td>
</tr>
</table>
</v-list-item>
<v-divider v-if="idx < selectedReport.hazards.length - 1" />
</div>
Expand All @@ -25,10 +50,29 @@
</div>
</template>

<script setup lang="ts">
<script setup>
import { DateTime } from "luxon";
import { storeToRefs } from "pinia";
import { useReportStore } from "@/store/ReportStore";
const reportStore = useReportStore();
const { selectedReport } = storeToRefs(reportStore);
function formatDate(input) {
if (!input) return "";
return DateTime.fromISO(input.toString()).toFormat("yyyy/MM/dd @ h:ma");
}
</script>

<style>
table.info-table {
width: 100%;
border-top: 1px #ccc solid;
border-left: 1px #ccc solid;
}
table.info-table td {
border-bottom: 1px #ccc solid;
border-right: 1px #ccc solid;
padding: 4px 10px;
}
</style>
14 changes: 12 additions & 2 deletions web/src/components/incident/CreateButton.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
<template>
<v-btn color="success" to="/report-an-incident" size="large">
<v-btn v-if="isOffline" color="success" to="/report-an-incident-offline" size="large">
<v-icon class="mr-4">mdi-clipboard-alert-outline</v-icon>
Report an Incident Offline</v-btn
>
<v-btn v-else color="success" to="/report-an-incident" size="large">
<v-icon class="mr-4">mdi-clipboard-alert-outline</v-icon>
Report an Incident</v-btn
>
</template>

<script setup lang="ts"></script>
<script setup lang="ts">
import { storeToRefs } from "pinia";
import { useInterfaceStore } from "@/store/InterfaceStore";
const interfaceStore = useInterfaceStore();
const { isOffline } = storeToRefs(interfaceStore);
</script>
28 changes: 8 additions & 20 deletions web/src/components/incident/CreatePage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -172,10 +172,6 @@

<div class="d-flex">
<v-btn color="primary" @click="saveReport" class="mb-0" :disabled="!canSave">Submit </v-btn>

<div v-if="isAuthenticated == false" class="pt-6 ml-4 text-warning">
* You must be authenticated to submit
</div>
</div>
</v-card-text>
</v-card>
Expand All @@ -186,42 +182,34 @@
<script setup>
import { computed, ref, watch } from "vue";
import { storeToRefs } from "pinia";
import { useAuth0 } from "@auth0/auth0-vue";
import { AuthHelper } from "@/plugins/auth";
import { router } from "@/routes";
import { useReportStore } from "@/store/ReportStore";
import { useInterfaceStore } from "@/store/InterfaceStore";
import DateTimeSelector from "@/components/DateTimeSelector.vue";
import DirectorySelector from "@/components/DirectorySelector.vue";
const reportStore = useReportStore();
const { initialize, addReport } = reportStore;
const { locations, urgencies } = storeToRefs(reportStore);
const interfaceStore = useInterfaceStore();
const { showOverlay, hideOverlay } = interfaceStore;
await initialize();
const report = ref({ eventType: null, date: new Date(), urgency: "Medium" });
const canSave = computed(() => {
return report.value.eventType && isAuthenticated.value == true;
return report.value.eventType;
});
const auth = useAuth0();
const isAuthenticated = computed(() => {
return AuthHelper.isAuthenticated.value;
});
watch(
() => auth.isAuthenticated,
(nv, ov) => {
console.log("AUTH CHANGED", nv, ov);
}
);
async function saveReport() {
report.value.createDate = new Date();
showOverlay();
await addReport(report.value).then(() => {
hideOverlay();
router.push("/report-an-incident/complete");
});
}
Expand Down
Loading

0 comments on commit c6c1166

Please sign in to comment.