Skip to content

Commit

Permalink
Merge pull request #680 from CrisisCleanup/tobi-develop
Browse files Browse the repository at this point in the history
feat(lists): Add lists functionality to work page
  • Loading branch information
tabiodun authored Jan 29, 2024
2 parents efcfc8a + b54fd09 commit d91a6c4
Show file tree
Hide file tree
Showing 6 changed files with 615 additions and 123 deletions.
5 changes: 5 additions & 0 deletions src/components/BaseButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ export default defineComponent({
type: Number,
default: 5000,
},
buttonClasses: {
type: Object,
default: () => ({}),
},
},
setup(props) {
Expand Down Expand Up @@ -131,6 +135,7 @@ export default defineComponent({
'items-center': true,
'justify-center': true,
'base-button': true,
...props.buttonClasses,
} as Record<string, string | boolean>;
if (props.variant) {
styleObject[props.variant] = true;
Expand Down
44 changes: 44 additions & 0 deletions src/hooks/lists/useCreateList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import useDialogs from '@/hooks/useDialogs';
import axios from 'axios';
import { i18n } from '@/modules/i18n';
import NewListForm from '@/pages/lists/NewListForm.vue';

export default function useCreateList(onComplete: () => void, model: string) {
const { component } = useDialogs();

const createList = async () => {
let newList = {};
const response = await component({
title: i18n.global.t('lists.create_new_list'),
component: NewListForm, // This should be your New List Form component
classes: 'w-full overflow-auto p-3',
modalClasses: 'bg-white max-w-3xl shadow',
props: {
model,
},
listeners: {
onNewList(payload: Record<string, any>) {
newList = payload;
},
},
});

if (response === 'ok' && newList) {
try {
await axios.post(
`${import.meta.env.VITE_APP_API_BASE_URL}/lists`,
newList,
);
i18n.global.t('lists.list_created_successfully');
} catch (error) {
console.error('Error creating list:', error);
}
}

onComplete();
};

return {
createList,
};
}
7 changes: 7 additions & 0 deletions src/pages/Work.vue
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,11 @@
</div>
<div v-if="showingTable" class="work-page__main-content--table">
<div class="items-center justify-end hidden md:flex">
<ListDropdown
:selected-table-items="selectedTableItems"
model-type="worksite_worksites"
:title="$t('~~Work Lists')"
/>
<base-button
class="ml-3 my-3 border p-1 px-4 bg-white"
data-testid="testPrintClaimedButton"
Expand Down Expand Up @@ -962,10 +967,12 @@ import type { Portal } from '@/models/types';
import WorksiteFeed from '@/components/WorksiteFeed.vue';
import { useRecentWorksites } from '@/hooks/useRecentWorksites';
import useAcl from '@/hooks/useAcl';
import ListDropdown from '@/pages/lists/ListDropdown.vue';
export default defineComponent({
name: 'Work',
components: {
ListDropdown,
WorksiteFeed,
WorksiteView,
WorksiteForm,
Expand Down
282 changes: 282 additions & 0 deletions src/pages/lists/ListDropdown.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
<template>
<v-popover placement="bottom-start">
<base-button
action="add"
suffix-icon="caret-down"
class="ml-3 my-3 border border-1.5 border-black p-1 px-4 bg-white"
>
{{ title }}
</base-button>
<template #popper>
<base-button
:button-classes="{ 'justify-start': true, 'justify-center': false }"
class="px-4 py-1 w-64 cursor-pointer hover:bg-primary-light"
:action="() => createList(getLists)"
>
{{ $t('~~Create New List') }}
</base-button>
<base-button
:button-classes="{ 'justify-start': true, 'justify-center': false }"
class="px-4 py-1 w-64 cursor-pointer hover:bg-primary-light"
:action="() => updateLists(userLists, selectedTableItems, null, false)"
>
{{ $t('~~Remove from all Lists') }}
</base-button>
<v-menu
placement="right-start"
:triggers="['hover', 'focus']"
instant-move
>
<div
class="px-4 py-1 w-64 cursor-pointer hover:bg-primary-light flex gap-1 items-center justify-between"
>
{{ $t('~~Remove from List') }}
<ccu-icon size="xs" type="arrow-right" class="inline-block" />
</div>

<template #popper>
<v-menu
placement="right-start"
:triggers="['hover', 'focus']"
instant-move
:delay="500"
>
<div v-for="list in userLists" :key="list.id">
<base-button
:button-classes="{
'justify-start': true,
'justify-center': false,
}"
class="px-4 py-1 w-64 cursor-pointer hover:bg-primary-light"
:action="
() =>
updateLists(userLists, selectedTableItems, list.id, false)
"
>
{{ list.name }}
</base-button>
</div>
</v-menu>
</template>
</v-menu>
<v-menu
placement="right-start"
:triggers="['hover', 'focus']"
instant-move
>
<div
class="px-4 py-1 w-64 cursor-pointer hover:bg-primary-light flex gap-1 items-center justify-between"
>
{{ $t('~~Add to Existing List') }}
<ccu-icon size="xs" type="arrow-right" class="inline-block" />
</div>

<template #popper>
<v-menu
placement="right-start"
:triggers="['hover', 'focus']"
instant-move
:delay="500"
>
<div v-for="list in userLists" :key="list.id">
<base-button
:button-classes="{
'justify-start': true,
'justify-center': false,
}"
class="px-4 py-1 w-64 cursor-pointer hover:bg-primary-light"
:action="
() =>
updateLists(userLists, selectedTableItems, list.id, true)
"
>
{{ list.name }}
</base-button>
</div>
</v-menu>
</template>
</v-menu>
<v-menu
placement="right-start"
:triggers="['hover', 'focus']"
instant-move
:delay="500"
>
<div
class="px-4 py-1 w-64 cursor-pointer hover:bg-primary-light flex gap-1 items-center justify-between"
>
{{ $t('~~View List') }}
<ccu-icon size="xs" type="arrow-right" class="inline-block" />
</div>

<template #popper>
<v-menu
placement="right-start"
:triggers="['hover', 'focus']"
instant-move
>
<div v-for="list in userLists" :key="list.id" class="flex flex-col">
<base-button
:button-classes="{
'justify-start': true,
'justify-center': false,
}"
class="px-4 py-1 cursor-pointer hover:bg-primary-light flex-grow"
:action="() => $router.push(`/lists/${list.id}`)"
>
<div class="flex items-center gap-2 w-full">
<span class="w-40 text-left">{{ list.name }}</span>
<span class="w-40">
{{ getUser(list.created_by).first_name }}
</span>
<span class="">
{{ length(list.object_ids) }} {{ $t('~~items') }}
</span>
</div>
</base-button>
</div>
</v-menu>
</template>
</v-menu>
</template>
</v-popover>
</template>

<script setup lang="ts">
import axios from 'axios';
import { ref, onMounted, watch } from 'vue';
import debounce from 'lodash/debounce';
import BaseButton from '@/components/BaseButton.vue';
import CcuIcon from '@/components/BaseIcon.vue';
import { useToast } from 'vue-toastification';
import User from '@/models/User';
import useCreateList from '@/hooks/lists/useCreateList';
interface List {
id: number;
name: string;
object_ids?: number[];
created_by?: number;
}
const props = defineProps({
modelType: String,
incident: Number,
selectedTableItems: {
type: Array,
required: true,
},
title: {
type: String,
required: true,
},
});
const userLists = ref<List[]>([]);
const $toasted = useToast();
const baseUrl = `${import.meta.env.VITE_APP_API_BASE_URL}/lists`;
async function getUsers(userIds) {
await User.api().get(`/users?id__in=${userIds.join(',')}`, {
dataKey: 'results',
});
}
async function getLists() {
try {
const response = await axios.get<{ results: List[] }>(baseUrl, {
params: {
incident: props.incident,
model: props.modelType,
},
});
userLists.value = response.data.results;
const userIds = [];
for (const list of userLists.value) {
userIds.push(list.created_by);
}
await getUsers(userIds);
} catch (error) {
console.error('Error fetching user lists:', error);
}
}
onMounted(async () => {
await getLists();
});
/**
* Updates the lists based on the action to add or remove object IDs.
* @param {Array} userLists - Array of list objects.
* @param {Array} objectIds - Array of IDs to add or remove.
* @param {number|null} specificListId - Specific list ID for targeted addition/removal, null for all lists.
* @param {boolean} isAdding - Determines whether to add or remove the objectIds.
*/
const updateLists = async (userLists, objectIds, specificListId, isAdding) => {
const updates = [];
for (const list of userLists) {
// Initialize object_ids as an empty array if it's null
if (!list.object_ids) {
list.object_ids = [];
}
// When removing from all lists or a specific list, or adding to a specific list
if (specificListId === null || list.id === specificListId) {
let updated = false;
for (const objectId of objectIds) {
const index = list.object_ids.indexOf(objectId);
if (isAdding) {
// Add only if the object ID is not already in the list
if (index === -1) {
list.object_ids.push(objectId);
updated = true;
}
} else {
// Remove if the object ID is in the list
if (index !== -1) {
list.object_ids.splice(index, 1);
updated = true;
}
}
}
// If there are changes, add the update promise to the array
if (updated) {
updates.push(
axios.put(
`${import.meta.env.VITE_APP_API_BASE_URL}/lists/${list.id}`,
list,
),
);
}
// If updating a specific list, break after processing it
if (specificListId !== null) {
break;
}
}
}
// Execute all update calls in parallel
try {
await Promise.all(updates);
$toasted.success('Lists updated successfully.');
await getLists();
} catch (error) {
console.error('Error updating lists:', error);
}
};
const getUser = (id: number) => {
return User.find(id);
};
const { createList } = useCreateList(getLists, props.modelType);
const length = (array) => {
if (array) {
return array.length;
}
return 0;
};
</script>
Loading

0 comments on commit d91a6c4

Please sign in to comment.