Skip to content
This repository has been archived by the owner on Feb 5, 2024. It is now read-only.

Frontend: emails opmaken en versturen #423

Merged
merged 21 commits into from
May 19, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions web/src/assets/scripts/templateParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { BuildingQuery, Result } from "@selab-2/groep-1-query";

export function parseTemplate(
building: Result<BuildingQuery>,
template: string,
) {
const variables: { name: string; description: string }[] = [
{
name: "\\$\\(syndicus_voornaam\\)",
description: building.syndicus.user.first_name,
},
{
name: "\\$\\(syndicus_achternaam\\)",
description: building.syndicus.user.last_name,
},
{
name: "\\$\\(syndicus_naam\\)",
description: `${building.syndicus.user.first_name} ${building.syndicus.user.last_name}`,
},
{ name: "\\$\\(gebouw_naam\\)", description: building.name },
{
name: "\\$\\(gebouw_adres\\)",
description: `${building.address.street} ${building.address.number}, ${building.address.zip_code} ${building.address.city}`,
},
{ name: "\\$\\(ivago_id\\)", description: building.ivago_id },
];

let parsedString = template;
variables.forEach((variable) => {
const { name, description } = variable;
parsedString = parsedString.replace(new RegExp(name, "g"), description);
});

return parsedString;
}
6 changes: 6 additions & 0 deletions web/src/layouts/MainLayout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@
title="Rondes"
value="rounds"
></v-list-item>
<v-list-item
:to="{ name: 'template_overview' }"
prepend-icon="mdi-email"
title="Templates"
value="templates"
></v-list-item>
</div>
</v-list>

Expand Down
32 changes: 31 additions & 1 deletion web/src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ import BuildingScreen from "@/views/building/BuildingScreen.vue";
import BuildingFollowup from "@/views/building/BuildingFollowup.vue";
import GarbageMaker from "@/views/building/GarbageMaker.vue";
import UserOverview from "@/views/dashboard/Users.vue";
import TemplateOverview from "@/views/dashboard/Template.vue";
import BuildingOverview from "@/views/dashboard/Buildings.vue";
import RoundOverview from "@/views/dashboard/Round.vue";
import ContactSyndicus from "@/views/contact/ContactSyndicus.vue";
import Auth from "@/views/dev/Auth.vue";
import { useAuthStore } from "@/stores/auth";
import TryOrAlert from "@/views/dev/TryOrAlert.vue";
import Round from "@/views/round/Round.vue";
import TemplateBuilder from "@/views/contact/TemplateBuilder.vue";

const routes: any[] = [
{
Expand Down Expand Up @@ -183,7 +185,7 @@ const routes: any[] = [
},
},
{
path: "/contact",
path: "/contact/:id?",
name: "contact_syndicus",
component: ContactSyndicus,
meta: {
Expand Down Expand Up @@ -266,6 +268,34 @@ const routes: any[] = [
) => superstudent || admin,
},
},
{
path: "/sjabloon/nieuw/:id?",
component: TemplateBuilder,
name: "template_new",
meta: {
title: "",
auth: (
student: boolean,
superstudent: boolean,
syndicus: boolean,
admin: boolean,
) => superstudent || admin,
},
},
{
path: "/sjabloon",
component: TemplateOverview,
name: "template_overview",
meta: {
title: "Sjablonen",
auth: (
student: boolean,
superstudent: boolean,
syndicus: boolean,
admin: boolean,
) => superstudent || admin,
},
},
],
},
];
Expand Down
71 changes: 71 additions & 0 deletions web/src/types/Template.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { Header } from "@/components/table/Header";
import { TableEntity } from "@/components/table/TableEntity";
import { RowType } from "@/components/table/RowType";
import { MailTemplateQuery, Result } from "@selab-2/groep-1-query";
import { tryOrAlertAsync } from "@/try";
import router from "@/router";

export class Template implements TableEntity<Result<MailTemplateQuery>> {
headers(): Array<Header<Result<MailTemplateQuery>>> {
return Template.headers();
}

static headers(): Array<Header<Result<MailTemplateQuery>>> {
return [
{
id: 0,
name: "Template naam",
fit: false,
get: (e: Result<MailTemplateQuery>) => e.name,
type: RowType.TEXT,
sortable: true,
},
{
id: 1,
name: "",
fit: true,
get: () => "mdi-pencil",
type: RowType.ICONBUTTON,
sortable: false,
onClick: (e: Result<MailTemplateQuery>) => {
router.push({ name: "template_new", params: { id: e.id } });
},
},
{
id: 2,
name: "",
fit: true,
get: () => "mdi-close",
type: RowType.ICONBUTTON,
sortable: false,
onClick: (
e: Result<MailTemplateQuery>,
list: Array<Result<MailTemplateQuery> | null>,
) => {
tryOrAlertAsync(async () => {
await new MailTemplateQuery().deleteOne({ id: e.id });
const index = list.findIndex((x) => x === e);
list[index] = null;
});
},
},
].map((e) => new Header<Result<MailTemplateQuery>>(e));
}

route(item: Result<MailTemplateQuery>): {
name: string;
params: { id: number };
} {
return Template.route(item);
}

static route(item: Result<MailTemplateQuery>): {
name: string;
params: { id: number };
} {
return {
name: "building_id",
params: { id: item.id },
};
}
}
11 changes: 5 additions & 6 deletions web/src/views/building/BuildingScreen.vue
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ import DateRange from "@/components/filter/DateRange.vue";
import { daysFromDate } from "@/assets/scripts/date";
import SyndicusButtons from "@/components/building/SyndicusButtons.vue";
import CardPopup from "@/components/popups/CardPopup.vue";
import router from "@/router";

const showRemovePopup = ref(false);

Expand All @@ -244,18 +245,16 @@ function getBuildingDescription(): string {
return castedBuilding.description;
}

function mail() {
router.push({ name: "contact_syndicus", params: { id: building.value?.id } });
}

function call(number: string | undefined) {
if (number) {
location.href = "tel:" + number;
}
}

function mail(address: string | undefined) {
if (address) {
location.href = "mailto:" + address;
}
}

function tomaps() {
window.open(
`https://maps.google.com/maps?q=${building.value?.address.number}+${building.value?.address.street},+${building.value?.address.city},+${building.value?.address.zip_code}`,
Expand Down
115 changes: 60 additions & 55 deletions web/src/views/contact/ContactSyndicus.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,17 @@
</v-select>
</v-col>

<v-col cols="3" class="flex-grow-0 flex-shrink-0 py-0 my-0">
<v-col cols="5" class="flex-grow-1 flex-shrink-0 py-0 my-0">
<v-select
label="Sjabloon"
v-model="selected_template"
:items="templates"
:items="allTemplates"
:item-value="'name'"
:item-title="'name'"
return-object
prepend-inner-icon="mdi-list-box"
:disabled="building === null"
@update:model-value="updateFields()"
>
</v-select>
</v-col>
Expand All @@ -63,90 +64,94 @@
class="flex-grow-1 flex-shrink-0 py-0 my-0"
>
<v-text-field
v-model="subject"
prepend-inner-icon="mdi-text-short"
label="Onderwerp"
:model-value="selected_template.subject"
>
</v-text-field>
</v-col>
</v-row>
<v-row class="py-0 my-0 mx-3"
><v-col
cols="1"
style="min-width: 100px; max-width: 100%"
class="flex-grow-1 flex-shrink-0 py-0 my-0"
><v-file-input
prepend-icon=""
prepend-inner-icon="mdi-upload"
label="Bestanden"
></v-file-input></v-col
></v-row>
<v-row class="py-0 my-0 mx-3"
><v-col
cols="1"
style="min-width: 100px; max-width: 100%"
class="flex-grow-1 flex-shrink-0 py-0 my-0"
>
<v-textarea
rows="17"
label="Inhoud"
:model-value="selected_template.content"
></v-textarea
<v-textarea v-model="content" rows="17" label="Inhoud"></v-textarea
></v-col>
</v-row>
<v-btn
color="primary"
prepend-icon="mdi-send"
variant="tonal"
class="mb-6 ml-6"
@click="sendMail()"
>Versturen</v-btn
>
</BorderCard>
</HFillWrapper>
</template>

<script lang="ts" setup>
import Template from "@/components/models/Template";
import { ref, computed, reactive, Ref } from "vue";
import { ref, onMounted } from "vue";
import HFillWrapper from "@/layouts/HFillWrapper.vue";
import BorderCard from "@/layouts/CardLayout.vue";
import { tryOrAlertAsync } from "@/try";
import { Result, BuildingQuery } from "@selab-2/groep-1-query";
import {
Result,
BuildingQuery,
MailTemplateQuery,
MailQuery,
} from "@selab-2/groep-1-query";
import { parseTemplate } from "@/assets/scripts/templateParser";
import { useRoute } from "vue-router";

const allTemplates = ref<Result<MailTemplateQuery>[]>([]);
const selected_template = ref<Result<MailTemplateQuery>>();

const buildings = ref<Result<BuildingQuery>[]>();
const building = ref<Result<BuildingQuery>>();

const subject = ref<string>("");
const content = ref<string>("");

const route = useRoute();
const buildingId: number = Number(route.params.id);

onMounted(() => {
tryOrAlertAsync(async () => {
allTemplates.value = await new MailTemplateQuery().getAll();
});

const building: Ref<Result<BuildingQuery> | null> = ref(null);
tryOrAlertAsync(async () => {
buildings.value = await new BuildingQuery().getAll();
});

const buildings =
(await tryOrAlertAsync<Array<Result<BuildingQuery>>>(async () => {
return await new BuildingQuery().getAll({});
})) ?? [];
tryOrAlertAsync(async () => {
building.value = await new BuildingQuery().getOne(buildingId);
});
});

const templates: Template[] = reactive([
{ name: "Andere", subject: "", content: "" },
{
name: "Opmerking",
subject: computed(() => `Opmerking: ${building.value?.name}`),
content: computed(
() =>
`Beste ${building?.value?.syndicus?.user.first_name},\n\nWij hebben een opmerking ontvangen..\n\nMet vriendelijke groeten,\nDr Trottoir team`,
),
},
{
name: "Klacht",
subject: computed(() => `Klacht: ${building.value?.name}`),
content: computed(
() =>
`Beste ${building?.value?.syndicus?.user.first_name},\n\nWij zijn ontevreden met..\n\nMet vriendelijke groeten,\nDr Trottoir team`,
),
},
{
name: "Schade",
subject: computed(() => `Schade: ${building.value?.name}`),
content: computed(
() =>
`Beste ${building?.value?.syndicus?.user.first_name},Wij hebben schade vastgesteld..\n\nMet vriendelijke groeten,\nDr Trottoir team`,
),
},
]);
function parseString(str: string | undefined): string {
if (building.value && str) {
return parseTemplate(building.value, str);
}
return "";
}
function updateFields() {
subject.value = parseString(selected_template?.value?.mail_subject);
content.value = parseString(selected_template?.value?.content);
}
function sendMail() {
tryOrAlertAsync(async () => {
await new MailQuery().createOne({
to: building.value?.syndicus.user.email,
subject: subject.value,
content: content.value,
});

const selected_template = ref<Template>(templates[0]);
subject.value = "";
content.value = "";
});
}
</script>
Loading