Widget Missing Information

joshuahigha
Tera Contributor

Hi, 

 

I am trying to create a widget on the Employee Centre to show a list of Services that are available to the user. Within the filter you should be able to filter on the Portfolio associated to the Service and it only shows services within that Portfolio to the user. 

When a user then selects the Service, it will display all the information associated to that service.

 

I have the services there and the portfolios on the filter however the list is empty whenever a portfolio is selected, despite the relationship being correct. 

 

And when I select the service the Service Offerings related to the Service & the Portfolio Owner are not being populated

 

Any advice would be greatly appreciated. 

 

HTML

 

 

<div class="container">
  <h3 class="text-center">Business Services We Offer</h3>

  <div class="row mb-3">
    <div class="col-md-6">
      <input type="text" class="form-control"
             placeholder="Search by name or description..."
             ng-model="searchTerm" ng-change="filterOfferings()" />
    </div>

    <!-- Portfolio Filter -->
    <div class="col-md-6">
      <select class="form-control"
              ng-model="selectedPortfolio"
              ng-change="filterOfferings()">
        <option value="">All Portfolios</option>
        <option ng-repeat="portfolio in portfolios"
                value="{{portfolio.sys_id}}">
          {{portfolio.name}}
        </option>
      </select>
    </div>
  </div>

  <div class="list-group" ng-if="!selectedOffering">
    <a ng-repeat="offering in filteredOfferings"
       class="list-group-item list-group-item-action"
       ng-click="showDetails(offering)">
      <h5 class="mb-1">{{offering.name}}</h5>
      <p class="mb-1"><strong>Service Owner: </strong>{{offering.owned_by.display_value}}</p>
    </a>
    <p ng-if="filteredOfferings.length === 0" class="text-center mt-3">
      No offerings match your criteria.
    </p>
  </div>
  
  
  <!-- Offering Details View -->
  <div class="card mt-3" ng-if="selectedOffering">
    <div class="card-header">
      <h4>{{selectedOffering.name}}</h4>
      <button class="btn btn-sm btn-secondary float-end" ng-click="closeDetails()">Close</button>
    </div>
    <div class="card-body">
      <p><strong>Short Description:</strong> {{selectedOffering.short_description || 'No description available.'}}</p>
      <p><strong>Description:</strong> {{selectedOffering.description || 'No description available.'}}</p>
      <p><strong>Portfolio:</strong> {{selectedOffering.spm_service_portfolio.display_value || 'N/A'}}</p>
      <p><strong>Service Owner:</strong> {{selectedOffering.owned_by.display_value || 'Unassigned'}}</p>
      <p><strong>Portfolio Owner:</strong> {{selectedOffering.spm_service_portfolio.owner.display_value || 'Unassigned'}}</p>
      <p><strong>Offerings:</strong> <span ng-repeat="off in selectedOffering.offerings">{{offerings.display_value}}{{$last ? '' : ', '}}</span>
    </div>
  </div>
</div>

 

 

 

Client Script

 

 

function ($scope, $http) {
  $scope.offerings = [];
  $scope.filteredOfferings = [];
  $scope.searchTerm = '';
  $scope.selectedPortfolio = ''; // Selected portfolio
  $scope.selectedOffering = null;

  // Fetch portfolios
  $scope.portfolios = [];
  $http.get('/api/now/table/spm_service_portfolio?sysparm_fields=sys_id,name&sysparm_query=active=true')
    .then(function (response) {
      $scope.portfolios = response.data.result;
    })
    .catch(function (error) {
      console.error('Error fetching portfolios:', error);
    });

  // Fetch active business services with portfolio information
function fetchOfferings() {
  const query = 'active=true';
  const url = `/api/now/table/cmdb_ci_service_business?sysparm_query=${query}` +
            `&sysparm_display_value=true&` +
            `sysparm_fields=sys_id,name,short_description,spm_service_portfolio,` +
            `spm_service_portfolio.owner,description,assigned_to,offerings,owned_by`;

  $http.get(url)
    .then(function (response) {
      console.log('Service Offerings:', response.data.result); // Log the offerings
      if (!response.data.result || response.data.result.length === 0) {
        console.warn('No offerings returned from API.');
      }
      $scope.offerings = response.data.result;
      $scope.filteredOfferings = angular.copy($scope.offerings);
      $scope.filterOfferings();  // Ensure initial filtering happens
    })
    .catch(function (error) {
      console.error('Error fetching service offerings:', error);
    });
}

  // Initial fetch of offerings
  fetchOfferings();

  // Filter offerings based on portfolio and search term
$scope.filterOfferings = function () {
  console.log('Current selected portfolio:', $scope.selectedPortfolio);
  console.log('Current search term:', $scope.searchTerm);
  console.log('Total offerings before filtering:', $scope.offerings.length);

  $scope.filteredOfferings = $scope.offerings.filter(function (offering) {
    console.log('Checking offering:', offering);

    // Ensure spm_service_portfolio is defined
    const portfolioMatch = $scope.selectedPortfolio
      ? offering.spm_service_portfolio && offering.spm_service_portfolio.sys_id === $scope.selectedPortfolio
      : true;

    console.log('Portfolio match for offering:', offering.name, 'is', portfolioMatch);

    const searchMatch = $scope.searchTerm
      ? offering.name.toLowerCase().includes($scope.searchTerm.toLowerCase()) ||
        (offering.short_description || '').toLowerCase().includes($scope.searchTerm.toLowerCase())
      : true;

    console.log('Search match for offering:', offering.name, 'is', searchMatch);

    return portfolioMatch && searchMatch;
  });

  console.log('Filtered Offerings:', $scope.filteredOfferings);
};

  // Display detailed information about a selected offering
  $scope.showDetails = function (offering) {
    console.log('Selected Offering:', offering);
    $scope.selectedOffering = offering;
  };

  // Close the details view
  $scope.closeDetails = function () {
    $scope.selectedOffering = null;
  };
}

 

 

 

 

0 REPLIES 0