diff --git a/Images/APIAccess.png b/Images/APIAccess.png index 36c3215d..603f0144 100644 Binary files a/Images/APIAccess.png and b/Images/APIAccess.png differ diff --git a/Images/AddTab_ChampionAdd.png b/Images/AddTab_ChampionAdd.png index e8815df6..df60e856 100644 Binary files a/Images/AddTab_ChampionAdd.png and b/Images/AddTab_ChampionAdd.png differ diff --git a/Images/AddTab_Teams.png b/Images/AddTab_Teams.png index 03c54acb..8378695b 100644 Binary files a/Images/AddTab_Teams.png and b/Images/AddTab_Teams.png differ diff --git a/Images/AppLoading.png b/Images/AppLoading.png new file mode 100644 index 00000000..cee0eea0 Binary files /dev/null and b/Images/AppLoading.png differ diff --git a/Images/AppRefresh.png b/Images/AppRefresh.png new file mode 100644 index 00000000..ede0856e Binary files /dev/null and b/Images/AppRefresh.png differ diff --git a/Images/ManageAppLogo.png b/Images/ManageAppLogo.png new file mode 100644 index 00000000..b065862a Binary files /dev/null and b/Images/ManageAppLogo.png differ diff --git a/Images/ManageTournament.png b/Images/ManageTournament.png index 6c954af4..f5541bf2 100644 Binary files a/Images/ManageTournament.png and b/Images/ManageTournament.png differ diff --git a/Images/MultipleBadges.png b/Images/MultipleBadges.png index 9c2aefcf..220d3a28 100644 Binary files a/Images/MultipleBadges.png and b/Images/MultipleBadges.png differ diff --git a/Images/MyDashboard.png b/Images/MyDashboard.png index 87a8155b..2fe389eb 100644 Binary files a/Images/MyDashboard.png and b/Images/MyDashboard.png differ diff --git a/Images/SuccessMessage.png b/Images/SuccessMessage.png new file mode 100644 index 00000000..78f6d8a1 Binary files /dev/null and b/Images/SuccessMessage.png differ diff --git a/Images/SyncToTeams.png b/Images/SyncToTeams.png index 0b51d488..32740488 100644 Binary files a/Images/SyncToTeams.png and b/Images/SyncToTeams.png differ diff --git a/Images/TOTHome.png b/Images/TOTHome.png index a2dc1f34..8ebd3408 100644 Binary files a/Images/TOTHome.png and b/Images/TOTHome.png differ diff --git a/Images/TOTLeaderBoard.png b/Images/TOTLeaderBoard.png index 9302d0b7..f7f919eb 100644 Binary files a/Images/TOTLeaderBoard.png and b/Images/TOTLeaderBoard.png differ diff --git a/Images/Upgrade-1.png b/Images/Upgrade-1.png new file mode 100644 index 00000000..42bfbc21 Binary files /dev/null and b/Images/Upgrade-1.png differ diff --git a/Images/Upgrade-2.png b/Images/Upgrade-2.png new file mode 100644 index 00000000..6e15a1d7 Binary files /dev/null and b/Images/Upgrade-2.png differ diff --git a/Images/Upgrade-3.png b/Images/Upgrade-3.png new file mode 100644 index 00000000..ca8cacaa Binary files /dev/null and b/Images/Upgrade-3.png differ diff --git a/Images/Upgrade-4.png b/Images/Upgrade-4.png new file mode 100644 index 00000000..eeff86f1 Binary files /dev/null and b/Images/Upgrade-4.png differ diff --git a/Images/Upgrade2.0-1.png b/Images/Upgrade2.0-1.png deleted file mode 100644 index 015f64dc..00000000 Binary files a/Images/Upgrade2.0-1.png and /dev/null differ diff --git a/Images/Upgrade2.0-2.png b/Images/Upgrade2.0-2.png deleted file mode 100644 index cf276e06..00000000 Binary files a/Images/Upgrade2.0-2.png and /dev/null differ diff --git a/Images/Upgrade2.0-4.png b/Images/Upgrade2.0-4.png deleted file mode 100644 index 90b08d1c..00000000 Binary files a/Images/Upgrade2.0-4.png and /dev/null differ diff --git a/Wiki/Deployment-Guide.md b/Wiki/Deployment-Guide.md index dd559953..cb3579c3 100644 --- a/Wiki/Deployment-Guide.md +++ b/Wiki/Deployment-Guide.md @@ -69,6 +69,8 @@ Continuing below steps you can take the cmp.sppkg file from the ***cmp.sppkg*** ### Deploy your Package to SharePoint +IMPORTANT NOTE: If you have just created a new tenant please wait for around 15 minutes before starting with the below steps. + 1. Open SharePoint and sign in using the administrator account. Click on the "dots" icon in the top left corner of the banner.
@@ -119,7 +121,7 @@ Continuing below steps you can take the cmp.sppkg file from the ***cmp.sppkg*** 10. A confirmation dialog is displayed. Ensure the checkbox for "Make this solution available to all sites in the organization" is chosen and click ***Deploy***.
-![Quick Start Guide](../Images/Upgrade2.0-2.png) +![Quick Start Guide](../Images/Upgrade-2.png)
11. Return to the ***SharePoint admin center***. Under expand the ***Advanced*** menu in the left navigation and select ***API access***. Select and approve all pending requests associated with ***championmanagement***
@@ -128,11 +130,14 @@ Continuing below steps you can take the cmp.sppkg file from the ***cmp.sppkg***
12. Return to app list in the App Catalog and select the ***championmanagement*** app. Select the Files tab in the ribbon and click the ***Sync to Teams*** button.
+ ![Quick Start Guide](../Images/SyncToTeams.png)
-### First Run Experience:Add ***Champion Management Platform*** Tab in Teams +### First Run Experience:Add ***Champion Management Platform*** Tab in Teamst + +IMPORTANT NOTE: Please wait for around 20 minutes for API access approvals done in the previous section to take effect before proceeding with the below steps. 1. Navigate to Microsoft teams, select the Team and channel where you want to install Champion Management Platform. Click ***Add a tab***, choose ***Champion Management Platform*** from the app list, and Save. (Search for Champion) @@ -146,11 +151,21 @@ This first run experience needs to be completed by the person who will be an adm ![Quick Start Guide](../Images/AddTab_ChampionAdd.png) 2. Click 'Add' to create the ***'Champion Management Platform'*** tab to your Teams (Alternately you can also just load the app as a personal app in the left rail here too). -3. The landing page for an Admin will have access to the *Champion Leaderboard, Digital Badge, Enable Tournament of Teams* and the SharePoint lists (*Champions, Events,Event track Details, Digital Badges*), as well as Manage Approvals. +3. After clicking on 'Add', the app set up will start and you will see a spinner as shown below. The set up may take around 1-2 minutes.
+![App Set Up](../Images/AppLoading.png) +4. After the set up is complete a success message is shown as below. The below snapshot is from Teams in browser. The same message would look different in Teams client. +
+![App Set Up](../Images/SuccessMessage.png) +5. On clicking 'OK' the landing page is displayed.
+![App Set Up](../Images/AddTab_Teams.png) +6. If you still see the spinner message and do not see the success message even after 2 minutes or if you see any error alerts, try clicking on refresh icon on top right. If you see the landing page with all the icons shown in the above picture the app set up is complete.
+![App Set Up](../Images/Apprefresh.png) +If you still do not see the landing page after refresh please create an issue in the Github. +7. The landing page for an Admin will have access to the *Champion Leaderboard, Digital Badge, Enable Tournament of Teams* and the SharePoint lists (*Champions, Events,Event track Details, Digital Badges*), as well as Manage Approvals.
![Quick Start Guide](../Images/AddTab_Teams.png) -4. Grant Permissions to users: +8. Grant Permissions to users: · Navigate to the URL for the Champion Management Platform site as the administrator. diff --git a/Wiki/Release-Notes.md b/Wiki/Release-Notes.md index d2c3aff5..5be6dae9 100644 --- a/Wiki/Release-Notes.md +++ b/Wiki/Release-Notes.md @@ -3,6 +3,7 @@ This page contains the different release details for Champion Management Platfor ## Version history | Version | Release Date | |----|----| +| 2.1 | Apr 4, 2022 | | 2.0 | Dec 9, 2021 | | 1.3 | Sep 03, 2021 | | 1.2 | Jul 22, 2021 | @@ -10,6 +11,18 @@ This page contains the different release details for Champion Management Platfor ## Release notes +### 2.1 (Apr 4, 2022) + +Below improvements released, + +- Bug Fix: Addressed an issue with tournament ranks/points not getting updated correctly for some customers within the Tournament of Teams leaderboard. +- New Feature: Multi-Tournament support within the Tournament of Teams module. Organizations can now have multiple tournaments occurring at one time +- New Feature: Introduced image preview feature for the Digital Badge module. Users can now preview their profile pictures with different badges available to them before applying it. +- New Feature: Implemented the Digital Badge feature native to Tournament of Teams. Users can now use the Digital Badge feature without being added as a member of the Champion Management Platform. +- New Feature: Implemented an ability that allows admins to update the logo of the Champion Management Platform that sits in the top application bar. Admins will now see a new icon "Manage App Logo" under the "Admin" section in the home page of the App. Upon replacing the image in the SharePoint library, the new app logo will be shown. +- New Feature: Implemented multilingual support for the application. English support at the moment +- Other Changes: Minor UI fixes. + ### 2.0 (Dec 9, 2021) Below improvements released, diff --git a/Wiki/Solution-Overview.md b/Wiki/Solution-Overview.md index 8bc8353f..d07c1c9f 100644 --- a/Wiki/Solution-Overview.md +++ b/Wiki/Solution-Overview.md @@ -57,13 +57,21 @@ Admin and current Champions of the program can nominate(***Add Members***) peers The Admin can approve/Reject the champion nominations using Manage Approvals screen. +### Manage App Logo +![ManageAppLogo](../Images/ManageAppLogo.png) +"Manage App Logo" option can be found under "Admin Tools" section on home page. Clicking on this icon opens the "CMP Logo" SharePoint library. New image can be uploaded to the library to replace the App logo. The new image needs to have below specifications: + +Name: AppLogo.jpg
+Type: JPG
+Dimensions: 32 X 32

+ ### Digital Badge ![Champion logo](../Images/Champion_small.png) Digital Badge is intended to allow Microsoft 365 Champions to apply a ‘Champion’ badge on their profile image. It provides an easy and seamless process to share the recognition as a champion with the team.
![Quick Start Guide](../Images/Digitalbadge.png) -CMP administrators can upload multiple badges in the “Digital Badge Assets” library in the share point site. Champions can select from multiple badges and apply on their profile picture. +CMP administrators can upload multiple badges in the “Digital Badge Assets” library in the share point site. Champions can select from multiple badges, preview the profile picture and apply on their profile picture.

![Quick Start Guide](../Images/MultipleBadges.png) diff --git a/Wiki/Tournament-of-Teams.md b/Wiki/Tournament-of-Teams.md index c63c88cd..eac9563c 100644 --- a/Wiki/Tournament-of-Teams.md +++ b/Wiki/Tournament-of-Teams.md @@ -6,7 +6,13 @@ Tournament of Teams is not enabled by default. CMP admin needs to enable it from On enabling “Tournament of Teams”: -1. The lists are provisioned in the CMP share point site +1. Below lists are provisioned in the CMP share point site: + +> * Actions List +> * ToT Admins +> * Tournament Actions +> * Tournaments +> * User Actions 2. “Tournament of Teams” link is visible under “Quick Start Guide” section of CMP home page. This link navigates the users to “Tournament of Teams” landing page. @@ -35,19 +41,24 @@ Below are the master lists in Share Point that need to be managed by the TOT Adm ![Quick Start Guide](../Images/CreateTournament.png) -### Start/End Tournament +### Manage Tournaments + + - A tournament can be started or ended by an admin using “Manage Tournaments” screen. - - A tournament can be started or ended by an admin using “Start/End Tournament” screen. + - Multiple tournaments can be can be active at a time. - - Only one tournament can be active at a time. To start a new tournament the active tournament must be ended. ![Quick Start Guide](../Images/ManageTournament.png) ### My Dashboard -- Champions can start participating in the active tournament using “My Dashboard”. +- Champions can start participating in any of the active tournaments using “My Dashboard”. + +- Champions can select the tournament from either "My Tournaments" or "Active Tournaments" dropdown list. If a user has participated in any of the active tournaments already they will be listed under "My Tournaments" list. All the other active tournaments will be listed under "Active Tournaments" list. -- “My Dashboard” shows the Teams Actions available in the current active tournament. The champions can select the completed actions and save to earn points and a rank in that tournament which is displayed on the left side section of the page. +- “My Dashboard” will show the Teams Actions available in the selected tournament. The champions can select the completed actions and save to earn points and a rank in that tournament which is displayed on the left side section of the page. + +- By default, the first tournament in the "My Tournaments" will be selected. If the user has not participated in any of the tournaments yet, first tournament in the "Active Tournaments" will be selected. - The users must complete at least one action to be considered as a participant in that tournament. The ranks are calculated only among the participants. @@ -55,6 +66,8 @@ Below are the master lists in Share Point that need to be managed by the TOT Adm ### Leaderboard -Leader board is available to everyone. It shows the list of all participants in the current active tournament along with their points and ranks. +- Leader board is available to everyone. It shows the list of all participants in the selected active tournament along with their points and ranks. +- By default, the first tournament in the "My Tournaments" will be selected. If the user has not participated in any of the tournaments yet, first tournament in the "Active Tournaments" will be selected. + ![Quick Start Guide](../Images/TOTLeaderBoard.png) \ No newline at end of file diff --git a/Wiki/Upgrade.md b/Wiki/Upgrade.md index bded7c18..3a515dc7 100644 --- a/Wiki/Upgrade.md +++ b/Wiki/Upgrade.md @@ -1,33 +1,37 @@ -### Upgrade to version 2.0 from 1.3 and 1.2 +### Upgrade to version 2.1 from 2.0, 1.3 and 1.2 -If you are already having version 1.3 or 1.2 installed on your tenant follow the below steps to upgrade to version 2.0: +If you are already having version 2.0, 1.3 or 1.2 installed on your tenant follow the below steps to upgrade to version 2.1 with an admin account: -1. Navigate to App Catalog with a tenant Admin account. Do not delete the existing 1.3 or 1.2 package. Upload the new package that is downloaded from "sharepoint/solution" folder which will replace the existing package. +1. Navigate to App Catalog with a tenant Admin account. Do not delete the existing package. Upload the new package that is downloaded from "sharepoint/solution" folder which will replace the existing package. -![Upgrade 1.3](../Images/Upgrade2.0-1.png) +![Upgrade 1.3](../Images/Upgrade-1.png) 2. Click on "Deploy". -![Upgrade 1.3](../Images/Upgrade2.0-2.png) +![Upgrade 1.3](../Images/Upgrade-2.png) 3. "Check In" the package. ![Upgrade 1.3](../Images/Upgrade2.0-3.png) -4. Select the package and click "Sync to Teams" from the ribbon and wait for the "Successfully synced to teams solution" message to appear +4. Select the package and click "Sync to Teams" from the ribbon and wait for the "Successfully synced to teams solution" message to appear. -![Upgrade 1.3](../Images/SyncToTeams.png) +![SyncToTeams](../Images/SyncToTeams.png) -5. Navigate to SharePoint admin center. Under Advanced menu in the left navigation select API access. Select and approve the additional pending request associated with championmanagement as shown below. -After approving the permission, it would take some time for it to take effect. +5. The Champion Management Platform will be updated to the latest version and you will see changes reflected in Teams. Please note that if you do not see changes reflected in Teams after 30 minutes you can log out and back in and clear the Teams cache to see changes immediately. -![Upgrade 1.3](../Images/Upgrade2.0-4.png) +6. If you already had "Tournament of Teams" enabled before the upgrade, click on "Enable Tournament of Teams" under "Admin Tools" section again. This is to upgrade the Tournament of Teams module which includes the bug fix for inaccurate tournament rankings and points. "Tournament of Teams" icon will not be visible in the "Get Started" section without this step. +NOTE: If there is any active tournament before the upgrade the leader board will show emails instead of User Display Names after the upgrade. This will not happen for new users or new tournaments. -6. The Champion Management Platform will be updated to the latest version and you will see changes reflected in Teams. Please note that if you do not see changes reflected in Teams after 30 minutes you can log out and back in and clear the Teams cache to see changes immediately. +![Upgrade-3](../Images/Upgrade-3.png) -### Upgrade to version 2.0 from 1.1 +7. The below graph permission can be removed from "API Permissions" in sharePoint admin portal only if this permission is not used in any other apps in your tenant. The upgraded package for CMP is not using this API anymore. -If you are already having 1.1 installed on your tenant and want to upgrade to 2.0 the existing app and SharePoint site 'ChampionManagementSite' have to be deleted. +![Upgrade-4](../Images/Upgrade-4.png) + +### Upgrade to version 2.1 from 1.1 + +If you are already having 1.1 installed on your tenant and want to upgrade to 2.1 the existing app and SharePoint site 'ChampionManagementSite' have to be deleted. If you have current members and events you will want to export those list items and import re-import them into the respective list areas. We have expanded our lists to have some additional data as well so you may need to populate additional fields. diff --git a/config/package-solution.json b/config/package-solution.json index dd3cf1a5..2bc97b57 100644 --- a/config/package-solution.json +++ b/config/package-solution.json @@ -3,7 +3,7 @@ "solution": { "name": "championmanagement", "id": "5099a3ef-113f-4baf-bb6a-a73b1dd286f8", - "version": "2.0.0.0", + "version": "2.1.0.0", "includeClientSideAssets": true, "skipFeatureDeployment": true, "isDomainIsolated": false,"developer": { @@ -25,12 +25,7 @@ { "resource": "Microsoft Graph", "scope": "Sites.Manage.All" - }, - { - "resource": "Microsoft Graph", - "scope": "User.ReadBasic.All" - } - + } ] }, diff --git a/gulpfile.js b/gulpfile.js index 8b326472..2c065bb9 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -18,7 +18,14 @@ build.configureWebpack.mergeConfig({ } }); +var getTasks = build.rig.getTasks; +build.rig.getTasks = function () { + var result = getTasks.call(build.rig); + result.set('serve', result.get('serve-deprecated')); + + return result; +}; build.initialize(gulp); build.configureWebpack.mergeConfig({ diff --git a/package.json b/package.json index f22987c5..1de645d2 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "bootstrap": "^4.6.0", "classnames": "^2.3.1", "ejs": "^3.1.6", + "follow-redirects": "^1.14.7", "gulp-update": "0.0.2", "immer": "^9.0.6", "jquery": "^3.6.0", @@ -48,8 +49,11 @@ "lodash": "^4.17.20", "lodash.template": "^4.5.0", "merge": "^2.1.1", + "minimist": "^1.2.6", "moment": "^2.29.1", "msteams-ui-components-react": "^0.5.0", + "node-fetch": "^2.6.7", + "node-forge": "^1.3.0", "nodemon": "1.12.1", "office-ui-fabric-react": "6.189.2", "popper.js": "^1.16.1", @@ -69,6 +73,7 @@ "rxjs": "^6.6.7", "typescript": "^4.4.4", "typestyle": "1.5.1", + "url-parse": "^1.5.6", "webpack": "^1.15.0" }, "resolutions": { @@ -84,9 +89,11 @@ "ansi-html": "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41", "browserslist": "4.16.5", "css-what": "5.0.1", - "minimist": "0.2.1", + "minimist": "1.2.6", "ansi-regex": "^5.0.1", - "json-schema": "^0.4.0" + "json-schema": "^0.4.0", + "node-fetch": "2.6.7", + "node-forge": "^1.3.0" }, "devDependencies": { "@microsoft/rush-stack-compiler-2.9": "0.7.16", diff --git a/sharepoint/solution/cmp.sppkg b/sharepoint/solution/cmp.sppkg index 89fb3825..11fd6ef6 100644 Binary files a/sharepoint/solution/cmp.sppkg and b/sharepoint/solution/cmp.sppkg differ diff --git a/src/webparts/clbHome/Common/CommonServices.ts b/src/webparts/clbHome/Common/CommonServices.ts index 83b8abff..11c053e9 100644 --- a/src/webparts/clbHome/Common/CommonServices.ts +++ b/src/webparts/clbHome/Common/CommonServices.ts @@ -44,6 +44,12 @@ export default class CommonServices { return items; } + //Get list items based on a filter and with specific columns + public async getFilteredListItemsWithSpecificColumns(listname: string, columns: string, filter: string): Promise { + var items: any[] = []; + items = await sp.web.lists.getByTitle(listname).items.select(columns).filter(filter).getAll(); + return items; + } //Create list item public async createListItem(listname: string, data: any): Promise { return sp.web.lists.getByTitle(listname).items.add(data); @@ -69,17 +75,17 @@ export default class CommonServices { //execute the batch and add field to default view batch.execute().then(async () => { - let addingStatus=[]; + let addingStatus = []; for (let i = 0; i < fieldsToCreate.length; i++) { const parser = new DOMParser(); const xml = parser.parseFromString(fieldsToCreate[i], 'text/xml'); let fieldDisplayName = xml.querySelector('Field').getAttribute('DisplayName'); - let listView= await listContext.views.getByTitle("All Items").fields.add(fieldDisplayName); + let listView = await listContext.defaultView.fields.add(fieldDisplayName); addingStatus.push(listView); } - Promise.all(addingStatus).then(() => { - resolve("Success"); - }); + Promise.all(addingStatus).then(() => { + resolve("Success"); + }); }); } catch (error) { console.error("CommonServices_createListFields_FailedToCreatedField \n", error); @@ -96,7 +102,6 @@ export default class CommonServices { } - //Filter and get all badge imagesfrom 'Digital Badges' library for the current user public async getAllBadgeImages(listName: string, userEmail: string): Promise { var badgeImagesArray: any[] = []; @@ -120,15 +125,22 @@ export default class CommonServices { //if TOT is enabled get all the badges and filter for completed tournaments else { badgeImagesArray = await sp.web.lists.getByTitle(stringsConstants.DigitalBadgeLibrary).items.select("Title", "Tournament/Title", "File/Name").expand("Tournament", "File").get(); + //Checking if the user is in member list + let filterQuery = "Title eq '" + userEmail.toLowerCase() + "'" + " and Status eq 'Approved'"; + let isApprovedChampion = await this.getItemsWithOnlyFilter(stringsConstants.MemberList, filterQuery); + //Loop through badges and filter based on user's tournaments completion status for (let i = 0; i < badgeImagesArray.length; i++) { //For global badges do not check for Tournaments completion status if (badgeImagesArray[i].Tournament == undefined) { - finalImagesArray.push({ - title: badgeImagesArray[i].Title, - url: rootSiteURL + "/" + listName + "/" + badgeImagesArray[i].File.Name - }); + // Show the global badges only for champion + if (isApprovedChampion.length > 0) { + finalImagesArray.push({ + title: badgeImagesArray[i].Title, + url: rootSiteURL + "/" + listName + "/" + badgeImagesArray[i].File.Name + }); + } } else { var tournamentCompleted = await this.getTournamentCompletedFlag(badgeImagesArray[i].Tournament.Title, userEmail); @@ -147,8 +159,8 @@ export default class CommonServices { //Check if the user has completed the tournament public async getTournamentCompletedFlag(tournamentName: string, currentUserEmail: string): Promise { let tournamentCompletedFlag: boolean = false; - - tournamentName = tournamentName.replace(/'/g,"''") ; + + tournamentName = tournamentName.replace(/'/g, "''"); //Get total number of actions for the tournament let filterTournamentActions: string = "Title eq '" + tournamentName + "'"; var tournamentActionsCount: number = 0; @@ -164,82 +176,83 @@ export default class CommonServices { return tournamentCompletedFlag; } - //get all user action for active tournament and bind to table - public async getUserActions(activeTournamentName,allUsersDetails): Promise { + //get all user action for active tournament and bind to table + public async getUserActions(activeTournamentName): Promise { return new Promise(async (resolve, reject) => { - try { + try { let userActionsWithDisplayName: any = []; let userRanks: any = []; - let getAllUserActions: any = []; - - //get active tournament's participants - let filterQuery = "Tournament_x0020_Name eq '" + activeTournamentName.replace(/'/g,"''") + "'"; - //get first batch of items - let getUserActions = await sp.web.lists.getByTitle(stringsConstants.UserActionsList).items. - filter(filterQuery).select("Title", "Points").top(5000).getPaged(); - if (getUserActions.results.length > 0) - { + let getAllUserActions: any = []; + + //get active tournament's participants + let filterQuery = "Tournament_x0020_Name eq '" + activeTournamentName.replace(/'/g, "''") + "'"; + //get first batch of items + let getUserActions = await sp.web.lists.getByTitle(stringsConstants.UserActionsList).items. + filter(filterQuery).select("Title", "Points", "UserName").top(5000).getPaged(); + if (getUserActions.results.length > 0) { + getAllUserActions.push(...getUserActions.results); + //get next batch, if more items found + while (getUserActions.hasNext) { + getUserActions = await getUserActions.getNext(); getAllUserActions.push(...getUserActions.results); - //get next batch, if more items found - while (getUserActions.hasNext) { - getUserActions = await getUserActions.getNext(); - getAllUserActions.push(...getUserActions.results); + } + //groupby user and sum the points + var groupOfUniqueUsers = []; + getAllUserActions.reduce((res, value) => { + if (!res[value.Title]) { + res[value.Title] = { Title: value.Title, Points: 0 }; + groupOfUniqueUsers.push(res[value.Title]); } - //groupby user and sum the points - var groupOfUniqueUsers = []; - getAllUserActions.reduce((res, value) => { - if (!res[value.Title]) { - res[value.Title] = { Title: value.Title, Points: 0 }; - groupOfUniqueUsers.push(res[value.Title]); + res[value.Title].Points += value.Points; + res[value.Title].UserName = value.UserName; + return res; + }, {}); + //sorting by points and then by display name + groupOfUniqueUsers.sort((a, b) => { + if (a.Points < b.Points) return 1; + if (a.Points > b.Points) return -1; + if (a.Title > b.Title) return 1; + if (a.Title < b.Title) return -1; + }); + // get user Display Name for users + if (groupOfUniqueUsers.length > 0) { + for (let i = 0; i < groupOfUniqueUsers.length; i++) { + let itemEmail: string = groupOfUniqueUsers[i].Title.toLowerCase(); + let userDisplayName = itemEmail; + if (groupOfUniqueUsers[i].UserName != null) + userDisplayName = groupOfUniqueUsers[i].UserName; + + if (userDisplayName.length > 0) { + userActionsWithDisplayName.push( + { + User: userDisplayName.replace(',', ''), + Points: groupOfUniqueUsers[i].Points, + Email: itemEmail + }); } - res[value.Title].Points += value.Points; - return res; - }, {}); - //sorting by points and then by display name - groupOfUniqueUsers.sort((a, b) => { - if (a.Points < b.Points) return 1; - if (a.Points > b.Points) return -1; - if (a.Title > b.Title) return 1; - if (a.Title < b.Title) return -1; - }); - // get user Display Name for users - if (groupOfUniqueUsers.length > 0) { - for (let i = 0; i < groupOfUniqueUsers.length; i++) { - let itemEmail: string = groupOfUniqueUsers[i].Title.toLowerCase(); - let userDisplayName = allUsersDetails.filter( - (user) => user.email === itemEmail - ); - if (userDisplayName.length > 0) { - userActionsWithDisplayName.push( - { - User: userDisplayName[0].displayName.replace(',',''), - Points: groupOfUniqueUsers[i].Points, - Email:itemEmail - }); - } - }//for loop of getting user display name ends here - } - //associate rank on the sorted array of users - for (let j = 0; j < userActionsWithDisplayName.length; j++) { - userRanks.push( - { - Rank: j + 1, - User: userActionsWithDisplayName[j].User, - Points: userActionsWithDisplayName[j].Points, - Email:userActionsWithDisplayName[j].Email - }); - } - resolve(userRanks); + }//for loop of getting user display name ends here } - else{ - resolve(userRanks); + //associate rank on the sorted array of users + for (let j = 0; j < userActionsWithDisplayName.length; j++) { + userRanks.push( + { + Rank: j + 1, + User: userActionsWithDisplayName[j].User, + Points: userActionsWithDisplayName[j].Points, + Email: userActionsWithDisplayName[j].Email + }); } - + resolve(userRanks); + } + else { + resolve(userRanks); + } + } catch (error) { console.error("CommonServices_getUserActions \n", error); reject("Failed"); - } + } }); } } diff --git a/src/webparts/clbHome/assets/CMPImages/MSLogo.jpg b/src/webparts/clbHome/assets/CMPImages/MSLogo.jpg new file mode 100644 index 00000000..e41f0633 Binary files /dev/null and b/src/webparts/clbHome/assets/CMPImages/MSLogo.jpg differ diff --git a/src/webparts/clbHome/assets/CMPImages/ManageAppLogo.svg b/src/webparts/clbHome/assets/CMPImages/ManageAppLogo.svg new file mode 100644 index 00000000..35566d4a --- /dev/null +++ b/src/webparts/clbHome/assets/CMPImages/ManageAppLogo.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/webparts/clbHome/assets/stylesheets/components/_DigitalBadge.scss b/src/webparts/clbHome/assets/stylesheets/DigitalBadgeProfile.scss similarity index 94% rename from src/webparts/clbHome/assets/stylesheets/components/_DigitalBadge.scss rename to src/webparts/clbHome/assets/stylesheets/DigitalBadgeProfile.scss index 1987484d..77ccc207 100644 --- a/src/webparts/clbHome/assets/stylesheets/components/_DigitalBadge.scss +++ b/src/webparts/clbHome/assets/stylesheets/DigitalBadgeProfile.scss @@ -1,4 +1,4 @@ -@import "../../../../../../node_modules/office-ui-fabric-react/dist/sass/_References.scss"; +@import "../../../../../node_modules/office-ui-fabric-react/dist/sass/_References.scss"; .DigitalBadge { .container { diff --git a/src/webparts/clbHome/assets/stylesheets/components/_all.scss b/src/webparts/clbHome/assets/stylesheets/components/_all.scss deleted file mode 100644 index 2dd96b86..00000000 --- a/src/webparts/clbHome/assets/stylesheets/components/_all.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'DigitalBadge'; \ No newline at end of file diff --git a/src/webparts/clbHome/assets/stylesheets/main.scss b/src/webparts/clbHome/assets/stylesheets/main.scss deleted file mode 100644 index 2f615405..00000000 --- a/src/webparts/clbHome/assets/stylesheets/main.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'components/all'; \ No newline at end of file diff --git a/src/webparts/clbHome/components/ApproveChampion.tsx b/src/webparts/clbHome/components/ApproveChampion.tsx index 8a7b46dd..37044e5a 100644 --- a/src/webparts/clbHome/components/ApproveChampion.tsx +++ b/src/webparts/clbHome/components/ApproveChampion.tsx @@ -7,6 +7,7 @@ import { sp } from "@pnp/sp"; import * as React from "react"; import siteconfig from "../config/siteconfig.json"; import styles from "../scss/CMPApproveChampion.module.scss"; +import * as LocaleStrings from 'ClbHomeWebPartStrings'; @@ -143,12 +144,12 @@ class ApproveChampion extends React.Component { } else { if (status === 'Approved') { this.setState({ - approveMessage: `Your response has been ${status}.` + approveMessage: LocaleStrings.ChampionApprovedMessage }); } if (status === 'Rejected') { this.setState({ - rejectMessage: `Your response has been ${status}.` + rejectMessage: LocaleStrings.ChampionRejectedMessage }); } this._getListData(); @@ -166,12 +167,12 @@ class ApproveChampion extends React.Component { { this.props.onClickAddmember(); }} - title="Back" + title={LocaleStrings.CMPBreadcrumbLabel} > - Back + {LocaleStrings.CMPBreadcrumbLabel} - Manage Approval + {LocaleStrings.ManageApprovalsPageTitle} {this.state.approveMessage && } -
Champion List
+
{LocaleStrings.ChampionsListPageTitle}
- - - - - - {!this.props.isEmp && } - + + + + + + {!this.props.isEmp && } + {this.state.list && @@ -217,18 +218,18 @@ class ApproveChampion extends React.Component { @@ -248,7 +249,7 @@ class ApproveChampion extends React.Component { alt="norecordsicon" className={styles.noRecordsImg} /> - NO CHAMPION REQUESTS AVAILABLE + {LocaleStrings.NoChampionsMessage} ) } diff --git a/src/webparts/clbHome/components/ChampionLeaderBoard.tsx b/src/webparts/clbHome/components/ChampionLeaderBoard.tsx index 550cc394..0f2c5861 100644 --- a/src/webparts/clbHome/components/ChampionLeaderBoard.tsx +++ b/src/webparts/clbHome/components/ChampionLeaderBoard.tsx @@ -15,6 +15,7 @@ import * as _ from "lodash"; import Sidebar from "./Sidebar"; import ChampionvView from "./ChampionvView"; import siteconfig from "../config/siteconfig.json"; +import * as LocaleStrings from 'ClbHomeWebPartStrings'; type NewType = IPivotStyles; @@ -56,15 +57,15 @@ export default function ChampionLeaderBoard(props: ChampionLeaderBoardProps) { const _renderListAsync = async () => { props.context.spHttpClient - .get( "/" + inclusionpath + "/" + siteName + "/_api/web/lists/GetByTitle('Events List')/Items", SPHttpClient.configurations.v1) + .get("/" + inclusionpath + "/" + siteName + "/_api/web/lists/GetByTitle('Events List')/Items", SPHttpClient.configurations.v1) .then((response: SPHttpClientResponse) => { response .json() .then((eventdata) => { if (!eventdata.error) { setEventDropDown(_.orderBy(eventdata.value.filter(ed => ed.IsActive), ['Id'], ['asc'])); - props.context.spHttpClient.get( - "/"+inclusionpath+"/"+siteName+ "/_api/web/lists/GetByTitle('Member List')/Items?$top=1000&$filter= Status eq 'Approved'", SPHttpClient.configurations.v1) + props.context.spHttpClient.get( + "/" + inclusionpath + "/" + siteName + "/_api/web/lists/GetByTitle('Member List')/Items?$top=1000&$filter= Status eq 'Approved'", SPHttpClient.configurations.v1) // tslint:disable-next-line: no-shadowed-variable .then((response: SPHttpClientResponse) => { response.json().then((datada) => { @@ -89,7 +90,7 @@ export default function ChampionLeaderBoard(props: ChampionLeaderBoardProps) { const getUserPoints = (id: any) => { return props.context.spHttpClient.get( - + "/" + inclusionpath + "/" + @@ -124,8 +125,8 @@ export default function ChampionLeaderBoard(props: ChampionLeaderBoardProps) { if (c === usersd.length) { props.context.spHttpClient .get( - - "/"+inclusionpath+"/"+siteName+ + + "/" + inclusionpath + "/" + siteName + "/_api/web/lists/GetByTitle('Member List')/fields/GetByInternalNameOrTitle('Region')", SPHttpClient.configurations.v1 ) @@ -134,9 +135,9 @@ export default function ChampionLeaderBoard(props: ChampionLeaderBoardProps) { if (!focusAreas.error) { props.context.spHttpClient .get( - - "/"+ - inclusionpath+"/"+siteName+ + + "/" + + inclusionpath + "/" + siteName + "/_api/web/lists/GetByTitle('Member List')/fields/GetByInternalNameOrTitle('FocusArea')", SPHttpClient.configurations.v1 ) @@ -225,6 +226,20 @@ export default function ChampionLeaderBoard(props: ChampionLeaderBoardProps) { callBack={_renderListAsync} />
+
+ + { props.onClickCancel(); }} + title={LocaleStrings.CMPBreadcrumbLabel} + > + {LocaleStrings.CMPBreadcrumbLabel} + + + {LocaleStrings.ChampionLeaderBoardLabel} +
- + filterUsers("Region", event)} - placeholder="Select Near Me" + placeholder= {LocaleStrings.NearMePlaceHolder} options={options(regionDropdown)} styles={dropdownStyles} onRenderCaretDown={onRenderCaretDown} /> - + filterUsers("FocusArea", event)} - placeholder="Select By Specialty" + placeholder={LocaleStrings.BySpecialityPlaceHolder} options={options(countryDropdown)} styles={dropdownStyles} onRenderCaretDown={onRenderCaretDown} /> diff --git a/src/webparts/clbHome/components/Champions.tsx b/src/webparts/clbHome/components/Champions.tsx index a1f0baf6..5130ba2f 100644 --- a/src/webparts/clbHome/components/Champions.tsx +++ b/src/webparts/clbHome/components/Champions.tsx @@ -7,6 +7,8 @@ import Card from "react-bootstrap/Card"; import Table from "react-bootstrap/Table"; import * as microsoftTeams from "@microsoft/teams-js"; import { ILabelStyles, IStyleSet, Label, Icon, initializeIcons, } from "office-ui-fabric-react"; +import * as LocaleStrings from 'ClbHomeWebPartStrings'; + initializeIcons(); const labelStyles: Partial> = { @@ -62,13 +64,13 @@ export default class Champions extends Component< {this.props.type && ( )} {this.props.users.length === 0 && (
- Records Not Found + {LocaleStrings.RecordsNotFound}
)} {this.props.users.length > 0 && ( @@ -162,8 +164,8 @@ export default class Champions extends Component< {this.props.type && ( - {this.props.type} Top {this.props.filterBy} Champions - : My Rank + {this.props.type} {LocaleStrings.TopChampionsLabel} + : {LocaleStrings.MyRankLabel}
{this.state.isLoaded && ( @@ -196,7 +198,7 @@ export default class Champions extends Component<
- Rank {ind + 1} + {LocaleStrings.RankLabel} {ind + 1}
@@ -205,8 +207,8 @@ export default class Champions extends Component<
People NameRegionCountryFocusAreaGroupStatusAction{LocaleStrings.PeopleNameGridHeader}{LocaleStrings.RegionGridHeader}{LocaleStrings.CountryGridHeader}{LocaleStrings.FocusAreaGridHeader}{LocaleStrings.GroupGridHeader}{LocaleStrings.StatusGridHeader}{LocaleStrings.ActionGridHeader}
{Object.keys(rankedMember.eventpoints).length != 0 && - - + + } {Object.keys(rankedMember.eventpoints).map((e, i) => { diff --git a/src/webparts/clbHome/components/ChampionvView.tsx b/src/webparts/clbHome/components/ChampionvView.tsx index 043c4a4c..cf62e423 100644 --- a/src/webparts/clbHome/components/ChampionvView.tsx +++ b/src/webparts/clbHome/components/ChampionvView.tsx @@ -26,14 +26,17 @@ import { DataGrid } from "@material-ui/data-grid"; import * as moment from "moment"; import siteconfig from "../config/siteconfig.json"; import _ from "lodash"; -import { Label } from "@fluentui/react"; +import Col from "react-bootstrap/Col"; +import Row from "react-bootstrap/Row"; +import * as LocaleStrings from 'ClbHomeWebPartStrings'; const columns = [ - { field: "DateOfEvent", type: 'date', sortable: false, - headerName: "Date of Event", width: 200 + { + field: "DateOfEvent", type: 'date', sortable: false, + headerName: LocaleStrings.DateofEventGridLabel, width: 200 }, - { field: "type", headerName: "Type", width: 150 }, - { field: "Count", type: 'number', headerName: "Points", width: 150 }, + { field: "type", headerName: LocaleStrings.EventTypeGridLabel, width: 150 }, + { field: "Count", type: 'number', headerName: LocaleStrings.CMPSideBarPointsLabel, width: 150 }, ]; const DayPickerStrings: IDatePickerStrings = { months: [ @@ -211,14 +214,14 @@ export default class ChampionvView extends Component< public addDevice(data: ChampList, saved: any) { if (saved === "false") { - if((data.type == "" || data.type == "Select Event Type")) { - this.setState({showValidationError:true, validationError:"Please select event type!"}); + if ((data.type == "" || data.type == "Select Event Type")) { + this.setState({ showValidationError: true, validationError: LocaleStrings.EventTypeValidationMessage }); } - else if((data.Count > 5 || data.Count < 1)) { - this.setState({showValidationError:true, validationError:"Count should be between 1 and 5"}); + else if ((data.Count > 5 || data.Count < 1)) { + this.setState({ showValidationError: true, validationError: LocaleStrings.CountValidationMessage }); } else { - this.setState({ collectionNew: [], showValidationError:false }); + this.setState({ collectionNew: [], showValidationError: false }); const newBag = this.state.collectionNew.concat(data); this.setState({ collectionNew: newBag, @@ -227,7 +230,7 @@ export default class ChampionvView extends Component< }); this.setState({ selectedkey: 0 }); } - + } else { const newBag = this.state.collection.concat(data); this.setState({ @@ -619,7 +622,7 @@ export default class ChampionvView extends Component< eventKey="0" className="cursor cvw" > - View Dashboard + {LocaleStrings.ViewDashBoardLabel} @@ -648,15 +651,15 @@ export default class ChampionvView extends Component< eventKey="1" className="cursor" > - Record Event + {LocaleStrings.RecordEventLabel}
-
+
this.handleSelect(evt)} id="drp" options={this.options()} @@ -690,41 +693,41 @@ export default class ChampionvView extends Component< />
- -
-
- - this.addDevice( - { - id: 0, - type: this.state.type, - eventid: this.state.eventid, - memberid: this.state.memberid, - Count: this.state.points, - DateOfEvent: this.state.DateOfEvent, - MemberName: "test", - EventName: "evtest", - }, - "false" - ) - }/> -
+ "col-md-2", + controlClass.marginAuto, + controlClass.paddingRight + )}> + +
+
+ + this.addDevice( + { + id: 0, + type: this.state.type, + eventid: this.state.eventid, + memberid: this.state.memberid, + Count: this.state.points, + DateOfEvent: this.state.DateOfEvent, + MemberName: "test", + EventName: "evtest", + }, + "false" + ) + } /> +
{this.state.showValidationError && @@ -734,36 +737,37 @@ export default class ChampionvView extends Component< }
-
- {this.state.collectionNew.map((item) => ( -
- +
+ {this.state.collectionNew.map((item) => ( + +
tickIcon - - {item.DateOfEvent.toDateString()} - {item.type} - {item.Count} - - { - this.removeDevice(item.type, item.Count); - }} - /> - - + + {item.DateOfEvent.toDateString()} + {item.type} + {item.Count} + +
+ { + this.removeDevice(item.type, item.Count); + }} + /> +
+ + ))} - +
{this.state.collectionNew !== null && this.state.collectionNew.length !== 0 && (
- When you are done adding events, please - click on Submit button to save. + {LocaleStrings.EventsSubmitMessage}
)} {this.state.collectionNew !== null && this.state.collectionNew.length !== 0 && ( diff --git a/src/webparts/clbHome/components/ClbAddMember.tsx b/src/webparts/clbHome/components/ClbAddMember.tsx index 5cdb7e7f..18eba925 100644 --- a/src/webparts/clbHome/components/ClbAddMember.tsx +++ b/src/webparts/clbHome/components/ClbAddMember.tsx @@ -20,6 +20,7 @@ import Col from "react-bootstrap/Col"; import Row from "react-bootstrap/Row"; import siteconfig from "../config/siteconfig.json"; import styles from "../scss/CMPAddMember.module.scss"; +import * as LocaleStrings from 'ClbHomeWebPartStrings'; @@ -29,6 +30,7 @@ export interface IClbAddMemberProps { onClickBack: () => void; onClickSave: (userStatus: string) => void; siteUrl: string; + isAdmin: boolean; } export interface ISPLists { value: ISPList[]; @@ -361,7 +363,7 @@ class ClbAddMember extends React.Component { }); } else { this.setState({ - updatedMessage: "User Already a Champion!", + updatedMessage: LocaleStrings.UserExistingMessage, load: false }); } @@ -412,12 +414,12 @@ class ClbAddMember extends React.Component { { this.props.onClickBack(); }} - title="Back" + title={LocaleStrings.CMPBreadcrumbLabel} > - Back + {LocaleStrings.CMPBreadcrumbLabel} - Add Member + {this.props.isAdmin ? LocaleStrings.AddMemberPageTitle : LocaleStrings.NominateMemberPageTitle} {this.state.updatedMessage !== "" ? : null} {this.state.errorMessage !== "" ? : null} - + { principalTypes={[PrincipalType.User]} defaultSelectedUsers={this.state.selectedusers} resolveDelay={1000} - placeholder="For Adding a member please type member name" + placeholder={LocaleStrings.PeoplePickerPlaceholder} />

this.filterUsers("region", selectedOption)} - placeholder="Select Region" + placeholder={LocaleStrings.RegionPlaceholder} options={this.options(this.state.regions)} styles={dropdownStyles} /> @@ -451,7 +453,7 @@ class ClbAddMember extends React.Component { this.filterUsers("country", selectedOption)} - placeholder="Select Country" + placeholder={LocaleStrings.CountryPlaceholder} options={this.options(this.state.coutries)} styles={dropdownStyles} /> @@ -459,7 +461,7 @@ class ClbAddMember extends React.Component { this.filterUsers("group", selectedOption)} - placeholder="Select Group" + placeholder={LocaleStrings.GroupPlaceholder} options={this.options(this.state.groups)} styles={dropdownStyles} /> @@ -467,7 +469,7 @@ class ClbAddMember extends React.Component { this.filterUsers("focusArea", selectedOption)} - placeholder="Select Focus Area" + placeholder={LocaleStrings.FocusAreaPlaceholder} options={this.options(this.state.focusAreas)} styles={dropdownStyles} /> @@ -477,10 +479,10 @@ class ClbAddMember extends React.Component { {this.state.load &&
} diff --git a/src/webparts/clbHome/components/ClbChampionsList.tsx b/src/webparts/clbHome/components/ClbChampionsList.tsx index 5a97bd1e..9c9d3b2c 100644 --- a/src/webparts/clbHome/components/ClbChampionsList.tsx +++ b/src/webparts/clbHome/components/ClbChampionsList.tsx @@ -5,6 +5,7 @@ import { sp } from "@pnp/sp"; import * as React from "react"; import siteconfig from "../config/siteconfig.json"; import styles from "../scss/CMPChampionsList.module.scss"; +import * as LocaleStrings from 'ClbHomeWebPartStrings'; export interface IClbChampionsListProps { @@ -93,26 +94,26 @@ class ClbChampionsList extends React.Component { { this.props.onClickAddmember(); }} - title="Back" + title={LocaleStrings.CMPBreadcrumbLabel} > - Back + {LocaleStrings.CMPBreadcrumbLabel} - Champion List + {LocaleStrings.ChampionsListPageTitle} {this.props.userAdded ? : null} -
Champion List
+
{LocaleStrings.ChampionsListPageTitle}
Event TypeCount{LocaleStrings.EventTypeLabel}{LocaleStrings.CountLabel}
- - - - - + + + + + {!this.props.isEmp && } diff --git a/src/webparts/clbHome/components/ClbHome.tsx b/src/webparts/clbHome/components/ClbHome.tsx index a289aa49..d2935ab2 100644 --- a/src/webparts/clbHome/components/ClbHome.tsx +++ b/src/webparts/clbHome/components/ClbHome.tsx @@ -24,7 +24,8 @@ import EmployeeView from "./EmployeeView"; import Header from "./Header"; import { IClbHomeProps } from "./IClbHomeProps"; import TOTLandingPage from "./TOTLandingPage"; - +import * as LocaleStrings from 'ClbHomeWebPartStrings'; +import * as stringsConstants from "../constants/strings"; initializeIcons(); export interface IClbHomeState { @@ -48,6 +49,7 @@ export interface IClbHomeState { isUserAdded: boolean; userStatus: string; firstName: string; + appLogoURL: string; } //Global Variables @@ -83,7 +85,8 @@ export default class ClbHome extends React.Component< isTOTEnabled: false, isUserAdded: false, userStatus: "", - firstName: "" + firstName: "", + appLogoURL: "", }; this.checkUserRole = this.checkUserRole.bind(this); @@ -590,6 +593,8 @@ export default class ClbHome extends React.Component< }); //site is newly created and got 200 response, now create Digital Lib this.createDigitalBadgeLib(); + //create CMP Logo Library to store the organization logo that allows users to customize it. + this.getAppLogoImage(); } }).catch((error) => { @@ -797,7 +802,8 @@ export default class ClbHome extends React.Component< }); //site exists, check if Digital Badge lib exists, create if not present this.createDigitalBadgeLib(); - + //check if CMP Logo lib exists, create if not present + this.getAppLogoImage(); }//End of else part - CMP site already exists create only lists. }//First IF END @@ -811,7 +817,46 @@ export default class ClbHome extends React.Component< console.error("CMP_CLBHome_createSiteAndLists \n", error); alert(errorMessage + "while creating site and lists. Below are the details: \n" + error); } + } + //Create CMP Logo library to store organization logo + public async getAppLogoImage(): Promise { + try { + let logoImageURL: string; + const spListTitle: string = stringsConstants.CMPLogoLibrary; + //check if CMP Logo lib exists, create if doesn't exists and upload default logo + await spweb.lists.getByTitle(spListTitle).get().then(async () => { + var logoImageArray: any[] = []; + logoImageArray = await spweb.lists.getByTitle(spListTitle).items.select("File/Name").expand("File").get(); + if (logoImageArray.length > 0) { + for (let i = 0; i < logoImageArray.length; i++) { + if (logoImageArray[i].File.Name.toLowerCase() == "applogo.jpg") { + logoImageURL = rootSiteURL + "/" + siteconfig.inclusionPath + "/" + siteconfig.sitename + "/" + spListTitle + "/" + logoImageArray[i].File.Name; + this.setState({ + appLogoURL: logoImageURL, + }); + } + } + } else { + this.setState({ + appLogoURL: require("../assets/CMPImages/" + stringsConstants.MSLogo), + }); + } + }).catch(async () => { + //create library in SharePoint site to store the organization logo image + await spweb.lists.add(spListTitle, "", 101, true).then(async () => { + fetch(require("../assets/CMPImages/" + stringsConstants.MSLogo)).then(res => res.blob()).then((blob) => { + spweb.getFolderByServerRelativeUrl("/" + siteconfig.inclusionPath + "/" + siteconfig.sitename + "/" + spListTitle).files.add("AppLogo.jpg", blob, true); + this.setState({ + appLogoURL: require("../assets/CMPImages/" + stringsConstants.MSLogo), + }); + }); + }); + }); //catch end + } + catch (error) { + console.error("CMP_CLBHome_getAppLogoImage \n", error); + } } //create digital lib to store the badges private async createDigitalBadgeLib() { @@ -918,6 +963,7 @@ export default class ClbHome extends React.Component< < div className={styles.container} >
this.setState({ @@ -937,10 +983,10 @@ export default class ClbHome extends React.Component< !this.state.dB && !this.state.enableTOT && (
- Welcome {this.state.firstName}! + {LocaleStrings.WelcomeLabel} {this.state.firstName}!
-
Get Started
+
{LocaleStrings.GetStartedLabel}
Champion Leader Board -
- Champion Leader Board +
+ {LocaleStrings.ChampionLeaderBoardLabel}
@@ -973,11 +1019,13 @@ export default class ClbHome extends React.Component<
Adding Members Start adding the people you will collaborate with in your... -
Add Members
+
+ {(this.state.clB && !this.state.cV) ? LocaleStrings.AddMemberLabel : LocaleStrings.NominateMemberLabel} +
@@ -991,11 +1039,11 @@ export default class ClbHome extends React.Component<
Digital Badge, Get your Champion Badge -
Digital Badge
+
{LocaleStrings.DigitalBadgeLabel}
@@ -1010,12 +1058,12 @@ export default class ClbHome extends React.Component<
Tournament of Teams - {this.state.isTOTEnabled && (
- Tournament of Teams
)} + {this.state.isTOTEnabled && (
+ {LocaleStrings.TOTLabel}
)}
@@ -1023,7 +1071,7 @@ export default class ClbHome extends React.Component< {this.state.clB && !this.state.cV && ( -
Admin Tools
)} +
{LocaleStrings.AdminToolsLabel}
)} {this.state.clB && !this.state.cV && ( @@ -1036,12 +1084,12 @@ export default class ClbHome extends React.Component< > Accessing Champions List -
Champions List
+
{LocaleStrings.ChampionListLabel}
@@ -1054,12 +1102,12 @@ export default class ClbHome extends React.Component< > Accessing Events List -
Events List
+
{LocaleStrings.EventsListLabel}
@@ -1072,13 +1120,13 @@ export default class ClbHome extends React.Component< > Accessing Event Track List -
- Event Track List +
+ {LocaleStrings.EventsTrackListLabel}
@@ -1095,12 +1143,12 @@ export default class ClbHome extends React.Component<
Approve Champion -
- Manage Approvals +
+ {LocaleStrings.ManageApprovalsLabel}
@@ -1114,12 +1162,12 @@ export default class ClbHome extends React.Component<
Enable Tournament of Teams - {!this.state.isTOTEnabled && (
- Enable Tournament of Teams
)} + {!this.state.isTOTEnabled && (
+ {LocaleStrings.EnableTOTLabel}
)}
)} @@ -1132,13 +1180,33 @@ export default class ClbHome extends React.Component< > Manage Digital Badges + +
+ {LocaleStrings.ManageDigitalBadgesLabel} +
+
+ + +
+ +
+ + {LocaleStrings.ManageAppLogoToolTip} -
- Manage Digital Badges +
+ {LocaleStrings.ManageAppLogoLabel}
@@ -1187,6 +1255,7 @@ export default class ClbHome extends React.Component< this.setState({ addMember: false, ChampionsList: true, isUserAdded: false }) } diff --git a/src/webparts/clbHome/components/DigitalBadge.tsx b/src/webparts/clbHome/components/DigitalBadge.tsx index fa6ba59b..1dbf745d 100644 --- a/src/webparts/clbHome/components/DigitalBadge.tsx +++ b/src/webparts/clbHome/components/DigitalBadge.tsx @@ -34,7 +34,7 @@ import { MessageBarType, } from "office-ui-fabric-react/lib/MessageBar"; import * as strings from "../constants/strings"; -import "../assets/stylesheets/main.scss"; +import "../assets/stylesheets/DigitalBadgeProfile.scss"; import * as $ from "jquery"; import IProfileImage from "../models/IProfileImage"; import { WebPartContext } from "@microsoft/sp-webpart-base"; @@ -43,10 +43,9 @@ import { Icon } from '@fluentui/react/lib/Icon'; import { mergeStyleSets } from '@fluentui/react/lib/Styling'; import siteconfig from "../config/siteconfig.json"; import commonServices from '../Common/CommonServices'; -import * as stringsConstants from "../constants/strings"; import { List } from '@fluentui/react/lib/List'; import { Label } from "@microsoft/office-ui-fabric-react-bundle"; - +import * as LocaleStrings from 'ClbHomeWebPartStrings'; const config = { baseFontSize: 16, @@ -130,6 +129,11 @@ const classNames = mergeStyleSets({ fontSize: "16px", fontWeight: "bolder", opacity: 1 + }, + profileImage: { + display: "block", + margin: "0 auto", + borderRadius: "50%" } }); @@ -202,7 +206,7 @@ export default class DigitalBadge extends TeamsBaseComponent< error: "", imageDownloaded: false, showAccept: false, - downloadText: strings.DownloadButtonText, + downloadText: LocaleStrings.DownloadButtonText, userletters: "", sitename: siteconfig.sitename, inclusionpath: siteconfig.inclusionPath, @@ -230,7 +234,58 @@ export default class DigitalBadge extends TeamsBaseComponent<
{ this.onBadgeSelected(item.url); }}> - + {this.state.profileImage.url && + this.state.profileImage.url !== + "../assets/images/noimage.png" && ( +
+ {LocaleStrings.ProfileImageAlt} + {LocaleStrings.BadgeImageAlt} +
+ )} + {this.state.profileImage.url && + this.state.profileImage.url === + "../assets/images/noimage.png" && ( +
+ {LocaleStrings.ProfileImageAlt} + {this.state.userletters} + + {LocaleStrings.BadgeImageAlt} +
+ )} + {item.title}
@@ -240,7 +295,7 @@ export default class DigitalBadge extends TeamsBaseComponent< catch (error) { console.error("CMP_DigitalBadge_onRenderCell \n", JSON.stringify(error)); this.setState({ - error: stringsConstants.TOTErrorMessage + "while displaying the digital badges. Below are the details: \n" + JSON.stringify(error), + error: strings.TOTErrorMessage + "while displaying the digital badges. Below are the details: \n" + JSON.stringify(error), isApplying: false, isLoading: false }); @@ -260,7 +315,7 @@ export default class DigitalBadge extends TeamsBaseComponent< catch (error) { console.error("CMP_DigitalBadge_onBadgeSelected \n", JSON.stringify(error)); this.setState({ - error: stringsConstants.TOTErrorMessage + "while applying the digital badge. Below are the details: \n" + JSON.stringify(error), + error: strings.TOTErrorMessage + "while applying the digital badge. Below are the details: \n" + JSON.stringify(error), isApplying: false, isLoading: false }); @@ -272,7 +327,7 @@ export default class DigitalBadge extends TeamsBaseComponent< try { this.setState({ isLoading: true }); let commonServiceManager: commonServices = new commonServices(this.props.context, this.props.siteUrl); - const resultImages: any[] = await commonServiceManager.getAllBadgeImages(stringsConstants.DigitalBadgeLibrary, this.props.context.pageContext.user.email.toLowerCase()); + const resultImages: any[] = await commonServiceManager.getAllBadgeImages(strings.DigitalBadgeLibrary, this.props.context.pageContext.user.email.toLowerCase()); if (resultImages.length == 0) this.setState({ noBadgesFlag: true, isLoading: false }); else @@ -281,7 +336,7 @@ export default class DigitalBadge extends TeamsBaseComponent< catch (error) { console.error("CMP_DigitalBadge_getAllBadgeImages \n", JSON.stringify(error)); this.setState({ - error: stringsConstants.TOTErrorMessage + "while retrieving the digital badges. Below are the details: \n" + JSON.stringify(error), + error: strings.TOTErrorMessage + "while retrieving the digital badges. Below are the details: \n" + JSON.stringify(error), isApplying: false, isLoading: false }); @@ -302,19 +357,7 @@ export default class DigitalBadge extends TeamsBaseComponent< ) .then((responseuser: SPHttpClientResponse) => { responseuser.json().then((datauser: any) => { - this.props.context.spHttpClient - .get( - "/" + this.state.inclusionpath + "/" + this.state.sitename + - "/_api/web/lists/GetByTitle('Member List')/Items?$filter=Title eq '" + datauser.Email.toLowerCase() + "'", - SPHttpClient.configurations.v1 - ) - .then((response: SPHttpClientResponse) => { - response.json().then((datada) => { - let dataexists = datada.value.find( - (x: any) => - x.Title.toLowerCase() == datauser.Email.toLowerCase() - ); - if (dataexists) { + let userassignletters = ""; let usernamearray = datauser.DisplayName.split(" "); if (usernamearray.length === 1) { @@ -330,7 +373,7 @@ export default class DigitalBadge extends TeamsBaseComponent< showAccept: true, userletters: userassignletters, }); - } + this.updateTheme(context.theme); upn = datauser.Email; this.setState({ @@ -338,9 +381,7 @@ export default class DigitalBadge extends TeamsBaseComponent< entityId: context.entityId, upn: context.upn, }); - this.showUserInformation(upn); - }); - }); + this.showUserInformation(upn); }); }); }); @@ -353,8 +394,8 @@ export default class DigitalBadge extends TeamsBaseComponent< {this.state.isLoading && ( )} @@ -404,12 +445,12 @@ export default class DigitalBadge extends TeamsBaseComponent< - Back + {LocaleStrings.CMPBreadcrumbLabel} - Digital Badge + {LocaleStrings.DigitalBadgePageTitle}
@@ -418,8 +459,8 @@ export default class DigitalBadge extends TeamsBaseComponent< {this.state.isLoading && ( )} @@ -434,13 +475,13 @@ export default class DigitalBadge extends TeamsBaseComponent< {!this.state.hasAccepted && (
- {strings.PreAcceptPageTitle} + {LocaleStrings.PreAcceptPageTitle}

{strings.BannerImageAlt} {this.state.badgeImgURL}
@@ -451,7 +492,7 @@ export default class DigitalBadge extends TeamsBaseComponent<

)} @@ -461,7 +502,7 @@ export default class DigitalBadge extends TeamsBaseComponent<

)} @@ -471,11 +512,11 @@ export default class DigitalBadge extends TeamsBaseComponent<

- How to get Champion Badge + {LocaleStrings.HowtoGetDigitalBadgeText}

)} @@ -485,14 +526,14 @@ export default class DigitalBadge extends TeamsBaseComponent<

{this.state.noBadgesFlag && (

)} @@ -506,7 +547,7 @@ export default class DigitalBadge extends TeamsBaseComponent< )} {this.state.hasAccepted && this.state.hasImageSelected && (

- {strings.PageTitle} + {LocaleStrings.DigitalBadgeSubPageTitle}
)}
@@ -527,7 +568,7 @@ export default class DigitalBadge extends TeamsBaseComponent< }} src={this.state.profileImage.url} id={"profileImage"} - alt={strings.ProfileImageAlt} + alt={LocaleStrings.ProfileImageAlt} /> {strings.BadgeImageAlt}
@@ -553,7 +594,7 @@ export default class DigitalBadge extends TeamsBaseComponent< src={require("../assets/images/noimage.png")} style={{ width: `100px` }} id={"profileImage"} - alt={strings.ProfileImageAlt} + alt={LocaleStrings.ProfileImageAlt} />
{this.state.userletters} @@ -564,7 +605,7 @@ export default class DigitalBadge extends TeamsBaseComponent< marginTop: `-100px`, }} id={"badgeImage"} - alt={strings.BadgeImageAlt} + alt={LocaleStrings.BadgeImageAlt} src={this.state.imageURL} />
@@ -576,7 +617,7 @@ export default class DigitalBadge extends TeamsBaseComponent< @@ -595,19 +636,19 @@ export default class DigitalBadge extends TeamsBaseComponent< contextCSS )} onClick={this._onApplyProfileImage} - ariaLabel={strings.ApplyButtonText} + ariaLabel={LocaleStrings.ApplyButtonText} ariaDescription={ - strings.ApplyButtonAriaDescription + LocaleStrings.ApplyButtonAriaDescription } disabled={ this.state.isApplying || this.state.isApplied || this.state.error.length > 0 } - title="Apply" + title={LocaleStrings.ApplyButton} > - {strings.ApplyButtonText} + {LocaleStrings.ApplyButtonText}
{this.state.profileImage.url !== @@ -623,13 +664,13 @@ export default class DigitalBadge extends TeamsBaseComponent< .container} ${dbStyles.downloadBtn}` } style={{ ...styleProps }} - title="Download" + title={LocaleStrings.DownloadButtonText} onClick={this._onDownloadImage} ariaLabel={ - strings.DownloadButtonText + LocaleStrings.DownloadButtonText } ariaDescription={ - strings.DownloadButtonAriaDescription + LocaleStrings.DownloadButtonAriaDescription } disabled={ this.state.isApplying || @@ -645,8 +686,8 @@ export default class DigitalBadge extends TeamsBaseComponent< > { this.state.imageDownloaded - ? strings.DownloadedButtonSecondaryText - : strings.DownloadButtonSecondaryText + ? LocaleStrings.DownloadedButtonSecondaryText + : LocaleStrings.DownloadButtonSecondaryText } @@ -664,7 +705,7 @@ export default class DigitalBadge extends TeamsBaseComponent< "../assets/images/noimage.png" && (

@@ -675,7 +716,7 @@ export default class DigitalBadge extends TeamsBaseComponent< this.state.profileImage.url && (

@@ -687,7 +728,7 @@ export default class DigitalBadge extends TeamsBaseComponent< this.state.hasImageSelected && (

@@ -699,14 +740,14 @@ export default class DigitalBadge extends TeamsBaseComponent< - {strings.AcceptButtonText} + {LocaleStrings.AcceptButtonText} )} {!this.state.hasAccepted && @@ -715,7 +756,7 @@ export default class DigitalBadge extends TeamsBaseComponent< className={"description"} style={{ color: "red" }} dangerouslySetInnerHTML={this.createMarkup( - strings.UnauthorizedText + LocaleStrings.UnauthorizedText )} /> )} @@ -725,9 +766,9 @@ export default class DigitalBadge extends TeamsBaseComponent< !this.state.isApplied && (

@@ -736,12 +777,12 @@ export default class DigitalBadge extends TeamsBaseComponent< !this.state.isApplying && (
@@ -954,7 +995,7 @@ export default class DigitalBadge extends TeamsBaseComponent< private _onDownloadImage() { this.setState({ imageDownloaded: true, - downloadText: strings.DownloadingButtonText, + downloadText: LocaleStrings.DownloadingButtonText, }); let canvasDownload: any = document.getElementById("profileCanvasDownload"); let link: HTMLAnchorElement = document.createElement("a"); @@ -962,7 +1003,7 @@ export default class DigitalBadge extends TeamsBaseComponent< if (canvasDownload.msToBlob) { // for IE let blob = canvasDownload.msToBlob(); - this.setState({ downloadText: strings.DownloadedButtonText }); + this.setState({ downloadText: LocaleStrings.DownloadedButtonText }); } else { // other browsers canvasDownload.toBlob((blob: any) => { @@ -973,7 +1014,7 @@ export default class DigitalBadge extends TeamsBaseComponent< document.body.appendChild(link); link.click(); document.body.removeChild(link); - this.setState({ downloadText: strings.DownloadedButtonText }); + this.setState({ downloadText: LocaleStrings.DownloadedButtonText }); }); } } diff --git a/src/webparts/clbHome/components/EmployeeView.tsx b/src/webparts/clbHome/components/EmployeeView.tsx index 415376c1..8a64e0b5 100644 --- a/src/webparts/clbHome/components/EmployeeView.tsx +++ b/src/webparts/clbHome/components/EmployeeView.tsx @@ -7,6 +7,7 @@ import { SPHttpClient, SPHttpClientResponse } from "@microsoft/sp-http"; import * as microsoftTeams from "@microsoft/teams-js"; import Champions from "./Champions"; import siteconfig from "../config/siteconfig.json"; +import * as LocaleStrings from 'ClbHomeWebPartStrings'; interface EmployeeViewState { siteUrl: string; users: any; @@ -104,7 +105,7 @@ export default class EmployeeView extends Component< {this.state.isLoaded && (
diff --git a/src/webparts/clbHome/components/Header.tsx b/src/webparts/clbHome/components/Header.tsx index aae11c3c..a9e780d8 100644 --- a/src/webparts/clbHome/components/Header.tsx +++ b/src/webparts/clbHome/components/Header.tsx @@ -8,6 +8,7 @@ import { mergeStyleSets } from '@fluentui/react/lib/Styling'; import { TooltipHost, ITooltipHostStyles } from '@fluentui/react/lib/Tooltip'; import { Callout, Link, Text, FontWeights } from 'office-ui-fabric-react'; import * as Strings from '../constants/strings'; +import * as LocaleStrings from 'ClbHomeWebPartStrings'; // Import package version const packageSolution: any = require("../../../../config/package-solution.json"); @@ -60,6 +61,7 @@ const style = mergeStyleSets({ interface IHeaderProps { showSearch: boolean; clickcallback: () => void; //will redirects to home + logoImageURL: string; } interface HeaderState { isCalloutVisible: boolean; @@ -87,18 +89,18 @@ export default class Header extends Component { mslogo - Champion Management Platform + {LocaleStrings.AppHeaderTitleLabel}
{ directionalHint={3} > - About the Champion Management Platform (CMP): + {LocaleStrings.AboutHeaderLabel} - Our Champion Management Platform was created with organizational Champions / Adoption Specialists in mind. Hearing from the Microsoft 365 Champion Community this app was developed to deliver a platform to help create and sustain your own communities. Starting with inspiration through execution in helping you achieve more within your own communities! + {LocaleStrings.AboutContentLabel} - Additional Resources: + {LocaleStrings.AdditionalResourcesHeaderLabel} - The Microsoft Teams Customer Advocacy Group is focused on delivering solutions like these to inspire and help you achieve your goals. Follow and join in through these other resources to learn more from us and the community: + {LocaleStrings.AdditionalResourcesContentLabel} - Microsoft 365 Champion Community + {LocaleStrings.M365ChampionCommunityLinkLabel} - Driving Adoption on the Microsoft Technical Community + {LocaleStrings.DrivingAdoptionLinkLabel} ---- - Current Version: {packageSolution.solution.version} + {LocaleStrings.CurrentVersionLabel} {packageSolution.solution.version} - Latest Version: CMP GitHub + {LocaleStrings.LatestVersionLabel} {LocaleStrings.CMPGitHubLinkLabel} ---- - Visit the Champion Management Platform pages to learn more: + {LocaleStrings.VisitLabel} - Overview & Information on our Microsoft Adoption Hub + {LocaleStrings.OverviewLabel} {LocaleStrings.MSAdoptionHubLinkLabel} - Solution technical documentation and architectural overview on GitHub + {LocaleStrings.DocumentationLabel} {LocaleStrings.CMPGitHubLinkLabel} )} @@ -164,7 +166,7 @@ export default class Header extends Component {
{
roles: [], status: [], memberData: { region: "", role: "", status: "", country: "" }, - buttonText: "Become a Champion", + buttonText: LocaleStrings.BecomeChampionLabel, bFlag: true, isMember: false, emailValue: "", @@ -302,14 +304,14 @@ export default class Sidebar extends React.Component this.setState({ emailValue: datauser.Email, isMember: false, - buttonText: "Become a Champion", //if employee want to become a champion enabling this button + buttonText: LocaleStrings.BecomeChampionLabel, //if employee want to become a champion enabling this button isLoaded: true, }); else this.setState({ emailValue: datauser.Email, isMember: true, - buttonText: "Champion submission pending", //if employee already raised for a champion enabling this button + buttonText: LocaleStrings.ChampionSubmissionPendingLabel, //if employee already raised for a champion enabling this button isLoaded: true, }); localStorage.setItem("memberid", memberData); // storing memberid in local storage @@ -491,7 +493,7 @@ export default class Sidebar extends React.Component .post(url, SPHttpClient.configurations.v1, spHttpClientOptions) .then((response: SPHttpClientResponse) => { if (response.status === 201) { - alert("Champion request submission successful"); + alert(LocaleStrings.ChampionRequestSubmitSuccessMessage); { this.props.onClickCancel(); } @@ -579,10 +581,6 @@ export default class Sidebar extends React.Component
{this.state.isLoaded && (
-
- - Back -
{/* user profile image*/}
-
{this.state.totalUserPointsfromList} Points
+
{this.state.totalUserPointsfromList} {LocaleStrings.CMPSideBarPointsLabel}
- {this.state.userRank} Global Rank
- of {this.state.totalUsers} Champions + {this.state.userRank} {LocaleStrings.CMPSideBarGlobalRankLabel}
+ of {this.state.totalUsers} {LocaleStrings.CMPSideBarChampionsLabel}
@@ -640,7 +638,7 @@ export default class Sidebar extends React.Component
onChange={(evt) => this.handleInput(evt, "FirstName")} /> onChange={(evt) => this.handleInput(evt, "LastName")} /> onChange={(evt) => this.handleInput(evt, "Title")} /> this.filterUsers("region", event)} - placeholder="Select an Region" + placeholder={LocaleStrings.RegionPlaceholder} options={this.options(this.state.regions)} styles={this.dropdownStyles} onRenderCaretDown={this.onRenderCaretDown} defaultValue={this.state.currentUser.Region} /> this.filterUsers("country", event) } - placeholder="Select an Country" + placeholder={LocaleStrings.CountryPlaceholder} options={this.options(this.state.coutries)} styles={this.dropdownStyles} onRenderCaretDown={this.onRenderCaretDown} defaultValue={this.state.currentUser.Country} /> this.filterUsers("focusarea", event) } - placeholder="Select Focus Area" + placeholder={LocaleStrings.FocusAreaPlaceholder} options={this.options(this.state.status)} styles={this.dropdownStyles} onRenderCaretDown={this.onRenderCaretDown} defaultValue={this.state.currentUser.FocusArea} /> this.filterUsers("group", event)} - placeholder="Select Group" + placeholder={LocaleStrings.GroupPlaceholder} options={this.options(this.state.roles)} styles={this.dropdownStyles} onRenderCaretDown={this.onRenderCaretDown} @@ -725,7 +723,7 @@ export default class Sidebar extends React.Component type="reset" onClick={() => this._createorupdateItem()} > - Submit + {LocaleStrings.SubmitButton}
diff --git a/src/webparts/clbHome/components/TOTCreateTournament.tsx b/src/webparts/clbHome/components/TOTCreateTournament.tsx index 310404d8..f640740f 100644 --- a/src/webparts/clbHome/components/TOTCreateTournament.tsx +++ b/src/webparts/clbHome/components/TOTCreateTournament.tsx @@ -23,6 +23,7 @@ import { TreeViewSelectionMode, } from "@pnp/spfx-controls-react/lib/TreeView"; import { ITextFieldStyles } from "office-ui-fabric-react/lib/components/TextField/TextField.types"; +import * as LocaleStrings from 'ClbHomeWebPartStrings'; export interface ICreateTournamentProps { context?: WebPartContext; @@ -340,7 +341,7 @@ export default class TOTCreateTournament extends React.Component< this.setState({ showError: true, errorMessage: - "Tournament name already exists. Enter another name for tournament.", + LocaleStrings.DuplicateTournamentNameError, }); } } @@ -367,18 +368,18 @@ export default class TOTCreateTournament extends React.Component< this.props.onClickCancel()} - title="Tournament of Teams" + title={LocaleStrings.TOTBreadcrumbLabel} > - Tournament of Teams + {LocaleStrings.TOTBreadcrumbLabel} - Create Tournament + {LocaleStrings.CreateTournamentPageTitle}
{this.state.showSuccess && ( )} @@ -394,9 +395,9 @@ export default class TOTCreateTournament extends React.Component<
this.handleInput(evt, "tournamentName")} @@ -404,7 +405,7 @@ export default class TOTCreateTournament extends React.Component< /> {this.state.tournamentError && ( )} @@ -413,10 +414,10 @@ export default class TOTCreateTournament extends React.Component< this.handleInput(evt, "tournamentDescription") @@ -430,11 +431,11 @@ export default class TOTCreateTournament extends React.Component< {this.state.showForm && ( this.props.onClickCancel()} styles={backBtnStyles} diff --git a/src/webparts/clbHome/components/TOTEnableTournament.tsx b/src/webparts/clbHome/components/TOTEnableTournament.tsx index 69afe73e..24bdbf2a 100644 --- a/src/webparts/clbHome/components/TOTEnableTournament.tsx +++ b/src/webparts/clbHome/components/TOTEnableTournament.tsx @@ -3,7 +3,6 @@ import { WebPartContext } from "@microsoft/sp-webpart-base"; import commonServices from "../Common/CommonServices"; import * as stringsConstants from "../constants/strings"; import styles from "../scss/TOTEnableTournament.module.scss"; -import * as strings from "../constants/strings"; //React Boot Strap import Row from "react-bootstrap/Row"; @@ -14,10 +13,8 @@ import { IButtonStyles, IChoiceGroupStyles, PrimaryButton } from "@fluentui/reac import { Label } from "@fluentui/react/lib/Label"; import { ChoiceGroup, IChoiceGroupOption } from "@fluentui/react"; import { Dialog, DialogType, DialogFooter } from "@fluentui/react/lib/Dialog"; -import { Icon, IIconProps } from '@fluentui/react/lib/Icon'; -import { mergeStyleSets } from '@fluentui/react/lib/Styling'; -import { TooltipHost, ITooltipHostStyles } from '@fluentui/react/lib/Tooltip'; -import { DirectionalHint } from "@microsoft/office-ui-fabric-react-bundle"; +import { IIconProps } from '@fluentui/react/lib/Icon'; +import * as LocaleStrings from 'ClbHomeWebPartStrings'; //Global Variables let commonServiceManager: commonServices; @@ -62,37 +59,27 @@ export interface IEnableTournamentProps { interface IEnableTournamentState { tournamentsList: any; + activeTournamentsList: any; selectedTournament: string; selectedTournamentId: string; - activeTournament: string; + selectedActiveTournament: string; + selectedActiveTournamentId: string; activeTournamentId: string; activeTournamentFlag: boolean; showSuccess: boolean; successMessage: string; showError: boolean; errorMessage: string; - tournamentError: boolean; + startTournamentError: boolean; + endTournamentError: boolean; hideDialog: boolean; noTournamentsFlag: boolean; + tournamentAction: string; } -const tooltipStyles = { - calloutProps: { gapSpace: 0, style: { paddingLeft: "4%" } } -}; - -const hostStyles: Partial = { root: { display: 'inline-block' } }; - -const classes = mergeStyleSets({ - icon: { - fontSize: '16px', - color: '#1d0f62', - cursor: 'pointer', - fontWeight: 'bolder', - } -}); - const endButtonStyles: Partial = { root: { + marginTop: "32px", marginBottom: "20px", height: "38px" }, @@ -144,41 +131,49 @@ export default class TOTEnableTournament extends React.Component< //Set default values for state this.state = { tournamentsList: [], + activeTournamentsList: [], selectedTournament: "", - activeTournament: "None", + selectedTournamentId: "", + selectedActiveTournament: "", + selectedActiveTournamentId: "", activeTournamentId: "", activeTournamentFlag: true, - selectedTournamentId: "", showSuccess: false, showError: false, errorMessage: "", - tournamentError: false, + startTournamentError: false, + endTournamentError: false, successMessage: "", hideDialog: true, noTournamentsFlag: false, + tournamentAction: "", + }; commonServiceManager = new commonServices( this.props.context, this.props.siteUrl ); //Bind Methods - this.getTournamentsList = this.getTournamentsList.bind(this); + this.getPendingTournaments = this.getPendingTournaments.bind(this); this.onTournamentSelect = this.onTournamentSelect.bind(this); + this.onActiveTournamentSelect = this.onActiveTournamentSelect.bind(this); this.enableTournament = this.enableTournament.bind(this); - this.getActiveTournament = this.getActiveTournament.bind(this); + this.getActiveTournaments = this.getActiveTournaments.bind(this); this.completeTournament = this.completeTournament.bind(this); + this.startTournament = this.startTournament.bind(this); + this.endTournament = this.endTournament.bind(this); } - //On load of app bind tournaments to choice list and populate the current Active tournament + //On load of app bind active and pending tournaments to choice lists public async componentDidMount() { - this.getTournamentsList(); - this.getActiveTournament(); + this.getPendingTournaments(); + this.getActiveTournaments(); } - //get active tournament from master list and populate the label - private async getActiveTournament() { + //get active tournament from Tournaments list and populate the label + private async getActiveTournaments() { console.log( - stringsConstants.TotLog + "Getting active tournament from master list." + stringsConstants.TotLog + "Getting active tournament from Tournaments list." ); try { let filterActive: string = @@ -189,14 +184,24 @@ export default class TOTEnableTournament extends React.Component< filterActive ); - if (activeTournamentsArray.length > 0) - this.setState({ - activeTournament: activeTournamentsArray[0]["Title"], - activeTournamentId: activeTournamentsArray[0]["Id"], + var activeTournamentsChoices = []; + if (activeTournamentsArray.length > 0) { + //Loop through all "Active" tournaments and create an array with key and text + await activeTournamentsArray.forEach((eachTournament) => { + activeTournamentsChoices.push({ + key: eachTournament["Id"], + text: eachTournament["Title"], + }); }); - else this.setState({ activeTournamentFlag: false }); + + this.setState({ activeTournamentsList: activeTournamentsChoices }); + } + //If no active tournaments are found in the Tournaments list, set the flag + else + this.setState({ activeTournamentFlag: false }); + } catch (error) { - console.error("TOT_TOTEnableTournament_getActiveTournament \n", error); + console.error("TOT_TOTEnableTournament_getActiveTournaments \n", error); this.setState({ showError: true, errorMessage: @@ -207,10 +212,10 @@ export default class TOTEnableTournament extends React.Component< } } - // Get a list of all tournaments that are "Not Started" and to bind to Choice List - private async getTournamentsList() { + // Get a list of all tournaments that are in "Not Started" status and to bind to Choice List + private async getPendingTournaments() { console.log( - stringsConstants.TotLog + "Getting tournaments from master list." + stringsConstants.TotLog + "Getting tournaments with 'Not Started' status from Tournaments list." ); try { let selectFilter: string = @@ -232,10 +237,10 @@ export default class TOTEnableTournament extends React.Component< this.setState({ tournamentsList: tournamentsChoices }); } - //If no tournaments are found in the master list set the flag + //If no pending tournaments are found in the Tournaments list, set the flag else this.setState({ noTournamentsFlag: true }); } catch (error) { - console.error("TOT_TOTEnableTournament_getTournamentsList \n", error); + console.error("TOT_TOTEnableTournament_getPendingTournaments \n", error); this.setState({ showError: true, errorMessage: @@ -246,7 +251,7 @@ export default class TOTEnableTournament extends React.Component< } } - //on select of a tournament set the state + //on select of a tournament, set the state private onTournamentSelect = async ( ev: React.FormEvent, option: IChoiceGroupOption @@ -257,19 +262,50 @@ export default class TOTEnableTournament extends React.Component< }); } - //On enabling a tournament change the status in master list - private enableTournament() { + //on select of an active tournament, set the state + private onActiveTournamentSelect = async ( + ev: React.FormEvent, + option: IChoiceGroupOption + ): Promise => { + this.setState({ + selectedActiveTournament: option.text, + selectedActiveTournamentId: option.key, + }); + } + + //Start Tournament + + //Show popup for starting a tournament + private startTournament() { + //clear previous error messages on the form + this.setState({ + showError: false, + startTournamentError: false, + hideDialog: true, + errorMessage: "", + successMessage: "", + showSuccess: false, + }); + + if (this.state.selectedTournament == "") + this.setState({ startTournamentError: true }); + else + this.setState({ hideDialog: false, tournamentAction: stringsConstants.StartTournamentAction }); + } + + //On enabling a tournament change the status in Tournaments list + private async enableTournament() { try { //clear previous error messages on the form this.setState({ showError: false, - tournamentError: false, + startTournamentError: false, hideDialog: true, }); + console.log(stringsConstants.TotLog + "Enabling selected tournament."); if (this.state.selectedTournament == "") - this.setState({ tournamentError: true }); + this.setState({ startTournamentError: true }); else { - console.log(stringsConstants.TotLog + "Enabling selected tournament."); let submitTournamentObject: any = { Status: stringsConstants.TournamentStatusActive, }; @@ -280,38 +316,36 @@ export default class TOTEnableTournament extends React.Component< this.state.selectedTournamentId ) .then((result) => { - //Set Enabled tournament as Active tournament once enabled - this.setState({ - activeTournamentFlag: true, - activeTournament: this.state.selectedTournament, - activeTournamentId: this.state.selectedTournamentId, - }); - //clear the state values - this.setState({ selectedTournament: "", selectedTournamentId: "" }); - //Show success message - this.setState({ - showSuccess: true, - successMessage: "Tournament enabled successfully.", + //Set selected tournament as Active tournament once enabled + + let addSelectedTournament: any[] = this.state.activeTournamentsList; + addSelectedTournament.push({ + key: this.state.selectedTournamentId, + text: this.state.selectedTournament, }); + this.setState({ activeTournamentsList: addSelectedTournament, activeTournamentFlag: true, }); //Refresh the tournaments list after enabling a tournament by deleting it from the array let newTournamentsRefresh: any[] = this.state.tournamentsList; - for ( - var counter = 0; - counter < newTournamentsRefresh.length; - counter++ - ) { - if ( - newTournamentsRefresh[counter]["text"] == - this.state.activeTournament - ) { - newTournamentsRefresh.splice(counter, 1); - this.setState({ tournamentsList: newTournamentsRefresh }); - break; - } - } - if (newTournamentsRefresh.length == 0) + + var removeIndex = newTournamentsRefresh.map((item) => { + return item.text; + }).indexOf(this.state.selectedTournament); + + newTournamentsRefresh.splice(removeIndex, 1); + this.setState({ tournamentsList: newTournamentsRefresh }); + + if (newTournamentsRefresh.length == 0) { this.setState({ noTournamentsFlag: true }); + } + + //Show success message and clear state variable + this.setState({ + showSuccess: true, + successMessage: LocaleStrings.EnableTournamentSuccessMessage, + selectedTournament: "", + selectedTournamentId: "" + }); }) .catch((error) => { console.error("TOT_TOTEnableTournament_enableTournament \n", error); @@ -336,52 +370,81 @@ export default class TOTEnableTournament extends React.Component< } } - //On Completing a tournament change the status in the master list - private completeTournament() { + //Show popup for ending a tournament + private endTournament() { + //clear previous error messages on the form + this.setState({ + showError: false, + endTournamentError: false, + hideDialog: true, + showSuccess: false, + successMessage: "", + errorMessage: "" + }); + if (this.state.selectedActiveTournament == "") + this.setState({ endTournamentError: true }); + else + this.setState({ hideDialog: false, tournamentAction: stringsConstants.EndTournamentAction }); + } + + //On Completing a tournament change the status in the Tournaments list + private async completeTournament() { try { //clear previous error messages on the form this.setState({ showError: false, - tournamentError: false, + endTournamentError: false, hideDialog: true, }); console.log(stringsConstants.TotLog + "Completing active tournament."); - let submitTournamentObject: any = { - Status: stringsConstants.TournamentStatusCompleted, - }; + if (this.state.selectedActiveTournament == "") + this.setState({ endTournamentError: true }); + else { + let submitTournamentObject: any = { + Status: stringsConstants.TournamentStatusCompleted, + }; - commonServiceManager - .updateListItem( - stringsConstants.TournamentsMasterList, - submitTournamentObject, - this.state.activeTournamentId - ) - .then((result) => { - //Reset the state - this.setState({ - activeTournamentFlag: false, - activeTournament: "None", - activeTournamentId: "", - selectedTournament: "", - selectedTournamentId: "", - }); - //Show success message - this.setState({ - showSuccess: true, - successMessage: "Tournament ended successfully.", - }); - }) - .catch((error) => { - console.error("TOT_TOTEnableTournament_completeTournament \n", error); - this.setState({ - showError: true, - errorMessage: - stringsConstants.TOTErrorMessage + - "while completing the tournament. Below are the details: \n" + - JSON.stringify(error), + commonServiceManager + .updateListItem( + stringsConstants.TournamentsMasterList, + submitTournamentObject, + this.state.selectedActiveTournamentId + ) + .then((result) => { + //Refresh the active tournaments list after completing a tournament by deleting it from the array + let newActiveTournamentsRefresh: any[] = this.state.activeTournamentsList; + + var removeIndex = newActiveTournamentsRefresh.map((item) => { + return item.text; + }).indexOf(this.state.selectedActiveTournament); + + newActiveTournamentsRefresh.splice(removeIndex, 1); + this.setState({ activeTournamentsList: newActiveTournamentsRefresh }); + + if (newActiveTournamentsRefresh.length == 0) + this.setState({ activeTournamentFlag: false }); + + + //Show success message and clear state for selected item + this.setState({ + selectedActiveTournament: "", + selectedActiveTournamentId: "", + showSuccess: true, + successMessage: LocaleStrings.EndTournamentSuccessMessage, + }); + }) + .catch((error) => { + console.error("TOT_TOTEnableTournament_completeTournament \n", error); + this.setState({ + showError: true, + errorMessage: + stringsConstants.TOTErrorMessage + + "while completing the tournament. Below are the details: \n" + + JSON.stringify(error), + }); }); - }); + } } catch (error) { console.error("TOT_TOTEnableTournament_completeTournament \n", error); this.setState({ @@ -405,56 +468,57 @@ export default class TOTEnableTournament extends React.Component< this.props.onClickCancel()} - title="Tournament of Teams" + title={LocaleStrings.TOTBreadcrumbLabel} > - Tournament of Teams + {LocaleStrings.TOTBreadcrumbLabel} - Manage Tournaments + {LocaleStrings.ManageTournamentsPageTitle} -
Manage Tournaments
+
{LocaleStrings.ManageTournamentsPageTitle}
- - + +

- + )}
{this.state.showSuccess && ( )} - {this.state.showError && (
+
+

{LocaleStrings.ActiveTournamentLabel}

+
-
-
-   - -
+
+ + {!this.state.activeTournamentFlag && ( + + )} + {this.state.endTournamentError && ( + + )} - - this.setState({ hideDialog: false })} - className={ - !this.state.activeTournamentFlag - ? styles.disabledBtn - : styles.completeBtn - } - /> + + {this.state.activeTournamentFlag && ( + + )}
-

Start Tournament{" "}

-
- - - -
+

{LocaleStrings.StartTournamentHeaderLabel}

{this.state.noTournamentsFlag && ( )} - {this.state.tournamentError && ( + {this.state.startTournamentError && ( )} @@ -539,25 +601,20 @@ export default class TOTEnableTournament extends React.Component<
{!this.state.noTournamentsFlag && ( this.setState({ hideDialog: false })} - className={ - this.state.activeTournamentFlag - ? styles.disabledBtn - : styles.enableBtn - } + onClick={this.startTournament} + className={styles.enableBtn} /> )}     this.props.onClickCancel()} diff --git a/src/webparts/clbHome/components/TOTLandingPage.tsx b/src/webparts/clbHome/components/TOTLandingPage.tsx index 464147bb..f7c82df2 100644 --- a/src/webparts/clbHome/components/TOTLandingPage.tsx +++ b/src/webparts/clbHome/components/TOTLandingPage.tsx @@ -17,6 +17,9 @@ import TOTMyDashboard from "./TOTMyDashboard"; import TOTCreateTournament from "./TOTCreateTournament"; import TOTEnableTournament from "./TOTEnableTournament"; import Navbar from "react-bootstrap/Navbar"; +import * as LocaleStrings from 'ClbHomeWebPartStrings'; +import DigitalBadge from "./DigitalBadge"; +import { ThemeStyle } from "msteams-ui-styles-core"; export interface ITOTLandingPageProps { context?: any; @@ -36,7 +39,7 @@ interface ITOTLandingPageState { manageTournament: boolean; isAdmin: boolean; isShowLoader: boolean; - activeTournamentExists: boolean; + digitalBadge: boolean; } let commonService: commonServices; class TOTLandingPage extends React.Component< @@ -58,7 +61,7 @@ class TOTLandingPage extends React.Component< leaderBoard: false, isAdmin: false, isShowLoader: false, - activeTournamentExists: false + digitalBadge: false }; commonService = new commonServices(this.props.context, this.props.siteUrl); this.redirectTotHome = this.redirectTotHome.bind(this); @@ -76,7 +79,6 @@ class TOTLandingPage extends React.Component< //if isTOTEnabled is true then just check for role else run provisioning to add missing lists and fields if (this.props.isTOTEnabled == true) { this.checkUserRole(); - this.checkActiveTournament(); } else { //verify tot lists/fields are present, create missing lists/fields @@ -86,8 +88,6 @@ class TOTLandingPage extends React.Component< this.createLookupField(); } }); - //get active tournament details, and dynamic text change for manage tournament link - this.checkActiveTournament(); } } catch (error) { @@ -96,19 +96,6 @@ class TOTLandingPage extends React.Component< } } - //get active tournament details, and dynamic text change for manage tournament link - private async checkActiveTournament() { - let tournamentDetails = await commonService.getActiveTournamentDetails(); - if (tournamentDetails.length == 0) { - //no active tournament - this.setState({ activeTournamentExists: false }); - } - else { - //there is an active tournament - this.setState({ activeTournamentExists: true }); - } - } - //Check current users's is admin from "ToT admin List" and set the UI components accordingly private async checkUserRole() { try { @@ -282,8 +269,8 @@ class TOTLandingPage extends React.Component< createTournament: false, manageTournament: false, dashboard: false, + digitalBadge: false, }); - this.checkActiveTournament(); } //Create tournament name look up field in Digital badge assets lib @@ -501,18 +488,18 @@ class TOTLandingPage extends React.Component< {!this.state.leaderBoard && !this.state.createTournament && !this.state.dashboard && + !this.state.digitalBadge && !this.state.manageTournament && (
- Tournament of Teams + {LocaleStrings.TOTBreadcrumbLabel}
{this.state.showSuccess && ( )} {this.state.showError && ( @@ -521,7 +508,7 @@ class TOTLandingPage extends React.Component< )}
-
Quick Links
+
{LocaleStrings.QuickLinksLabel}
Leader Board -
Leader Board
+
{LocaleStrings.TOTLeaderBoardPageTitle}
@@ -557,19 +544,35 @@ class TOTLandingPage extends React.Component<
My Dashboard -
My Dashboard
+
{LocaleStrings.TOTMyDashboardPageTitle}
+
+ this.setState({ digitalBadge: !this.state.digitalBadge })} + > +
+ {LocaleStrings.DigitalMembersToolTip} +
{LocaleStrings.DigitalBadgeLabel}
+
+
+ {this.state.isAdmin && (
-
Admin Tools
+
{LocaleStrings.AdminToolsLabel}
)} @@ -584,13 +587,13 @@ class TOTLandingPage extends React.Component< > Accessing Tournament Actions List
-
- Manage Tournament Actions +
+ {LocaleStrings.ManageTournamentActionsLabel}
@@ -608,12 +611,12 @@ class TOTLandingPage extends React.Component<
Create Tournament -
- Create Tournament +
+ {LocaleStrings.CreateTournamentPageTitle}
@@ -628,25 +631,15 @@ class TOTLandingPage extends React.Component< }) } > - {this.state.activeTournamentExists && (
+
End Current Tournament -
End Current Tournament
-
)} - {!this.state.activeTournamentExists && (
- Start New Tournament -
Start New Tournament
-
)} - +
{LocaleStrings.ManageTournamentsLabel}
+
@@ -659,12 +652,12 @@ class TOTLandingPage extends React.Component< > Accessing Admin List -
Manage Admins
+
{LocaleStrings.ManageAdminsLabel}
@@ -677,13 +670,13 @@ class TOTLandingPage extends React.Component< > Manage Digital Badges -
- Manage Digital Badges +
+ {LocaleStrings.ManageDigitalBadgesLabel}
@@ -700,11 +693,9 @@ class TOTLandingPage extends React.Component< siteUrl={this.props.siteUrl} context={this.props.context} onClickCancel={() => { - this.checkActiveTournament(); this.setState({ leaderBoard: false }); }} onClickMyDashboardLink={() => { - this.checkActiveTournament(); this.setState({ dashboard: true, leaderBoard: false }); }} /> @@ -716,12 +707,27 @@ class TOTLandingPage extends React.Component< siteUrl={this.props.siteUrl} context={this.props.context} onClickCancel={() => { - this.checkActiveTournament(); this.setState({ dashboard: false }); } } /> ) + } + { + this.state.digitalBadge && ( + this.setState({ digitalBadge: false })} + clickcallchampionview={() => + this.setState({ digitalBadge: false }) + } + /> + ) } { this.state.createTournament && ( @@ -729,7 +735,6 @@ class TOTLandingPage extends React.Component< siteUrl={this.props.siteUrl} context={this.props.context} onClickCancel={() => { - this.checkActiveTournament(); this.setState({ createTournament: false }); }} /> @@ -742,7 +747,6 @@ class TOTLandingPage extends React.Component< context={this.props.context} onClickCancel={() => { this.setState({ manageTournament: false }); - this.checkActiveTournament(); }} /> )} diff --git a/src/webparts/clbHome/components/TOTLeaderBoard.tsx b/src/webparts/clbHome/components/TOTLeaderBoard.tsx index 34a5f8d9..c816af75 100644 --- a/src/webparts/clbHome/components/TOTLeaderBoard.tsx +++ b/src/webparts/clbHome/components/TOTLeaderBoard.tsx @@ -1,36 +1,58 @@ -//FluentUI controls -import { IButtonStyles, DefaultButton } from "@fluentui/react"; -import { Icon, IIconProps } from '@fluentui/react/lib/Icon'; -import { Label } from "@fluentui/react/lib/Label"; -import { MSGraphClient } from "@microsoft/sp-http"; -import { WebPartContext } from "@microsoft/sp-webpart-base"; import * as React from "react"; +import { WebPartContext } from "@microsoft/sp-webpart-base"; //React Boot Strap import BootstrapTable from "react-bootstrap-table-next"; import paginationFactory from "react-bootstrap-table2-paginator"; +import Row from "react-bootstrap/Row"; +import Col from "react-bootstrap/Col"; +//FluentUI controls +import { IButtonStyles, DefaultButton } from "@fluentui/react"; +import { Icon, IIconProps } from '@fluentui/react/lib/Icon'; +import { Label } from "@fluentui/react/lib/Label"; +import { ComboBox, IComboBox, IComboBoxOption } from '@fluentui/react/lib/ComboBox'; +import { TooltipHost, ITooltipHostStyles } from '@fluentui/react/lib/Tooltip'; +import { mergeStyleSets } from '@fluentui/react/lib/Styling'; import commonServices from "../Common/CommonServices"; import * as stringsConstants from "../constants/strings"; import styles from "../scss/TOTLeaderBoard.module.scss"; import TOTSidebar from "./TOTSideBar"; +import { RxJsEventEmitter } from "../events/RxJsEventEmitter"; +import { EventData } from "../events/EventData"; +import * as LocaleStrings from 'ClbHomeWebPartStrings'; -//global variables +//Global variables let commonService: commonServices; -let allUsersDetails: any = []; +let currentUserEmail: string = ""; + const columns = [ { dataField: "Rank", - text: "Rank", + text: LocaleStrings.RankLabel, }, { dataField: "User", - text: "User", + text: LocaleStrings.UserLabel, }, { dataField: "Points", - text: "Points", + text: LocaleStrings.PointsLabel, }, ]; +const hostStyles: Partial = { root: { display: 'inline-block', cursor: 'pointer' } }; + +const calloutProps = { gapSpace: 0 }; + +const classes = mergeStyleSets({ + icon: { + fontSize: '16px', + paddingLeft: '10px', + fontWeight: 'bolder', + color: '#1d0f62', + position: 'relative', + top: '3px' + } +}); const backIcon: IIconProps = { iconName: 'NavigateBack' }; const backBtnStyles: Partial = { @@ -74,10 +96,15 @@ interface ITOTLeaderBoardState { showSuccess: Boolean; showError: Boolean; noActiveParticipants: boolean; - noActiveTournament: boolean; + noActiveTournament: boolean; errorMessage: string; - tournamentName: string; + tournamentName: any; tournamentDescription: string; + activeTournamentsList: Array; + myTournamentsList: Array; + activeTournamentName: any; + myTournamentName: any; + tournamentDescriptionList: Array; allUserActions: any; isShowLoader: boolean; currentUserDetails: any; @@ -88,6 +115,8 @@ export default class TOTLeaderBoard extends React.Component< ITOTLeaderBoardProps, ITOTLeaderBoardState > { + private readonly _eventEmitter: RxJsEventEmitter = + RxJsEventEmitter.getInstance(); constructor(props: ITOTLeaderBoardProps, state: ITOTLeaderBoardState) { super(props); //Set default values @@ -99,6 +128,11 @@ export default class TOTLeaderBoard extends React.Component< errorMessage: "", tournamentName: "", tournamentDescription: "", + activeTournamentsList: [], + myTournamentsList: [], + activeTournamentName: "", + myTournamentName: "", + tournamentDescriptionList: [], allUserActions: [], isShowLoader: false, currentUserDetails: [], @@ -106,101 +140,184 @@ export default class TOTLeaderBoard extends React.Component< }; //Create object for commonServices class commonService = new commonServices(this.props.context, this.props.siteUrl); + // Bind methods + this.getActiveTournamentActions = this.getActiveTournamentActions.bind(this); + this.getMyTournamentActions = this.getMyTournamentActions.bind(this); } - public _graphClient: MSGraphClient; //Get User Actions from list and bind to table public componentDidMount() { this.setState({ isShowLoader: true, }); - this.getAllUsers().then((res) => { - if (res == "Success") { - this.getUserActions(); - } - }); + //Get list of active tournaments from Tournaments list + this.getActiveTournaments(); } - //get all users properties and store in array - private async getAllUsers(): Promise { - return new Promise(async (resolve, reject) => { - this._graphClient = - await this.props.context.msGraphClientFactory.getClient(); - await this._graphClient - .api("/users") - .get() - .then(async (users: any, rawResponse?: any) => { - for (let user of users.value) { - if (user.mail != null) { - allUsersDetails.push({ - email: user.mail.toLowerCase(), - displayName: user.displayName, - }); - } + //Get list of active tournaments from Tournaments list and binding it to dropdowns + private async getActiveTournaments() { + console.log(stringsConstants.TotLog + "Getting list of active tournaments from Tournaments list."); + try { + //Get current users's email + currentUserEmail = + this.props.context.pageContext.user.email.toLowerCase(); + + //Get current active tournament details + let activeTournamentDetails: any[] = + await commonService.getActiveTournamentDetails(); + + var activeTournamentsChoices = []; + var myTournamentsChoices = []; + var tournamentDescriptionChoices = []; + //If active tournament found + if (activeTournamentDetails.length > 0) { + + //Get current user's active tournament details + let filterUserTournaments: string = "Title eq '" + currentUserEmail + "'"; + + const currentUserTournaments: any[] = + await commonService.getFilteredListItemsWithSpecificColumns( + stringsConstants.UserActionsList, "Tournament_x0020_Name", + filterUserTournaments + ); + + const uniqueUserTournaments: any[] = currentUserTournaments.filter((value, index) => { + const _value = JSON.stringify(value); + return index === currentUserTournaments.findIndex(item => { + return JSON.stringify(item) === _value; + }); + }); + + //Loop through all "Active" tournaments and create an array with key and text + await activeTournamentDetails.forEach((eachTournament) => { + + //Create an array for My Tournaments dropdown + if (uniqueUserTournaments.some(tournament => tournament.Tournament_x0020_Name == eachTournament["Title"])) { + myTournamentsChoices.push({ + key: eachTournament["Title"], + text: eachTournament["Title"] + }); + } + else { + //Create an array for Active Tournaments dropdown + activeTournamentsChoices.push({ + key: eachTournament["Title"], + text: eachTournament["Title"] + }); } - resolve("Success"); - }) - .catch((err) => { - console.error("TOT_TOTLeaderboard_getAllUsers \n", err); + tournamentDescriptionChoices.push({ + key: eachTournament["Title"], + text: eachTournament["Description"] + }); + }); + activeTournamentsChoices.sort((a, b) => a.text.localeCompare(b.text)); + myTournamentsChoices.sort((a, b) => a.text.localeCompare(b.text)); + + //Set state variables for dropdown options + this.setState({ + activeTournamentsList: activeTournamentsChoices, + myTournamentsList: myTournamentsChoices, + tournamentDescriptionList: tournamentDescriptionChoices, + isShowLoader: false + }); + + //Set the first option as a default tournament for My Tournaments dropdown + if (myTournamentsChoices.length > 0) { this.setState({ - showError: true, - errorMessage: - stringsConstants.TOTErrorMessage + - " while getting users. Below are the details: \n" + - JSON.stringify(err), - showSuccess: false, + myTournamentName: myTournamentsChoices[0].text, + activeTournamentName: null, + tournamentName: myTournamentsChoices[0].text, }); - reject("Failed"); + } + //If My Tournaments is empty, Set the first option as a default tournament for Active Tournaments dropdown + else if (activeTournamentsChoices.length > 0) { + this.setState({ + myTournamentName: null, + activeTournamentName: activeTournamentsChoices[0].text, + tournamentName: activeTournamentsChoices[0].text, + }); + } + } + //If there is no active tournament + else { + this.setState({ + showError: true, + errorMessage: LocaleStrings.NoActiveTournamentMessage, + noActiveTournament: true, + userLoaded: "1", + isShowLoader: false }); + } + } + catch (error) { + console.error("TOT_TOTMyDashboard_getActiveTournaments \n", error); + } + } + + //Set a value when an option is selected in My Tournaments dropdown and reset the Active Tournaments dropdown + public getMyTournamentActions = (ev: React.FormEvent, option?: IComboBoxOption): void => { + this.setState({ + tournamentName: option.key, + myTournamentName: option.key, + activeTournamentName: null + }); + } + + //Set a value when an option is selected in Active Tournaments dropdown and reset the My Tournaments dropdown + public getActiveTournamentActions = (ev: React.FormEvent, option?: IComboBoxOption): void => { + this.setState({ + tournamentName: option.key, + activeTournamentName: option.key, + myTournamentName: null }); } + //Refresh the user details table whenever the tournament name is selected + public componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot?: any): void { + if (prevState.tournamentName != this.state.tournamentName) { + if (this.state.tournamentName !== "") + this.getUserActions(); + //Refresh the points and rank in the sidebar when a tournament is selected in My Tournaments / Active tournaments dropdown + this._eventEmitter.emit("rebindSideBar:start", { + tournamentName: this.state.tournamentName, + } as EventData); + } + } - //get all user action for active tournament and bind to table + //Get all user action for active tournament and bind to table private async getUserActions(): Promise { return new Promise(async (resolve, reject) => { try { - //get all users - await this.getAllUsers(); - //get active tournament details - let tournamentDetails = - await commonService.getActiveTournamentDetails(); - if (tournamentDetails.length != 0) { - this.setState({ - tournamentName: tournamentDetails[0]["Title"], - tournamentDescription: tournamentDetails[0]["Description"], - }); - //get active tournament's participants - await commonService - .getUserActions(this.state.tournamentName, allUsersDetails) - .then((res) => { - if (res.length > 0) { - this.setState({ - allUserActions: res, - isShowLoader: false, - userLoaded: "1", - }); - } else if (res.length == 0) { - this.setState({ - userLoaded: "0", - showError: true, - noActiveParticipants: true, - errorMessage: stringsConstants.NoActiveParticipantsMessage, - isShowLoader: false, - }); - } else if (res == "Failed") { - console.error("TOT_TOTLeaderboard_getUserActions \n"); - } - }); - } else { - //no active tournaments - this.setState({ - userLoaded: "1", - showError: true, - errorMessage: stringsConstants.NoActiveTournamentMessage, - noActiveTournament: true, - isShowLoader: false, + + //Set the description for selected tournament + var tournmentDesc = this.state.tournamentDescriptionList.find((item) => item.key == this.state.tournamentName); + + this.setState({ + tournamentDescription: tournmentDesc.text + }); + //get active tournament's participants + await commonService + .getUserActions(this.state.tournamentName) + .then((res) => { + if (res.length > 0) { + this.setState({ + allUserActions: res, + isShowLoader: false, + userLoaded: "1", + }); + } else if (res.length == 0) { + this.setState({ + allUserActions: [], + userLoaded: "0", + showError: true, + noActiveParticipants: true, + errorMessage: LocaleStrings.NoActiveParticipantsMessage, + isShowLoader: false, + }); + } else if (res == "Failed") { + console.error("TOT_TOTLeaderboard_getUserActions \n"); + } }); - } + } catch (error) { console.error("TOT_TOTLeaderboard_getUserActions \n", error); this.setState({ @@ -238,27 +355,60 @@ export default class TOTLeaderBoard extends React.Component< this.props.onClickCancel()} - title="Tournament of Teams" + title={LocaleStrings.TOTBreadcrumbLabel} > - Tournament of Teams + {LocaleStrings.TOTBreadcrumbLabel} - Leader Board - + {LocaleStrings.TOTLeaderBoardPageTitle} + +
+ + {this.state.myTournamentsList.length > 0 && ( +
+ {LocaleStrings.MyTournamentsLabel} : + + + + + + + )} + {this.state.myTournamentsList.length > 0 && this.state.activeTournamentsList.length > 0 && ( + + {LocaleStrings.OrLabel} + + )} + {this.state.activeTournamentsList.length > 0 && ( + + {LocaleStrings.ActiveTournamentLabel} : + + + )} + + {this.state.tournamentName != "" && ( -
+
{this.state.tournamentName != "" && (
    -
  • - Tournament: - - {this.state.tournamentName} - -
  • {this.state.tournamentDescription && (
  • - Description + {LocaleStrings.DescriptionLabel} : @@ -282,20 +432,19 @@ export default class TOTLeaderBoard extends React.Component< headerClasses="header-class" /> ) - : -
    - {this.state.showError && this.state.noActiveParticipants && ( - - )} -
    - } + : +
    + {this.state.showError && this.state.noActiveParticipants && ( + + )} +
    + }
)} @@ -307,13 +456,13 @@ export default class TOTLeaderBoard extends React.Component< )}
- this.props.onClickCancel()} - styles={backBtnStyles}> - + this.props.onClickCancel()} + styles={backBtnStyles}> +
diff --git a/src/webparts/clbHome/components/TOTMyDashboard.tsx b/src/webparts/clbHome/components/TOTMyDashboard.tsx index fbee63f3..304d45bd 100644 --- a/src/webparts/clbHome/components/TOTMyDashboard.tsx +++ b/src/webparts/clbHome/components/TOTMyDashboard.tsx @@ -1,27 +1,31 @@ import * as React from "react"; import { WebPartContext } from "@microsoft/sp-webpart-base"; -import commonServices from "../Common/CommonServices"; -import * as stringsConstants from "../constants/strings"; -import styles from "../scss/TOTMyDashBoard.module.scss"; -import TOTSidebar from "./TOTSideBar"; -import { RxJsEventEmitter } from "../events/RxJsEventEmitter"; - //React Boot Strap import Row from "react-bootstrap/Row"; import Col from "react-bootstrap/Col"; - //FluentUI controls import { IButtonStyles, PrimaryButton, DefaultButton } from "@fluentui/react/lib/Button"; import { Label } from "@fluentui/react/lib/Label"; import { Spinner, SpinnerSize } from "@fluentui/react/lib/Spinner"; import { Icon, IIconProps } from '@fluentui/react/lib/Icon'; - +import { ComboBox, IComboBox, IComboBoxOption } from '@fluentui/react/lib/ComboBox'; +import { TooltipHost, ITooltipHostStyles } from '@fluentui/react/lib/Tooltip'; +import { mergeStyleSets } from '@fluentui/react/lib/Styling'; //PNP import { TreeView, ITreeItem, TreeViewSelectionMode, + SelectChildrenMode, + TreeItemActionsDisplayMode, } from "@pnp/spfx-controls-react/lib/TreeView"; +import commonServices from "../Common/CommonServices"; +import * as stringsConstants from "../constants/strings"; +import styles from "../scss/TOTMyDashBoard.module.scss"; +import TOTSidebar from "./TOTSideBar"; +import { RxJsEventEmitter } from "../events/RxJsEventEmitter"; +import { EventData } from "../events/EventData"; +import * as LocaleStrings from 'ClbHomeWebPartStrings'; //Global Variables let commonServiceManager: commonServices; @@ -32,6 +36,19 @@ export interface ITOTMyDashboardProps { onClickCancel: Function; } +const hostStyles: Partial = { root: { display: 'inline-block', cursor: 'pointer' } }; + +const calloutProps = { gapSpace: 0 }; +const classes = mergeStyleSets({ + icon: { + fontSize: '16px', + paddingLeft: '10px', + fontWeight: 'bolder', + color: '#1d0f62', + position: 'relative', + top: '3px' + } +}); const backBtnStyles: Partial = { root: { marginLeft: "1.5%", @@ -67,6 +84,7 @@ const backBtnStyles: Partial = { const saveIcon: IIconProps = { iconName: 'Save' }; const backIcon: IIconProps = { iconName: 'NavigateBack' }; + interface ITOTMyDashboardState { actionsList: ITreeItem[]; selectedActionsList: ITreeItem[]; @@ -76,10 +94,16 @@ interface ITOTMyDashboardState { noActiveTournament: boolean; errorMessage: string; actionsError: boolean; - tournamentName: string; + tournamentName: any; showSpinner: boolean; noPendingActions: boolean; - tournamentDescription: string; + tournamentDescription: any; + activeTournamentsList: Array; + myTournamentsList: Array; + activeTournamentName: any; + myTournamentName: any; + tournamentDescriptionList: Array; + treeViewSelectedKeys?: string[]; } export default class TOTMyDashboard extends React.Component< @@ -104,6 +128,13 @@ export default class TOTMyDashboard extends React.Component< showSpinner: false, noPendingActions: false, tournamentDescription: "", + activeTournamentsList: [], + myTournamentsList: [], + activeTournamentName: "", + myTournamentName: "", + tournamentDescriptionList: [], + treeViewSelectedKeys: [], + }; //Create object for CommonServices class commonServiceManager = new commonServices( @@ -115,235 +146,375 @@ export default class TOTMyDashboard extends React.Component< this.onActionSelected = this.onActionSelected.bind(this); this.getPendingActions = this.getPendingActions.bind(this); this.saveActions = this.saveActions.bind(this); + this.getActiveTournamentActions = this.getActiveTournamentActions.bind(this); + this.getMyTournamentActions = this.getMyTournamentActions.bind(this); } - //Get Actions from Master list and bind it to treeview on app load - public componentDidMount() { - //Get Actions from Master list and bind it to Treeview - this.getPendingActions(); - } - //On select of a tree node change the state of selected actions - private onActionSelected(items: ITreeItem[]) { - this.setState({ selectedActionsList: items }); + public componentDidMount() { + //Get list of active tournaments from Tournaments list + this.getActiveTournaments(); } - //Get Actions from Master list and bind it to Treeview - private async getPendingActions() { - console.log(stringsConstants.TotLog + "Getting actions from master list."); + //Get list of active tournaments from Tournaments list and binding it to dropdowns + private async getActiveTournaments() { + console.log(stringsConstants.TotLog + "Getting list of active tournaments from Tournaments list."); try { //Get current users's email currentUserEmail = this.props.context.pageContext.user.email.toLowerCase(); //Get current active tournament details - let tournamentDetails: any[] = + let activeTournamentDetails: any[] = await commonServiceManager.getActiveTournamentDetails(); + var activeTournamentsChoices = []; + var myTournamentsChoices = []; + var tournamentDescriptionChoices = []; //If active tournament found - if (tournamentDetails.length != 0) { - this.setState({ - tournamentName: tournamentDetails[0]["Title"], - tournamentDescription: tournamentDetails[0]["Description"], - }); - let filterActive: string = - "Title eq '" + - tournamentDetails[0]["Title"].replace(/'/g, "''") + - "'"; - let filterUserTournaments: string = - "Tournament_x0020_Name eq '" + - tournamentDetails[0]["Title"].replace(/'/g, "''") + - "'" + - " and Title eq '" + - currentUserEmail + - "'"; - //Get all actions for the tournament from "Tournament Actions" list - const allTournamentsActionsArray: any[] = - await commonServiceManager.getItemsWithOnlyFilter( - stringsConstants.TournamentActionsMasterList, - filterActive - ); + if (activeTournamentDetails.length > 0) { - //Sort on Category - allTournamentsActionsArray.sort((a, b) => a.Category.localeCompare(b.Category)); + //Get current user's active tournament details + let filterUserTournaments: string = "Title eq '" + currentUserEmail + "'"; - //Get all actions completed by the current user for the current tournament - const userActionsArray: any[] = - await commonServiceManager.getItemsWithOnlyFilter( - stringsConstants.UserActionsList, + const currentUserTournaments: any[] = + await commonServiceManager.getFilteredListItemsWithSpecificColumns( + stringsConstants.UserActionsList, "Tournament_x0020_Name", filterUserTournaments ); - var treeItemsArray: ITreeItem[] = []; - var completedTreeItemsArray: ITreeItem[] = []; - - //Build the Parent Nodes(Categories) in Treeview. Skip the items which are already completed by the user in "User Actions" list - await allTournamentsActionsArray.forEach((vAction) => { - //Check if the category is present in the 'User Actions' list - var compareCategoriesArray = userActionsArray.filter((elArray) => { - return ( - elArray.Action == vAction["Action"] && - elArray.Category == vAction["Category"] - ); + + const uniqueUserTournaments: any[] = currentUserTournaments.filter((value, index) => { + const _value = JSON.stringify(value); + return index === currentUserTournaments.findIndex(item => { + return JSON.stringify(item) === _value; }); - const tree: ITreeItem = { - key: vAction["Category"], - label: vAction["Category"], - children: [], - }; - - //If the category is not present in User Actions list add it to 'Pending Tree view' - var found: boolean; - if (compareCategoriesArray.length == 0) { - //Check if Category is already added to the Treeview. If yes, skip adding. - found = treeItemsArray.some((value) => { - return value.label === vAction["Category"]; + }); + + //Loop through all "Active" tournaments and create an array with key and text + await activeTournamentDetails.forEach((eachTournament) => { + + //Create an array for My Tournaments dropdown + if (uniqueUserTournaments.some(tournament => tournament.Tournament_x0020_Name == eachTournament["Title"])) { + myTournamentsChoices.push({ + key: eachTournament["Title"], + text: eachTournament["Title"] }); - if (!found) treeItemsArray.push(tree); } - //If the category is present in User Actions list add it to 'Completed Tree view' else { - //Check if Category is already added to the Treeview. If yes, skip adding. - found = completedTreeItemsArray.some((value) => { - return value.label === vAction["Category"]; + //Create an array for Active Tournaments dropdown + activeTournamentsChoices.push({ + key: eachTournament["Title"], + text: eachTournament["Title"] }); - if (!found) completedTreeItemsArray.push(tree); } - }); //For Loop - - //Build the child nodes(Actions) in Treeview. Skip the items which are already completed by the user in "User Actions" list - await allTournamentsActionsArray.forEach((vAction) => { - //Check if the action is present in the 'User Actions' list - var compareActionsArray = userActionsArray.filter((elChildArray) => { - return ( - elChildArray.Action == vAction["Action"] && - elChildArray.Category == vAction["Category"] - ); + tournamentDescriptionChoices.push({ + key: eachTournament["Title"], + text: eachTournament["Description"] }); + }); + activeTournamentsChoices.sort((a, b) => a.text.localeCompare(b.text)); + myTournamentsChoices.sort((a, b) => a.text.localeCompare(b.text)); - //If the action is not present in User Actions list add it to 'Pending Tree view' - let tree: ITreeItem; - if (compareActionsArray.length == 0) { - if (vAction["HelpURL"] === 'null' || vAction["HelpURL"] == "") { - tree = { - key: vAction.Id, - label: vAction["Action"], - data: - vAction["Category"] + - stringsConstants.StringSeperator + - vAction["HelpURL"], - subLabel: - vAction["Points"] + - stringsConstants.PointsDisplayString + - vAction["Description"] - }; - } - else { - tree = { - key: vAction.Id, - label: vAction["Action"], - data: - vAction["Category"] + - stringsConstants.StringSeperator + - vAction["HelpURL"], - subLabel: - vAction["Points"] + - stringsConstants.PointsDisplayString + - vAction["Description"], - actions: [ - { - iconProps: { - iconName: "Info", - title: "Find out more about this action" - }, - id: "GetItem", - actionCallback: async (treeItem: ITreeItem) => { - window.open(vAction["HelpURL"]); - }, - }, - ], - }; - } - var treeCol: Array = treeItemsArray.filter((value) => { - return value.label == vAction["Category"]; - }); - if (treeCol.length != 0) { - treeCol[0].children.push(tree); - } - } - //If the action present in User Actions list add it to 'Completed Tree view' - else { - if (vAction["HelpURL"] === 'null' || vAction["HelpURL"] == "") { - tree = { - key: vAction.Id, - label: vAction["Action"], - data: - vAction["Category"] + - stringsConstants.StringSeperator + - vAction["HelpURL"], - subLabel: - vAction["Points"] + - stringsConstants.PointsDisplayString + - vAction["Description"], - iconProps: { - iconName: "SkypeCheck", - }, - }; - } - else { - tree = { - key: vAction.Id, - label: vAction["Action"], - data: - vAction["Category"] + - stringsConstants.StringSeperator + - vAction["HelpURL"], - subLabel: - vAction["Points"] + - stringsConstants.PointsDisplayString + - vAction["Description"], - iconProps: { - iconName: "SkypeCheck", - }, - actions: [ - { - iconProps: { - iconName: "Info", - title: "Find out more about this action" - }, - id: "GetItem", - actionCallback: async (treeItem: ITreeItem) => { - window.open(vAction["HelpURL"]); - }, - }, - ], - }; - } - var treeColCompleted: Array = - completedTreeItemsArray.filter((value) => { - return value.label == vAction["Category"]; - }); - if (treeColCompleted.length != 0) { - treeColCompleted[0].children.push(tree); - } - } - }); //For loop - - if (treeItemsArray.length == 0) - this.setState({ noPendingActions: true }); + //Set state variables for dropdown options this.setState({ - actionsList: treeItemsArray, - completedActionsList: completedTreeItemsArray, + activeTournamentsList: activeTournamentsChoices, + myTournamentsList: myTournamentsChoices, + tournamentDescriptionList: tournamentDescriptionChoices }); - } // IF END + //When an user participates in an active tournament, move that tournament to My Tournaments dropdown. + if (this.state.tournamentName != "") { + this.setState({ + myTournamentName: this.state.tournamentName, + activeTournamentName: null, + tournamentName: this.state.tournamentName, + }); + this.getPendingActions(); + } + //Set the first option as a default tournament for My Tournaments dropdown + else if (myTournamentsChoices.length > 0) { + this.setState({ + myTournamentName: myTournamentsChoices[0].text, + activeTournamentName: null, + tournamentName: myTournamentsChoices[0].text, + }); + } + //If My Tournaments is empty, Set the first option as a default tournament for Active Tournaments dropdown + else if (activeTournamentsChoices.length > 0) { + this.setState({ + myTournamentName: null, + activeTournamentName: activeTournamentsChoices[0].text, + tournamentName: activeTournamentsChoices[0].text, + }); + } + } //If there is no active tournament else { this.setState({ showError: true, - errorMessage: stringsConstants.NoActiveTournamentMessage, + errorMessage: LocaleStrings.NoActiveTournamentMessage, noActiveTournament: true }); } + } + catch (error) { + console.error("TOT_TOTMyDashboard_getActiveTournaments \n", error); + } + } + + //Set a value when an option is selected in My Tournaments dropdown and reset the Active Tournaments dropdown + public getMyTournamentActions = (ev: React.FormEvent, option?: IComboBoxOption): void => { + this.setState({ + tournamentName: option.key, + myTournamentName: option.key, + activeTournamentName: null + }); + + } + + //Set a value when an option is selected in Active Tournaments dropdown and reset the My Tournaments dropdown + public getActiveTournamentActions = (ev: React.FormEvent, option?: IComboBoxOption): void => { + this.setState({ + tournamentName: option.key, + activeTournamentName: option.key, + myTournamentName: null + }); + } + + //Refresh the tournament actions whenever the tournament name is selected + public componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot?: any): void { + if (prevState.tournamentName != this.state.tournamentName) { + this.setState({ noPendingActions: false }); + if (this.state.tournamentName !== "") + this.getPendingActions(); + //Refresh the points and rank in the sidebar when a tournament is selected in My Tournaments / Active tournaments dropdown + this._eventEmitter.emit("rebindSideBar:start", { + tournamentName: this.state.tournamentName, + } as EventData); + } + } + + //On select of a tree node change the state of selected actions + private onActionSelected(items: ITreeItem[]) { + this.setState({ selectedActionsList: items, treeViewSelectedKeys: items["key"] }); + } + + //Get Actions from Tournament Actions list and bind it to Treeview + private async getPendingActions() { + console.log(stringsConstants.TotLog + "Getting actions from Tournament Actions list."); + try { + // Reset state variables + this.setState({ + actionsList: [], + completedActionsList: [], + selectedActionsList: [], + actionsError: false, + treeViewSelectedKeys: [], + }); + + + //Get current users's email + currentUserEmail = + this.props.context.pageContext.user.email.toLowerCase(); + + let filterActive: string = + "Title eq '" + + this.state.tournamentName.replace(/'/g, "''") + + "'"; + let filterUserTournaments: string = + "Tournament_x0020_Name eq '" + + this.state.tournamentName.replace(/'/g, "''") + + "'" + + " and Title eq '" + + currentUserEmail + + "'"; + + //Set the description for selected tournament + var tournmentDesc = this.state.tournamentDescriptionList.find((item) => item.key == this.state.tournamentName); + + this.setState({ + tournamentDescription: tournmentDesc.text + }); + + //Get all actions for the tournament from "Tournament Actions" list + const allTournamentsActionsArray: any[] = + await commonServiceManager.getItemsWithOnlyFilter( + stringsConstants.TournamentActionsMasterList, + filterActive + ); + + //Sort on Category + allTournamentsActionsArray.sort((a, b) => a.Category.localeCompare(b.Category)); + + //Get all actions completed by the current user for the current tournament + const userActionsArray: any[] = + await commonServiceManager.getItemsWithOnlyFilter( + stringsConstants.UserActionsList, + filterUserTournaments + ); + + var treeItemsArray: ITreeItem[] = []; + var completedTreeItemsArray: ITreeItem[] = []; + + //Build the Parent Nodes(Categories) in Treeview. Skip the items which are already completed by the user in "User Actions" list + await allTournamentsActionsArray.forEach((vAction) => { + //Check if the category is present in the 'User Actions' list + var compareCategoriesArray = userActionsArray.filter((elArray) => { + return ( + elArray.Action == vAction["Action"] && + elArray.Category == vAction["Category"] + ); + }); + const tree: ITreeItem = { + key: vAction["Category"], + label: vAction["Category"], + children: [], + }; + + //If the category is not present in User Actions list add it to 'Pending Tree view' + var found: boolean; + if (compareCategoriesArray.length == 0) { + //Check if Category is already added to the Treeview. If yes, skip adding. + found = treeItemsArray.some((value) => { + return value.label === vAction["Category"]; + }); + if (!found) treeItemsArray.push(tree); + } + //If the category is present in User Actions list add it to 'Completed Tree view' + else { + //Check if Category is already added to the Treeview. If yes, skip adding. + found = completedTreeItemsArray.some((value) => { + return value.label === vAction["Category"]; + }); + if (!found) completedTreeItemsArray.push(tree); + } + }); //For Loop + + //Build the child nodes(Actions) in Treeview. Skip the items which are already completed by the user in "User Actions" list + await allTournamentsActionsArray.forEach((vAction) => { + //Check if the action is present in the 'User Actions' list + var compareActionsArray = userActionsArray.filter((elChildArray) => { + return ( + elChildArray.Action == vAction["Action"] && + elChildArray.Category == vAction["Category"] + ); + }); + + //If the action is not present in User Actions list add it to 'Pending Tree view' + let tree: ITreeItem; + if (compareActionsArray.length == 0) { + if (vAction["HelpURL"] === 'null' || vAction["HelpURL"] == "") { + tree = { + key: vAction.Id, + label: vAction["Action"], + data: + vAction["Category"] + + stringsConstants.StringSeperator + + vAction["HelpURL"], + subLabel: + vAction["Points"] + + stringsConstants.PointsDisplayString + + vAction["Description"] + }; + } + else { + tree = { + key: vAction.Id, + label: vAction["Action"], + data: + vAction["Category"] + + stringsConstants.StringSeperator + + vAction["HelpURL"], + subLabel: + vAction["Points"] + + stringsConstants.PointsDisplayString + + vAction["Description"], + actions: [ + { + iconProps: { + iconName: "Info", + title: LocaleStrings.MyDashboardInfoIconMessage + }, + id: "GetItem", + actionCallback: async (treeItem: ITreeItem) => { + window.open(vAction["HelpURL"]); + }, + }, + ], + }; + } + var treeCol: Array = treeItemsArray.filter((value) => { + return value.label == vAction["Category"]; + }); + if (treeCol.length != 0) { + treeCol[0].children.push(tree); + } + } + //If the action present in User Actions list add it to 'Completed Tree view' + else { + if (vAction["HelpURL"] === 'null' || vAction["HelpURL"] == "") { + tree = { + key: vAction.Id, + label: vAction["Action"], + data: + vAction["Category"] + + stringsConstants.StringSeperator + + vAction["HelpURL"], + subLabel: + vAction["Points"] + + stringsConstants.PointsDisplayString + + vAction["Description"], + iconProps: { + iconName: "SkypeCheck", + }, + }; + } + else { + tree = { + key: vAction.Id, + label: vAction["Action"], + data: + vAction["Category"] + + stringsConstants.StringSeperator + + vAction["HelpURL"], + subLabel: + vAction["Points"] + + stringsConstants.PointsDisplayString + + vAction["Description"], + iconProps: { + iconName: "SkypeCheck", + }, + actions: [ + { + iconProps: { + iconName: "Info", + title: LocaleStrings.MyDashboardInfoIconMessage + }, + id: "GetItem", + actionCallback: async (treeItem: ITreeItem) => { + window.open(vAction["HelpURL"]); + }, + }, + ], + }; + } + var treeColCompleted: Array = + completedTreeItemsArray.filter((value) => { + return value.label == vAction["Category"]; + }); + if (treeColCompleted.length != 0) { + treeColCompleted[0].children.push(tree); + } + } + }); //For loop + + if (treeItemsArray.length == 0) + this.setState({ noPendingActions: true }); + this.setState({ + actionsList: treeItemsArray, + completedActionsList: completedTreeItemsArray, + }); + } catch (error) { console.error("TOT_TOTMyDashboard_getPendingActions \n", error); this.setState({ @@ -404,6 +575,7 @@ export default class TOTMyDashboard extends React.Component< Points: filterChildNodesArray[iCount].subLabel .split(stringsConstants.StringSeperatorPoints)[0] .replace(stringsConstants.PointsReplaceString, ""), + UserName: this.props.context.pageContext.user.displayName }; let createItems = await commonServiceManager.createListItem( stringsConstants.UserActionsList, @@ -415,11 +587,11 @@ export default class TOTMyDashboard extends React.Component< } this.setState({ actionsList: [], selectedActionsList: [] }); Promise.all(createActionsPromise).then(() => { - this.getPendingActions().then(() => { + this.getActiveTournaments().then(() => { this.setState({ showSpinner: false }); this._eventEmitter.emit("rebindSideBar:start", { - currentNumber: "1", - }); + tournamentName: this.state.tournamentName, + } as EventData); }); }); }); @@ -459,12 +631,12 @@ export default class TOTMyDashboard extends React.Component< this.props.onClickCancel()} - title="Tournament of Teams" + title={LocaleStrings.TOTBreadcrumbLabel} > - Tournament of Teams + {LocaleStrings.TOTBreadcrumbLabel} - My Dashboard + {LocaleStrings.TOTMyDashboardPageTitle} {this.state.showError && (
@@ -474,36 +646,69 @@ export default class TOTMyDashboard extends React.Component< {this.state.errorMessage} this.props.onClickCancel()} styles={backBtnStyles}> -
+ ) - : - - } + : + + } )} +
+ + {this.state.myTournamentsList.length > 0 && ( +
+ {LocaleStrings.MyTournamentsLabel} : + + + + + + + )} + {this.state.myTournamentsList.length > 0 && this.state.activeTournamentsList.length > 0 && ( + + {LocaleStrings.OrLabel} + + )} + {this.state.activeTournamentsList.length > 0 && ( + + {LocaleStrings.ActiveTournamentLabel} : + + + )} + + {this.state.tournamentName != "" && (
{this.state.tournamentName != "" && (
    -
  • - Tournament: - - {this.state.tournamentName} - -
  • {this.state.tournamentDescription && (
  • - Description: + {LocaleStrings.DescriptionLabel}: {this.state.tournamentDescription} @@ -518,38 +723,39 @@ export default class TOTMyDashboard extends React.Component<
{this.state.noPendingActions && ( )} {this.state.actionsError && ( )} {this.state.showSpinner && ( )}
{this.state.actionsList.length != 0 && ( this.props.onClickCancel()} styles={backBtnStyles} @@ -567,7 +773,7 @@ export default class TOTMyDashboard extends React.Component<
0) { this.setState({ isShowLoader: true, - userDisplayName: filterCurrentUser[0].User, + userDisplayName: this.props.context.pageContext.user.displayName, userEmail: filterCurrentUser[0].Email, userPoints: filterCurrentUser[0].Points, userRank: filterCurrentUser[0].Rank, @@ -98,101 +98,61 @@ export default class TOTSideBar extends React.Component< } } - //get all users properties and store in array - private async getAllUserProperties(): Promise { - return new Promise(async (resolve, reject) => { - this._graphClient = - await this.props.context.msGraphClientFactory.getClient(); - await this._graphClient - .api("/users") - .get() - .then(async (users: any, rawResponse?: any) => { - for (let user of users.value) { - if (user.mail != null) { - allUsersProperties.push({ - email: user.mail.toLowerCase(), - displayName: user.displayName, - }); - } - } - this.setState({ allUserProps: allUsersProperties }); - resolve("Success"); - }) - .catch((err) => { - console.error("TOT_TOTSideBar_getAllUserProperties \n", err); - this.setState({ - showError: true, - errorMessage: - stringsConstants.TOTErrorMessage + - " while getting users. Below are the details: \n" + - JSON.stringify(err), - showSuccess: false, - }); - reject("Failed"); - }); - }); - } - - private async getCurrentUserDetails(data?: any): Promise { + private async getCurrentUserDetails(data?: EventData): Promise { return new Promise(async (resolve, reject) => { try { + + let tournamentName: string; let allUserActions: any = []; - //get all users - let getAllUserStatus = await this.getAllUserProperties(); - console.log(getAllUserStatus); - //get active tournament details - let tournamentDetails = - await commonService.getActiveTournamentDetails(); - if (tournamentDetails.length != 0) { - await commonService - .getUserActions( - tournamentDetails[0]["Title"], - this.state.allUserProps - ) - .then((res) => { - if (res.length > 0) { - allUserActions = res; - let filterCurrentUser = allUserActions.filter( - (e) => - e.Email === - this.props.context.pageContext.user.email.toLowerCase() - ); - //bind current user Ranks,Name,Points - if (filterCurrentUser.length > 0) { - this.setState({ - userDisplayName: filterCurrentUser[0].User, - userEmail: filterCurrentUser[0].Email, - userRank: filterCurrentUser[0].Rank, - userPoints: filterCurrentUser[0].Points, - totalParticipants: res.length, - isShowLoader: false, - }); - } else { - this.setState({ - userDisplayName: - this.props.context.pageContext.user.displayName, - userEmail: this.props.context.pageContext.user.email, - userRank: "0", - userPoints: "0", - totalParticipants: res.length, - isShowLoader: false, - }); - } - } else if (res.length == 0) { - //no active participants + + //Setting varaible based on the value received from Event Emitter + if (data != undefined) + tournamentName = data.tournamentName; + + if (tournamentName != undefined && tournamentName != "") { + //Getting user actions for the selected tournament to calculate points and rank + await commonService.getUserActions(tournamentName).then((res) => { + if (res.length > 0) { + allUserActions = res; + let filterCurrentUser = allUserActions.filter( + (e) => + e.Email === + this.props.context.pageContext.user.email.toLowerCase() + ); + //bind current user Ranks,Name,Points + if (filterCurrentUser.length > 0) { this.setState({ - userDisplayName: - this.props.context.pageContext.user.displayName, + userDisplayName: this.props.context.pageContext.user.displayName, + userEmail: filterCurrentUser[0].Email, + userRank: filterCurrentUser[0].Rank, + userPoints: filterCurrentUser[0].Points, + totalParticipants: res.length, + isShowLoader: false, + }); + } else { + this.setState({ + userDisplayName: this.props.context.pageContext.user.displayName, userEmail: this.props.context.pageContext.user.email, userRank: "0", userPoints: "0", - totalParticipants: "0", + totalParticipants: res.length, isShowLoader: false, }); - } else if (res == "Failed") { - console.error("TOT_TOTSideBar_getUserActions \n"); } - }); + } else if (res.length == 0) { + //no active participants + this.setState({ + userDisplayName: this.props.context.pageContext.user.displayName, + userEmail: this.props.context.pageContext.user.email, + userRank: "0", + userPoints: "0", + totalParticipants: "0", + isShowLoader: false, + }); + } else if (res == "Failed") { + console.error("TOT_TOTSideBar_getUserActions \n"); + } + }); } else { //no active tournaments this.setState({ @@ -250,13 +210,13 @@ export default class TOTSideBar extends React.Component<
-
{this.state.userPoints} Points
+
{this.state.userPoints} {LocaleStrings.PointsLabel}
- Tournament Rank
+ {LocaleStrings.TournamentRankLabel}
{this.state.userRank} -  of {this.state.totalParticipants}
Participants +  of {this.state.totalParticipants}
{LocaleStrings.ParticipantsLabel}
diff --git a/src/webparts/clbHome/config/siteconfig.json b/src/webparts/clbHome/config/siteconfig.json index d093918b..ce61c8d6 100644 --- a/src/webparts/clbHome/config/siteconfig.json +++ b/src/webparts/clbHome/config/siteconfig.json @@ -331,7 +331,7 @@ "" ], "masterData": [{ - "Title": "Communication in Teams", //title is tournament name + "Title": "Communication in Teams", "Category": "Chat", "Action": "Send 1:1", "Description": "Send a 1:1 chat to someone else in the organization", @@ -339,7 +339,7 @@ "HelpURL": "https://support.microsoft.com/en-us/office/start-a-chat-in-teams-0c71b32b-c050-4930-a887-5afbe742b3d8" }, { - "Title": "Communication in Teams", //title is tournament name + "Title": "Communication in Teams", "Category": "Chat", "Action": "Send GIF", "Description": "Send a GIF to someone else in org", @@ -347,7 +347,7 @@ "HelpURL": "https://support.microsoft.com/en-us/office/send-an-emoji-gif-or-sticker-in-teams-174248c9-e64d-4de1-9f41-3199cc0751ad" }, { - "Title": "Communication in Teams", //title is tournament name + "Title": "Communication in Teams", "Category": "Channel", "Action": "Attach a file", "Description": "Attach a file to a conversation in a channel post", @@ -355,7 +355,7 @@ "HelpURL": "https://support.microsoft.com/en-us/office/send-a-file-picture-or-link-in-teams-0e930dcd-46fd-42c3-8d7d-15af4f9bcfca" }, { - "Title": "Communication in Teams", //title is tournament name + "Title": "Communication in Teams", "Category": "Chat", "Action": "Mark message as urgent", "Description": "Mark message as urgent for place importance", @@ -363,7 +363,7 @@ "HelpURL": "https://support.microsoft.com/en-us/office/mark-a-message-as-important-or-urgent-in-teams-ea99d5b6-1317-4550-8d75-86ff14cd4462#:~:text=To%20do%20that%2C%20tap%20More%20options%20%3E%20Message,just%20retrace%20your%20steps%20and%20tap%20it%20again." }, { - "Title": "Communication in Teams", //title is tournament name + "Title": "Communication in Teams", "Category": "Chat", "Action": "Pin conversation", "Description": "Pin (Save the message) chat on your message thread for visibility", @@ -371,7 +371,7 @@ "HelpURL": "https://support.microsoft.com/en-us/office/hide-unhide-mute-or-pin-a-chat-in-teams-9aee02ef-713d-495b-8a73-9762d8e4b066" }, { - "Title": "Communication in Teams", //title is tournament name + "Title": "Communication in Teams", "Category": "Calendar", "Action": "Schedule a meeting", "Description": "Schedule a meeting with someone", @@ -379,7 +379,7 @@ "HelpURL": "https://docs.microsoft.com/en-us/microsoftteams/teams-add-in-for-outlook" }, { - "Title": "Communication in Teams", //title is tournament name + "Title": "Communication in Teams", "Category": "Calling", "Action": "Create a recording", "Description": "Record call to share with members", @@ -387,7 +387,7 @@ "HelpURL": "https://support.microsoft.com/en-us/office/record-a-meeting-in-teams-34dfbe7f-b07d-4a27-b4c6-de62f1348c24" }, { - "Title": "Communication in Teams", //title is tournament name + "Title": "Communication in Teams", "Category": "Calling", "Action": "Invite someone to an existing call", "Description": "Add another person to a call invitation", @@ -395,7 +395,7 @@ "HelpURL": "https://docs.microsoft.com/en-us/microsoftteams/teams-add-in-for-outlook" }, { - "Title": "Collaboration in Teams", //title is tournament name + "Title": "Collaboration in Teams", "Category": "Calling", "Action": "Place a phone call", "Description": "Place a phone call to another teams users", @@ -403,7 +403,7 @@ "HelpURL": "https://support.microsoft.com/en-us/office/first-things-to-know-about-calls-in-microsoft-teams-2b883a81-dd15-41bd-a6ba-39deef141027" }, { - "Title": "Collaboration in Teams", //title is tournament name + "Title": "Collaboration in Teams", "Category": "Calling", "Action": "Listen to voicemail", "Description": "Listen to a voicemail from someone", @@ -411,7 +411,7 @@ "HelpURL": "https://support.microsoft.com/en-us/office/check-your-voicemail-in-teams-f8d568ce-7329-4fe2-a6a2-325ec2e2b419#:~:text=Check%20your%20voicemail%20in%20Teams%20To%20check%20your,someone%20back%2C%20select%20More%20options%20%3E%20Call%20back." }, { - "Title": "Collaboration in Teams", //title is tournament name + "Title": "Collaboration in Teams", "Category": "Channel", "Action": "Create a channel ", "Description": "Create a channel for your team", @@ -419,7 +419,7 @@ "HelpURL": "https://support.microsoft.com/en-us/office/create-a-channel-in-teams-fda0b75e-5b90-4fb8-8857-7e102b014525" }, { - "Title": "Collaboration in Teams", //title is tournament name + "Title": "Collaboration in Teams", "Category": "Channel", "Action": "Add members to channel", "Description": "Add the people in your team to channel", @@ -427,7 +427,7 @@ "HelpURL": "https://support.microsoft.com/en-us/office/create-a-channel-in-teams-fda0b75e-5b90-4fb8-8857-7e102b014525" }, { - "Title": "Collaboration in Teams", //title is tournament name + "Title": "Collaboration in Teams", "Category": "Channel", "Action": "Create a folder for files", "Description": "Create a folder to store your files", @@ -435,7 +435,7 @@ "HelpURL": "https://support.microsoft.com/en-us/topic/collaborate-on-files-in-microsoft-teams-9b200289-dbac-4823-85bd-628a5c7bb0ae" }, { - "Title": "Collaboration in Teams", //title is tournament name + "Title": "Collaboration in Teams", "Category": "Channel", "Action": "Mention someone with @", "Description": "Use @ mention to get the attention of someone", @@ -443,7 +443,7 @@ "HelpURL": "https://support.microsoft.com/en-us/office/get-attention-with-mentions-b2ffb135-7069-4880-84ee-5b27f402418b#:~:text=%20Get%20an%20entire%20team%20or%20channel%27s%20attention,Finish%20your%20message%20and%20select%20Send.%20More%20" }, { - "Title": "Collaboration in Teams", //title is tournament name + "Title": "Collaboration in Teams", "Category": "Channel", "Action": "Create a private channel", "Description": "Create a private channel to enhance security ", @@ -451,7 +451,7 @@ "HelpURL": "https://docs.microsoft.com/en-us/MicrosoftTeams/private-channels#:~:text=Private%20channel%20owner%20and%20member%20actions%20%20,%20%20No%20%204%20more%20rows%20" }, { - "Title": "Apps in Teams", //title is tournament name + "Title": "Apps in Teams", "Category": "Apps", "Action": "Add app to channel", "Description": "Include app in your Team channel to enhance productivity", @@ -459,7 +459,7 @@ "HelpURL": "https://support.microsoft.com/en-us/office/add-an-app-to-microsoft-teams-b2217706-f7ed-4e64-8e96-c413afd02f77" }, { - "Title": "Apps in Teams", //title is tournament name + "Title": "Apps in Teams", "Category": "Apps", "Action": "Add app to chat", "Description": "Include app in your chat to enhance collaboration", @@ -467,7 +467,7 @@ "HelpURL": "https://support.microsoft.com/en-us/office/add-an-app-to-microsoft-teams-b2217706-f7ed-4e64-8e96-c413afd02f77" }, { - "Title": "Apps in Teams", //title is tournament name + "Title": "Apps in Teams", "Category": "Apps", "Action": "Include app in meeting invite", "Description": "Include app in your invite to prepare for collaboration", @@ -475,7 +475,7 @@ "HelpURL": "https://support.microsoft.com/en-us/office/use-apps-in-teams-meetings-62bca572-ba7e-4e21-9190-a47c61319739" }, { - "Title": "Apps in Teams", //title is tournament name + "Title": "Apps in Teams", "Category": "Apps", "Action": "Pin and unpin apps", "Description": "Easily access apps by pinning them on your top bar", @@ -483,7 +483,7 @@ "HelpURL": "https://support.microsoft.com/en-us/office/pin-an-app-for-easy-access-3045fd44-6604-4ba7-8ecc-1c0d525e89ec" }, { - "Title": "Apps in Teams", //title is tournament name + "Title": "Apps in Teams", "Category": "Apps", "Action": "Configure app notifications", "Description": "Control your notification", @@ -491,7 +491,7 @@ "HelpURL": "https://support.microsoft.com/en-us/office/add-an-app-to-microsoft-teams-b2217706-f7ed-4e64-8e96-c413afd02f77" }, { - "Title": "Apps in Teams", //title is tournament name + "Title": "Apps in Teams", "Category": "Apps", "Action": "Send app content in messages", "Description": "Send informative messages or highlight individuals with Praise badge", @@ -507,7 +507,8 @@ "", "", "", - "" + "", + "" ] } diff --git a/src/webparts/clbHome/constants/strings.ts b/src/webparts/clbHome/constants/strings.ts index 0b47ab24..25c172d2 100644 --- a/src/webparts/clbHome/constants/strings.ts +++ b/src/webparts/clbHome/constants/strings.ts @@ -1,3 +1,5 @@ +export const ToTLabel = "Tournament of Teams"; +export const CMPLabel = "Champion Management Platform"; export const PropertyPaneDescription = "Description"; export const BasicGroupName = "Group Name"; export const DescriptionFieldLabel = "Description Field"; @@ -77,6 +79,9 @@ export const UserActionsList = "User Actions"; export const NoActiveTournamentMessage = "There is no active tournament at the moment. Please check back later."; export const formSavingMessage = "Saving your details...."; export const DigitalBadgeLibrary = "Digital Badge Assets"; +export const CMPLogoLibrary = "CMP Logo"; +export const MSLogo ="MSLogo.jpg"; +export const MemberList = "Member List"; export const NoActiveParticipantsMessage = "There are no active participants at the moment. Be the first to participate and log an activity from My Dashboard!"; @@ -91,6 +96,7 @@ export const FeedbackUrl = "https://aka.ms/adoptionfeedback"; export const M365CMP = "https://aka.ms/m365cmp"; export const M365CmpApp = "https://aka.ms/m365cmpapp"; + //ManageTournaments -export const ManageTotLabel1 = "One tournament can be enabled at a time. See below for the current active tournament, ability to end the tournament, as well as the ability to start a new tournament."; -export const ManageTotLabel2 = "Once a tournament has been started and completed it will not be available to start again; however, you can create a new tournament using the same actions if you would like to complete a similar tournament!"; \ No newline at end of file +export const StartTournamentAction = "Start"; +export const EndTournamentAction = "End"; \ No newline at end of file diff --git a/src/webparts/clbHome/events/EventData.ts b/src/webparts/clbHome/events/EventData.ts new file mode 100644 index 00000000..a797530a --- /dev/null +++ b/src/webparts/clbHome/events/EventData.ts @@ -0,0 +1,3 @@ +export class EventData { + public tournamentName: string; + } \ No newline at end of file diff --git a/src/webparts/clbHome/loc/en-us.js b/src/webparts/clbHome/loc/en-us.js index 89f98bc1..837cb943 100644 --- a/src/webparts/clbHome/loc/en-us.js +++ b/src/webparts/clbHome/loc/en-us.js @@ -1,7 +1,245 @@ -define([], function() { +define([], function () { return { + "PropertyPaneDescription": "Description", "BasicGroupName": "Group Name", - "DescriptionFieldLabel": "Description Field" + "DescriptionFieldLabel": "Description Field", + + //-------------------------------------CMP---------------------------------------------- + + //Common + "CMPBreadcrumbLabel": "Champion Management Platform", + "SaveButton": "Save", + "BackButton": "Back", + + //CMP Home + "GetStartedLabel": "Get Started", + "AdminToolsLabel": "Admin Tools", + "ChampionLeaderBoardLabel": "Champion LeaderBoard", + "AddMemberLabel": "Add Member", + "NominateMemberLabel": "Nominate Member", + "DigitalBadgeLabel": "Digital Badge", + "TOTLabel": "Tournament of Teams", + "ChampionListLabel": "Champions List", + "EventsListLabel": "Events List", + "EventsTrackListLabel": "Events Track List", + "ManageApprovalsLabel": "Manage Approvals", + "ManageDigitalBadgesLabel": "Manage Digital Badges", + "EnableTOTLabel": "Enable Tournament of Teams", + "AddMembersToolTip": "Adding Members Start adding the people you will collaborate with in your...", + "DigitalMembersToolTip": "Digital Badge, Get your Champion Badge", + "ChampionsListToolTip": "Accessing Champions List", + "EventsListToolTip": "Accessing Events List", + "EventTrackListToolTip": "Accessing Event Track List", + "ManageApprovalToolTip": "Approve Champion", + "EnableTOTToolTip": "Enable Tournament of Teams", + "ManageDigitalBadgesToolTip": "Manage Digital Badges", + "WelcomeLabel": "Welcome", + "ManageAppLogoLabel": "Manage App Logo", + "ManageAppLogoToolTip": "Manage App Logo", + + //Header + "AppHeaderTitleLabel": "Champion Management Platform", + "AppLogoToolTip": "Home", + "MoreInfoToolTip": "More Info", + "SupportToolTip": "Support", + "FeedbackToolTip": "Feedback", + + //More Info Icon Content + "AboutHeaderLabel": "About the Champion Management Platform (CMP):", + "AboutContentLabel": "Our Champion Management Platform was created with organizational Champions / Adoption Specialists in mind. Hearing from the Microsoft 365 Champion Community this app was developed to deliver a platform to help create and sustain your own communities. Starting with inspiration through execution in helping you achieve more within your own communities!", + "AdditionalResourcesHeaderLabel": "Additional Resources:", + "AdditionalResourcesContentLabel": "The Microsoft Teams Customer Advocacy Group is focused on delivering solutions like these to inspire and help you achieve your goals. Follow and join in through these other resources to learn more from us and the community:", + "M365ChampionCommunityLinkLabel": "Microsoft 365 Champion Community", + "DrivingAdoptionLinkLabel": "Driving Adoption on the Microsoft Technical Community", + "CurrentVersionLabel": "Current Version:", + "LatestVersionLabel": "Latest Version:", + "CMPGitHubLinkLabel": "GitHub", + "VisitLabel": "Visit the Champion Management Platform pages to learn more:", + "OverviewLabel": "Overview & Information on our", + "MSAdoptionHubLinkLabel": "Microsoft Adoption Hub", + "DocumentationLabel": "Solution technical documentation and architectural overview on", + + //Add Member + "AddMemberPageTitle": "Add Member", + "NominateMemberPageTitle": "Nominate Member", + "PeoplePickerPlaceholder": "For Adding a member please type member name", + "RegionPlaceholder": "Select Region", + "CountryPlaceholder": "Select Country", + "GroupPlaceholder": "Select Group", + "FocusAreaPlaceholder": "Select Focus Area", + "UserExistingMessage": "User Already a Champion!", + + //CLBChampionsList and ManageApprovals + "ChampionsListPageTitle": "Champions List", + "UserNominatedMessage": "User Nominated Successfully!", + "UserAddedMessage": "User Added Successfully!", + "PeopleNameGridHeader":"Member Name", + "RegionGridHeader":"Region", + "CountryGridHeader": "Country", + "GroupGridHeader":"Group", + "FocusAreaGridHeader":"Focus Area", + "StatusGridHeader": "Status", + "ManageApprovalsPageTitle": "Manage Approvals", + "ActionGridHeader": "Action", + "ApproveButton": "Approve", + "RejectButton": "Reject", + "NoChampionsMessage": "NO CHAMPION REQUESTS AVAILABLE", + "ChampionApprovedMessage": "Champion Request Approved!", + "ChampionRejectedMessage": "Champion Request Rejected!", + + //Digital Badge" + "DigitalBadgePageTitle":"Digital Badge", + "LoadingSpinnerLabel": "Loading...", + "DigitalBadgeAppBannerAltText":"Microsoft team members engaging in a discussion", + "PreAcceptPageTitle":"Claim your digital badge!", + "PreAcceptDisclaimer": "Welcome to the digital badge utility

As part of your membership to the Champions Program, you have earned a digital badge! This utility will enable you to 'claim' your digital badge by overlaying it on top of your existing profile picture.

Please note that this will update your profile picture across all of Microsoft 365 (Exchange, Teams, SharePoint, etc).
", + "PreAcceptDisclaimer2":"If desired, you will have an opportunity to 'save' a copy of your existing profile picture without the digital badge overlay in the succeeding steps. Please click ‘Accept’ to continue (you might see a pop-up open and prompt for sign-in).", + "NotQualifiedPreAcceptDisclaimer":"Welcome to the digital badge utility. As part of your membership of the Champions Program, you can earn a digital badge! This utility will enable you to 'claim' your digital badge by overlaying it on top of your existing profile picture. Please note that this will update your profile picture across all of Microsoft 365 (Exchange, Teams, SharePoint, etc.)", + "HowtoGetDigitalBadgeText":"How to get Champion Badge", + "MultipleBadgeMessage":"Select a badge from the below list. Selected badge will be applied on your profile picture.", + "NoBadgeMessage":"No badges found for you to select. Please check with your App Administrator.", + "DigitalBadgeSubPageTitle":"Here's how your digital badge will look!", + "ProfileImageAlt":"My profile image used across O365, Teams, etc", + "BadgeImageAlt":"Medal-shaped award with the letter C in the middle signifying I am a part of the Champions Program", + "NoProfileImageAlt":"Silhouette of a person, signifying no profile image is currently applied to your account", + "ApplyButtonText": "Apply image", + "ApplyButtonAriaDescription": "By clicking this button you're giving the Champions Program web part permission to update your profile picture on your behalf.", + "ApplyButton": "Apply", + "DownloadButtonText":"Download", + "DownloadButtonAriaDescription":"Click to download your current profile image", + "DownloadingButtonText":"Downloading...", + "DownloadedButtonText": "Downloaded", + "DownloadedButtonSecondaryText": "Your downloaded profile image can be found in your device's Downloads folder.", + "DownloadButtonSecondaryText": "Download your current profile image", + "PreApplyDisclaimer":"This is a preview of your new profile picture including the digital badge. If you wish to download your existing profile picture prior to applying the digital badge, click on the 'Download your current profile image' link.
", + "PreApplyDisclaimer1":"
Clicking 'Apply Image' will update your Microsoft 365 profile picture with this new image. Please note this will update your profile picture across all of Microsoft 365 and cannot be reverted through this page.

If you'd like to change your picture back, you can use the Outlook web application.", + "NoProfileImageDescription":"If you have already associated a picture with your profile, and you’re still seeing this message, your picture might not be available yet. Please try again later, or contact the helpdesk if it still doesn’t show up after 24 hours.", + "AcceptButtonText":"Accept", + "AcceptButtonAriaDescription":"By clicking this button you're giving the Champions Program web part permission to update your profile picture on your behalf.", + "UnauthorizedText": "You are not qualified for Champion Badge Program at this time", + "ApplySpinnerLabel":"Applying new profile image to your account...", + "DigitalBadgeSuccessMessage":"Congratulations! You have successfully claimed your digital badge and your profile picture has been updated. Please allow up to 24 hours to see the update reflected across all Microsoft 365 applications (including Teams).", + + //CMP Sidebar + "CMPSideBarPointsLabel": "Points", + "CMPSideBarGlobalRankLabel": "Global Rank", + "CMPSideBarChampionsLabel": "Champions", + "FirstNameLabel":"First Name", + "LastNameLabel":"Last Name", + "EmailIDLabel": "Email Id", + "BecomeChampionLabel": "Become a Champion", + "ChampionSubmissionPendingLabel": "Champion submission pending", + "ChampionRequestSubmitSuccessMessage":"Champion request submission successful", + + //ChampionLeaderBoard + "PivotHeaderGlobal": "Global", + "PivotHeaderNearMe": "Near Me", + "PivotHeaderBySpeciality": "By Specialty", + "NearMePlaceHolder": "Select Near Me", + "BySpecialityPlaceHolder": "Select By Specialty", + + //Champions + "TopChampionsLabel": "Top Champions", + "RecordsNotFound":"Records Not Found", + "EventTypeLabel": "Event Type", + "CountLabel": "Count", + "MyRankLabel": "My Rank", + + //ChampionView + "ViewDashBoardLabel":"View Dashboard", + "DateofEventGridLabel": "Date of Event", + "EventTypeGridLabel": "Type", + "RecordEventLabel": "Record Event", + "MonthAndDateLabel": "Month and Date", + "EventTypeGridLabelPlaceHolder": "Select Event Type", + "EventTypeValidationMessage":"Please select event type!", + "CountValidationMessage": "Count should be between 1 and 5", + "EventsSubmitMessage": "When you are done adding events, please click on Submit button to save.", + "SubmitButton":"Submit", + + //Employee View + "SearchLabel": "Search", + + + //----------------------------------------------------------------------------------------- + + //-------------------------------------TOT------------------------------------------------- + // Create Tournament + "CreateTournamentPageTitle": "Create Tournament", + "CreateTournamentSuccessLabel": "Tournament created successfully.", + "TournamentNameLabel": "Tournament Name", + "TournamentNameErrorLabel": "Tournament Name is required.", + "TournamentDescriptionLabel": "Tournament Description", + "TournamentDescPlaceHolderLabel": "Tournament Description(Max 500 characters)", + "SelectTeamsActionsLabel": "Select Teams Actions:", + "TeamsActionInfoToolTip": "Select from the below available Teams actions to include in the new tournament. To add new tournament actions to choose from, visit the Manage Tournament Actions from the Admin Tools.", + "ActionErrorLabel": "Select atleast one action to create a tournament.", + "CreateTournamentButton": "Create Tournament", + "DuplicateTournamentNameError": "Tournament name already exists. Enter another name for tournament.", + + + + //TOT Common + "TOTBreadcrumbLabel": "Tournament of Teams", + + //TOT Landing + "EnableTOTSuccessMessage": "Tournament of Teams enabled successfully. Please refresh the app before using.", + "TOTLeaderBoardPageTitle": "Leader Board", + "TOTMyDashboardPageTitle": "My Dashboard", + "ManageTournamentActionsToolTip": "Accessing Tournament Actions List", + "ManageTournamentActionsLabel": "Manage Tournament Actions", + "ManageTournamentsLabel": "Manage Tournaments", + "ManageAdminsToolTip": "Accessing Admin List", + "ManageAdminsLabel": "Manage Admins", + "QuickLinksLabel": "Quick Links", + + //Enable Tournament + "ManageTournamentsPageTitle": "Manage Tournaments", + "ManageToTLabel1": "Multiple tournaments can be enabled at a time. See below for the current active tournaments, ability to end the tournament, as well as the ability to start a new tournament.", + "ManageToTLabel2": "Once a tournament has been started and completed it will not be available to start again; however, you can create a new tournament using the same actions if you would like to complete a similar tournament!", + "EndTournamentDialogMessage": "Are you sure want to end the tournament?", + "StartTournamentDialogMessage": "Are you sure want to start the new tournament?", + "ConfirmLabel": "Confirm", + "YesButton": "Yes", + "NoButton": "No", + "ActiveTournamentLabel": "Active Tournaments", + "EndTournamentButton": "End Tournament", + "StartTournamentHeaderLabel": "Start Tournament", + "StartTournamentInfoToolTip": "A new tournament can only be started if there is no active tournament. To start a new tournament end the current tournament.", + "NoTournamentMessage": "No tournaments found.", + "SelectTournamentMessage": "Select a tournament to enable.", + "NoActiveTournamentMessage": "No active tournaments found.", + "SelectEndTournamentMessage": "Select a tournament to end.", + "StartTournamentButton": "Start Tournament", + "EndTournamentSuccessMessage": "Tournament ended successfully.", + "EnableTournamentSuccessMessage": "Tournament enabled successfully.", + + //TOT Leaderboard & TOT My Dashboard + "NoActiveParticipantsMessage": "There are no active participants at the moment. Be the first to participate and log an activity from My Dashboard!", + "NoActiveTournamentMessage": "There is no active tournament at the moment. Please check back later.", + "TournamentLabel": "Tournament", + "DescriptionLabel": "Description", + "NoActiveParticipantsErrorMessage": "There are no active participants at the moment. Be the first to participate and log an activity from ", + "PendingActionsLabel": "Pending Actions", + "PendingActionsSuccessMessage": "There are no more pending actions in this tournament.", + "SelectActionsErrorMessage": "Select atleast one action to proceed.", + "FormSavingMessage": "Saving your details....", + "CompletedActionsLabel": "Completed Actions", + "RankLabel": "Rank", + "UserLabel": "User", + "MyDashboardInfoIconMessage": "Find out more about this action", + "SelectTournamentPlaceHolder": "Select Tournament", + "MyTournamentsLabel": "My Tournaments", + "OrLabel": "OR", + "MyTournamentsTooltip": "Active tournaments that you have participated in already", + + //TOT SideBar + "PointsLabel": "Points", + "TournamentRankLabel": "Tournament Rank", + "ParticipantsLabel": "Participants", + + //----------------------------------------------------------------------------------------- + } }); \ No newline at end of file diff --git a/src/webparts/clbHome/loc/mystrings.d.ts b/src/webparts/clbHome/loc/mystrings.d.ts index 89fcd7c9..3dc25167 100644 --- a/src/webparts/clbHome/loc/mystrings.d.ts +++ b/src/webparts/clbHome/loc/mystrings.d.ts @@ -2,6 +2,242 @@ declare interface IClbHomeWebPartStrings { PropertyPaneDescription: string; BasicGroupName: string; DescriptionFieldLabel: string; + + //-------------------------------------CMP---------------------------------------------- + + //CMP Common + CMPBreadcrumbLabel: string; + SaveButton: string; + BackButton: string; + + //CMP Home + GetStartedLabel: string; + AdminToolsLabel: string; + ChampionLeaderBoardLabel: string; + AddMemberLabel: string; + NominateMemberLabel: string; + DigitalBadgeLabel: string; + TOTLabel: string; + ChampionListLabel: string; + EventsListLabel: string; + EventsTrackListLabel: string; + ManageApprovalsLabel: string; + ManageDigitalBadgesLabel: string; + EnableTOTLabel: string; + AddMembersToolTip: string; + DigitalMembersToolTip: string; + ChampionsListToolTip: string; + EventsListToolTip: string; + EventTrackListToolTip: string; + ManageApprovalToolTip: string; + EnableTOTToolTip: string; + ManageDigitalBadgesToolTip: string; + ManageAppLogoLabel: string; + ManageAppLogoToolTip: string; + WelcomeLabel: string; + + //Header + AppHeaderTitleLabel: string; + AppLogoToolTip: string; + MoreInfoToolTip: string; + SupportToolTip: string; + FeedbackToolTip: string; + + //More Info Icon Content + AboutHeaderLabel: string; + AboutContentLabel: string; + AdditionalResourcesHeaderLabel: string; + AdditionalResourcesContentLabel: string; + M365ChampionCommunityLinkLabel: string; + DrivingAdoptionLinkLabel: string; + CurrentVersionLabel: string; + LatestVersionLabel: string; + CMPGitHubLinkLabel: string; + VisitLabel: string; + OverviewLabel: string; + MSAdoptionHubLinkLabel: string; + DocumentationLabel: string; + + //Add Member + AddMemberPageTitle: string; + NominateMemberPageTitle: string; + PeoplePickerPlaceholder: string; + RegionPlaceholder: string; + CountryPlaceholder: string; + GroupPlaceholder: string; + FocusAreaPlaceholder: string; + UserExistingMessage: string; + + //CLBChampionsList and Manage Approvals + ChampionsListPageTitle: string; + UserNominatedMessage: string; + UserAddedMessage: string; + PeopleNameGridHeader:string; + RegionGridHeader:string; + CountryGridHeader:string; + GroupGridHeader:string; + FocusAreaGridHeader:string; + StatusGridHeader: string; + ManageApprovalsPageTitle: string; + ActionGridHeader: string; + ApproveButton: string; + RejectButton: string; + NoChampionsMessage: string; + ChampionApprovedMessage: string; + ChampionRejectedMessage: string; + + //DigitalBadge + DigitalBadgePageTitle: string; + LoadingSpinnerLabel: string; + DigitalBadgeAppBannerAltText: string; + PreAcceptPageTitle: string; + PreAcceptDisclaimer: string; + PreAcceptDisclaimer2: string; + NotQualifiedPreAcceptDisclaimer: string; + HowtoGetDigitalBadgeText: string; + MultipleBadgeMessage: string; + NoBadgeMessage: string; + DigitalBadgeSubPageTitle: string; + ProfileImageAlt: string; + BadgeImageAlt: string; + NoProfileImageAlt: string; + ApplyButtonText: string; + ApplyButtonAriaDescription: string; + ApplyButton: string; + DownloadButtonText: string; + DownloadButtonAriaDescription: string; + DownloadingButtonText: string; + DownloadedButtonText: string; + DownloadedButtonSecondaryText: string; + DownloadButtonSecondaryText: string; + PreApplyDisclaimer: string; + PreApplyDisclaimer1: string; + NoProfileImageDescription: string; + AcceptButtonText: string; + AcceptButtonAriaDescription: string; + UnauthorizedText: string; + ApplySpinnerLabel: string; + DigitalBadgeSuccessMessage: string; + + //CMP Sidebar + CMPSideBarPointsLabel: string; + CMPSideBarGlobalRankLabel: string; + CMPSideBarChampionsLabel:string; + FirstNameLabel:string; + LastNameLabel:string; + EmailIDLabel: string; + BecomeChampionLabel: string; + ChampionSubmissionPendingLabel: string; + ChampionRequestSubmitSuccessMessage: string; + + //ChampionLeaderBoard + PivotHeaderGlobal: string; + PivotHeaderNearMe: string; + PivotHeaderBySpeciality: string; + NearMePlaceHolder: string; + BySpecialityPlaceHolder:string; + + //Champions + TopChampionsLabel: string; + RecordsNotFound: string; + EventTypeLabel: string; + CountLabel:string; + MyRankLabel: string; + + //ChampionView + ViewDashBoardLabel: string; + DateofEventGridLabel: string; + EventTypeGridLabel: string; + RecordEventLabel: string; + MonthAndDateLabel: string; + EventTypeGridLabelPlaceHolder: string; + EventTypeValidationMessage: string; + CountValidationMessage: string; + EventsSubmitMessage: string; + SubmitButton:string; + + //Employee View + SearchLabel: string; + + //---------------------------------------------------------------------------------------- + + + //-------------------------------------TOT------------------------------------------------- + // Create Tournament + CreateTournamentPageTitle: string; + CreateTournamentSuccessLabel: string; + TournamentNameLabel: string; + TournamentNameErrorLabel: string; + TournamentDescriptionLabel: string; + TournamentDescPlaceHolderLabel: string; + SelectTeamsActionsLabel: string; + TeamsActionInfoToolTip: string; + ActionErrorLabel: string; + CreateTournamentButton: string; + DuplicateTournamentNameError: string; + + //TOT Common + TOTBreadcrumbLabel: string; + + //TOT Landing + EnableTOTSuccessMessage: string; + TOTLeaderBoardPageTitle: string; + TOTMyDashboardPageTitle: string; + ManageTournamentActionsToolTip: string; + ManageTournamentActionsLabel: string; + EndCurrentTournamentLabel: string; + ManageTournamentsLabel: string; + ManageAdminsToolTip: string; + ManageAdminsLabel: string; + ManageDigitalBadgesLabel: string; + QuickLinksLabel: string; + + //Enable Tournament + ManageTournamentsPageTitle: string; + ManageToTLabel1: string; + ManageToTLabel2: string; + EndTournamentDialogMessage: string; + StartTournamentDialogMessage: string; + ConfirmLabel: string; + YesButton: string; + NoButton: string; + ActiveTournamentLabel: string; + EndTournamentButton: string; + StartTournamentHeaderLabel: string; + StartTournamentInfoToolTip: string; + NoTournamentMessage: string; + SelectTournamentMessage: string; + NoActiveTournamentMessage: string; + SelectEndTournamentMessage: string; + StartTournamentButton: string; + EndTournamentSuccessMessage: string; + EnableTournamentSuccessMessage: string; + + // TOT LeaderBoard & TOT My Dashboard + NoActiveParticipantsMessage; string; + NoActiveTournamentMessage: string; + TournamentLabel: string; + DescriptionLabel: string; + NoActiveParticipantsErrorMessage: string; + PendingActionsLabel: string; + PendingActionsSuccessMessage: string; + SelectActionsErrorMessage: string; + FormSavingMessage: string; + CompletedActionsLabel: string; + RankLabel: string; + UserLabel: string; + MyDashboardInfoIconMessage: string; + SelectTournamentPlaceHolder: string; + MyTournamentsLabel: string; + OrLabel: string; + MyTournamentsTooltip: string; + + //TOT SideBar + PointsLabel: string; + TournamentRankLabel: string; + ParticipantsLabel: string; + + //----------------------------------------------------------------------------------------- } declare module 'ClbHomeWebPartStrings' { diff --git a/src/webparts/clbHome/scss/Championleaderboard.scss b/src/webparts/clbHome/scss/Championleaderboard.scss index 4ca2a876..1a17b8b2 100644 --- a/src/webparts/clbHome/scss/Championleaderboard.scss +++ b/src/webparts/clbHome/scss/Championleaderboard.scss @@ -1,434 +1,518 @@ .Championleaderboard { - display: flex !important; - .sidenav { - background: transparent url("../assets/CMPImages/Sidebar.svg") 0% 0% no-repeat padding-box; - width: 212px; - min-height: 100vh; - height: 1018px; - opacity: 1; - .profilepic { - height: 100px; - width: 100px; - border-radius: 50%; - margin-top: 5rem; - display: block; - margin-left: auto; - margin-right: auto; - border-radius: 50%; - background-color: #7577b9; - border: 4px solid #464775; - box-shadow: 0 0 0 3px #fff; + display: flex !important; + + .sidenav { + background: transparent url("../assets/CMPImages/Sidebar.svg") 0% 0% no-repeat padding-box; + background-color: #6264a7; + width: 212px; + min-height: 100vh; + height: auto; + opacity: 1; + + .profilepic { + height: 100px; + width: 100px; + border-radius: 50%; + margin-top: 5rem; + display: block; + margin-left: auto; + margin-right: auto; + border-radius: 50%; + background-color: #7577b9; + border: 4px solid #464775; + box-shadow: 0 0 0 3px #fff; + } + + .championname { + font-size: 16px; + font: normal normal bold 18px/24px Segoe UI; + font-weight: 600; + color: white; + margin-top: 0.5rem; + text-align: center; + } + + .pointcircle { + border-radius: 50%; + background-color: #7577b9; + border: 4px solid #464775; + box-shadow: 0 0 0 3px #fff; + background: #7577b9 0% 0% no-repeat padding-box; + width: 153px; + height: 154px; + border-radius: 102px; + margin-top: 2.2rem; + display: block; + margin-left: auto; + margin-right: auto; + + .insidecircle { + text-align: center; + color: white; + margin: 1rem; + + .pointsscale { + text-align: center; + + .yellowStar { + color: #ffc206; + width: 25px; + height: 23px; + } } - .championname { - font-size: 16px; - font: normal normal bold 18px/24px Segoe UI; - font-weight: 600; - color: white; - margin-top: 0.5rem; - text-align: center; + } + + .score { + font-weight: 800; + font-size: 14px; + margin: auto; + } + + .monthlypoints { + font-size: 14px; + } + + .line { + height: 1px; + width: auto; + background-color: #39f; + margin: 0.5rem; + } + + .globalrank { + font: normal normal normal 14px/19px Segoe UI; + } + + .bold { + font-size: 14px; + font-weight: 800; + } + } + + .myrank { + margin-top: 5%; + color: #fff; + width: 60px; + margin-left: auto; + margin-right: auto; + font-weight: 600; + } + + .Countscale { + text-align: center; + color: white; + font-size: 16px; + font-weight: 600; + + #star { + color: #f3ca3e; + vertical-align: bottom; + margin-right: 4px; + } + + & { + color: white; + } + } + + .Count { + text-align: center; + font-size: 12px; + color: white; + } + + .bc-btn, + .bc-btn:active { + display: block; + margin: 4rem auto; + font: normal normal 700 14px/20px Segoe UI; + background-color: #6264a7; + color: #fff; + border-color: #6264a7; + } + + .bc-btn:active { + background-color: #6264a7 + } + + .bc-form { + margin: 1rem; + border: 1px solid white; + border-radius: 6px; + padding: 1rem; + + .bc-label { + color: white; + } + + .sub-btn { + text-align: center; + margin: 1rem auto; + display: block; + background-color: #464775; + color: #fff; + border-color: #464775; + font: normal normal 700 14px/20px Segoe UI; + } + } + } + + .gtcLabel { + margin-top: 2%; + } + + .content-tab { + width: 100vw; + min-height: 100vh; + margin: 1% 4% 2%; + + .ClbPath { + display: inline-flex; + margin-bottom: 2%; + text-align: center; + letter-spacing: 0px; + color: #33344A; + opacity: 1; + + .backImg { + width: 7px; + height: 12px; + margin-right: 10px; + margin-top: 6px; + } + + .backLabel { + padding-right: 10px; + cursor: pointer; + font: normal normal bold 14px/24px Segoe UI; + } + + .ClbBorder { + width: 0px; + height: 14px; + border: 1px solid #33344A; + margin-top: 5px; + } + + .ClbLabel { + padding-left: 10px; + font: normal normal bold 18px/23px Segoe UI; + } + } + + .location-icon { + vertical-align: bottom; + height: 34px; + width: auto; + padding: 0rem 0.2rem; + } + + .paddingTop { + padding-top: 4%; + } + + .gtc { + color: #000; + font: normal normal normal 18px/24px Segoe UI; + } + + .gtc-cards { + margin: 0 2%; + + .cards { + width: 160px; + height: 160px; + background: #F4F4F8 0% 0% no-repeat padding-box; + box-shadow: 3px 6px 12px #00000029; + margin: 2% 5% 0 0; + border-radius: 6px; + + img { + display: block; + position: relative; + float: right; + height: 50px; } - .pointcircle { - border-radius: 50%; - background-color: #7577b9; - border: 4px solid #464775; - box-shadow: 0 0 0 3px #fff; - background: #7577b9 0% 0% no-repeat padding-box; - width: 153px; - height: 154px; - border-radius: 102px; - margin-top: 2.2rem; - display: block; - margin-left: auto; - margin-right: auto; - .insidecircle { - text-align: center; - color: white; - margin: 1rem; - .pointsscale { - text-align: center; - .yellowStar { - color: #ffc206; - width: 25px; - height: 23px; - } - } - } - .score { - font-weight: 800; - font-size: 14px; - margin: auto; - } - .monthlypoints { - font-size: 14px; - } - .line { - height: 1px; - width: auto; - background-color: #39f; - margin: 0.5rem; - } - .globalrank { - font: normal normal normal 14px/19px Segoe UI; - } - .bold { - font-size: 14px; - font-weight: 800; - } + + .rank { + position: relative; + top: 9%; + left: 85%; + transform: translate(0%, 0%); + font: normal normal bold 9px/12px Segoe UI; + color: #464775; } - .myrank { - margin-top: 5%; - color: #fff; - width: 60px; - margin-left: auto; - margin-right: auto; - font-weight: 600; + + .profile-img { + margin: -2rem 2.5rem; + height: 85px; + width: 84px; + border-radius: 50%; + border: 4px solid #7d7ebc; } - .Countscale { - text-align: center; - color: white; - font-size: 16px; - font-weight: 600; - #star { - color: #f3ca3e; - vertical-align: bottom; - margin-right: 4px; - } - & { - color: white; - } + + .gtc-name { + text-align: center; + color: #464775; + margin-top: 5.7rem; + font-weight: 600; + width: auto; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + padding: 0 0.5rem; } - .Count { - text-align: center; - font-size: 12px; - color: white; + + .gtc-name2 { + text-align: center; + color: #464775; + margin-top: 5.7rem; + font-weight: 600; + width: auto; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + padding: 0 0.5rem; } - .bc-btn, .bc-btn:active { - display: block; - margin: 4rem auto; - font: normal normal 700 14px/20px Segoe UI; - background-color: #6264a7; - color: #fff; - border-color: #6264a7; + + .gtc-star { + text-align: center; + color: white; + font-weight: 600; + + #count { + margin-right: 2px; + color: #f3ca3e; + } + + .chat-icon { + display: inline-flex; + position: relative; + float: left; + margin: 0 0.5rem; + cursor: pointer; + color: #464775; + font-size: 20px; + } + + .mail-icon { + float: right; + color: #464775; + font-size: 20px; + margin: 0 0.5rem; + cursor: pointer; + } + + .totalPoints { + color: #464775; + padding-left: 2px; + } } - .bc-btn:active { - background-color: #6264a7 + } + + .top-card { + height: 175px; + bottom: 0.8rem; + position: relative; + } + } + + .table-content { + margin: 1rem 0rem; + position: relative; + overflow: auto; + + .topChampCards { + border: 0 !important; + border-radius: 0 !important; + } + + .gttc-row-left { + cursor: pointer; + width: 74%; + float: left; + padding-top: 1px; + background-color: #BDBDE6; + } + + .gttc-row-right { + cursor: pointer; + float: right; + width: 26%; + background-color: #6264A7; + padding: 8px 8px 6.5px 8px; + color: #fff; + } + + .gttc-img { + height: 31px; + width: 31px; + border-radius: 50%; + display: inline; + margin: 5px; + } + + .gttc-img-name { + font-size: 14px; + color: #000; + display: inline-block; + padding-left: 0.5rem; + position: relative; + width: 30%; + vertical-align: middle; + } + + .gttc-star { + font-weight: 600; + display: inline-block; + vertical-align: middle; + width: 45%; + + #count2 { + color: #f3ca3e; + margin-right: 2px; } - .bc-form { - margin: 1rem; - border: 1px solid white; - border-radius: 6px; - padding: 1rem; - .bc-label { - color: white; - } - .sub-btn { - text-align: center; - margin: 1rem auto; - display: block; - background-color: #464775; - color: #fff; - border-color: #464775; - font: normal normal 700 14px/20px Segoe UI; - } + + .points { + padding-left: 5px; + vertical-align: top; } - .back-btn { - margin-top: 5%; - color: #fff; - width: 60px; - margin-left: auto; - margin-right: auto; - :hover { - cursor: pointer; - } - .backText { - padding-left: 2px; - position: relative; - top: -2px; - } - .back { - position: relative; - margin: auto; - display: block; - } + } + + .vline { + height: 1.6rem; + width: 2px; + background: #f5f5f5; + display: inline-block; + margin-right: 10px; + vertical-align: middle; + } + + .gttc-rank { + text-align: center; + font-size: 14px; + display: inline; + padding: 0; + vertical-align: middle; + } + + .card-header { + padding: 0 !important; + margin-bottom: 5px; + border: 0 !important; + } + + .card-body { + width: 70%; + } + + .table { + margin-bottom: 0 !important; + + // color: white; + .td { + border: none !important; } + } + + .countHeader { + text-align: center; + } + + .eventTypeCol { + width: 100%; + } + + .gttc-tap-data { + text-align: center; + } + + .you { + display: inline-block; + padding: 0 0.2rem; + color: white; + } } - .gtcLabel{ - margin-top: 2%; + + /* width */ + ::-webkit-scrollbar { + width: 5px; } - .content-tab { - width: 100vw; - min-height: 100vh; - margin: 2% 4%; - .location-icon { - vertical-align: bottom; - height: 34px; - width: auto; - padding: 0rem 0.2rem; - } - .paddingTop { - padding-top: 4%; - } - .gtc { - color: #000; - font: normal normal normal 18px/24px Segoe UI; - } - .gtc-cards { - margin: 0 2%; - .cards { - width: 160px; - height: 160px; - background: #F4F4F8 0% 0% no-repeat padding-box; - box-shadow: 3px 6px 12px #00000029; - margin: 2% 5% 0 0; - border-radius: 6px; - img { - display: block; - position: relative; - float: right; - height: 50px; - } - .rank { - position: relative; - top: 9%; - left: 85%; - transform: translate(0%, 0%); - font: normal normal bold 9px/12px Segoe UI; - color: #464775; - } - .profile-img { - margin: -2rem 2.5rem; - height: 85px; - width: 84px; - border-radius: 50%; - border: 4px solid #7d7ebc; - } - .gtc-name { - text-align: center; - color: #464775; - margin-top: 5.7rem; - font-weight: 600; - width: auto; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - padding: 0 0.5rem; - } - .gtc-name2 { - text-align: center; - color: #464775; - margin-top: 5.7rem; - font-weight: 600; - width: auto; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - padding: 0 0.5rem; - } - .gtc-star { - text-align: center; - color: white; - font-weight: 600; - #count { - margin-right: 2px; - color: #f3ca3e; - } - .chat-icon { - display: inline-flex; - position: relative; - float: left; - margin: 0 0.5rem; - cursor: pointer; - color: #464775; - font-size: 20px; - } - .mail-icon { - float: right; - color: #464775; - font-size: 20px; - margin: 0 0.5rem; - cursor: pointer; - } - .totalPoints{ - color: #464775; - padding-left: 2px; - } - } - } - .top-card { - height: 175px; - bottom: 0.8rem; - position: relative; - } - } - .table-content { - margin: 1rem 0rem; - position: relative; - overflow: auto; - .topChampCards { - border: 0 !important; - border-radius: 0 !important; - } - .gttc-row-left { - cursor: pointer; - width: 74%; - float: left; - padding-top: 1px; - background-color: #BDBDE6; - } - .gttc-row-right { - cursor: pointer; - float: right; - width: 26%; - background-color: #6264A7; - padding: 8px 8px 6.5px 8px; - color: #fff; - } - .gttc-img { - height: 31px; - width: 31px; - border-radius: 50%; - display: inline; - margin: 5px; - } - .gttc-img-name { - font-size: 14px; - color: #000; - display: inline-block; - padding-left: 0.5rem; - position: relative; - width: 30%; - vertical-align: middle; - } - .gttc-star { - font-weight: 600; - display: inline-block; - vertical-align: middle; - width: 45%; - #count2 { - color: #f3ca3e; - margin-right: 2px; - } - .points { - padding-left: 5px; - vertical-align: top; - } - } - .vline { - height: 1.6rem; - width: 2px; - background: #f5f5f5; - display: inline-block; - margin-right: 10px; - vertical-align: middle; - } - .gttc-rank { - text-align: center; - font-size: 14px; - display: inline; - padding: 0; - vertical-align: middle; - } - .card-header { - padding: 0 !important; - margin-bottom: 5px; - border: 0 !important; - } - .card-body { - width: 70%; - } - .table { - margin-bottom: 0 !important; - // color: white; - .td { - border: none !important; - } - } - .countHeader { - text-align: center; - } - .eventTypeCol { - width: 100%; - } - .gttc-tap-data { - text-align: center; - } - .you { - display: inline-block; - padding: 0 0.2rem; - color: white; - } - } - /* width */ - ::-webkit-scrollbar { - width: 5px; - } - /* Track */ - ::-webkit-scrollbar-track { - background: #f1f1f1; - } + /* Track */ + ::-webkit-scrollbar-track { + background: #f1f1f1; + } + + /* Handle */ + ::-webkit-scrollbar-thumb { + background: #888; + } + + /* Handle on hover */ + ::-webkit-scrollbar-thumb:hover { + background: #555; + } + + .pivotControl { + div[role="tablist"] { + button { + width: 33.33%; - /* Handle */ - ::-webkit-scrollbar-thumb { - background: #888; + div { + display: inline; + } } + } - /* Handle on hover */ - ::-webkit-scrollbar-thumb:hover { - background: #555; + button { + height: 51px; + cursor: pointer; + text-align: center; + overflow: hidden; + text-overflow: ellipsis; + font: normal normal 600 18px/24px Segoe UI; + margin-right: 0px; + color: #000000; + background: #dedef3 0% 0% no-repeat padding-box; + + &:hover { + background-color: lightgrey; + border-color: lightgrey; } - .pivotControl { - div[role="tablist"] { - button { - width: 33.33%; - div { - display: inline; - } - } - } - button { - height: 51px; - cursor: pointer; - text-align: center; - overflow: hidden; - text-overflow: ellipsis; - font: normal normal 600 18px/24px Segoe UI; - margin-right: 0px; - color: #000000; - background: #dedef3 0% 0% no-repeat padding-box; - &:hover { - background-color: lightgrey; - border-color: lightgrey; - } - &[aria-selected="true"] { - background: #6264a7 0% 0% no-repeat padding-box; - color: #fff; - fill: #fff; - } - } + + &[aria-selected="true"] { + background: #6264a7 0% 0% no-repeat padding-box; + color: #fff; + fill: #fff; } + } } - .loader { - border: 16px solid #dde2eb; - border-radius: 50%; - border-top: 10px solid #3498db; - width: 120px; - height: 120px; - -webkit-animation: spin 2s linear infinite; - animation: spin 2s linear infinite; - margin: auto; - left: 0; - right: 0; - top: 0; - bottom: 0; - position: fixed; + } + + .loader { + border: 16px solid #dde2eb; + border-radius: 50%; + border-top: 10px solid #3498db; + width: 120px; + height: 120px; + -webkit-animation: spin 2s linear infinite; + animation: spin 2s linear infinite; + margin: auto; + left: 0; + right: 0; + top: 0; + bottom: 0; + position: fixed; + } + + @keyframes spin { + 0% { + transform: rotate(0deg); } - @keyframes spin { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } + + 100% { + transform: rotate(360deg); } + } } diff --git a/src/webparts/clbHome/scss/Championview.scss b/src/webparts/clbHome/scss/Championview.scss index d7c43281..67c68c7e 100644 --- a/src/webparts/clbHome/scss/Championview.scss +++ b/src/webparts/clbHome/scss/Championview.scss @@ -1,153 +1,181 @@ .Championview { - .main { - width: 100%; - height: 100%; - // background-color: rgba(158, 187, 208, 0.5); - .ag-theme-alpine > div > div { - background-color: #fff; - } - .loader { - border: 16px solid #dde2eb; - border-radius: 50%; - border-top: 10px solid #3498db; - width: 120px; - height: 120px; - -webkit-animation: spin 2s linear infinite; /* Safari */ - animation: spin 2s linear infinite; - margin: auto; - top: 25%; - transform: translate(-50%, -50%); - position: relative; - } - - /* Safari */ - @-webkit-keyframes spin { - 0% { - -webkit-transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(360deg); - } - } - - @keyframes spin { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } - } - } - .eventsCards { - border: 0 !important; - border-radius: 0 !important; - margin-bottom: 10px; - } - .cv { - color: #163655; - text-align: center; - font-size: 20px; - padding: 1rem 0; - font-weight: bold; - } - .cvW { - width: 100%; - } - .form-fields { - width: auto; - height: auto; - border-radius: 6px; - color: #000; - font: normal normal normal 18px/24px Segoe UI; - .form-data { - .form-group { - background-color: #F6F5F4; - padding: 10px; - } - } - } - .cb { - background-color: #fff; - .float-end { - float: right; - margin: 1rem 0; - .ms-Button-flexContainer { - height: auto !important; - } - } - .AddEventIcon { - font-size: 24px; - color: #6264A7; - cursor: pointer; - } - .zeroPadding { - padding-right: 0!important; - padding-left: 0!important; - } - .tickImage { - width: 25px; - height: 25px; - margin-bottom: 1%; - } - .eventDetails { - margin-right: 10%; - } - .deleteEvent { - font-size: 24px; - color: #979593; - cursor: pointer; - float: right; - margin-top: 2px; - } - .helpText { - font: normal normal normal 16px/21px Segoe UI; - color: #979593; - padding-left: 8px; - } - .btnSubmit { - background-color: #6264a7; - color: #fff; - } - .errorMessage { - color: #ED2939; - font: normal normal 600 18px/24px Segoe UI; - letter-spacing: 0px; - opacity: 1; - } - .MuiDataGrid-root{ - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - } - .MuiDataGrid-root .MuiDataGrid-columnsContainer { - background-color: #F6F5F4; - color: #000; - font: normal normal 600 18px/24px Segoe UI; - } - } - .cursor { - cursor: pointer; - background-color: #BDBDE6; - color: #000; - } - - /* width */ - ::-webkit-scrollbar { - width: 5px; - height: 5px; - } - - /* Track */ - ::-webkit-scrollbar-track { - background: #f1f1f1; - } - - /* Handle */ - ::-webkit-scrollbar-thumb { - background: #888; - } - - /* Handle on hover */ - ::-webkit-scrollbar-thumb:hover { - background: #555; + .main { + width: 100%; + height: 100%; + + // background-color: rgba(158, 187, 208, 0.5); + .ag-theme-alpine>div>div { + background-color: #fff; + } + + .loader { + border: 16px solid #dde2eb; + border-radius: 50%; + border-top: 10px solid #3498db; + width: 120px; + height: 120px; + -webkit-animation: spin 2s linear infinite; + /* Safari */ + animation: spin 2s linear infinite; + margin: auto; + top: 25%; + transform: translate(-50%, -50%); + position: relative; + } + + /* Safari */ + @-webkit-keyframes spin { + 0% { + -webkit-transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(360deg); + } + } + + @keyframes spin { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } + } + } + + .eventsCards { + border: 0 !important; + border-radius: 0 !important; + margin-bottom: 10px; + } + + .cv { + color: #163655; + text-align: center; + font-size: 20px; + padding: 1rem 0; + font-weight: bold; + } + + .cvW { + width: 100%; + } + + .form-fields { + width: auto; + height: auto; + border-radius: 6px; + color: #000; + font: normal normal normal 18px/24px Segoe UI; + + .form-data { + .form-group { + background-color: #F6F5F4; + padding: 10px; + } + } + } + + .cb { + background-color: #fff; + + .float-end { + float: right; + margin: 1rem 0; + + .ms-Button-flexContainer { + height: auto !important; + } + } + + .AddEventIcon { + font-size: 24px; + color: #6264A7; + cursor: pointer; + } + + .row-margin { + margin-top: 1.5rem !important; } + + .tick { + position: relative; + left: 10px; + bottom: 4px; + } + + .tickImage { + width: 25px; + height: 25px; + } + + .eventDetails { + margin-right: 10%; + } + + .deleteEvent { + font-size: 24px; + color: #979593; + cursor: pointer; + float: right; + line-height: 22px; + } + + .helpText { + font: normal normal normal 16px/21px Segoe UI; + color: #979593; + padding-left: 8px; + } + + .btnSubmit { + background-color: #6264a7; + color: #fff; + } + + .errorMessage { + color: #ED2939; + font: normal normal 600 18px/24px Segoe UI; + letter-spacing: 0px; + opacity: 1; + } + + .MuiDataGrid-root { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + } + + .MuiDataGrid-root .MuiDataGrid-columnsContainer { + background-color: #F6F5F4; + color: #000; + font: normal normal 600 18px/24px Segoe UI; + } + } + + .cursor { + cursor: pointer; + background-color: #BDBDE6; + color: #000; + } + + /* width */ + ::-webkit-scrollbar { + width: 5px; + height: 5px; + } + + /* Track */ + ::-webkit-scrollbar-track { + background: #f1f1f1; + } + + /* Handle */ + ::-webkit-scrollbar-thumb { + background: #888; + } + + /* Handle on hover */ + ::-webkit-scrollbar-thumb:hover { + background: #555; + } } diff --git a/src/webparts/clbHome/scss/TOTLeaderBoard.module.scss b/src/webparts/clbHome/scss/TOTLeaderBoard.module.scss index 085f1495..edfca69f 100644 --- a/src/webparts/clbHome/scss/TOTLeaderBoard.module.scss +++ b/src/webparts/clbHome/scss/TOTLeaderBoard.module.scss @@ -52,6 +52,7 @@ text-align: justify; font: normal normal normal 18px/24px Segoe UI; -webkit-font-smoothing: antialiased; + overflow-wrap: anywhere; } .totLeaderboardPath { @@ -136,7 +137,18 @@ .contentArea { margin-left: 0.5%; } +.dropdownArea { + margin-left: 1%; + margin-top: 2%; +} +.dropdownCol { + padding: 0.5em; +} + +.labelCol { + margin-top: auto; +} .backBtn { background-color: #fff !important; color: #000 !important; diff --git a/src/webparts/clbHome/scss/TOTMyDashBoard.module.scss b/src/webparts/clbHome/scss/TOTMyDashBoard.module.scss index dbb8d735..75bf53cd 100644 --- a/src/webparts/clbHome/scss/TOTMyDashBoard.module.scss +++ b/src/webparts/clbHome/scss/TOTMyDashBoard.module.scss @@ -48,6 +48,7 @@ text-align: justify; font: normal normal normal 18px/24px Segoe UI; -webkit-font-smoothing: antialiased; + overflow-wrap: anywhere; } .subHeaderUnderline { @@ -128,6 +129,18 @@ margin-left: 1%; } +.dropdownArea { + margin-left: 1%; + margin-top: 2%; +} + +.dropdownCol { + padding: 0.5em; +} + +.labelCol { + margin-top: auto; +} .contentTab { width: 100vw; min-height: 100vh; diff --git a/teams/TeamsSPFxApp.zip b/teams/TeamsSPFxApp.zip index 0bed1fed..4b63e9c1 100644 Binary files a/teams/TeamsSPFxApp.zip and b/teams/TeamsSPFxApp.zip differ diff --git a/teams/manifest.json b/teams/manifest.json index 66c7c72b..29ebf234 100644 --- a/teams/manifest.json +++ b/teams/manifest.json @@ -3,7 +3,7 @@ "manifestVersion": "1.11", "packageName": "CMPHomeWebPart", "id": "6df47bd5-d84a-41ab-8c4a-9352076e8b6c", - "version": "2.0.0.0", + "version": "2.1.0.0", "developer": { "name": "Teams Customer Advocacy Group", "websiteUrl": "https://products.office.com/en-us/sharepoint/collaboration", diff --git a/tsconfig.json b/tsconfig.json index 0f163570..0a3426d2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -27,7 +27,8 @@ "lib": [ "es5", "dom", - "es2015.collection" + "es2015.collection", + "es2016" ] }, "include": [
People NameRegionCountryFocus AreaGroup{LocaleStrings.PeopleNameGridHeader}{LocaleStrings.RegionGridHeader}{LocaleStrings.CountryGridHeader}{LocaleStrings.FocusAreaGridHeader}{LocaleStrings.GroupGridHeader}Status