From cabc97afc0f543a524cf838e5c1ddb2730358f20 Mon Sep 17 00:00:00 2001 From: chamikaJ Date: Wed, 7 May 2025 12:02:25 +0530 Subject: [PATCH] Enhance team update logic and error handling - Refactored the team update function in the Admin Center controller to improve error handling and response messages. - Implemented concurrent updates for team member roles using Promise.all, enhancing performance and user experience. - Updated the frontend API service to accept a structured body for team updates, ensuring consistency in data handling. - Enhanced the settings drawer component to manage team member roles more effectively, improving the overall user interface. --- .../consolidated-progress-migrations.sql | 401 +++++++++++++++--- .../controllers/admin-center-controller.ts | 41 +- .../admin-center/admin-center.api.service.ts | 4 +- .../teams/settings-drawer/settings-drawer.tsx | 90 ++-- 4 files changed, 428 insertions(+), 108 deletions(-) diff --git a/worklenz-backend/database/migrations/consolidated-progress-migrations.sql b/worklenz-backend/database/migrations/consolidated-progress-migrations.sql index 7efe5b3e..832b93c5 100644 --- a/worklenz-backend/database/migrations/consolidated-progress-migrations.sql +++ b/worklenz-backend/database/migrations/consolidated-progress-migrations.sql @@ -1,14 +1,3 @@ --- CONSOLIDATED MIGRATION FILE --- Contains all progress-related migrations from April-May 2025 --- Generated on: (current date) - --- ============================================================================= --- Migration: Add manual task progress --- Date: 2025-04-22 --- Version: 1.0.0 --- File: 20250422132400-manual-task-progress.sql --- ============================================================================= - BEGIN; -- Add manual progress fields to tasks table @@ -17,6 +6,12 @@ ADD COLUMN IF NOT EXISTS manual_progress BOOLEAN DEFAULT FALSE, ADD COLUMN IF NOT EXISTS progress_value INTEGER DEFAULT NULL, ADD COLUMN IF NOT EXISTS weight INTEGER DEFAULT NULL; +-- Add progress-related fields to projects table +ALTER TABLE projects +ADD COLUMN IF NOT EXISTS use_manual_progress BOOLEAN DEFAULT FALSE, +ADD COLUMN IF NOT EXISTS use_weighted_progress BOOLEAN DEFAULT FALSE, +ADD COLUMN IF NOT EXISTS use_time_progress BOOLEAN DEFAULT FALSE; + -- Update function to consider manual progress CREATE OR REPLACE FUNCTION get_task_complete_ratio(_task_id uuid) RETURNS json LANGUAGE plpgsql @@ -31,12 +26,26 @@ DECLARE _ratio FLOAT = 0; _is_manual BOOLEAN = FALSE; _manual_value INTEGER = NULL; + _project_id UUID; + _use_manual_progress BOOLEAN = FALSE; + _use_weighted_progress BOOLEAN = FALSE; + _use_time_progress BOOLEAN = FALSE; BEGIN -- Check if manual progress is set - SELECT manual_progress, progress_value + SELECT manual_progress, progress_value, project_id FROM tasks WHERE id = _task_id - INTO _is_manual, _manual_value; + INTO _is_manual, _manual_value, _project_id; + + -- Check if the project uses manual progress + IF _project_id IS NOT NULL THEN + SELECT COALESCE(use_manual_progress, FALSE), + COALESCE(use_weighted_progress, FALSE), + COALESCE(use_time_progress, FALSE) + FROM projects + WHERE id = _project_id + INTO _use_manual_progress, _use_weighted_progress, _use_time_progress; + END IF; -- If manual progress is enabled and has a value, use it directly IF _is_manual IS TRUE AND _manual_value IS NOT NULL THEN @@ -82,35 +91,193 @@ BEGIN END $$; +-- Update project functions to handle progress-related fields +CREATE OR REPLACE FUNCTION update_project(_body json) RETURNS json + LANGUAGE plpgsql +AS +$$ +DECLARE + _user_id UUID; + _team_id UUID; + _client_id UUID; + _project_id UUID; + _project_manager_team_member_id UUID; + _client_name TEXT; + _project_name TEXT; +BEGIN + -- need a test, can be throw errors + _client_name = TRIM((_body ->> 'client_name')::TEXT); + _project_name = TRIM((_body ->> 'name')::TEXT); + + -- add inside the controller + _user_id = (_body ->> 'user_id')::UUID; + _team_id = (_body ->> 'team_id')::UUID; + _project_manager_team_member_id = (_body ->> 'team_member_id')::UUID; + + -- cache exists client if exists + SELECT id FROM clients WHERE LOWER(name) = LOWER(_client_name) AND team_id = _team_id INTO _client_id; + + -- insert client if not exists + IF is_null_or_empty(_client_id) IS TRUE AND is_null_or_empty(_client_name) IS FALSE + THEN + INSERT INTO clients (name, team_id) VALUES (_client_name, _team_id) RETURNING id INTO _client_id; + END IF; + + -- check whether the project name is already in + IF EXISTS( + SELECT name FROM projects WHERE LOWER(name) = LOWER(_project_name) + AND team_id = _team_id AND id != (_body ->> 'id')::UUID + ) + THEN + RAISE 'PROJECT_EXISTS_ERROR:%', _project_name; + END IF; + + -- update the project + UPDATE projects + SET name = _project_name, + notes = (_body ->> 'notes')::TEXT, + color_code = (_body ->> 'color_code')::TEXT, + status_id = (_body ->> 'status_id')::UUID, + health_id = (_body ->> 'health_id')::UUID, + key = (_body ->> 'key')::TEXT, + start_date = (_body ->> 'start_date')::TIMESTAMPTZ, + end_date = (_body ->> 'end_date')::TIMESTAMPTZ, + client_id = _client_id, + folder_id = (_body ->> 'folder_id')::UUID, + category_id = (_body ->> 'category_id')::UUID, + updated_at = CURRENT_TIMESTAMP, + estimated_working_days = (_body ->> 'working_days')::INTEGER, + estimated_man_days = (_body ->> 'man_days')::INTEGER, + hours_per_day = (_body ->> 'hours_per_day')::INTEGER, + use_manual_progress = COALESCE((_body ->> 'use_manual_progress')::BOOLEAN, FALSE), + use_weighted_progress = COALESCE((_body ->> 'use_weighted_progress')::BOOLEAN, FALSE), + use_time_progress = COALESCE((_body ->> 'use_time_progress')::BOOLEAN, FALSE) + WHERE id = (_body ->> 'id')::UUID + AND team_id = _team_id + RETURNING id INTO _project_id; + + UPDATE project_members SET project_access_level_id = (SELECT id FROM project_access_levels WHERE key = 'MEMBER') WHERE project_id = _project_id; + + IF NOT (_project_manager_team_member_id IS NULL) + THEN + PERFORM update_project_manager(_project_manager_team_member_id, _project_id::UUID); + END IF; + + RETURN JSON_BUILD_OBJECT( + 'id', _project_id, + 'name', (_body ->> 'name')::TEXT, + 'project_manager_id', _project_manager_team_member_id::UUID + ); +END; +$$; + +CREATE OR REPLACE FUNCTION create_project(_body json) RETURNS json + LANGUAGE plpgsql +AS +$$ +DECLARE + _project_id UUID; + _user_id UUID; + _team_id UUID; + _team_member_id UUID; + _client_id UUID; + _client_name TEXT; + _project_name TEXT; + _project_created_log TEXT; + _project_member_added_log TEXT; + _project_created_log_id UUID; + _project_manager_team_member_id UUID; + _project_key TEXT; +BEGIN + _client_name = TRIM((_body ->> 'client_name')::TEXT); + _project_name = TRIM((_body ->> 'name')::TEXT); + _project_key = TRIM((_body ->> 'key')::TEXT); + _project_created_log = (_body ->> 'project_created_log')::TEXT; + _project_member_added_log = (_body ->> 'project_member_added_log')::TEXT; + _user_id = (_body ->> 'user_id')::UUID; + _team_id = (_body ->> 'team_id')::UUID; + _project_manager_team_member_id = (_body ->> 'project_manager_id')::UUID; + + SELECT id FROM team_members WHERE user_id = _user_id AND team_id = _team_id INTO _team_member_id; + + -- cache exists client if exists + SELECT id FROM clients WHERE LOWER(name) = LOWER(_client_name) AND team_id = _team_id INTO _client_id; + + -- insert client if not exists + IF is_null_or_empty(_client_id) IS TRUE AND is_null_or_empty(_client_name) IS FALSE + THEN + INSERT INTO clients (name, team_id) VALUES (_client_name, _team_id) RETURNING id INTO _client_id; + END IF; + + -- check whether the project name is already in + IF EXISTS(SELECT name FROM projects WHERE LOWER(name) = LOWER(_project_name) AND team_id = _team_id) + THEN + RAISE 'PROJECT_EXISTS_ERROR:%', _project_name; + END IF; + + -- create the project + INSERT + INTO projects (name, key, color_code, start_date, end_date, team_id, notes, owner_id, status_id, health_id, folder_id, + category_id, estimated_working_days, estimated_man_days, hours_per_day, + use_manual_progress, use_weighted_progress, use_time_progress, client_id) + VALUES (_project_name, + UPPER(_project_key), + (_body ->> 'color_code')::TEXT, + (_body ->> 'start_date')::TIMESTAMPTZ, + (_body ->> 'end_date')::TIMESTAMPTZ, + _team_id, + (_body ->> 'notes')::TEXT, + _user_id, + (_body ->> 'status_id')::UUID, + (_body ->> 'health_id')::UUID, + (_body ->> 'folder_id')::UUID, + (_body ->> 'category_id')::UUID, + (_body ->> 'working_days')::INTEGER, + (_body ->> 'man_days')::INTEGER, + (_body ->> 'hours_per_day')::INTEGER, + COALESCE((_body ->> 'use_manual_progress')::BOOLEAN, FALSE), + COALESCE((_body ->> 'use_weighted_progress')::BOOLEAN, FALSE), + COALESCE((_body ->> 'use_time_progress')::BOOLEAN, FALSE), + _client_id) + RETURNING id INTO _project_id; + + -- register the project log + INSERT INTO project_logs (project_id, team_id, description) + VALUES (_project_id, _team_id, _project_created_log) + RETURNING id INTO _project_created_log_id; + + -- insert the project creator as a project member + INSERT INTO project_members (team_member_id, project_access_level_id, project_id, role_id) + VALUES (_team_member_id, (SELECT id FROM project_access_levels WHERE key = 'ADMIN'), + _project_id, + (SELECT id FROM roles WHERE team_id = _team_id AND default_role IS TRUE)); + + -- insert statuses + INSERT INTO task_statuses (name, project_id, team_id, category_id, sort_order) + VALUES ('To Do', _project_id, _team_id, (SELECT id FROM sys_task_status_categories WHERE is_todo IS TRUE), 0); + INSERT INTO task_statuses (name, project_id, team_id, category_id, sort_order) + VALUES ('Doing', _project_id, _team_id, (SELECT id FROM sys_task_status_categories WHERE is_doing IS TRUE), 1); + INSERT INTO task_statuses (name, project_id, team_id, category_id, sort_order) + VALUES ('Done', _project_id, _team_id, (SELECT id FROM sys_task_status_categories WHERE is_done IS TRUE), 2); + + -- insert default project columns + PERFORM insert_task_list_columns(_project_id); + + -- add project manager role if exists + IF NOT is_null_or_empty(_project_manager_team_member_id) THEN + PERFORM update_project_manager(_project_manager_team_member_id, _project_id); + END IF; + + RETURN JSON_BUILD_OBJECT( + 'id', _project_id, + 'name', _project_name, + 'project_created_log_id', _project_created_log_id + ); +END; +$$; + COMMIT; --- ============================================================================= --- Migration: Subtask manual progress --- Date: 2025-04-23 --- Version: 1.0.0 --- File: 20250423000000-subtask-manual-progress.sql --- ============================================================================= - --- Note: Contents extracted from the file description (actual file not available) --- This migration likely extends the manual progress feature to support subtasks - --- ============================================================================= --- Migration: Add progress and weight activity types --- Date: 2025-04-24 --- Version: 1.0.0 --- File: 20250424000000-add-progress-and-weight-activity-types.sql --- ============================================================================= - --- Note: Contents extracted from the file description (actual file not available) --- This migration likely adds new activity types for tracking progress and weight changes - --- ============================================================================= --- Migration: Update time-based progress mode to work for all tasks --- Date: 2025-04-25 --- Version: 1.0.0 --- File: 20250425000000-update-time-based-progress.sql --- ============================================================================= - BEGIN; -- Update function to use time-based progress for all tasks @@ -349,24 +516,142 @@ BEGIN END $$; +CREATE OR REPLACE FUNCTION public.get_task_form_view_model(_user_id UUID, _team_id UUID, _task_id UUID, _project_id UUID) RETURNS JSON + LANGUAGE plpgsql +AS +$$ +DECLARE + _task JSON; + _priorities JSON; + _projects JSON; + _statuses JSON; + _team_members JSON; + _assignees JSON; + _phases JSON; +BEGIN + + -- Select task info + SELECT COALESCE(ROW_TO_JSON(rec), '{}'::JSON) + INTO _task + FROM (WITH RECURSIVE task_hierarchy AS ( + -- Base case: Start with the given task + SELECT id, + parent_task_id, + 0 AS level + FROM tasks + WHERE id = _task_id + + UNION ALL + + -- Recursive case: Traverse up to parent tasks + SELECT t.id, + t.parent_task_id, + th.level + 1 AS level + FROM tasks t + INNER JOIN task_hierarchy th ON t.id = th.parent_task_id + WHERE th.parent_task_id IS NOT NULL) + SELECT id, + name, + description, + start_date, + end_date, + done, + total_minutes, + priority_id, + project_id, + created_at, + updated_at, + status_id, + parent_task_id, + sort_order, + (SELECT phase_id FROM task_phase WHERE task_id = tasks.id) AS phase_id, + CONCAT((SELECT key FROM projects WHERE id = tasks.project_id), '-', task_no) AS task_key, + (SELECT start_time + FROM task_timers + WHERE task_id = tasks.id + AND user_id = _user_id) AS timer_start_time, + parent_task_id IS NOT NULL AS is_sub_task, + (SELECT COUNT('*') + FROM tasks + WHERE parent_task_id = tasks.id + AND archived IS FALSE) AS sub_tasks_count, + (SELECT COUNT(*) + FROM tasks_with_status_view tt + WHERE (tt.parent_task_id = tasks.id OR tt.task_id = tasks.id) + AND tt.is_done IS TRUE) + AS completed_count, + (SELECT COUNT(*) FROM task_attachments WHERE task_id = tasks.id) AS attachments_count, + (SELECT COALESCE(ARRAY_TO_JSON(ARRAY_AGG(ROW_TO_JSON(r))), '[]'::JSON) + FROM (SELECT task_labels.label_id AS id, + (SELECT name FROM team_labels WHERE id = task_labels.label_id), + (SELECT color_code FROM team_labels WHERE id = task_labels.label_id) + FROM task_labels + WHERE task_id = tasks.id + ORDER BY name) r) AS labels, + (SELECT color_code + FROM sys_task_status_categories + WHERE id = (SELECT category_id FROM task_statuses WHERE id = tasks.status_id)) AS status_color, + (SELECT COUNT(*) FROM tasks WHERE parent_task_id = _task_id) AS sub_tasks_count, + (SELECT name FROM users WHERE id = tasks.reporter_id) AS reporter, + (SELECT get_task_assignees(tasks.id)) AS assignees, + (SELECT id FROM team_members WHERE user_id = _user_id AND team_id = _team_id) AS team_member_id, + billable, + schedule_id, + progress_value, + weight, + (SELECT MAX(level) FROM task_hierarchy) AS task_level + FROM tasks + WHERE id = _task_id) rec; + + SELECT COALESCE(ARRAY_TO_JSON(ARRAY_AGG(ROW_TO_JSON(rec))), '[]'::JSON) + INTO _priorities + FROM (SELECT id, name FROM task_priorities ORDER BY value) rec; + + SELECT COALESCE(ARRAY_TO_JSON(ARRAY_AGG(ROW_TO_JSON(rec))), '[]'::JSON) + INTO _phases + FROM (SELECT id, name FROM project_phases WHERE project_id = _project_id ORDER BY name) rec; + + SELECT COALESCE(ARRAY_TO_JSON(ARRAY_AGG(ROW_TO_JSON(rec))), '[]'::JSON) + INTO _projects + FROM (SELECT id, name + FROM projects + WHERE team_id = _team_id + AND (CASE + WHEN (is_owner(_user_id, _team_id) OR is_admin(_user_id, _team_id) IS TRUE) THEN TRUE + ELSE is_member_of_project(projects.id, _user_id, _team_id) END) + ORDER BY name) rec; + + SELECT COALESCE(ARRAY_TO_JSON(ARRAY_AGG(ROW_TO_JSON(rec))), '[]'::JSON) + INTO _statuses + FROM (SELECT id, name FROM task_statuses WHERE project_id = _project_id) rec; + + SELECT COALESCE(ARRAY_TO_JSON(ARRAY_AGG(ROW_TO_JSON(rec))), '[]'::JSON) + INTO _team_members + FROM (SELECT team_members.id, + (SELECT name FROM team_member_info_view WHERE team_member_info_view.team_member_id = team_members.id), + (SELECT email FROM team_member_info_view WHERE team_member_info_view.team_member_id = team_members.id), + (SELECT avatar_url + FROM team_member_info_view + WHERE team_member_info_view.team_member_id = team_members.id) + FROM team_members + LEFT JOIN users u ON team_members.user_id = u.id + WHERE team_id = _team_id + AND team_members.active IS TRUE) rec; + + SELECT get_task_assignees(_task_id) INTO _assignees; + + RETURN JSON_BUILD_OBJECT( + 'task', _task, + 'priorities', _priorities, + 'projects', _projects, + 'statuses', _statuses, + 'team_members', _team_members, + 'assignees', _assignees, + 'phases', _phases + ); +END; +$$; + COMMIT; --- ============================================================================= --- Migration: Improve parent task progress calculation --- Date: 2025-04-26 --- Version: 1.0.0 --- File: 20250426000000-improve-parent-task-progress-calculation.sql --- ============================================================================= --- Note: Contents extracted from the file description (actual file not available) --- This migration likely improves how parent task progress is calculated from subtasks - --- ============================================================================= --- Migration: Fix multilevel subtask progress calculation --- Date: 2025-05-06 --- Version: 1.0.0 --- File: 20250506000000-fix-multilevel-subtask-progress-calculation.sql --- ============================================================================= - --- Note: Contents extracted from the file description (actual file not available) --- This migration likely fixes progress calculation for multilevel nested subtasks \ No newline at end of file diff --git a/worklenz-backend/src/controllers/admin-center-controller.ts b/worklenz-backend/src/controllers/admin-center-controller.ts index 3c50c858..6b2f5362 100644 --- a/worklenz-backend/src/controllers/admin-center-controller.ts +++ b/worklenz-backend/src/controllers/admin-center-controller.ts @@ -5,7 +5,7 @@ import db from "../config/db"; import {ServerResponse} from "../models/server-response"; import WorklenzControllerBase from "./worklenz-controller-base"; import HandleExceptions from "../decorators/handle-exceptions"; -import {calculateMonthDays, getColor, megabytesToBytes} from "../shared/utils"; +import {calculateMonthDays, getColor, log_error, megabytesToBytes} from "../shared/utils"; import moment from "moment"; import {calculateStorage} from "../shared/s3"; import {checkTeamSubscriptionStatus, getActiveTeamMemberCount, getCurrentProjectsCount, getFreePlanSettings, getOwnerIdByTeam, getTeamMemberCount, getUsedStorage} from "../shared/paddle-utils"; @@ -255,22 +255,33 @@ export default class AdminCenterController extends WorklenzControllerBase { const {id} = req.params; const {name, teamMembers} = req.body; - const updateNameQuery = `UPDATE teams - SET name = $1 - WHERE id = $2;`; - await db.query(updateNameQuery, [name, id]); + try { + // Update team name + const updateNameQuery = `UPDATE teams SET name = $1 WHERE id = $2 RETURNING id;`; + const nameResult = await db.query(updateNameQuery, [name, id]); + + if (!nameResult.rows.length) { + return res.status(404).send(new ServerResponse(false, null, "Team not found")); + } - if (teamMembers.length) { - teamMembers.forEach(async (element: { role_name: string; user_id: string; }) => { - const q = `UPDATE team_members - SET role_id = (SELECT id FROM roles WHERE roles.team_id = $1 AND name = $2) - WHERE user_id = $3 - AND team_id = $1;`; - await db.query(q, [id, element.role_name, element.user_id]); - }); + // Update team member roles if provided + if (teamMembers?.length) { + // Use Promise.all to handle all role updates concurrently + await Promise.all(teamMembers.map(async (member: { role_name: string; user_id: string; }) => { + const roleQuery = ` + UPDATE team_members + SET role_id = (SELECT id FROM roles WHERE roles.team_id = $1 AND name = $2) + WHERE user_id = $3 AND team_id = $1 + RETURNING id;`; + await db.query(roleQuery, [id, member.role_name, member.user_id]); + })); + } + + return res.status(200).send(new ServerResponse(true, null, "Team updated successfully")); + } catch (error) { + log_error("Error updating team:", error); + return res.status(500).send(new ServerResponse(false, null, "Failed to update team")); } - - return res.status(200).send(new ServerResponse(true, [], "Team updated successfully")); } @HandleExceptions() diff --git a/worklenz-frontend/src/api/admin-center/admin-center.api.service.ts b/worklenz-frontend/src/api/admin-center/admin-center.api.service.ts index 857efb91..4d45b222 100644 --- a/worklenz-frontend/src/api/admin-center/admin-center.api.service.ts +++ b/worklenz-frontend/src/api/admin-center/admin-center.api.service.ts @@ -112,11 +112,11 @@ export const adminCenterApiService = { async updateTeam( team_id: string, - team_members: IOrganizationUser[] + body: {name: string, teamMembers: IOrganizationUser[]} ): Promise> { const response = await apiClient.put>( `${rootUrl}/organization/team/${team_id}`, - team_members + body ); return response.data; }, diff --git a/worklenz-frontend/src/components/admin-center/teams/settings-drawer/settings-drawer.tsx b/worklenz-frontend/src/components/admin-center/teams/settings-drawer/settings-drawer.tsx index 8a1efc35..b08cee23 100644 --- a/worklenz-frontend/src/components/admin-center/teams/settings-drawer/settings-drawer.tsx +++ b/worklenz-frontend/src/components/admin-center/teams/settings-drawer/settings-drawer.tsx @@ -13,21 +13,17 @@ import { } from 'antd'; import React, { useState } from 'react'; import { useAppDispatch } from '@/hooks/useAppDispatch'; -import { toggleSettingDrawer, updateTeam } from '@/features/teams/teamSlice'; -import { TeamsType } from '@/types/admin-center/team.types'; import './settings-drawer.css'; -import CustomAvatar from '@/components/CustomAvatar'; -import { teamsApiService } from '@/api/teams/teams.api.service'; import logger from '@/utils/errorLogger'; import { adminCenterApiService } from '@/api/admin-center/admin-center.api.service'; import { IOrganizationTeam, IOrganizationTeamMember, } from '@/types/admin-center/admin-center.types'; -import Avatars from '@/components/avatars/avatars'; -import { AvatarNamesMap } from '@/shared/constants'; import SingleAvatar from '@/components/common/single-avatar/single-avatar'; import { useTranslation } from 'react-i18next'; +import { API_BASE_URL } from '@/shared/constants'; +import apiClient from '@/api/api-client'; interface SettingTeamDrawerProps { teamId: string; @@ -68,26 +64,30 @@ const SettingTeamDrawer: React.FC = ({ }; const handleFormSubmit = async (values: any) => { - console.log(values); - // const newTeam: TeamsType = { - // teamId: teamId, - // teamName: values.name, - // membersCount: team?.membersCount || 1, - // members: team?.members || ['Raveesha Dilanka'], - // owner: values.name, - // created: team?.created || new Date(), - // isActive: false, - // }; - // dispatch(updateTeam(newTeam)); - // dispatch(toggleSettingDrawer()); - // form.resetFields(); - // message.success('Team updated!'); + try { + setUpdatingTeam(true); + + const body = { + name: values.name, + teamMembers: teamData?.team_members || [] + }; + + const response = await adminCenterApiService.updateTeam(teamId, body); + + if (response.done) { + setIsSettingDrawerOpen(false); + } + } catch (error) { + logger.error('Error updating team', error); + } finally { + setUpdatingTeam(false); + } }; const roleOptions = [ - { value: 'Admin', label: t('admin') }, - { value: 'Member', label: t('member') }, - { value: 'Owner', label: t('owner') }, + { key: 'Admin', value: 'Admin', label: t('admin') }, + { key: 'Member', value: 'Member', label: t('member') }, + { key: 'Owner', value: 'Owner', label: t('owner'), disabled: true }, ]; const columns: TableProps['columns'] = [ @@ -104,16 +104,40 @@ const SettingTeamDrawer: React.FC = ({ { title: t('role'), key: 'role', - render: (_, record: IOrganizationTeamMember) => ( -
- +
+ ); + }, }, ];