JSFiddle - React, Tailwind, and code Playground

HTML

<div ng-app="app">
  <table ng-controller="MainController as mainController" border="1">
    <tr>
      <th ng-repeat="a in mainController.getActivityCells()" ng-controller="ActivityCellController as ctrl">
        {{ctrl.getText()}}
      </th>            
    </tr>
    <tr>
      <th ng-repeat="t in mainController.getTaskCells()" ng-controller="TaskCellController as ctrl">
        {{ctrl.getText()}}
      </th>
    </tr>
    <tr ng-repeat-start="r in mainController.getReleaseRows()">
      <th colspan="{{mainController.getTaskCells().length}}">
        {{r.id}}
      </th>        
    </tr>
    <tr ng-repeat-end>
      <td ng-repeat="t in mainController.getTaskCells()">
        <ul>
          <li ng-repeat="s in r.storiesByTasks[t.id]">
            {{s.id}}
          </li>
        </ul>
      </td>
    </tr>
    </table>    
</div>

CSS

th { width: 70px }

JavaScript

angular.module('app', [])
// contrôleur chargé de l'affichage d'une cellule Activity
.controller("ActivityCellController", ["$scope", function($scope) {
	var a = $scope.a;
  this.getText = function() {
  	return a.id != null ? a.id : "";
  }
}])
// contrôleur chargé de l'affichage d'une cellule Task
.controller("TaskCellController", ["$scope", function($scope) {
	var t = $scope.t;
  this.getText = function() {
  	return t.id != null ? t.id : "";
  }
}])
// contrôleur principal
.controller("MainController", ["DataStore", "$scope", function(DataStore, $scope) {
	var vm = this;
  var activityCells = [];
  vm.getActivityCells = function() {
  	return activityCells;
  };
  var taskCells = [];
  vm.getTaskCells = function() {
  	return taskCells;
  };
  vm.getReleaseRows = function() {
    return DataStore.rootData.releases;
  };
  $scope.$watchCollection(function () {
  	return DataStore.rootData.activities;
  }, updateActivityCells);
  function updateActivityCells() {
  	angular.copy([], activityCells);
    angular.copy([], taskCells);
  	angular.forEach(DataStore.rootData.activities, function(a) {
    	activityCells.push(a);
      angular.forEach(a.tasks, function(t, index) {
      	if (index > 0) {
        	activityCells.push({});
        }
        taskCells.push(t);
      });
      if (a.tasks.length == 0) {
      	taskCells.push({});
      }
      $scope.$watchCollection(function () {
      	return a.tasks;
      }, function (n,o) {
      	if (n !== o) {
        	updateActivityCells();
        }
      });
    });
  }
}])
.service("DataStore", [function() {
	this.rootData = {
  	activities: [
    	{
      	id: 'A1',
        tasks: [
        	{
          	id: 'A1.T1'
          },
        	{
          	id: 'A1.T2'
          }
        ]
      },
    	{
      	id: 'A2',
        tasks: [
        	{
          	id: 'A2.T1'
          }
        ]
      },
      {
      	id: 'A3',
        tasks: []
      }
    ],
    releases: [
    	{
      	id : 'R1',
       ...