diff --git a/backend/src/database/repositories/member/memberOrganizationsRepository.ts b/backend/src/database/repositories/member/memberOrganizationsRepository.ts index fc7059a9ea..80a7caf8c4 100644 --- a/backend/src/database/repositories/member/memberOrganizationsRepository.ts +++ b/backend/src/database/repositories/member/memberOrganizationsRepository.ts @@ -70,24 +70,13 @@ class MemberOrganizationsRepository { data: Partial, options: IRepositoryOptions, ) { - const transaction = await SequelizeRepository.createTransaction(options) - try { - const txOptions = { ...options, transaction } - const qx = SequelizeRepository.getQueryExecutor(txOptions, transaction) + const qx = SequelizeRepository.getQueryExecutor(options) - // Create member organization - await createMemberOrganization(qx, memberId, data) + // Create member organization + await createMemberOrganization(qx, memberId, data) - await SequelizeRepository.commitTransaction(transaction) - - // List all member organizations - return await this.list(memberId, options) - } catch (err) { - if (transaction) { - await SequelizeRepository.rollbackTransaction(transaction) - } - throw err - } + // List all member organizations + return this.list(memberId, options) } static async update( @@ -96,45 +85,23 @@ class MemberOrganizationsRepository { data: Partial, options: IRepositoryOptions, ) { - const transaction = await SequelizeRepository.createTransaction(options) - try { - const txOptions = { ...options, transaction } - const qx = SequelizeRepository.getQueryExecutor(txOptions, transaction) + const qx = SequelizeRepository.getQueryExecutor(options) - // Update member organization - await updateMemberOrganization(qx, memberId, id, data) - - await SequelizeRepository.commitTransaction(transaction) + // Update member organization + await updateMemberOrganization(qx, memberId, id, data) - // List all member organizations - return await this.list(memberId, options) - } catch (err) { - if (transaction) { - await SequelizeRepository.rollbackTransaction(transaction) - } - throw err - } + // List all member organizations + return this.list(memberId, options) } static async delete(id: string, memberId: string, options: IRepositoryOptions) { - const transaction = await SequelizeRepository.createTransaction(options) - try { - const txOptions = { ...options, transaction } - const qx = SequelizeRepository.getQueryExecutor(txOptions, transaction) - - // Delete organization - await deleteMemberOrganization(qx, memberId, id) + const qx = SequelizeRepository.getQueryExecutor(options) - await SequelizeRepository.commitTransaction(transaction) + // Delete organization + await deleteMemberOrganization(qx, memberId, id) - // List all member organizations - return await this.list(memberId, options) - } catch (err) { - if (transaction) { - await SequelizeRepository.rollbackTransaction(transaction) - } - throw err - } + // List all member organizations + return this.list(memberId, options) } } diff --git a/backend/src/database/repositories/memberRepository.ts b/backend/src/database/repositories/memberRepository.ts index 9b2b45bae0..f3de374efe 100644 --- a/backend/src/database/repositories/memberRepository.ts +++ b/backend/src/database/repositories/memberRepository.ts @@ -1555,7 +1555,7 @@ class MemberRepository { offset: 0, segmentId, include: { - memberOrganizations: true, + memberOrganizations: false, lfxMemberships: true, identities: false, segments: true, diff --git a/frontend/src/modules/contributor/components/details/work-history/contributor-details-work-history-item.vue b/frontend/src/modules/contributor/components/details/work-history/contributor-details-work-history-item.vue index 16f170436b..afa541e134 100644 --- a/frontend/src/modules/contributor/components/details/work-history/contributor-details-work-history-item.vue +++ b/frontend/src/modules/contributor/components/details/work-history/contributor-details-work-history-item.vue @@ -70,7 +70,7 @@ import LfIcon from '@/ui-kit/icon/Icon.vue'; import { Contributor } from '@/modules/contributor/types/Contributor'; import LfSvg from '@/shared/svg/svg.vue'; import LfAvatar from '@/ui-kit/avatar/Avatar.vue'; -import { Organization, OrganizationSource } from '@/modules/organization/types/Organization'; +import { Organization } from '@/modules/organization/types/Organization'; import moment from 'moment'; import { storeToRefs } from 'pinia'; import { useLfSegmentsStore } from '@/modules/lf/segments/store'; @@ -93,7 +93,7 @@ const props = defineProps<{ const emit = defineEmits<{(e:'edit'): void}>(); const { selectedProjectGroup } = storeToRefs(useLfSegmentsStore()); -const { updateContributor } = useContributorStore(); +const { deleteContributorOrganization } = useContributorStore(); const { trackEvent } = useProductTracking(); const hovered = ref(false); @@ -128,29 +128,7 @@ const removeWorkHistory = () => { }, }); - const orgs = props.contributor.organizations.filter((o: Organization) => !(o.id === props.organization?.id - && o.memberOrganizations?.title === props.organization?.memberOrganizations?.title - && o.memberOrganizations?.dateStart === props.organization?.memberOrganizations?.dateStart - && o.memberOrganizations?.dateEnd === props.organization?.memberOrganizations?.dateEnd)) - .map((o) => ({ - id: o.id, - name: o.name, - ...o.memberOrganizations?.title && { - title: o.memberOrganizations?.title, - }, - ...o.memberOrganizations?.dateStart && { - startDate: o.memberOrganizations?.dateStart, - }, - ...o.memberOrganizations?.dateEnd && { - endDate: o.memberOrganizations?.dateEnd, - }, - source: OrganizationSource.UI, - })); - - updateContributor(props.contributor.id, { - organizationsReplace: true, - organizations: orgs, - }) + deleteContributorOrganization(props.contributor.id, props.organization.memberOrganizations.id) .then(() => { Message.success('Work experience deleted successfully'); }) diff --git a/frontend/src/modules/contributor/components/edit/work-history/contributor-work-history-edit.vue b/frontend/src/modules/contributor/components/edit/work-history/contributor-work-history-edit.vue index 0a35666b74..43864636b4 100644 --- a/frontend/src/modules/contributor/components/edit/work-history/contributor-work-history-edit.vue +++ b/frontend/src/modules/contributor/components/edit/work-history/contributor-work-history-edit.vue @@ -115,7 +115,7 @@ import LfIcon from '@/ui-kit/icon/Icon.vue'; import LfInput from '@/ui-kit/input/Input.vue'; import LfCheckbox from '@/ui-kit/checkbox/Checkbox.vue'; import { useContributorStore } from '@/modules/contributor/store/contributor.store'; -import { Organization, OrganizationSource } from '@/modules/organization/types/Organization'; +import { MemberOrganization, Organization, OrganizationSource } from '@/modules/organization/types/Organization'; import LfField from '@/ui-kit/field/Field.vue'; import LfOrganizationSelect from '@/modules/organization/components/shared/organization-select.vue'; import moment from 'moment/moment'; @@ -136,7 +136,7 @@ const props = defineProps<{ const emit = defineEmits<{(e: 'update:modelValue', value: boolean): void}>(); -const { updateContributor } = useContributorStore(); +const { createContributorOrganization, updateContributorOrganization } = useContributorStore(); const { trackEvent } = useProductTracking(); const isEdit = computed(() => !!props.organization); @@ -181,14 +181,12 @@ const rules = { const $v = useVuelidate(rules, form); const updateWorkExperience = () => { - const data: Organization = { - ...(props.organization || {}), - ...(form.organization as Organization), - memberOrganizations: { - title: form.title, - dateStart: form.dateStart ? moment(form.dateStart).startOf('month').format('YYYY-MM-DDTHH:mm:ss.SSS[Z]') : undefined, - dateEnd: !form.currentlyWorking && form.dateEnd ? moment(form.dateEnd).startOf('month').format('YYYY-MM-DDTHH:mm:ss.SSS[Z]') : undefined, - }, + const data: Partial = { + organizationId: (props.organization || form.organization)?.id, + source: OrganizationSource.UI, + title: form.title, + dateStart: form.dateStart ? moment(form.dateStart).startOf('month').format('YYYY-MM-DDTHH:mm:ss.SSS[Z]') : undefined, + dateEnd: !form.currentlyWorking && form.dateEnd ? moment(form.dateEnd).startOf('month').format('YYYY-MM-DDTHH:mm:ss.SSS[Z]') : undefined, }; if (isEdit.value) { @@ -226,23 +224,9 @@ const updateWorkExperience = () => { sending.value = true; - updateContributor(props.contributor.id, { - organizationsReplace: true, - organizations: orgs.map((o) => ({ - id: o.id, - name: o.name, - ...o.memberOrganizations?.title && { - title: o.memberOrganizations?.title, - }, - ...o.memberOrganizations?.dateStart && { - startDate: o.memberOrganizations?.dateStart, - }, - ...o.memberOrganizations?.dateEnd && { - endDate: o.memberOrganizations?.dateEnd, - }, - source: OrganizationSource.UI, - })), - }) + (isEdit.value + ? updateContributorOrganization(props.contributor.id, props.organization?.memberOrganizations?.id!, data) + : createContributorOrganization(props.contributor.id, data)) .then(() => { Message.success(`Work experience ${isEdit.value ? 'updated' : 'added'} successfully`); isModalOpen.value = false; diff --git a/frontend/src/modules/contributor/services/contributor.organizations.api.service.ts b/frontend/src/modules/contributor/services/contributor.organizations.api.service.ts new file mode 100644 index 0000000000..ac0d6331b9 --- /dev/null +++ b/frontend/src/modules/contributor/services/contributor.organizations.api.service.ts @@ -0,0 +1,44 @@ +import authAxios from '@/shared/axios/auth-axios'; +import { AuthService } from '@/modules/auth/services/auth.service'; +import { MemberOrganization } from '@/modules/organization/types/Organization'; + +export class ContributorOrganizationsApiService { + static async list(memberId: string, segments: string[]) { + const tenantId = AuthService.getTenantId(); + + return authAxios.get( + `/tenant/${tenantId}/member/${memberId}/organization`, + { + params: { + segments, + }, + }, + ).then(({ data }) => Promise.resolve(data)); + } + + static async create(memberId: string, data: Partial) { + const tenantId = AuthService.getTenantId(); + + return authAxios.post( + `/tenant/${tenantId}/member/${memberId}/organization`, + data, + ).then(({ data }) => Promise.resolve(data)); + } + + static async update(memberId: string, id: string, organization: Partial) { + const tenantId = AuthService.getTenantId(); + + return authAxios.patch( + `/tenant/${tenantId}/member/${memberId}/organization/${id}`, + organization, + ).then(({ data }) => Promise.resolve(data)); + } + + static async delete(memberId: string, id: string) { + const tenantId = AuthService.getTenantId(); + + return authAxios.delete( + `/tenant/${tenantId}/member/${memberId}/organization/${id}`, + ).then(({ data }) => Promise.resolve(data)); + } +} diff --git a/frontend/src/modules/contributor/store/contributor.actions.ts b/frontend/src/modules/contributor/store/contributor.actions.ts index 801539d15e..f547a555c3 100644 --- a/frontend/src/modules/contributor/store/contributor.actions.ts +++ b/frontend/src/modules/contributor/store/contributor.actions.ts @@ -5,11 +5,14 @@ import { Contributor, ContributorIdentity } from '@/modules/contributor/types/Co import { ContributorIdentitiesApiService } from '@/modules/contributor/services/contributor.identities.api.service'; import { MergeActionsService } from '@/shared/modules/merge/services/merge-actions.service'; import { MergeAction } from '@/shared/modules/merge/types/MemberActions'; +import { MemberOrganization, Organization } from '@/modules/organization/types/Organization'; +import { ContributorOrganizationsApiService } from '@/modules/contributor/services/contributor.organizations.api.service'; export default { getContributor(id: string): Promise { const { selectedProjectGroup } = storeToRefs(useLfSegmentsStore()); this.getContributorIdentities(id); + this.getContributorOrganizations(id); return ContributorApiService.find(id, [selectedProjectGroup.value?.id as string]) .then((contributor) => { @@ -69,4 +72,30 @@ export default { return ContributorIdentitiesApiService.delete(memberId, id) .then(this.setIdentities); }, + + /** IDENTITIES * */ + setOrganizations(organizations: Organization[]) { + this.contributor = { + ...this.contributor, + organizations, + }; + return Promise.resolve(organizations); + }, + getContributorOrganizations(memberId: string) { + const { selectedProjectGroup } = storeToRefs(useLfSegmentsStore()); + return ContributorOrganizationsApiService.list(memberId, [selectedProjectGroup.value?.id as string]) + .then(this.setOrganizations); + }, + createContributorOrganization(memberId: string, organization: Partial): Promise { + return ContributorOrganizationsApiService.create(memberId, organization) + .then(this.setOrganizations); + }, + updateContributorOrganization(memberId: string, id: string, organization: Partial): Promise { + return ContributorOrganizationsApiService.update(memberId, id, organization) + .then(this.setOrganizations); + }, + deleteContributorOrganization(memberId: string, id: string): Promise { + return ContributorOrganizationsApiService.delete(memberId, id) + .then(this.setOrganizations); + }, }; diff --git a/frontend/src/modules/organization/types/Organization.ts b/frontend/src/modules/organization/types/Organization.ts index 2b344d1d8d..31c1542dae 100644 --- a/frontend/src/modules/organization/types/Organization.ts +++ b/frontend/src/modules/organization/types/Organization.ts @@ -32,6 +32,15 @@ export interface OrganizationIdentity { integrationId?: string; } +export interface MemberOrganization { + id: string; + title: string; + organizationId?: string; + dateStart?: string; + dateEnd?: string; + source: OrganizationSource; +} + export interface Organization { attributes: Record, activeOn: string[]; @@ -72,12 +81,7 @@ export interface Organization { updatedAt: string; updatedById: string; url: string; - memberOrganizations: { - title: string; - dateStart?: string; - dateEnd?: string; - source: OrganizationSource; - } + memberOrganizations: MemberOrganization lfxMembership?: { accountDomain: string; accountName: string; diff --git a/services/libs/data-access-layer/src/members/organizations.ts b/services/libs/data-access-layer/src/members/organizations.ts index bcc0d4d057..33822f57c7 100644 --- a/services/libs/data-access-layer/src/members/organizations.ts +++ b/services/libs/data-access-layer/src/members/organizations.ts @@ -7,7 +7,7 @@ export async function fetchMemberOrganizations( ): Promise { return qx.select( ` - SELECT * + SELECT "id", "organizationId", "dateStart", "dateEnd", "title", "memberId", "source" FROM "memberOrganizations" WHERE "memberId" = $(memberId) `, @@ -44,8 +44,8 @@ export async function createMemberOrganization( ): Promise { return qx.result( ` - INSERT INTO "memberOrganizations"("memberId", "organizationId", "dateStart", "dateEnd", "title") - VALUES($(memberId), $(organizationId), $(dateStart), $("dateStart"), $(title)) + INSERT INTO "memberOrganizations"("memberId", "organizationId", "dateStart", "dateEnd", "title", "source", "createdAt", "updatedAt") + VALUES($(memberId), $(organizationId), $(dateStart), $(dateEnd), $(title), $(source), $(date), $(date)) ON CONFLICT DO NOTHING; `, { @@ -54,6 +54,8 @@ export async function createMemberOrganization( dateStart: data.dateStart, dateEnd: data.dateEnd, title: data.title, + source: data.source, + date: new Date().toISOString(), }, ) } @@ -71,7 +73,9 @@ export async function updateMemberOrganization( "organizationId" = $(organizationId), "dateStart" = $(dateStart), "dateEnd" = $(dateEnd), - title = $(title) + title = $(title), + source = $(source), + "updatedAt" = $(updatedAt) WHERE "memberId" = $(memberId) AND "id" = $(id); `, { @@ -81,6 +85,8 @@ export async function updateMemberOrganization( dateStart: data.dateStart, dateEnd: data.dateEnd, title: data.title, + source: data.source, + updatedAt: new Date().toISOString(), }, ) }