Initial dashboard work
This commit is contained in:
parent
61ab341e33
commit
2bc585f205
166
src/api/controllers/Dashboard.php
Normal file
166
src/api/controllers/Dashboard.php
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
<?php
|
||||||
|
use RedBeanPHP\R;
|
||||||
|
|
||||||
|
class Dashboard extends BaseController {
|
||||||
|
|
||||||
|
public function getMyBoardInfo($request, $response) {
|
||||||
|
$status = $this->secureRoute($request, $response, SecurityLevel::USER);
|
||||||
|
if ($status !== 200) {
|
||||||
|
return $this->jsonResponse($response, $status);
|
||||||
|
}
|
||||||
|
|
||||||
|
$boards = $this->loadAllBoards($request);
|
||||||
|
|
||||||
|
if (!count($boards)) {
|
||||||
|
$this->apiJson->addAlert('info', $this->strings->api_noBoards);
|
||||||
|
|
||||||
|
return $this->jsonResponse($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->apiJson->setSuccess();
|
||||||
|
$this->apiJson->addData($this->convertBoardData($boards));
|
||||||
|
|
||||||
|
return $this->jsonResponse($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMyTaskInfo($request, $response) {
|
||||||
|
$status = $this->secureRoute($request, $response, SecurityLevel::USER);
|
||||||
|
if ($status !== 200) {
|
||||||
|
return $this->jsonResponse($response, $status);
|
||||||
|
}
|
||||||
|
|
||||||
|
$boards = $this->loadAllBoards($request);
|
||||||
|
|
||||||
|
if (!count($boards)) {
|
||||||
|
$this->apiJson->addAlert('info', $this->strings->api_noBoards);
|
||||||
|
|
||||||
|
return $this->jsonResponse($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->apiJson->setSuccess();
|
||||||
|
$userId = Auth::GetUserId($request);
|
||||||
|
$this->apiJson->addData($this->convertTaskData($boards, $userId));
|
||||||
|
|
||||||
|
return $this->jsonResponse($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function convertBoardData($boards) {
|
||||||
|
$retVal = [];
|
||||||
|
|
||||||
|
foreach($boards as $board) {
|
||||||
|
if ($board["is_active"] !== '1') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$retVal[] = (object)array(
|
||||||
|
"id" => $board["id"],
|
||||||
|
"name" => $board["name"],
|
||||||
|
"columns" => [],
|
||||||
|
"categories" => []
|
||||||
|
);
|
||||||
|
|
||||||
|
$index = count($retVal) - 1;
|
||||||
|
|
||||||
|
foreach($board["ownColumn"] as $column) {
|
||||||
|
$retVal[$index]->columns[] = (object)array(
|
||||||
|
"name" => $column["name"],
|
||||||
|
"tasks" => count($column["ownTask"])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($board["ownCategory"] as $category) {
|
||||||
|
$retVal[$index]->categories[] = (object)array(
|
||||||
|
"name" => $category["name"],
|
||||||
|
"tasks" => count($category["sharedTask"])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function convertTaskData($boards, $userId) {
|
||||||
|
$retVal = [];
|
||||||
|
|
||||||
|
foreach($boards as $board) {
|
||||||
|
foreach($board["ownColumn"] as $column) {
|
||||||
|
foreach($column["ownTask"] as $task) {
|
||||||
|
$isMine = false;
|
||||||
|
|
||||||
|
foreach($task["sharedUser"] as $assignee) {
|
||||||
|
if ($assignee["id"] === (string)$userId) {
|
||||||
|
$isMine = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$isMine) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$attachments = R::exec(
|
||||||
|
"SELECT COUNT(id) AS num FROM attachment WHERE task_id = ?",
|
||||||
|
[ $task["id"] ]
|
||||||
|
);
|
||||||
|
|
||||||
|
$comments = R::exec(
|
||||||
|
"SELECT COUNT(id) AS num FROM comment WHERE task_id = ?",
|
||||||
|
[ $task["id"] ]
|
||||||
|
);
|
||||||
|
|
||||||
|
$retVal[] = (object)array(
|
||||||
|
"board" => $board["name"],
|
||||||
|
"board_id" => $board["id"],
|
||||||
|
"title" => $task["title"],
|
||||||
|
"color" => $task["color"],
|
||||||
|
"column" => $column["name"],
|
||||||
|
"date_due" => $task["due_date"],
|
||||||
|
"points" => $task["points"],
|
||||||
|
"attachments" => $attachments,
|
||||||
|
"comments" => $comments
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function loadAllBoards($request) {
|
||||||
|
$boards = [];
|
||||||
|
$boardBeans = R::findAll('board');
|
||||||
|
|
||||||
|
if (count($boardBeans)) {
|
||||||
|
foreach ($boardBeans as $bean) {
|
||||||
|
if (Auth::HasBoardAccess($request, $bean->id)) {
|
||||||
|
$this->cleanBoard($bean);
|
||||||
|
$boards[] = $bean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return R::exportAll($boards);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function cleanBoard(&$board) {
|
||||||
|
foreach ($board->sharedUserList as $user) {
|
||||||
|
$user = $this->cleanUser($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($board->xownColumnList as $column) {
|
||||||
|
foreach ($column->xownTaskList as $task) {
|
||||||
|
foreach ($task->sharedUserList as $user) {
|
||||||
|
$user = $this->cleanUser($user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function cleanUser($user) {
|
||||||
|
unset($user->password_hash);
|
||||||
|
unset($user->active_token);
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -106,6 +106,9 @@ $app->post('/logout', 'Auth:logout'); // Unsecured (clears JWT)
|
|||||||
$app->post('/authenticate', 'Auth:authenticate'); // Unsecured (checks JWT)
|
$app->post('/authenticate', 'Auth:authenticate'); // Unsecured (checks JWT)
|
||||||
$app->post('/refresh', 'Auth:refreshToken'); // Unsecured (checks and updates JWT)
|
$app->post('/refresh', 'Auth:refreshToken'); // Unsecured (checks and updates JWT)
|
||||||
|
|
||||||
|
$app->get('/dashboard/boards', 'Dashboard:getMyBoardInfo'); // User (by board access)
|
||||||
|
$app->get('/dashboard/tasks', 'Dashboard:getMyTaskInfo'); // User (by board access)
|
||||||
|
|
||||||
$app->run();
|
$app->run();
|
||||||
R::close();
|
R::close();
|
||||||
|
|
||||||
|
@ -1,134 +1,72 @@
|
|||||||
<tb-top-nav page-name="Dashboard"></tb-top-nav>
|
<tb-top-nav page-name="{{ pageName }}"></tb-top-nav>
|
||||||
|
|
||||||
<div class="dashboard">
|
<div class="dashboard">
|
||||||
<section>
|
<tb-my-items [boardsLoading]="boardsLoading"
|
||||||
<h2>Boards and Tasks</h2>
|
[boards]="boards" [boardsMessage]="boardsMessage"
|
||||||
|
[tasksLoading]="tasksLoading" [tasks]="tasks"
|
||||||
<div class="row">
|
[tasksMessage]="tasksMessage"
|
||||||
<h3>My Boards</h3>
|
[strings]="strings"></tb-my-items>
|
||||||
|
|
||||||
<table class="alternating">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Board</th>
|
|
||||||
<th>Columns</th>
|
|
||||||
<th>Categories</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td><a href="#">Personal Projects</a></td>
|
|
||||||
<td>
|
|
||||||
To Do <span class="badge" title="Tasks in Column">8</span>
|
|
||||||
Doing <span class="badge" title="Tasks in Column">3</span>
|
|
||||||
Done <span class="badge" title="Tasks in Column">0</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
List <span class="badge" title="Tasks in Category">3</span>
|
|
||||||
Thing 1 <span class="badge" title="Tasks in Category">4</span>
|
|
||||||
Thing 2 <span class="badge" title="Tasks in Category">4</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><a href="#">TaskBoard</a></td>
|
|
||||||
<td>
|
|
||||||
Backlog <span class="badge" title="Tasks in Column">23</span>
|
|
||||||
Ready <span class="badge" title="Tasks in Column">5</span>
|
|
||||||
In Work <span class="badge" title="Tasks in Column">3</span>
|
|
||||||
Test <span class="badge" title="Tasks in Column">2</span>
|
|
||||||
Done <span class="badge" title="Tasks in Column">18</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
Front-End <span class="badge" title="Tasks in Category">19</span>
|
|
||||||
Back-End <span class="badge" title="Tasks in Category">28</span>
|
|
||||||
Test <span class="badge" title="Tasks in Category">2</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<h3>My Tasks</h3>
|
|
||||||
|
|
||||||
<table class="alternating">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Board</th>
|
|
||||||
<th>Task</th>
|
|
||||||
<th>Details</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td><a href="#">TaskBoard</a></td>
|
|
||||||
<td>
|
|
||||||
<a href="#">An Important Task</a>
|
|
||||||
<span class="badge" title="Task Color"
|
|
||||||
style="background-color:#debee8"> </span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="details">Column: <em>In Work</em></span>
|
|
||||||
<span class="details">Due: <em>12/31/2016</em></span>
|
|
||||||
<span class="details">Points: <em>8</em></span>
|
|
||||||
<span class="details">Attachments: <em>2</em></span>
|
|
||||||
<span class="details">Comments: <em>5</em></span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><a href="#">Personal Projects</a></td>
|
|
||||||
<td>
|
|
||||||
<a href="#">Make a List</a>
|
|
||||||
<span class="badge" title="Task Color"
|
|
||||||
style="background-color:#bee7f4"> </span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="details">Column: <em>To Do</em></span>
|
|
||||||
<span class="details">Comments: <em>2</em></span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2>Analytics</h2>
|
<h2>{{ strings['dashboard_analytics'] }}</h2>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<select>
|
<select [(ngModel)]="analyticsBoardId" (change)="updateAnalytics()">
|
||||||
<option>Select Board...</option>
|
<option [ngValue]="null">
|
||||||
|
{{ strings['boards_selectBoard'] }}...
|
||||||
|
</option>
|
||||||
|
|
||||||
|
<option *ngFor="let board of boards">
|
||||||
|
{{ board.name }}
|
||||||
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row" *ngIf="analyticsBoardId">
|
||||||
<h3>Task Burndown</h3>
|
<h3>Task Burndown</h3>
|
||||||
|
|
||||||
<label class="inline">Start Date: <input type="date"></label>
|
<label class="inline">
|
||||||
<label class="inline">End Date: <input type="date"></label>
|
Start Date:
|
||||||
|
<input type="date" [(ngModel)]="burndownDates.start"
|
||||||
|
(change)="validateDates()">
|
||||||
|
</label>
|
||||||
|
|
||||||
<tb-charts chart-name="chartBurndown" chart-type="line"
|
<label class="inline">
|
||||||
series="29,26,21,18,13,8,3"
|
End Date:
|
||||||
labels="12/31/2015,1/1/2016,1/2/2016,1/3/2016,1/4/2016,1/5/2016,1/6/2016"
|
<input type="date" [(ngModel)]="burndownDates.end"
|
||||||
table-head="Date"></tb-charts>
|
(change)="validateDates()">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div *ngIf="datesError.length" class="error">{{ datesError }}</div>
|
||||||
|
|
||||||
|
<div *ngIf="!showBurndown">Select dates to display burndown chart.</div>
|
||||||
|
|
||||||
|
<tb-charts *ngIf="showBurndown"
|
||||||
|
chart-name="chartBurndown" chart-type="line"
|
||||||
|
series="29,26,21,18,13,8,3"
|
||||||
|
labels="12/31/2015,1/1/2016,1/2/2016,1/3/2016,1/4/2016,1/5/2016,1/6/2016"
|
||||||
|
table-head="Date"></tb-charts>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row" *ngIf="analyticsBoardId">
|
||||||
<div class="half-page">
|
<div class="half-page">
|
||||||
<h3>Task Distribution by User</h3>
|
<h3>Task Distribution by User</h3>
|
||||||
|
|
||||||
<tb-charts chart-name="chartByUser" series="7,13,8,5"
|
<tb-charts chart-name="chartByUser" series="7,13,8,5"
|
||||||
labels="admin,tester,user,another"
|
labels="admin,tester,user,another"
|
||||||
table-head="User"></tb-charts>
|
table-head="User"></tb-charts>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="half-page">
|
<div class="half-page">
|
||||||
<h3>Task Distribution by Column</h3>
|
<h3>Task Distribution by Column</h3>
|
||||||
|
|
||||||
<tb-charts chart-name="chartByColumn" series="18,3,7"
|
<tb-charts chart-name="chartByColumn" series="18,3,7"
|
||||||
labels="To Do,Doing,Done"
|
labels="To Do,Doing,Done"
|
||||||
table-head="Column"></tb-charts>
|
table-head="Column"></tb-charts>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row" *ngIf="analyticsBoardId">
|
||||||
<tb-calendar board-id="1"></tb-calendar>
|
<tb-calendar board-id="1"></tb-calendar>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@ -1,17 +1,123 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||||
import { Title } from '@angular/platform-browser';
|
import { Title } from '@angular/platform-browser';
|
||||||
|
|
||||||
// import { Charts } from './charts/charts.component';
|
import { DashboardService } from './dashboard.service';
|
||||||
// import { Calendar } from './calendar/calendar.component';
|
import { StringsService } from '../shared/services';
|
||||||
|
|
||||||
|
interface BurndownDates {
|
||||||
|
start: string;
|
||||||
|
end: string;
|
||||||
|
|
||||||
|
startDate: Date;
|
||||||
|
endDate: Date;
|
||||||
|
}
|
||||||
|
|
||||||
/* istanbul ignore next */
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-dashboard',
|
selector: 'tb-dashboard',
|
||||||
templateUrl: './dashboard.component.html'
|
templateUrl: './dashboard.component.html'
|
||||||
})
|
})
|
||||||
export class DashboardComponent {
|
export class DashboardComponent implements OnInit, OnDestroy {
|
||||||
constructor(public title: Title) {
|
private subs: any[];
|
||||||
title.setTitle('TaskBoard - Dashboard');
|
|
||||||
|
public boards: any;
|
||||||
|
public boardsLoading: boolean;
|
||||||
|
public boardsMessage: string;
|
||||||
|
|
||||||
|
public tasks: any;
|
||||||
|
public tasksLoading: boolean;
|
||||||
|
public tasksMessage: string;
|
||||||
|
|
||||||
|
public strings: any;
|
||||||
|
public pageName: string;
|
||||||
|
|
||||||
|
public analyticsBoardId: number;
|
||||||
|
public burndownDates: BurndownDates;
|
||||||
|
public datesError: string;
|
||||||
|
|
||||||
|
get showBurndown() {
|
||||||
|
return this.burndownDates.start &&
|
||||||
|
this.burndownDates.end && !this.datesError.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(public title: Title,
|
||||||
|
private service: DashboardService,
|
||||||
|
public stringsService: StringsService) {
|
||||||
|
this.subs = [];
|
||||||
|
this.boardsLoading = true;
|
||||||
|
this.tasksLoading = true;
|
||||||
|
|
||||||
|
this.burndownDates = {
|
||||||
|
start: null,
|
||||||
|
end: null,
|
||||||
|
startDate: null,
|
||||||
|
endDate: null
|
||||||
|
};
|
||||||
|
this.datesError = '';
|
||||||
|
|
||||||
|
this.subs.push(
|
||||||
|
stringsService.stringsChanged.subscribe(newStrings => {
|
||||||
|
this.strings = newStrings;
|
||||||
|
|
||||||
|
title.setTitle('TaskBoard - ' + this.strings.dashboard);
|
||||||
|
this.pageName = this.strings.dashboard;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.service.getBoardInfo().subscribe(res => {
|
||||||
|
this.boards = res.data[1];
|
||||||
|
|
||||||
|
if (res.status === 'failure') {
|
||||||
|
this.boardsMessage = res.alerts[0].text;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.boardsLoading = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.service.getTaskInfo().subscribe(res => {
|
||||||
|
this.tasks = res.data[1];
|
||||||
|
|
||||||
|
if (res.status === 'failure') {
|
||||||
|
this.tasksMessage = res.alerts[0].text;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tasksLoading = false;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy() {
|
||||||
|
this.subs.forEach(sub => sub.unsubscribe());
|
||||||
|
}
|
||||||
|
|
||||||
|
validateDates() {
|
||||||
|
if (this.burndownDates.start === null || this.burndownDates.end === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.datesError = '';
|
||||||
|
|
||||||
|
this.burndownDates.startDate = new Date(this.burndownDates.start);
|
||||||
|
this.burndownDates.endDate = new Date(this.burndownDates.end);
|
||||||
|
|
||||||
|
const start = this.burndownDates.startDate.valueOf();
|
||||||
|
const end = this.burndownDates.endDate.valueOf();
|
||||||
|
const now = new Date().valueOf();
|
||||||
|
|
||||||
|
if (start > end) {
|
||||||
|
this.datesError = 'End date must be after start date.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start > now) {
|
||||||
|
this.datesError += ' Start date must be today or earlier.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end > now) {
|
||||||
|
this.datesError += ' End date must be today or earlier.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAnalytics() {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,26 +1,35 @@
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
|
||||||
import { SharedModule } from '../shared/shared.module';
|
import { SharedModule } from '../shared/shared.module';
|
||||||
|
|
||||||
import { DashboardComponent } from './dashboard.component';
|
|
||||||
import { CalendarComponent } from './calendar/calendar.component';
|
import { CalendarComponent } from './calendar/calendar.component';
|
||||||
import { ChartsComponent } from './charts/charts.component';
|
import { ChartsComponent } from './charts/charts.component';
|
||||||
|
import { DashboardComponent } from './dashboard.component';
|
||||||
|
import { DashboardService } from './dashboard.service';
|
||||||
|
import { MyItemsComponent } from './my-items/my-items.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
FormsModule,
|
||||||
SharedModule
|
SharedModule
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
DashboardComponent,
|
|
||||||
CalendarComponent,
|
CalendarComponent,
|
||||||
ChartsComponent
|
ChartsComponent,
|
||||||
|
DashboardComponent,
|
||||||
|
MyItemsComponent
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
DashboardService
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
DashboardComponent,
|
|
||||||
CalendarComponent,
|
CalendarComponent,
|
||||||
ChartsComponent
|
ChartsComponent,
|
||||||
|
DashboardComponent,
|
||||||
|
MyItemsComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class DashboardModule { }
|
export class DashboardModule { }
|
||||||
|
33
src/app/dashboard/dashboard.service.ts
Normal file
33
src/app/dashboard/dashboard.service.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { LocationStrategy } from '@angular/common';
|
||||||
|
|
||||||
|
import { Observable, of } from 'rxjs';
|
||||||
|
import { map, catchError } from 'rxjs/operators';
|
||||||
|
|
||||||
|
import { ApiResponse } from '../shared/models';
|
||||||
|
import { ApiService } from '../shared/services';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DashboardService extends ApiService {
|
||||||
|
|
||||||
|
constructor(private http: HttpClient, strat: LocationStrategy) {
|
||||||
|
super(strat);
|
||||||
|
}
|
||||||
|
|
||||||
|
getBoardInfo(): Observable<ApiResponse> {
|
||||||
|
return this.http.get(this.apiBase + 'dashboard/boards')
|
||||||
|
.pipe(
|
||||||
|
map((response: ApiResponse) => response),
|
||||||
|
catchError((err) => of(err.error as ApiResponse))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getTaskInfo(): Observable<ApiResponse> {
|
||||||
|
return this.http.get(this.apiBase + 'dashboard/tasks')
|
||||||
|
.pipe(
|
||||||
|
map((response: ApiResponse) => response),
|
||||||
|
catchError((err) => of(err.error as ApiResponse))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
112
src/app/dashboard/my-items/my-items.component.html
Normal file
112
src/app/dashboard/my-items/my-items.component.html
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<section class="boards-and-tasks">
|
||||||
|
<h2>{{ strings['dashboard_boardsAndTasks'] }}</h2>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<h3>{{ strings['dashboard_myBoards'] }}</h3>
|
||||||
|
|
||||||
|
<table class="alternating scrollable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{{ strings['settings_board'] }}</th>
|
||||||
|
<th>{{ strings['settings_columns'] }}</th>
|
||||||
|
<th>{{ strings['settings_categories'] }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody *ngIf="boardsLoading">
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">
|
||||||
|
Loading ...
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
<tbody *ngIf="!boardsLoading && !boards">
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">
|
||||||
|
{{ boardsMessage }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
<tbody *ngIf="boards && !boardsLoading">
|
||||||
|
<tr *ngFor="let board of boards">
|
||||||
|
<td><a href="./boards/{{ board.id }}">{{ board.name }}</a></td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<span *ngFor="let col of board.columns">
|
||||||
|
{{ col.name }}
|
||||||
|
<span class="badge" title="{{ strings['boards_tasksInColumn'] }}">
|
||||||
|
{{ col.tasks }}</span>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<!-- Ugly here, so it looks good in the browser -->
|
||||||
|
<span *ngFor="let cat of board.categories">{{ cat.name }}
|
||||||
|
<span class="badge"
|
||||||
|
title="{{ strings['dashboard_tasksInCategory'] }}">{{ cat.tasks }}</span></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<h3>{{ strings['dashboard_myTasks'] }}</h3>
|
||||||
|
|
||||||
|
<table class="alternating scrollable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{{ strings['settings_board'] }}</th>
|
||||||
|
<th>{{ strings['boards_task'] }}</th>
|
||||||
|
<th>{{ strings['dashboard_details'] }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody *ngIf="tasksLoading">
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">
|
||||||
|
Loading ...
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
<tbody *ngIf="!tasksLoading && !tasks">
|
||||||
|
<tr>
|
||||||
|
<td colspan="3">
|
||||||
|
{{ tasksMessage }}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
<tbody *ngIf="tasks && !tasksLoading">
|
||||||
|
<tr *ngFor="let task of tasks">
|
||||||
|
<td><a href="./boards/{{ task.board_id }}">{{ task.board }}</a></td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<!-- <a href="#">An Important Task</a> -->
|
||||||
|
{{ task.title }}
|
||||||
|
<span class="badge" title="Task Color"
|
||||||
|
style="background-color:{{ task.color }}"> </span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<span class="details">{{ strings['boards_taskColumn'] }}:
|
||||||
|
<em>{{ task.column }}</em></span>
|
||||||
|
<span class="details" *ngIf="task.date_due">{{ strings['boards_taskDateDue'] }}:
|
||||||
|
<em>{{ task.date_due | date }}</em></span>
|
||||||
|
<span class="details" *ngIf="task.points">{{ strings['boards_taskPoints'] }}:
|
||||||
|
<em>{{ task.points }}</em></span>
|
||||||
|
<span class="details" *ngIf="task.attachments.length">{{ strings['boards_taskAttachments'] }}:
|
||||||
|
<em>{{ task.attachments }}</em></span>
|
||||||
|
<span class="details" *ngIf="task.comments.length">{{ strings['boards_taskComments'] }}:
|
||||||
|
<em>{{ task.comments }}</em></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</section>
|
35
src/app/dashboard/my-items/my-items.component.ts
Normal file
35
src/app/dashboard/my-items/my-items.component.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { Component, Input } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'tb-my-items',
|
||||||
|
templateUrl: './my-items.component.html'
|
||||||
|
})
|
||||||
|
export class MyItemsComponent {
|
||||||
|
@Input()
|
||||||
|
boards: any;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
boardsLoading: boolean;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
boardsMessage: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
tasks: any;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
tasksLoading: boolean;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
tasksMessage: string;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
strings: any[];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.boardsLoading = true;
|
||||||
|
this.tasksLoading = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -94,7 +94,7 @@
|
|||||||
"settings_issueTrackerRegExp": "BUGID RegExp",
|
"settings_issueTrackerRegExp": "BUGID RegExp",
|
||||||
"settings_addIssueTracker": "Add Issue Tracker",
|
"settings_addIssueTracker": "Add Issue Tracker",
|
||||||
"settings_selectUsers": "Select Users",
|
"settings_selectUsers": "Select Users",
|
||||||
"settings_boardAdminMessage": "Including a Board Admin, makes them an admin of this board.",
|
"settings_boardAdminMessage": "Including a Board Admin makes them an admin of this board.",
|
||||||
"settings_adminAccessMessage": "Administrators have access to all boards and are not listed here.",
|
"settings_adminAccessMessage": "Administrators have access to all boards and are not listed here.",
|
||||||
"settings_saveBoard": "Save Board",
|
"settings_saveBoard": "Save Board",
|
||||||
"settings_noBoards": "You are not assigned to any boards. Contact an admin user to be added to a board.",
|
"settings_noBoards": "You are not assigned to any boards. Contact an admin user to be added to a board.",
|
||||||
@ -223,6 +223,13 @@
|
|||||||
"boards_sortByPosition": "Position",
|
"boards_sortByPosition": "Position",
|
||||||
"boards_sortByDueDate": "Due Date",
|
"boards_sortByDueDate": "Due Date",
|
||||||
"boards_sortByLastModified": "Last Modifed",
|
"boards_sortByLastModified": "Last Modifed",
|
||||||
"boards_sortByPoints": "Points"
|
"boards_sortByPoints": "Points",
|
||||||
|
|
||||||
|
"dashboard_boardsAndTasks": "Boards and Tasks",
|
||||||
|
"dashboard_myBoards": "My Boards",
|
||||||
|
"dashboard_myTasks": "My Tasks",
|
||||||
|
"dashboard_tasksInCategory": "Tasks In Category",
|
||||||
|
"dashboard_details": "Details",
|
||||||
|
"dashboard_analytics": "Analytics"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,6 +223,12 @@
|
|||||||
"boards_sortByPosition": "Posición",
|
"boards_sortByPosition": "Posición",
|
||||||
"boards_sortByDueDate": "Fecha de Vencimiento",
|
"boards_sortByDueDate": "Fecha de Vencimiento",
|
||||||
"boards_sortByLastModified": "Última Modificación",
|
"boards_sortByLastModified": "Última Modificación",
|
||||||
"boards_sortByPoints": "Puntos"
|
"boards_sortByPoints": "Puntos",
|
||||||
|
|
||||||
|
"dashboard_boardsAndTasks": "Tableros y Tareas",
|
||||||
|
"dashboard_myBoards": "Mis Tableros",
|
||||||
|
"dashboard_myTasks": "Mis Tareas",
|
||||||
|
"dashboard_tasksInCategory": "Tareas En Categoría",
|
||||||
|
"dashboard_details": "Detalles"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,5 +223,11 @@
|
|||||||
"boards_sortByPosition": "position",
|
"boards_sortByPosition": "position",
|
||||||
"boards_sortByDueDate": "date d'échéance",
|
"boards_sortByDueDate": "date d'échéance",
|
||||||
"boards_sortByLastModified": "dernière modification",
|
"boards_sortByLastModified": "dernière modification",
|
||||||
"boards_sortByPoints": "points"
|
"boards_sortByPoints": "points",
|
||||||
|
|
||||||
|
"dashboard_boardsAndTasks": "Tableaux et Tâches",
|
||||||
|
"dashboard_myBoards": "Mes Tableaux",
|
||||||
|
"dashboard_myTasks": "Mes Tâches",
|
||||||
|
"dashboard_tasksInCategory": "Tâches dans la Catégorie",
|
||||||
|
"dashboard_details": "Détails"
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,22 @@
|
|||||||
|
|
||||||
margin: 7px 1em;
|
margin: 7px 1em;
|
||||||
|
|
||||||
|
.boards-and-tasks {
|
||||||
|
.scrollable {
|
||||||
|
tbody {
|
||||||
|
display: block;
|
||||||
|
max-height: 26vh;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead, tbody tr, tfoot {
|
||||||
|
display: table;
|
||||||
|
table-layout: fixed;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.details {
|
.details {
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
}
|
}
|
||||||
@ -11,6 +27,11 @@
|
|||||||
@include grid-column(9 of 18);
|
@include grid-column(9 of 18);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
color: $color-secondary;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
.calendar {
|
.calendar {
|
||||||
td {
|
td {
|
||||||
width: 100px;
|
width: 100px;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
// chartist
|
// chartist
|
||||||
@import 'chartist-settings';
|
@import 'chartist-settings';
|
||||||
@import '../../node_modules/chartist/dist/chartist.css';
|
@import '../../node_modules/chartist/dist/scss/chartist.scss';
|
||||||
|
|
||||||
// highlight.js
|
// highlight.js
|
||||||
@import '../../node_modules/highlight.js/styles/tomorrow-night-eighties.css';
|
@import '../../node_modules/highlight.js/styles/tomorrow-night-eighties.css';
|
||||||
|
Reference in New Issue
Block a user