Skip to content

Commit

Permalink
Enhance Component Flexibility and Add New Features for Worksite Manag…
Browse files Browse the repository at this point in the history
…ement (#1314)

* feat: enhance personal info filters with dynamic field handling

- Replace hardcoded personal info checkboxes with dynamic field rendering
- Add support for `select` and `checkbox` field types based on incident form fields
- Introduce `propertyInfoFields` computed property to manage dynamic fields

* feat: add role description modal in OtherOrganizations table

- Add `showRoleDescription` function to display detailed modal for roles.
- Update role column template to include help icon triggering the modal.
- Extend `useDialogs` to include `confirm` for modal functionality.

* feat(map): add functionality to manage and filter location layers
- Add `removeLocationLayers` method to clear specific map layers in `useWorksiteMap`
- Implement debounce-based search for locations in `Work` page with dynamic filtering
- Introduce a `getAndApplyLocation` method to retrieve and apply location data on the map

feat(ui): enhance `BaseSelect` for improved styling and dynamic class handling
- Add `wrapperClasses` prop for customizable wrapper styling
- Adjust computed classes to include external `wrapperClasses` prop

feat(search): enhance `WorksiteSearchInput` with `search` icon support

feat(work-page): add location search dropdown to filter map by county or postal code
- Integrate `BaseSelect` to support location-based filtering with search and selection
- Style location dropdown with focused and consistent layout

* feat: add spinner for worksite downloads and separate loading state

- **Add** `Spinner` component to indicate worksite downloads progress
- **Introduce** `downloadingWorksites` ref to separate download-specific loading state
- **Replace** `loading` with `downloadingWorksites` in `downloadWorksites` function
- **Register** `Spinner` component in `Work.vue`
  • Loading branch information
tabiodun authored Dec 13, 2024
1 parent 9dd21f7 commit 984b460
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 26 deletions.
15 changes: 13 additions & 2 deletions src/components/BaseSelect.vue
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ export default defineComponent({
default:
'border relative mx-auto w-full flex items-center justify-end cursor-pointer bg-white text-base leading-snug outline-none',
},
wrapperClasses: {
type: String,
default:
'relative mx-auto w-full flex items-center justify-end box-border cursor-pointer outline-none min-h-[theme(spacing.12)]',
},
options: {
type: [Array, Function],
default() {
Expand Down Expand Up @@ -237,11 +242,13 @@ export default defineComponent({
const containerClassesObj = props.containerClasses
? { container: props.containerClasses }
: {};
const wrapperClassesObj = props.wrapperClasses
? { wrapper: props.wrapperClasses }
: {};
return {
...searchClassesObj,
...containerClassesObj,
wrapper:
'relative mx-auto w-full flex items-center justify-end box-border cursor-pointer outline-none min-h-[theme(spacing.12)]',
...wrapperClassesObj,
optionSelected: 'text-white bg-crisiscleanup-dark-200',
optionPointed: 'text-gray-800 bg-crisiscleanup-dark-100',
optionSelectedPointed:
Expand Down Expand Up @@ -372,4 +379,8 @@ export default defineComponent({
.multiselect-tags-search {
@apply h-full;
}
.multiselect-placeholder {
@apply text-crisiscleanup-dark-200;
}
</style>
105 changes: 86 additions & 19 deletions src/components/work/WorksiteFilters.vue
Original file line number Diff line number Diff line change
Expand Up @@ -324,25 +324,77 @@
<div class="my-1 text-base">
{{ $t('worksiteFilters.personal_info') }}
</div>
<base-checkbox
v-for="data in [
'older_than_60',
'children_in_home',
'first_responder',
'veteran',
]"
:key="data"
:data-testid="`testPersonal${data}Checkbox`"
class="block my-1"
:model-value="filters.form_data.data[data]"
@update:model-value="
(value) => {
filters.form_data.data[data] = value;
filters.form_data.data = { ...filters.form_data.data };
}
"
>{{ $t(`formLabels.${data}`) }}
</base-checkbox>
<template
v-for="field in propertyInfoFields"
:key="field.field_key"
>
<div class="py-1">
<template v-if="field.html_type === 'select' && field.values">
<div class="font-bold">
{{ $t(field.label_t) }}
</div>
<div>
<div>
<div
v-for="option in field.values.filter((option) =>
Boolean(option.value),
)"
:key="option.value"
:span="8"
>
<base-checkbox
:model-value="
filters.form_data.data[field.field_key] &&
filters.form_data.data[field.field_key].includes(
option.value,
)
"
@update:model-value="
(value) => {
filters.form_data.data[field.field_key] = value
? [
...(filters.form_data.data[
field.field_key
] || []),
option.value,
]
: filters.form_data.data[
field.field_key
].filter((item) => item !== option.value);
filters.form_data.data = {
...filters.form_data.data,
};
}
"
>
{{ $t(option.name_t) }}
</base-checkbox>
</div>
</div>
</div>
</template>
<template v-if="field.html_type === 'checkbox'">
<div class="flex">
<div>
<base-checkbox
:model-value="filters.form_data.data[field.field_key]"
@update:model-value="
(value: Boolean) => {
if (value) {
filters.form_data.data[field.field_key] = true;
} else {
delete filters.form_data.data[field.field_key];
}
}
"
>
{{ $t(field.label_t) }}
</base-checkbox>
</div>
</div>
</template>
</div>
</template>
</div>
</div>
<template v-if="currentSection === 'work'">
Expand Down Expand Up @@ -858,6 +910,20 @@ export default defineComponent({
return [];
});
const propertyInfoFields = computed(() => {
if (props.incident && props.incident.form_fields) {
return props.incident.form_fields.filter((field) => {
return (
field.field_parent_key === 'property_info' &&
['select', 'checkbox'].includes(field.html_type)
);
});
}
return [];
});
const fieldsCount = computed(() => {
return filters.value.fields.getCount() || 0;
});
Expand Down Expand Up @@ -1256,6 +1322,7 @@ export default defineComponent({
currentLocationType,
locationTypes,
createNewLocation,
propertyInfoFields,
};
},
});
Expand Down
1 change: 1 addition & 0 deletions src/components/work/WorksiteSearchInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
:input-style="{
'border-right': icon || tooltip ? '0' : '1px solid #dadada',
}"
icon="search"
@update:model-value="debouncedSearch"
@input.stop=""
@focus="onFocus"
Expand Down
10 changes: 10 additions & 0 deletions src/hooks/worksite/useWorksiteMap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export interface MapUtils {
hideMarkers: () => void;
showMarkers: () => void;
switchTileLayer: (useGoogleMaps: boolean) => void;
removeLocationLayers: () => void;
}

const DEFAULT_MAP_BOUNDS = [
Expand Down Expand Up @@ -334,6 +335,14 @@ export default (
}
};

async function removeLocationLayers() {
map.eachLayer((layer) => {
if ((layer as L.Layer & PixiLayer).location_id) {
map.removeLayer(layer);
}
});
}

async function applyLocation(locationId: string, value: boolean) {
if (value && map) {
await Location.api().fetchById(locationId);
Expand Down Expand Up @@ -436,6 +445,7 @@ export default (
hideMarkers,
showMarkers,
switchTileLayer,
removeLocationLayers,
};
return mapUtils;
};
30 changes: 28 additions & 2 deletions src/pages/OtherOrganizations.vue
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,18 @@
<base-text>{{ $t(slotProps.item.type_t) }}</base-text>
</template>
<template #role_t="slotProps">
<base-text>{{ $t(slotProps.item.role_t) }}</base-text>
<div class="flex items-center">
<base-text>{{ $t(slotProps.item.role_t) }}</base-text>
<ccu-icon
type="help"
size="medium"
:action="
() => {
showRoleDescription(slotProps.item);
}
"
/>
</div>
</template>
<template #case_overdue_count="slotProps">
<base-button
Expand Down Expand Up @@ -318,7 +329,7 @@ export default {
const tableUrl = '/other_organizations';
const { currentIncidentId } = useCurrentIncident();
const { component } = useDialogs();
const { component, confirm } = useDialogs();
const { t } = useI18n();
const getColumnWidth = (key) => {
Expand Down Expand Up @@ -432,6 +443,20 @@ export default {
}
};
async function showRoleDescription(organization) {
await confirm({
title: t(organization.role_t),
content: `
<div class="p-1">
<p>${t(organization.role_description_t)}</p>
</div>
<div class="p-1">
<p>${t(organization.role_limitations_t)}</p>
</div>
`,
});
}
watch(selectedColumns, updateColumns);
onMounted(async () => {
Expand All @@ -455,6 +480,7 @@ export default {
search,
downloadCsv,
showCapabilities,
showRoleDescription,
};
},
};
Expand Down
62 changes: 59 additions & 3 deletions src/pages/Work.vue
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,23 @@
@updated-filters="onUpdateFilters"
@updated-filter-labels="updateFilterLabels"
/>
<base-select
data-testid="testLocationSelect"
searchable
item-key="id"
label="name"
:placeholder="$t('~~Search by county or postal code')"
:options="onLocationSearch"
select-classes="bg-white outline-none w-72"
wrapper-classes="relative mx-auto w-full flex items-center justify-end box-border cursor-pointer outline-none h-10"
class="mr-2"
size="small"
@update:model-value="
(location) => {
getAndApplyLocation(location);
}
"
/>
<WorksiteActions
v-if="currentIncidentId"
:key="currentIncidentId"
Expand All @@ -424,6 +441,7 @@
@toggle-heat-map="toggleHeatMap"
@toggle-user-locations="toggleUserLocations"
/>
<spinner v-if="downloadingWorksites" size="small" />
</div>
<div
:class="collapsedUtilityBar ? 'w-full' : ''"
Expand Down Expand Up @@ -999,7 +1017,7 @@ import useDialogs from '../hooks/useDialogs';
import type { MapUtils } from '../hooks/worksite/useWorksiteMap';
import useWorksiteMap from '../hooks/worksite/useWorksiteMap';
import { numeral } from '@/utils/helpers';
import type Location from '@/models/Location';
import Location from '@/models/Location';
import UpdateCaseStatus from '@/components/UpdateCaseStatus.vue';
import useWorksiteTableActions from '@/hooks/worksite/useWorksiteTableActions';
import ShareWorksite from '@/components/modals/ShareWorksite.vue';
Expand All @@ -1020,10 +1038,13 @@ import AjaxTable from '@/components/AjaxTable.vue';
import { momentFromNow } from '@/filters';
import User from '@/models/User';
import { string } from 'zod';
import _ from 'lodash';
import Spinner from '@/components/Spinner.vue';
export default defineComponent({
name: 'Work',
components: {
Spinner,
AjaxTable,
BaseButton,
WorksiteSearchAndFilters,
Expand Down Expand Up @@ -1106,6 +1127,7 @@ export default defineComponent({
const collapsedForm = ref<boolean>(false);
const collapsedUtilityBar = ref<boolean>(false);
const loading = ref<boolean>(false);
const downloadingWorksites = ref<boolean>(false);
const allWorksiteCount = ref<number>(0);
const filteredWorksiteCount = ref<number>(0);
const searchWorksites = ref<any[]>([]);
Expand Down Expand Up @@ -1848,7 +1870,7 @@ export default defineComponent({
}
async function downloadWorksites(ids: any[], skipSizeCheck = false) {
loading.value = true;
downloadingWorksites.value = true;
try {
let params;
Expand Down Expand Up @@ -1903,7 +1925,7 @@ export default defineComponent({
} catch (error) {
await $toasted.error(getErrorMessage(error));
} finally {
loading.value = false;
downloadingWorksites.value = false;
}
}
Expand Down Expand Up @@ -1961,6 +1983,23 @@ export default defineComponent({
filterLabels.value = labels;
}
const onLocationSearch = _.debounce(async (value: string) => {
const params = {
type__key__in:
'boundary_political_home_local_division,boundary_political_home_postal_code,boundary_political_home_city',
incident_area: currentIncidentId.value,
limit: 10,
search: value,
sort: 'name',
fields: 'id,name,type',
};
const { data } = await axios.get(`/locations`, {
params,
});
return data.results;
}, 1000); // Every 300ms
watch(
() => worksiteQuery.value,
(value, previousValue) => {
Expand Down Expand Up @@ -2144,6 +2183,20 @@ export default defineComponent({
router.replace({ query: undefined });
}
async function getAndApplyLocation(locationId) {
if (!locationId) {
mapUtils?.removeLocationLayers();
goToIncidentCenter();
return;
}
await Location.api().fetchById(locationId);
applyLocation({
locationId: locationId,
value: true,
});
}
return {
addMarkerToMap,
clearCase,
Expand Down Expand Up @@ -2239,6 +2292,9 @@ export default defineComponent({
currentIncident,
userLocations,
toggleUserLocations,
onLocationSearch,
getAndApplyLocation,
downloadingWorksites,
};
},
});
Expand Down

0 comments on commit 984b460

Please sign in to comment.