Skip to content

Commit

Permalink
Implement project detail view, updates to project list view
Browse files Browse the repository at this point in the history
  • Loading branch information
wilwong89 committed Jul 26, 2024
1 parent a170cc2 commit eea38c1
Show file tree
Hide file tree
Showing 14 changed files with 579 additions and 56 deletions.
1 change: 1 addition & 0 deletions app/src/controllers/submission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ const controller = {
next: NextFunction
) => {
try {
// TBD: Implement filtering so proponents can only search for their own submissions
const response = await submissionService.searchSubmissions({
...req.query,
includeUser: isTruthy(req.query.includeUser)
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/assets/variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ $app-hover: #4696EC;
$app-outline-on-primary: #fff;
$app-out-of-focus: #ccc;
$app-error: #D8292F;
$app-green: #2e8540;
$app-grey: #868585;

// highlighted sections, table rows
$app-highlight-background: #d9e1e8;
Expand Down
11 changes: 10 additions & 1 deletion frontend/src/components/housing/enquiry/EnquiryIntakeForm.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script setup lang="ts">
import { storeToRefs } from 'pinia';
import { Form } from 'vee-validate';
import { onBeforeMount, ref, toRaw } from 'vue';
import { onBeforeMount, onMounted, ref, toRaw } from 'vue';
import { useRouter } from 'vue-router';
import { object, string } from 'yup';
Expand All @@ -22,11 +22,13 @@ import type { Ref } from 'vue';
// Props
type Props = {
activityId?: string;
confirmationId?: string;
enquiryId?: string;
};
const props = withDefaults(defineProps<Props>(), {
activityId: undefined,
confirmationId: undefined,
enquiryId: undefined
});
Expand Down Expand Up @@ -194,6 +196,11 @@ onBeforeMount(async () => {
if (props.enquiryId) {
response = (await enquiryService.getEnquiry(props.enquiryId)).data;
editable.value = response.intakeStatus === IntakeStatus.DRAFT;
} else if (props.confirmationId) {
response = {
isRelated: BasicResponse.YES,
relatedActivityId: props.confirmationId
};
}
// Default form values
Expand All @@ -217,6 +224,8 @@ onBeforeMount(async () => {
};
});
onMounted(() => {});
async function emailConfirmation(activityId: string) {
const configCC = getConfig.value.ches?.submission?.cc;
const body = confirmationTemplate({
Expand Down
64 changes: 64 additions & 0 deletions frontend/src/components/housing/projects/ProjectDetail.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<script setup lang="ts">
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { Spinner } from '@/components/layout';
import { Button, Column, DataTable, useConfirm, useToast } from '@/lib/primevue';
import { submissionService } from '@/services';
import { RouteName } from '@/utils/enums/application';
import { IntakeStatus } from '@/utils/enums/housing';
import { formatDate } from '@/utils/formatters';
import type { Ref } from 'vue';
import type { Submission } from '@/types';
// Props
type Props = {
loading: boolean;
submission: Submission;
};
const props = withDefaults(defineProps<Props>(), {});
// State
const selection: Ref<Submission | undefined> = ref(undefined);
// Actions
// const confirm = useConfirm();
// const toast = useToast();
const router = useRouter();
</script>

<template>
<div>
<Button
class="p-0"
text
>
<router-link :to="{ name: RouteName.HOUSING }">
<span class="app-primary-color">Housing</span>
</router-link>
</Button>
/
<Button
class="p-0"
text
>
<router-link :to="{ name: RouteName.HOUSING_PROJECTS }">
<span class="app-primary-color">Applications and Permits</span>
</router-link>
</Button>
/
<span class="font-bold">{{ 'Temp project name' }}</span>
</div>
<h1>Application and Permits</h1>
<div class="mt-1 mb-3 flex justify-content-between">
<h3 class="mb-0">My Projects</h3>
<Button
class="p-button-sm"
outlined
label="+ New project investigation"
@click="router.push({ name: RouteName.HOUSING_SUBMISSION_INTAKE })"
/>
</div>
</template>
23 changes: 23 additions & 0 deletions frontend/src/components/housing/projects/ProjectPermitCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<script setup lang="ts">
// Props
// type Props = {
// activityId: string;
// submissionId: string;
// };
// const props = withDefaults(defineProps<Props>(), {});
// State
// Actions
onMounted(async () => {
loading.value = false;
});
</script>

<template>
<div>TEST</div>
</template>

<style scoped lang="scss"></style>
26 changes: 4 additions & 22 deletions frontend/src/components/housing/projects/ProjectsList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,11 @@
import { ref } from 'vue';
import { Spinner } from '@/components/layout';
import { Button, Column, DataTable, useConfirm, useToast } from '@/lib/primevue';
import { submissionService } from '@/services';
import { Column, DataTable } from '@/lib/primevue';
import { RouteName } from '@/utils/enums/application';
import { IntakeStatus } from '@/utils/enums/housing';
import { formatDate } from '@/utils/formatters';
import type { Ref } from 'vue';
import type { Submission } from '@/types';
import type { Ref } from 'vue';
// Props
type Props = {
Expand All @@ -23,13 +20,6 @@ const props = withDefaults(defineProps<Props>(), {});
const selection: Ref<Submission | undefined> = ref(undefined);
// Actions
// const confirm = useConfirm();
// const toast = useToast();
const sortTest = (e = 'meh') => {
console.log('sorttest', e);
return -1;
};
</script>

<template>
Expand All @@ -44,9 +34,7 @@ const sortTest = (e = 'meh') => {
:rows="10"
>
<template #empty>
<div class="flex justify-content-center">
<h3>No active projects</h3>
</div>
<div>No active projects at the moment.</div>
</template>
<template #loading>
<Spinner />
Expand All @@ -61,7 +49,7 @@ const sortTest = (e = 'meh') => {
<div :data-activityId="data.activityId">
<router-link
:to="{
name: RouteName.HOUSING_SUBMISSION_INTAKE,
name: RouteName.HOUSING_PROJECT,
query: { activityId: data.activityId, submissionId: data.submissionId }
}"
>
Expand All @@ -88,11 +76,5 @@ const sortTest = (e = 'meh') => {
<div>{{ data?.user?.firstName }} {{ data?.user?.lastName }}</div>
</template>
</Column>
<Column
field="submittedAt"
header="date"
:sortable="true"
style="min-width: 150px"
/>
</DataTable>
</template>
51 changes: 32 additions & 19 deletions frontend/src/components/roadmap/Roadmap.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ import { roadmapService, userService } from '@/services';
import { useConfigStore, useSubmissionStore, useTypeStore } from '@/store';
import { PermitNeeded, PermitStatus } from '@/utils/enums/housing';
import { roadmapTemplate } from '@/utils/templates';
import { delimitEmails, setEmptyStringsToNull } from '@/utils/utils';
import {
delimitEmails,
getPermitTypeNamesByNeeded,
getPermitTypeNamesByStatus,
setEmptyStringsToNull
} from '@/utils/utils';
import type { Ref } from 'vue';
import type { Document } from '@/types';
Expand Down Expand Up @@ -85,19 +90,19 @@ const confirmSubmit = (data: any) => {
});
};
function getPermitTypeNamesByStatus(status: string) {
return getPermits.value
.map((p) => getPermitTypes.value.find((pt) => pt.permitTypeId === p.permitTypeId && p.status === status)?.name)
.filter((pt) => !!pt)
.map((name) => name as string);
}
// function getPermitTypeNamesByStatus(status: string) {
// return getPermits.value
// .map((p) => getPermitTypes.value.find((pt) => pt.permitTypeId === p.permitTypeId && p.status === status)?.name)
// .filter((pt) => !!pt)
// .map((name) => name as string);
// }
function getPermitTypeNamesByNeeded(needed: string) {
return getPermits.value
.map((p) => getPermitTypes.value.find((pt) => pt.permitTypeId === p.permitTypeId && p.needed === needed)?.name)
.filter((pt) => !!pt)
.map((name) => name as string);
}
// function getPermitTypeNamesByNeeded(needed: string) {
// return getPermits.value
// .map((p) => getPermitTypes.value.find((pt) => pt.permitTypeId === p.permitTypeId && p.needed === needed)?.name)
// .filter((pt) => !!pt)
// .map((name) => name as string);
// }
function onFileRemove(document: Document) {
selectedFiles.value = selectedFiles.value.filter((x) => x.documentId !== document.documentId);
Expand Down Expand Up @@ -132,14 +137,22 @@ watchEffect(async () => {
}
// Permits
const permitStateNew = getPermitTypeNamesByStatus(PermitStatus.NEW).filter((value) =>
getPermitTypeNamesByNeeded(PermitNeeded.YES).includes(value)
const permitStateNew = getPermitTypeNamesByStatus(PermitStatus.NEW, getPermits.value, getPermitTypes.value).filter(
(value) => getPermitTypeNamesByNeeded(PermitNeeded.YES, getPermits.value, getPermitTypes.value).includes(value)
);
const permitPossiblyNeeded = getPermitTypeNamesByStatus(
PermitStatus.NEW,
getPermits.value,
getPermitTypes.value
).filter((value) =>
getPermitTypeNamesByNeeded(PermitNeeded.UNDER_INVESTIGATION, getPermits.value, getPermitTypes.value).includes(value)
);
const permitPossiblyNeeded = getPermitTypeNamesByStatus(PermitStatus.NEW).filter((value) =>
getPermitTypeNamesByNeeded(PermitNeeded.UNDER_INVESTIGATION).includes(value)
const permitStateApplied = getPermitTypeNamesByStatus(PermitStatus.APPLIED, getPermits.value, getPermitTypes.value);
const permitStateCompleted = getPermitTypeNamesByStatus(
PermitStatus.COMPLETED,
getPermits.value,
getPermitTypes.value
);
const permitStateApplied = getPermitTypeNamesByStatus(PermitStatus.APPLIED);
const permitStateCompleted = getPermitTypeNamesByStatus(PermitStatus.COMPLETED);
const body = roadmapTemplate({
'{{ contactName }}':
Expand Down
11 changes: 10 additions & 1 deletion frontend/src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,16 @@ const routes: Array<RouteRecordRaw> = [
{
path: 'projects',
name: RouteName.HOUSING_PROJECTS,
component: () => import('@/views/housing/ProjectsView.vue'),
component: () => import('@/views/housing/project/ProjectListView.vue'),
meta: {
access: [Permissions.NAVIGATION_HOUSING]
}
},
{
path: 'project',
name: RouteName.HOUSING_PROJECT,
component: () => import('@/views/housing/project/ProjectView.vue'),
props: createProps,
meta: {
access: [Permissions.NAVIGATION_HOUSING]
}
Expand Down
1 change: 1 addition & 0 deletions frontend/src/utils/enums/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export enum RouteName {
HOUSING_ENQUIRY = 'housing_enquiry',
HOUSING_ENQUIRY_INTAKE = 'housing_enquiry_intake',
HOUSING_GUIDE = 'housing_guide',
HOUSING_PROJECT = 'housing_project',
HOUSING_PROJECTS = 'housing_projects',
HOUSING_SUBMISSION = 'housing_submission',
HOUSING_SUBMISSION_INTAKE = 'housing_submission_intake',
Expand Down
31 changes: 31 additions & 0 deletions frontend/src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,37 @@ export function getFilenameAndExtension(filename: string): { filename: string; e
return { filename, extension: undefined };
}
}
import type { Permit, PermitType } from '@/types';

/**
* @function getPermitTypeNamesByStatus
* Returns an array of permits joined by permit types, filters by status, and returns the names
* @param {string} status permit status to be filtered for
* @param {Array<Permit>} permits array of permits needing filtering for name
* @param {Array<PermitType>} permitTypes arry of permit types with names
* @returns {filename: string, extension: string} An object containing filename and extension
*/
export function getPermitTypeNamesByStatus(status: string, permits: Array<Permit>, permitTypes: Array<PermitType>) {
return permits
.map((p) => permitTypes.find((pt) => pt.permitTypeId === p.permitTypeId && p.status === status)?.name)
.filter((pt) => !!pt)
.map((name) => name as string);
}

/**
* @function getPermitTypeNamesByStatus
* Returns an array of permits joined by permit types, filters by needed, and returns the names
* @param {string} status permit status to be filtered for
* @param {Array<Permit>} permits array of permits needing filtering for name
* @param {Array<PermitType>} permitTypes arry of permit types with names
* @returns {filename: string, extension: string} An object containing filename and extension
*/
export function getPermitTypeNamesByNeeded(needed: string, permits: Array<Permit>, permitTypes: Array<PermitType>) {
return permits
.map((p) => permitTypes.find((pt) => pt.permitTypeId === p.permitTypeId && p.needed === needed)?.name)
.filter((pt) => !!pt)
.map((name) => name as string);
}

/**
* @function isDebugMode
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/views/housing/enquiry/EnquiryIntakeView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import type { Ref } from 'vue';
// Props
type Props = {
activityId?: string;
confirmationId?: string;
enquiryId?: string;
};
const props = withDefaults(defineProps<Props>(), {
activityId: undefined,
confirmationId: undefined,
enquiryId: undefined
});
Expand All @@ -29,6 +31,7 @@ onMounted(async () => {
<EnquiryIntakeForm
v-if="!loading"
:activity-id="props.activityId"
:confirmation-id="props.confirmationId"
:enquiry-id="props.enquiryId"
/>
</template>
Loading

0 comments on commit eea38c1

Please sign in to comment.