Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
512 changes: 233 additions & 279 deletions package-lock.json

Large diffs are not rendered by default.

24 changes: 23 additions & 1 deletion src/components/employee-beta/forms/EmployeeForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@
title="Emergency Contacts"
:class="{ invalid: !validTabs.emergencyContacts }"
></v-list-item>
<v-list-item
@click="cardName = 'Integrations'"
link
title="Integrations"
:class="{ invalid: false }"
></v-list-item>
</v-list>
</v-col>
<v-col>
Expand Down Expand Up @@ -244,6 +250,16 @@
<emergency-contacts-form :slot-props="props"></emergency-contacts-form>
</template>
</base-form>
<base-form
title="Integrations"
tab-id="integration"
v-model="editedEmployee"
:valid="true"
>
<template v-slot="{ props }">
<integration-data-form :slot-props="props"></integration-data-form>
</template>
</base-form>
</v-expansion-panels>
<div class="sticky-actions">
<v-card-actions>
Expand Down Expand Up @@ -310,6 +326,7 @@ import LanguagesForm from './LanguagesForm.vue';
import PersonalInfoForm from './PersonalInfoForm.vue';
import TechnologiesForm from './TechnologiesForm.vue';
import EmergencyContactsForm from './EmergencyContactsForm.vue';
import IntegrationDataForm from './IntegrationDataForm.vue';
import SubmitButton from '@/components/shared/buttons/SubmitButton.vue';

// |--------------------------------------------------|
Expand Down Expand Up @@ -350,7 +367,8 @@ const validTabs = reactive({
languages: true,
personal: true,
technologies: true,
emergencyContacts: true
emergencyContacts: true,
integration: true,
});

// template refs
Expand Down Expand Up @@ -690,6 +708,10 @@ async function selectTab() {
num = 9;
card = 'Emergency Contacts';
break;
case 'Integrations':
num = 10;
card = 'Integrations'
break;
default:
num = 0;
card = 'Personal';
Expand Down
102 changes: 102 additions & 0 deletions src/components/employee-beta/forms/IntegrationDataForm.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<template>
<div>
<h4>Unanet</h4>
<hr class="mb-4"/>
<v-row>
<v-col cols="6">
<v-autocomplete
label="Project Org Code"
v-model="editedEmployee.integrationData.unanet.projectOrgCode"
:items="projectOrgCodeItems"
/>
</v-col>
<v-col cols="6">
<v-text-field
label="Person Key"
v-model="editedEmployee.unanetPersonKey"
/>
</v-col>
</v-row>
</div>
</template>

<script setup>
import _isEmpty from 'lodash/isEmpty';
import { ref, onBeforeMount } from 'vue';

// |--------------------------------------------------|
// | |
// | SETUP |
// | |
// |--------------------------------------------------|

// passes in all slot props as a single object
const { slotProps } = defineProps(['slotProps']);
const editedEmployee = ref(slotProps.editedEmployee);

const projectOrgCodeItems = ref(["I_CASE", "I_HR", "I_FINANCE", "I_OPERATIONS", "I_EXECUTIVE"]);

onBeforeMount(() => {
const requiredFields = {
// dot-noted-object: default-value
'integrationData.unanet.projectOrgCode': null
};

// fill in fields as needed
for (let [field, defaultVal] of Object.entries(requiredFields)) {
let curr = editedEmployee.value;
let subs = field.split('.');
let last = subs.pop();
for (let sub of subs) {
curr[sub] ??= {};
curr = curr[sub];
}
curr[last] ??= defaultVal;
}
})

// |--------------------------------------------------|
// | |
// | METHODS |
// | |
// |--------------------------------------------------|

/**
* Gets validation rules for languages: no duplicate languages and no english
*/
function getLanguageRules() {
return [
// no duplicate languages
(v) => {
let count = 0;
for (let i = 0; i < editedEmployee.value.languages.length && count <= 2; i++) {
if (editedEmployee.value.languages[i].name === v) count++;
}
return count <= 1 || 'Duplicate language found, please remove duplicate entries';
},
// no english
(v) => {
return (!_isEmpty(v) && v.toLowerCase() !== 'english') || 'English is not a foreign language';
}
];
}

/**
* Adds a new language to the list
*
* @param {boolean} addToTop Whether to add to the top or bottom of the list
*/
function addLanguage(addToTop = true) {
const newLanguage = {
name: '',
proficiency: ''
};

if (addToTop) editedEmployee.value.languages.unshift(newLanguage);
else editedEmployee.value.languages.push(newLanguage);
}

function deleteLanguage(index) {
editedEmployee.value.languages.splice(index, 1);
}
</script>
45 changes: 42 additions & 3 deletions src/components/expense-types/forms/sections/Integrations.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,65 @@
item-value="url"
label="Basecamp Campfire (optional)"
clearable
></v-autocomplete>
/>
<div v-tooltip:top="unanetETs.length == 0 ? 'No Unanet expense types found' : null">
<v-autocomplete
variant="underlined"
:items="unanetETs"
v-model="modelValue.unanetExpenseType"
item-title="name"
item-value="key"
label="Unanet Expense Type (optional)"
:disabled="unanetETs.length == 0"
clearable
/>
</div>
<div v-tooltip:top="unanetProjects.length == 0 ? 'No Unanet projects found' : null">
<v-autocomplete
variant="underlined"
:items="unanetProjects"
v-model="modelValue.unanetProject"
item-title="name"
item-value="key"
label="Unanet Project (optional)"
:disabled="unanetProjects.length == 0"
clearable
>
<template v-slot:item="{ props, item }">
<v-list-item
v-bind="props"
:subtitle="item.raw.orgCode"
></v-list-item>
</template>
</v-autocomplete>
</div>
</div>
</template>
<script setup>
import { updateStoreCampfires } from '@/utils/storeUtils';
import { onBeforeMount, ref } from 'vue';
import { useStore } from 'vuex';
import api from '@/shared/api.js';

const props = defineProps({
modelValue: Object
});
const store = useStore();

const campfires = ref([]); // basecamp campfires
const campfires = ref([]); // Basecamp campfires
const unanetProjects = ref([]); // Unanet projects
const unanetETs = ref([]); // Unanet expense types

/**
* Gets and sets all employees.
*/
onBeforeMount(async () => {
await updateStoreCampfires();
// fetch data
let [unanetData, /* drop rest */] = await Promise.all([api.getUnanetExpenseTypes(), updateStoreCampfires()]);

// load data into variables
unanetProjects.value = unanetData.projects;
unanetETs.value = unanetData.expenseTypes;
campfires.value = store.getters.basecampCampfires;
});
</script>
14 changes: 7 additions & 7 deletions src/components/expenses/ConvertExpensesToCsv.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
<div>
<!-- Download CSV Buttons -->
<v-btn :disabled="midAction" @click="download" elevation="2">
<i class="material-icons">file_download</i> Download Grid Items</v-btn
>
<v-btn :disabled="midAction" @click="unanetReport" elevation="2" prepend-icon="mdi-file-download"
>Export Unanet Data</v-btn
>
<i class="material-icons">file_download</i> Download Grid Items
</v-btn>
<v-btn :disabled="midAction" @click="unanetReport" elevation="2" prepend-icon="mdi-file-download">
Export Unanet Data
</v-btn>
</div>
</template>

Expand All @@ -29,8 +29,8 @@ function download() {
/**
* Downloads expenses as CSV
*/
function unanetReport() {
expensesCsv.unanetReport(this.expenses);
async function unanetReport() {
await expensesCsv.unanetReport(this.expenses);
} // download

// |--------------------------------------------------|
Expand Down
3 changes: 3 additions & 0 deletions src/models/employee.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,9 @@ export class Employee {
/** @type {Object} */
this.preferences = properties.preferences ?? {};

/** @type {Object} */
this.integrationData = properties.integrationData ?? {};

// current address fields
/** @type {string} */
this.currentStreet = properties.currentStreet ?? '';
Expand Down
4 changes: 4 additions & 0 deletions src/models/expense-types/expenseType.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ export class ExpenseType extends Base {
this.tagBudgets = properties.tagBudgets ?? [];
/** @type {string} */
this.to = properties.to;
/** @type {string} */
this.unanetExpenseType = properties.unanetExpenseType;
/** @type {string} */
this.unanetProject = properties.unanetProject;

return new Proxy(this, {
set(target, key, value) {
Expand Down
13 changes: 11 additions & 2 deletions src/shared/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const PTO_CASH_OUTS = 'ptoCashOuts';
const SETTINGS = 'settings';
const TAGS = 'tags';
const ACCESS_GROUPS = 'accessGroups';
const UNANET = 'unanet';
const API_HOSTNAME = API_CONFIG.apiHostname;
const API_PORT = API_CONFIG.apiPort;
const PORT = API_PORT === '443' ? '' : `:${API_PORT}`;
Expand Down Expand Up @@ -506,7 +507,7 @@ async function getEmployeesFromAdp() {
* @return - Array of IDs of employees who can see the user's data
*/
async function getAccessControlUsers(id) {
return await execute('get', `/${ACCESS_GROUPS}/employee/groupUsers/${id}`)
return await execute('get', `/${ACCESS_GROUPS}/employee/groupUsers/${id}`);
}

/**
Expand All @@ -516,7 +517,14 @@ async function getAccessControlUsers(id) {
* @return - Array of IDs of employees who can see the user's data
*/
async function getUserProfileAccessControl(id) {
return await execute('get', `/${ACCESS_GROUPS}/employee/showOnProfile/${id}`)
return await execute('get', `/${ACCESS_GROUPS}/employee/showOnProfile/${id}`);
}

/**
* Gets Unanet expense type data. Useful for exports to Unanet.
*/
async function getUnanetExpenseTypes() {
return await execute('get', `/${UNANET}/expenseTypes`);
}

export default {
Expand Down Expand Up @@ -562,6 +570,7 @@ export default {
getEmployeesFromAdp,
getAccessControlUsers,
getUserProfileAccessControl,
getUnanetExpenseTypes,
EXPENSE_TYPES,
EXPENSES,
EMPLOYEES,
Expand Down
Loading