How i can reOrder the Channel Tabs using graph API inside our Teams apps
I am working on a Teams App >> which creates a new SharePoint modern Teams Site + new Channels + tabs inside the channels.
The final result will be as follow: –
Now I want to reorder the tabs inside the General channel, mainly by moving the Notes tab to be the last tab, here is the method i have: –
//ReOrder General Channel Tabs
public async reorderTabs(graph: any, teamGroupId: any, channelID: string, tabName: string) {
try {
//get URL for the tab
const tabsGraphEndPoint = graphConfig.teamsGraphEndpoint + “/” + teamGroupId + graphConfig.channelsGraphEndpoint + “/” + channelID + graphConfig.tabsGraphEndpoint;
const tabs = await this.getGraphData(tabsGraphEndPoint, graph)
// Find the “Notes” tab
const notesTab = tabs.value.find((tab: any) => tab.displayName === “Notes”);
if (notesTab)
{
// Update the orderHint to move the “Notes” tab to the end
const updateTabEndpoint = graphConfig.teamsGraphEndpoint + “/” + teamGroupId + graphConfig.channelsGraphEndpoint + “/” + channelID + graphConfig.tabsGraphEndpoint + “/”+ notesTab.id;
const lastOrderHint = tabs.value[tabs.value.length – 1].orderHint;
// Set the orderHint to something greater than the last tab’s orderHint
const newOrderHint = “6”;
await this.sendGraphPatchRequest(updateTabEndpoint, graph, {
sortOrderIndex: newOrderHint
});
return notesTab.displayName;
}
} catch (error) {
console.error(
constants.errorLogPrefix + “CommonService_getTabURL n”,
JSON.stringify(error)
);
throw error;
}
}
Here is the main method inside the .tsx file:-
// wrapper method to perform teams related operations
private async createTeamAndChannels(incidentId: any) {
try {
console.log(constants.infoLogPrefix + “M365 group creation starts”);
// call method to create Teams group
const groupInfo = await this.createTeamGroup(incidentId);
try {
console.log(constants.infoLogPrefix + “M365 group created”);
// create associated team with the group
const teamInfo = await this.createTeam(groupInfo);
if (teamInfo.status) {
//log trace
this.dataService.trackTrace(this.props.appInsights, “Incident Team created “, incidentId, this.props.userPrincipalName);
//Send invitations to the guest users
let returnInvitationObj: any;
if (this.state.toggleGuestUsers)
returnInvitationObj = this.sendInvitation(groupInfo.id, teamInfo.data.displayName, teamInfo.data.webUrl)
// create channels
await this.createChannels(teamInfo.data);
this.setState({ loaderMessage: this.props.localeStrings.createPlanloaderMessage });
//Get General channel id
const generalChannelId = await this.dataService.getChannelId(this.props.graph,
groupInfo.id, constants.General);
//Add TEOC app to the Incident Team General channel’s Active Dashboard Tab
await this.dataService.createActiveDashboardTab(this.props.graph, groupInfo.id,
generalChannelId, this.props.graphContextURL, this.props.appSettings);
//Create planner with the Group ID
const planID = await this.dataService.createPlannerPlan(groupInfo.id, incidentId, this.props.graph,
this.props.graphContextURL, this.props.tenantID, generalChannelId, false);
//added for GCCH tenant
if (this.props.graphBaseUrl !== constants.defaultGraphBaseURL) {
// wait for 5 seconds to ensure the SharePoint site is available via graph API
await this.timeout(5000);
}
// graph endpoint to get team site Id
const teamSiteURLGraphEndpoint = graphConfig.teamGroupsGraphEndpoint + “/” +
groupInfo.id + graphConfig.rootSiteGraphEndpoint;
// retrieve team site details
const teamSiteDetails = await this.dataService.getGraphData(teamSiteURLGraphEndpoint, this.props.graph);
//get the team site managed path
const teamSiteManagedPathURL = teamSiteDetails.webUrl.split(teamSiteDetails.siteCollection.hostname)[1];
console.log(constants.infoLogPrefix + “Site ManagedPath”, teamSiteManagedPathURL);
// create news channel and tab
const newsTabLink = await this.createNewsTab(groupInfo, teamSiteDetails.webUrl, teamSiteManagedPathURL,groupInfo.id);
// create assessment channel and tab
// await this.createAssessmentChannelAndTab(groupInfo.id, teamSiteDetails.webUrl, teamSiteManagedPathURL);
// call method to create assessment list
//await this.createAssessmentList(groupInfo.mailNickname, teamSiteDetails.id);
//Reorder Tabs
const tabname = await this.dataService.reorderTabs(this.props.graph,teamInfo.data.id,generalChannelId,”Notes”);
console.log(constants.infoLogPrefix + “Reorder General channel Tabs”);
//log trace
this.dataService.trackTrace(this.props.appInsights, “Assessment list created “, incidentId, this.props.userPrincipalName);
//change the M365 group visibility to Private for GCCH tenant
if (this.props.graphBaseUrl !== constants.defaultGraphBaseURL) {
this.graphEndpoint = graphConfig.teamGroupsGraphEndpoint + “/” + groupInfo.id;
await this.dataService.sendGraphPatchRequest(this.graphEndpoint, this.props.graph, { “visibility”: “Private” })
console.log(constants.infoLogPrefix + “Group setting changed to Private”);
}
//Update Team details, Plan ID, NewsTabLink in Incident Transation List
const updateItemObj = {
IncidentId: incidentId,
TeamWebURL: teamInfo.data.webUrl,
PlanID: planID,
NewsTabLink: newsTabLink
};
await this.updateIncidentItemInList(incidentId, updateItemObj);
console.log(constants.infoLogPrefix + “List item updated”);
let roles: any = this.state.roleAssignments;
roles.push({
role: constants.incidentCommanderRoleName,
userNamesString: this.state.incDetailsItem.incidentCommander.userName,
userDetailsObj: [this.state.incDetailsItem.incidentCommander]
});
//post incident message in General Channel
await this.postIncidentMessage(groupInfo.id);
// create the tags for incident commander and each selected roles
await this.createTagObject(teamInfo.data.id, roles);
//log trace
this.dataService.trackTrace(this.props.appInsights, “Tags are created “, incidentId, this.props.userPrincipalName);
Promise.allSettled([returnInvitationObj]).then((promiseObj: any) => {
this.setState({
showLoader: false,
formOpacity: 1
});
// Display success message if incident updated successfully
this.props.showMessageBar(this.props.localeStrings.incidentCreationSuccessMessage, constants.messageBarType.success);
// Display error message if guest invitations
if ((promiseObj[0]?.value !== undefined && !promiseObj[0].value?.isAllSucceeded))
this.props.showMessageBar(
((promiseObj[0]?.value !== undefined && !promiseObj[0]?.value?.isAllSucceeded) ? ” ” + promiseObj[0]?.value?.message + “. ” : “”),
constants.messageBarType.error);
this.props.onBackClick(constants.messageBarType.success);
});
}
else {
// delete the group if some error occured
await this.deleteTeamGroup(groupInfo.id);
// delete the item if error occured
await this.deleteIncident(incidentId);
this.setState({
showLoader: false,
formOpacity: 1
})
this.props.showMessageBar(this.props.localeStrings.genericErrorMessage + “, ” + this.props.localeStrings.errMsgForCreateIncident, constants.messageBarType.error);
}
}
catch (error) {
console.error(
constants.errorLogPrefix + “CreateIncident_createTeamAndChannels n”,
JSON.stringify(error)
);
// Log Exception
this.dataService.trackException(this.props.appInsights, error, constants.componentNames.IncidentDetailsComponent, ‘CreateIncident_createTeamAndChannels’, this.props.userPrincipalName);
// delete the group if some error occured
await this.deleteTeamGroup(groupInfo.id);
// delete the item if error occured
await this.deleteIncident(incidentId);
this.setState({
showLoader: false,
formOpacity: 1
})
this.props.showMessageBar(this.props.localeStrings.genericErrorMessage + “, ” + this.props.localeStrings.errMsgForCreateIncident, constants.messageBarType.error);
}
}
catch (error: any) {
console.error(
constants.errorLogPrefix + “CreateIncident_createTeamAndChannels n”,
JSON.stringify(error)
);
// Log Exception
this.dataService.trackException(this.props.appInsights, error, constants.componentNames.IncidentDetailsComponent, ‘CreateIncident_createTeamAndChannels’, this.props.userPrincipalName);
// delete the item if error occured
this.deleteIncident(incidentId);
this.setState({
showLoader: false,
formOpacity: 1
});
// Display error message if M365 group creation fails with access denied error
if (error?.statusCode === 403 && error?.code === constants.authorizationRequestDenied
&& error?.message === constants.groupCreationAccessDeniedErrorMessage) {
this.props.showMessageBar(this.props.localeStrings.genericErrorMessage + “, ” + this.props.localeStrings.m365GroupCreationFailedMessage, constants.messageBarType.error);
}
/* Display error message if M365 group creation fails with group already exists error
or any other error */
else {
this.props.showMessageBar(this.props.localeStrings.genericErrorMessage + “, ” + this.props.localeStrings.errMsgForCreateIncident, constants.messageBarType.error);
}
}
}
But after calling the above method during the site and channel creation the Notes will stay the 3rd tab. any advice?
I am working on a Teams App >> which creates a new SharePoint modern Teams Site + new Channels + tabs inside the channels.The final result will be as follow: – Now I want to reorder the tabs inside the General channel, mainly by moving the Notes tab to be the last tab, here is the method i have: – //ReOrder General Channel Tabs
public async reorderTabs(graph: any, teamGroupId: any, channelID: string, tabName: string) {
try {
//get URL for the tab
const tabsGraphEndPoint = graphConfig.teamsGraphEndpoint + “/” + teamGroupId + graphConfig.channelsGraphEndpoint + “/” + channelID + graphConfig.tabsGraphEndpoint;
const tabs = await this.getGraphData(tabsGraphEndPoint, graph)
// Find the “Notes” tab
const notesTab = tabs.value.find((tab: any) => tab.displayName === “Notes”);
if (notesTab)
{
// Update the orderHint to move the “Notes” tab to the end
const updateTabEndpoint = graphConfig.teamsGraphEndpoint + “/” + teamGroupId + graphConfig.channelsGraphEndpoint + “/” + channelID + graphConfig.tabsGraphEndpoint + “/”+ notesTab.id;
const lastOrderHint = tabs.value[tabs.value.length – 1].orderHint;
// Set the orderHint to something greater than the last tab’s orderHint
const newOrderHint = “6”;
await this.sendGraphPatchRequest(updateTabEndpoint, graph, {
sortOrderIndex: newOrderHint
});
return notesTab.displayName;
}
} catch (error) {
console.error(
constants.errorLogPrefix + “CommonService_getTabURL n”,
JSON.stringify(error)
);
throw error;
}
} Here is the main method inside the .tsx file:- // wrapper method to perform teams related operations
private async createTeamAndChannels(incidentId: any) {
try {
console.log(constants.infoLogPrefix + “M365 group creation starts”);
// call method to create Teams group
const groupInfo = await this.createTeamGroup(incidentId);
try {
console.log(constants.infoLogPrefix + “M365 group created”);
// create associated team with the group
const teamInfo = await this.createTeam(groupInfo);
if (teamInfo.status) {
//log trace
this.dataService.trackTrace(this.props.appInsights, “Incident Team created “, incidentId, this.props.userPrincipalName);
//Send invitations to the guest users
let returnInvitationObj: any;
if (this.state.toggleGuestUsers)
returnInvitationObj = this.sendInvitation(groupInfo.id, teamInfo.data.displayName, teamInfo.data.webUrl)
// create channels
await this.createChannels(teamInfo.data);
this.setState({ loaderMessage: this.props.localeStrings.createPlanloaderMessage });
//Get General channel id
const generalChannelId = await this.dataService.getChannelId(this.props.graph,
groupInfo.id, constants.General);
//Add TEOC app to the Incident Team General channel’s Active Dashboard Tab
await this.dataService.createActiveDashboardTab(this.props.graph, groupInfo.id,
generalChannelId, this.props.graphContextURL, this.props.appSettings);
//Create planner with the Group ID
const planID = await this.dataService.createPlannerPlan(groupInfo.id, incidentId, this.props.graph,
this.props.graphContextURL, this.props.tenantID, generalChannelId, false);
//added for GCCH tenant
if (this.props.graphBaseUrl !== constants.defaultGraphBaseURL) {
// wait for 5 seconds to ensure the SharePoint site is available via graph API
await this.timeout(5000);
}
// graph endpoint to get team site Id
const teamSiteURLGraphEndpoint = graphConfig.teamGroupsGraphEndpoint + “/” +
groupInfo.id + graphConfig.rootSiteGraphEndpoint;
// retrieve team site details
const teamSiteDetails = await this.dataService.getGraphData(teamSiteURLGraphEndpoint, this.props.graph);
//get the team site managed path
const teamSiteManagedPathURL = teamSiteDetails.webUrl.split(teamSiteDetails.siteCollection.hostname)[1];
console.log(constants.infoLogPrefix + “Site ManagedPath”, teamSiteManagedPathURL);
// create news channel and tab
const newsTabLink = await this.createNewsTab(groupInfo, teamSiteDetails.webUrl, teamSiteManagedPathURL,groupInfo.id);
// create assessment channel and tab
// await this.createAssessmentChannelAndTab(groupInfo.id, teamSiteDetails.webUrl, teamSiteManagedPathURL);
// call method to create assessment list
//await this.createAssessmentList(groupInfo.mailNickname, teamSiteDetails.id);
//Reorder Tabs
const tabname = await this.dataService.reorderTabs(this.props.graph,teamInfo.data.id,generalChannelId,”Notes”);
console.log(constants.infoLogPrefix + “Reorder General channel Tabs”);
//log trace
this.dataService.trackTrace(this.props.appInsights, “Assessment list created “, incidentId, this.props.userPrincipalName);
//change the M365 group visibility to Private for GCCH tenant
if (this.props.graphBaseUrl !== constants.defaultGraphBaseURL) {
this.graphEndpoint = graphConfig.teamGroupsGraphEndpoint + “/” + groupInfo.id;
await this.dataService.sendGraphPatchRequest(this.graphEndpoint, this.props.graph, { “visibility”: “Private” })
console.log(constants.infoLogPrefix + “Group setting changed to Private”);
}
//Update Team details, Plan ID, NewsTabLink in Incident Transation List
const updateItemObj = {
IncidentId: incidentId,
TeamWebURL: teamInfo.data.webUrl,
PlanID: planID,
NewsTabLink: newsTabLink
};
await this.updateIncidentItemInList(incidentId, updateItemObj);
console.log(constants.infoLogPrefix + “List item updated”);
let roles: any = this.state.roleAssignments;
roles.push({
role: constants.incidentCommanderRoleName,
userNamesString: this.state.incDetailsItem.incidentCommander.userName,
userDetailsObj: [this.state.incDetailsItem.incidentCommander]
});
//post incident message in General Channel
await this.postIncidentMessage(groupInfo.id);
// create the tags for incident commander and each selected roles
await this.createTagObject(teamInfo.data.id, roles);
//log trace
this.dataService.trackTrace(this.props.appInsights, “Tags are created “, incidentId, this.props.userPrincipalName);
Promise.allSettled([returnInvitationObj]).then((promiseObj: any) => {
this.setState({
showLoader: false,
formOpacity: 1
});
// Display success message if incident updated successfully
this.props.showMessageBar(this.props.localeStrings.incidentCreationSuccessMessage, constants.messageBarType.success);
// Display error message if guest invitations
if ((promiseObj[0]?.value !== undefined && !promiseObj[0].value?.isAllSucceeded))
this.props.showMessageBar(
((promiseObj[0]?.value !== undefined && !promiseObj[0]?.value?.isAllSucceeded) ? ” ” + promiseObj[0]?.value?.message + “. ” : “”),
constants.messageBarType.error);
this.props.onBackClick(constants.messageBarType.success);
});
}
else {
// delete the group if some error occured
await this.deleteTeamGroup(groupInfo.id);
// delete the item if error occured
await this.deleteIncident(incidentId);
this.setState({
showLoader: false,
formOpacity: 1
})
this.props.showMessageBar(this.props.localeStrings.genericErrorMessage + “, ” + this.props.localeStrings.errMsgForCreateIncident, constants.messageBarType.error);
}
}
catch (error) {
console.error(
constants.errorLogPrefix + “CreateIncident_createTeamAndChannels n”,
JSON.stringify(error)
);
// Log Exception
this.dataService.trackException(this.props.appInsights, error, constants.componentNames.IncidentDetailsComponent, ‘CreateIncident_createTeamAndChannels’, this.props.userPrincipalName);
// delete the group if some error occured
await this.deleteTeamGroup(groupInfo.id);
// delete the item if error occured
await this.deleteIncident(incidentId);
this.setState({
showLoader: false,
formOpacity: 1
})
this.props.showMessageBar(this.props.localeStrings.genericErrorMessage + “, ” + this.props.localeStrings.errMsgForCreateIncident, constants.messageBarType.error);
}
}
catch (error: any) {
console.error(
constants.errorLogPrefix + “CreateIncident_createTeamAndChannels n”,
JSON.stringify(error)
);
// Log Exception
this.dataService.trackException(this.props.appInsights, error, constants.componentNames.IncidentDetailsComponent, ‘CreateIncident_createTeamAndChannels’, this.props.userPrincipalName);
// delete the item if error occured
this.deleteIncident(incidentId);
this.setState({
showLoader: false,
formOpacity: 1
});
// Display error message if M365 group creation fails with access denied error
if (error?.statusCode === 403 && error?.code === constants.authorizationRequestDenied
&& error?.message === constants.groupCreationAccessDeniedErrorMessage) {
this.props.showMessageBar(this.props.localeStrings.genericErrorMessage + “, ” + this.props.localeStrings.m365GroupCreationFailedMessage, constants.messageBarType.error);
}
/* Display error message if M365 group creation fails with group already exists error
or any other error */
else {
this.props.showMessageBar(this.props.localeStrings.genericErrorMessage + “, ” + this.props.localeStrings.errMsgForCreateIncident, constants.messageBarType.error);
}
}
} But after calling the above method during the site and channel creation the Notes will stay the 3rd tab. any advice? Read More