Skip to content

Commit

Permalink
feat: remove members from organization (#154)
Browse files Browse the repository at this point in the history
  • Loading branch information
hudy9x authored Apr 1, 2024
1 parent 7b6c708 commit 163f08a
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 53 deletions.
23 changes: 22 additions & 1 deletion packages/be-gateway/src/routes/organization/member.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,28 @@ import {
import {
BaseController,
Controller,
Delete,
Get,
Post,
Put,
UseMiddleware
} from '../../core'
import { authMiddleware } from '../../middlewares'
import { AuthRequest } from '../../types'
import InternalServerException from '../../exceptions/InternalServerException'
import { InvitationStatus, OrganizationRole } from '@prisma/client'
import BadRequestException from '../../exceptions/BadRequestException'
import OrgMemberRemoveService from '../../services/orgMember/remove.service'

const MAX_ORGANIZATION_MEMBER = 25

@Controller('/org/member')
@UseMiddleware([authMiddleware])
export class OrganizationMemberController extends BaseController {
orgMemberRemoveService: OrgMemberRemoveService
constructor() {
super()
this.orgMemberRemoveService = new OrgMemberRemoveService()
}
@Get('/:orgId')
async getMembersByOrgId() {
const req = this.req
Expand Down Expand Up @@ -91,6 +97,21 @@ export class OrganizationMemberController extends BaseController {
return foundUser
}

@Delete('/remove/:orgId/:uid')
async removeMember() {
const { uid, orgId } = this.req.params as { uid: string, orgId: string }

try {
console.log('run 5')
const result = await this.orgMemberRemoveService.implement(uid, orgId)
return result
} catch (error) {
console.log(error)
throw new InternalServerException(error)
}

}

@Post('/search')
async searchMember() {
const req = this.req
Expand Down
71 changes: 71 additions & 0 deletions packages/be-gateway/src/services/orgMember/remove.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { pmClient } from "packages/shared-models/src/lib/_prisma"
import { CKEY, delCache } from "../../lib/redis"


export default class OrgMemberRemoveService {
async implement(uid: string, orgId: string) {
await pmClient.$transaction(async tx => {
const orgProjectIds = await tx.project.findMany({
where: {
organizationId: orgId
},
select: {
id: true
}
})

console.log('projects from organization', orgProjectIds)

const memberProjectIds = await tx.members.findMany({
where: {
uid,
projectId: {
in: orgProjectIds.map(p => p.id)
}
},
select: {
projectId: true
}
})

console.log('project Ids that member belong to', memberProjectIds)

const projectIds = memberProjectIds.map(p => p.projectId)

console.log('projec ids list', projectIds)

if (projectIds.length) {

await tx.members.deleteMany({
where: {
uid,
projectId: { in: projectIds }
}
})
}

await tx.organizationMembers.deleteMany({
where: {
uid
}
})

/// DELETE cache project member
const delPromises = []
for (let i = 0; i < projectIds.length; i++) {
const pid = projectIds[i];
const key = [CKEY.PROJECT_MEMBER, pid]

console.log('delete projectId', pid)

delPromises.push(delCache(key))
}

await Promise.all(delPromises)

})

return 1
}
}

9 changes: 1 addition & 8 deletions packages/be-gateway/src/services/task/update.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,22 @@ import { Task } from '@prisma/client'
import ActivityService from '../activity.service'
import {
CKEY,
delCache,
findCacheByTerm,
findNDelCaches,
incrCache,
setJSONCache
} from '../../lib/redis'
import {
ProjectSettingRepository,
mdProjectGet,
mdTaskAdd,
mdTaskGetOne,
mdTaskStatusWithDoneType,
mdTaskStatusWithTodoType,
mdTaskUpdate
} from '@shared/models'
import { deleteTodoCounter } from '..//todo.counter'
import { genFrontendUrl } from '../../lib/url'
import { notifyToWebUsers } from '../../lib/buzzer'
import InternalErrorException from '../../exceptions/InternalErrorException'
import { extracDatetime, padZero } from '@shared/libs'
import { serviceGetStatusById } from '../status'
import { serviceGetProjectById } from '../project'
import TaskReminderJob from '../../jobs/reminder.job'
import { boolean } from 'zod'

export default class TaskUpdateService {
activityService: ActivityService
Expand All @@ -49,6 +41,7 @@ export default class TaskUpdateService {
isDoneBefore,
isDueDateChanged
} = await this._genUpdateData({ body, userId })

const result = await mdTaskUpdate(taskData)

const isDone = result.done
Expand Down
105 changes: 68 additions & 37 deletions packages/ui-app/app/_features/SettingPeople/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,21 @@ import {
Avatar,
Button,
Form,
confirmAlert,
confirmWarning,
messageError,
messageSuccess,
messageWarning
messageSuccess
} from '@shared/ui'
import { useParams } from 'next/navigation'
import FormGroup from 'packages/shared-ui/src/components/FormGroup'
import { useState } from 'react'
import { HiOutlineMail } from 'react-icons/hi'
import { HiOutlineTrash } from 'react-icons/hi2'

export default function SettingPeopleContent() {
const { orgID } = useParams()
const { orgMembers } = useOrgMemberStore()
const { addNewMemberToOrg } = useServiceOrgMember()
const { addNewMemberToOrg, removeMemberFromOrg } = useServiceOrgMember()
const [email, setEmail] = useState('')
const [loading, setLoading] = useState(false)
useOrgMemberGet()
Expand Down Expand Up @@ -66,42 +67,72 @@ export default function SettingPeopleContent() {
}

return (
<div className="w-[600px] mx-auto pt-20">
<h2 className="text-gray-500 dark:text-gray-400 pb-3">{`Send an invitation via email here 👇`}</h2>
<FormGroup>
<Form.Input
value={email}
disabled={loading}
onChange={ev => {
setEmail(ev.target.value)
}}
className="w-72"
placeholder="[email protected]"
/>
<Button
loading={loading}
primary
onClick={sendInvitation}
leadingIcon={<HiOutlineMail />}
title="Let's invite"
/>
</FormGroup>
<div
className="overflow-y-auto pb-20 pt-20 custom-scrollbar"
style={{
height: 'calc(100vh - 84px)'
}}>
<div className="w-[600px] mx-auto">
<h2 className="text-gray-500 dark:text-gray-400 pb-3">{`Send an invitation via email here 👇`}</h2>
<FormGroup>
<Form.Input
value={email}
disabled={loading}
onChange={ev => {
setEmail(ev.target.value)
}}
onEnter={() => {
sendInvitation()
}}
className="w-72"
placeholder="[email protected]"
/>
<Button
loading={loading}
primary
onClick={sendInvitation}
leadingIcon={<HiOutlineMail />}
title="Let's invite"
/>
</FormGroup>

<h2></h2>
<div className="bg-white dark:bg-gray-900 dark:border-gray-700 rounded-md border shadow-lg shadow-indigo-100 dark:shadow-gray-900 divide-y dark:divide-gray-700 mt-5">
{orgMembers.map(mem => {
return (
<div key={mem.id} className="flex items-center gap-2 py-2 px-3">
<Avatar src={mem.photo || ''} name={mem.name || ''} />
<section className="text-gray-600 dark:text-gray-400">
<h2>{mem.name}</h2>
<div className="text-xs text-gray-400 dark:text-gray-500">
{mem.email}
<h2></h2>
<div className="grid grid-cols-2 gap-2 mt-5">
{orgMembers.map(mem => {
return (
<div
key={mem.id}
className="flex items-center justify-between gap-2 py-2 px-3 bg-white dark:bg-gray-900 dark:border-gray-700 rounded-md border shadow-lg shadow-indigo-100 dark:shadow-gray-900 dark:divide-gray-700">
<div className="flex items-center gap-2">
<Avatar src={mem.photo || ''} name={mem.name || ''} />
<section className="text-gray-600 dark:text-gray-400">
<h2>{mem.name}</h2>
<div className="text-xs text-gray-400 dark:text-gray-500">
{mem.email}
</div>
</section>
</div>
</section>
</div>
)
})}
<div>
<Button
size="sm"
leadingIcon={<HiOutlineTrash />}
onClick={() => {
confirmAlert({
title: `Are you sure you want to do this action ?`,
message: `This action will remove ${mem.email} from the organization and all projects in which he/she is currently participating`,
yes: () => {
removeMemberFromOrg(mem.id).then(res => {
messageSuccess(`Removed ${mem.email}`)
})
}
})
}}
/>
</div>
</div>
)
})}
</div>
</div>
</div>
)
Expand Down
17 changes: 14 additions & 3 deletions packages/ui-app/app/_hooks/useServiceOrgMember.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { orgMemberAdd } from '@/services/organizationMember'
import { orgMemberAdd, orgMemberRemove } from '@/services/organizationMember'
import { useOrgMemberStore } from '@/store/orgMember'
import { useUrl } from './useUrl'

export const useServiceOrgMember = () => {
const { addToOrg } = useOrgMemberStore()
const { orgID } = useUrl()
const { addToOrg, removeFromOrg } = useOrgMemberStore()

const addNewMemberToOrg = (data: { orgId: string; email: string }) => {
return new Promise((resolve, reject) => {
Expand All @@ -18,7 +20,16 @@ export const useServiceOrgMember = () => {
})
}

const removeMemberFromOrg = (uid: string) => {
removeFromOrg(uid)
return orgMemberRemove({
uid,
orgId: orgID
})
}

return {
addNewMemberToOrg
addNewMemberToOrg,
removeMemberFromOrg
}
}
4 changes: 2 additions & 2 deletions packages/ui-app/app/redirect/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import Image from 'next/image'
import UserChecking from './UserChecking'

const LoadingSpinnerIcon = () => {
Expand Down Expand Up @@ -29,7 +28,8 @@ export default async function Index() {
<UserChecking />
<div className="text-center">
<h2 className="logo -mt-10 w-16 h-16 inline-flex items-center justify-center mb-5">
<Image src={'/logo71x71.png'} width={70} height={70} alt="Logo" />
{/* <Image src={'/logo71x71.png'} width={70} height={70} alt="Logo" /> */}
<img className='w-[70px] h-[70px]' src='data:image/png;base64,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' />
</h2>
<div className="text-xs flex flex-col items-center gap-5 text-gray-900 dark:text-gray-400">
<p>Authenticating your session</p>
Expand Down
6 changes: 5 additions & 1 deletion packages/ui-app/services/organizationMember.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect } from 'react'
import { httpGet, httpPost } from './_req'
import { httpDel, httpGet, httpPost } from './_req'
import { messageError } from '@shared/ui'
import { useParams } from 'next/navigation'
import { useOrgMemberStore } from '../store/orgMember'
Expand Down Expand Up @@ -49,3 +49,7 @@ export const useOrgMemberGet = () => {
})
}, [orgID, addAllOrgMember])
}

export const orgMemberRemove = ({ orgId, uid }: { orgId: string, uid: string }) => {
return httpDel(`/api/org/member/remove/${orgId}/${uid}`)
}
7 changes: 6 additions & 1 deletion packages/ui-app/store/orgMember.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface OrgMemberState {
orgMembers: OrgMember[]
addAllOrgMember: (data: OrgMember[]) => void
addToOrg: (data: OrgMember) => void
removeFromOrg: (uid: string) => void
}

export const useOrgMemberStore = create<OrgMemberState>(set => ({
Expand All @@ -25,5 +26,9 @@ export const useOrgMemberStore = create<OrgMemberState>(set => ({
produce((state: OrgMemberState) => {
state.orgMembers.push(data)
})
)
),
removeFromOrg: (uid: string) => set(produce((state: OrgMemberState) => {
console.log(1)
state.orgMembers = state.orgMembers.filter(m => m.id !== uid)
}))
}))

0 comments on commit 163f08a

Please sign in to comment.