mirror of
https://github.com/simon987/task_tracker.git
synced 2025-04-19 18:16:45 +00:00
More work on UI
This commit is contained in:
parent
cbd32daf02
commit
1d656099f5
@ -53,6 +53,7 @@ func New() *WebAPI {
|
|||||||
api.router.POST("/project/create", LogRequestMiddleware(api.ProjectCreate))
|
api.router.POST("/project/create", LogRequestMiddleware(api.ProjectCreate))
|
||||||
api.router.GET("/project/get/:id", LogRequestMiddleware(api.ProjectGet))
|
api.router.GET("/project/get/:id", LogRequestMiddleware(api.ProjectGet))
|
||||||
api.router.GET("/project/stats/:id", LogRequestMiddleware(api.ProjectGetStats))
|
api.router.GET("/project/stats/:id", LogRequestMiddleware(api.ProjectGetStats))
|
||||||
|
api.router.GET("/project/stats", LogRequestMiddleware(api.ProjectGetAllStats))
|
||||||
|
|
||||||
api.router.POST("/task/create", LogRequestMiddleware(api.TaskCreate))
|
api.router.POST("/task/create", LogRequestMiddleware(api.TaskCreate))
|
||||||
api.router.GET("/task/get/:project", LogRequestMiddleware(api.TaskGetFromProject))
|
api.router.GET("/task/get/:project", LogRequestMiddleware(api.TaskGetFromProject))
|
||||||
|
@ -33,6 +33,12 @@ type GetProjectStatsResponse struct {
|
|||||||
Stats *storage.ProjectStats `json:"stats,omitempty"`
|
Stats *storage.ProjectStats `json:"stats,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetAllProjectsStatsResponse struct {
|
||||||
|
Ok bool `json:"ok"`
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
Stats *[]storage.ProjectStats `json:"stats,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
func (api *WebAPI) ProjectCreate(r *Request) {
|
func (api *WebAPI) ProjectCreate(r *Request) {
|
||||||
|
|
||||||
createReq := &CreateProjectRequest{}
|
createReq := &CreateProjectRequest{}
|
||||||
@ -125,3 +131,13 @@ func (api *WebAPI) ProjectGetStats(r *Request) {
|
|||||||
}, 404)
|
}, 404)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *WebAPI) ProjectGetAllStats(r *Request) {
|
||||||
|
|
||||||
|
stats := api.Database.GetAllProjectsStats()
|
||||||
|
|
||||||
|
r.OkJson(GetAllProjectsStatsResponse{
|
||||||
|
Ok: true,
|
||||||
|
Stats: stats,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
17
setup.sh
17
setup.sh
@ -1,17 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
export INSTALL_DIR="/home/drone/task_tracker"
|
|
||||||
|
|
||||||
mkdir ${INSTALL_DIR} 2> /dev/null
|
|
||||||
|
|
||||||
# Gogs
|
|
||||||
if [[ ! -d "${INSTALL_DIR}/gogs" ]]; then
|
|
||||||
wget "https://dl.gogs.io/0.11.79/gogs_0.11.79_linux_amd64.tar.gz"
|
|
||||||
tar -xzf "gogs_0.11.79_linux_amd64.tar.gz" -C ${INSTALL_DIR}
|
|
||||||
rm "gogs_0.11.79_linux_amd64.tar.gz"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Postgres
|
|
||||||
su - postgres -c "createuser task_tracker"
|
|
||||||
su - postgres -c "dropdb gogs"
|
|
||||||
su - postgres -c "createdb gogs"
|
|
@ -1,7 +1,6 @@
|
|||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"src/task_tracker/config"
|
"src/task_tracker/config"
|
||||||
@ -44,12 +43,6 @@ func (database *Database) SetupLoggerHook() {
|
|||||||
func (database *Database) GetLogs(since int64, level logrus.Level) *[]LogEntry {
|
func (database *Database) GetLogs(since int64, level logrus.Level) *[]LogEntry {
|
||||||
|
|
||||||
db := database.getDB()
|
db := database.getDB()
|
||||||
logs := getLogs(since, level, db)
|
|
||||||
|
|
||||||
return logs
|
|
||||||
}
|
|
||||||
|
|
||||||
func getLogs(since int64, level logrus.Level, db *sql.DB) *[]LogEntry {
|
|
||||||
|
|
||||||
var logs []LogEntry
|
var logs []LogEntry
|
||||||
|
|
||||||
|
@ -102,12 +102,6 @@ func scanProject(row *sql.Row) (*Project, error) {
|
|||||||
func (database *Database) GetProjectWithRepoName(repoName string) *Project {
|
func (database *Database) GetProjectWithRepoName(repoName string) *Project {
|
||||||
|
|
||||||
db := database.getDB()
|
db := database.getDB()
|
||||||
project := getProjectWithRepoName(repoName, db)
|
|
||||||
return project
|
|
||||||
}
|
|
||||||
|
|
||||||
func getProjectWithRepoName(repoName string, db *sql.DB) *Project {
|
|
||||||
|
|
||||||
row := db.QueryRow(`SELECT * FROM project WHERE LOWER(git_repo)=$1`, strings.ToLower(repoName))
|
row := db.QueryRow(`SELECT * FROM project WHERE LOWER(git_repo)=$1`, strings.ToLower(repoName))
|
||||||
|
|
||||||
project, err := scanProject(row)
|
project, err := scanProject(row)
|
||||||
@ -124,10 +118,6 @@ func getProjectWithRepoName(repoName string, db *sql.DB) *Project {
|
|||||||
func (database *Database) UpdateProject(project *Project) {
|
func (database *Database) UpdateProject(project *Project) {
|
||||||
|
|
||||||
db := database.getDB()
|
db := database.getDB()
|
||||||
updateProject(project, db)
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateProject(project *Project, db *sql.DB) {
|
|
||||||
|
|
||||||
res, err := db.Exec(`UPDATE project
|
res, err := db.Exec(`UPDATE project
|
||||||
SET (priority, name, clone_url, git_repo, version, motd) = ($1,$2,$3,$4,$5,$6) WHERE id=$7`,
|
SET (priority, name, clone_url, git_repo, version, motd) = ($1,$2,$3,$4,$5,$6) WHERE id=$7`,
|
||||||
@ -147,13 +137,6 @@ func updateProject(project *Project, db *sql.DB) {
|
|||||||
func (database *Database) GetProjectStats(id int64) *ProjectStats {
|
func (database *Database) GetProjectStats(id int64) *ProjectStats {
|
||||||
|
|
||||||
db := database.getDB()
|
db := database.getDB()
|
||||||
stats := getProjectStats(id, db)
|
|
||||||
|
|
||||||
return stats
|
|
||||||
}
|
|
||||||
|
|
||||||
func getProjectStats(id int64, db *sql.DB) *ProjectStats {
|
|
||||||
|
|
||||||
stats := ProjectStats{}
|
stats := ProjectStats{}
|
||||||
|
|
||||||
stats.Project = getProject(id, db)
|
stats.Project = getProject(id, db)
|
||||||
@ -169,7 +152,7 @@ func getProjectStats(id int64, db *sql.DB) *ProjectStats {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.WithError(err).WithFields(logrus.Fields{
|
logrus.WithError(err).WithFields(logrus.Fields{
|
||||||
"id": id,
|
"id": id,
|
||||||
}).Warn("???") //todo
|
}).Trace("Get project stats: No task for this project")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,3 +170,36 @@ func getProjectStats(id int64, db *sql.DB) *ProjectStats {
|
|||||||
|
|
||||||
return &stats
|
return &stats
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (database Database) GetAllProjectsStats() *[]ProjectStats {
|
||||||
|
var statsList []ProjectStats
|
||||||
|
|
||||||
|
db := database.getDB()
|
||||||
|
rows, err := db.Query(`SELECT
|
||||||
|
SUM(CASE WHEN status='new' THEN 1 ELSE 0 END) newCount,
|
||||||
|
SUM(CASE WHEN status='failed' THEN 1 ELSE 0 END) failedCount,
|
||||||
|
SUM(CASE WHEN status='closed' THEN 1 ELSE 0 END) closedCount,
|
||||||
|
p.*
|
||||||
|
FROM task INNER JOIN project p on task.project = p.id
|
||||||
|
GROUP BY p.id`)
|
||||||
|
handleErr(err)
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
|
||||||
|
stats := ProjectStats{}
|
||||||
|
p := &Project{}
|
||||||
|
err := rows.Scan(&stats.NewTaskCount, &stats.FailedTaskCount, &stats.ClosedTaskCount,
|
||||||
|
&p.Id, &p.Priority, &p.Motd, &p.Name, &p.CloneUrl, &p.GitRepo, &p.Version)
|
||||||
|
handleErr(err)
|
||||||
|
|
||||||
|
stats.Project = p
|
||||||
|
|
||||||
|
statsList = append(statsList, stats)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"statsList": statsList,
|
||||||
|
}).Trace("Get all projects stats")
|
||||||
|
|
||||||
|
return &statsList
|
||||||
|
}
|
||||||
|
@ -20,12 +20,6 @@ type Task struct {
|
|||||||
func (database *Database) SaveTask(task *Task, project int64) error {
|
func (database *Database) SaveTask(task *Task, project int64) error {
|
||||||
|
|
||||||
db := database.getDB()
|
db := database.getDB()
|
||||||
taskErr := saveTask(task, project, db)
|
|
||||||
|
|
||||||
return taskErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveTask(task *Task, project int64, db *sql.DB) error {
|
|
||||||
|
|
||||||
res, err := db.Exec(`
|
res, err := db.Exec(`
|
||||||
INSERT INTO task (project, max_retries, recipe, priority)
|
INSERT INTO task (project, max_retries, recipe, priority)
|
||||||
@ -52,12 +46,6 @@ func saveTask(task *Task, project int64, db *sql.DB) error {
|
|||||||
func (database *Database) GetTask(worker *Worker) *Task {
|
func (database *Database) GetTask(worker *Worker) *Task {
|
||||||
|
|
||||||
db := database.getDB()
|
db := database.getDB()
|
||||||
task := getTask(worker, db)
|
|
||||||
|
|
||||||
return task
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTask(worker *Worker, db *sql.DB) *Task {
|
|
||||||
|
|
||||||
row := db.QueryRow(`
|
row := db.QueryRow(`
|
||||||
UPDATE task
|
UPDATE task
|
||||||
@ -111,12 +99,6 @@ func getTaskById(id int64, db *sql.DB) *Task {
|
|||||||
func (database Database) ReleaseTask(id int64, workerId *uuid.UUID, success bool) bool {
|
func (database Database) ReleaseTask(id int64, workerId *uuid.UUID, success bool) bool {
|
||||||
|
|
||||||
db := database.getDB()
|
db := database.getDB()
|
||||||
res := releaseTask(workerId, id, success, db)
|
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func releaseTask(workerId *uuid.UUID, id int64, success bool, db *sql.DB) bool {
|
|
||||||
|
|
||||||
var res sql.Result
|
var res sql.Result
|
||||||
var err error
|
var err error
|
||||||
@ -139,15 +121,9 @@ func releaseTask(workerId *uuid.UUID, id int64, success bool, db *sql.DB) bool {
|
|||||||
return rowsAffected == 1
|
return rowsAffected == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (database *Database) GetTaskFromProject(worker *Worker, project int64) *Task {
|
func (database *Database) GetTaskFromProject(worker *Worker, projectId int64) *Task {
|
||||||
|
|
||||||
db := database.getDB()
|
db := database.getDB()
|
||||||
task := getTaskFromProject(worker, project, db)
|
|
||||||
|
|
||||||
return task
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTaskFromProject(worker *Worker, projectId int64, db *sql.DB) *Task {
|
|
||||||
|
|
||||||
row := db.QueryRow(`
|
row := db.QueryRow(`
|
||||||
UPDATE task
|
UPDATE task
|
||||||
@ -158,7 +134,7 @@ func getTaskFromProject(worker *Worker, projectId int64, db *sql.DB) *Task {
|
|||||||
FROM task
|
FROM task
|
||||||
INNER JOIN project p on task.project = p.id
|
INNER JOIN project p on task.project = p.id
|
||||||
WHERE assignee IS NULL AND p.id=$2
|
WHERE assignee IS NULL AND p.id=$2
|
||||||
ORDER BY p.priority DESC, task.priority DESC
|
ORDER BY task.priority DESC
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
)
|
)
|
||||||
RETURNING id`, worker.Id, projectId)
|
RETURNING id`, worker.Id, projectId)
|
||||||
|
@ -21,17 +21,6 @@ type Worker struct {
|
|||||||
func (database *Database) SaveWorker(worker *Worker) {
|
func (database *Database) SaveWorker(worker *Worker) {
|
||||||
|
|
||||||
db := database.getDB()
|
db := database.getDB()
|
||||||
saveWorker(worker, db)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (database *Database) GetWorker(id uuid.UUID) *Worker {
|
|
||||||
|
|
||||||
db := database.getDB()
|
|
||||||
worker := getWorker(id, db)
|
|
||||||
return worker
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveWorker(worker *Worker, db *sql.DB) {
|
|
||||||
|
|
||||||
identityId := getOrCreateIdentity(worker.Identity, db)
|
identityId := getOrCreateIdentity(worker.Identity, db)
|
||||||
|
|
||||||
@ -45,7 +34,9 @@ func saveWorker(worker *Worker, db *sql.DB) {
|
|||||||
}).Trace("Database.saveWorker INSERT worker")
|
}).Trace("Database.saveWorker INSERT worker")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getWorker(id uuid.UUID, db *sql.DB) *Worker {
|
func (database *Database) GetWorker(id uuid.UUID) *Worker {
|
||||||
|
|
||||||
|
db := database.getDB()
|
||||||
|
|
||||||
worker := &Worker{}
|
worker := &Worker{}
|
||||||
var identityId int64
|
var identityId int64
|
||||||
|
@ -10,7 +10,6 @@ func BenchmarkCreateTask(b *testing.B) {
|
|||||||
|
|
||||||
resp := createProject(api.CreateProjectRequest{
|
resp := createProject(api.CreateProjectRequest{
|
||||||
Name: "BenchmarkCreateTask" + strconv.Itoa(b.N),
|
Name: "BenchmarkCreateTask" + strconv.Itoa(b.N),
|
||||||
Priority: 1,
|
|
||||||
GitRepo: "benchmark_test" + strconv.Itoa(b.N),
|
GitRepo: "benchmark_test" + strconv.Itoa(b.N),
|
||||||
Version: "f09e8c9r0w839x0c43",
|
Version: "f09e8c9r0w839x0c43",
|
||||||
CloneUrl: "http://localhost",
|
CloneUrl: "http://localhost",
|
||||||
|
0
web/angular/e2e/protractor.conf.js
Normal file → Executable file
0
web/angular/e2e/protractor.conf.js
Normal file → Executable file
0
web/angular/e2e/src/app.e2e-spec.ts
Normal file → Executable file
0
web/angular/e2e/src/app.e2e-spec.ts
Normal file → Executable file
0
web/angular/e2e/src/app.po.ts
Normal file → Executable file
0
web/angular/e2e/src/app.po.ts
Normal file → Executable file
0
web/angular/e2e/tsconfig.e2e.json
Normal file → Executable file
0
web/angular/e2e/tsconfig.e2e.json
Normal file → Executable file
10
web/angular/src/app/api.service.ts
Normal file → Executable file
10
web/angular/src/app/api.service.ts
Normal file → Executable file
@ -12,10 +12,18 @@ export class ApiService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getLogs() {
|
getLogs() {
|
||||||
return this.http.get(this.url + "/logs");
|
return this.http.post(this.url + "/logs", "{\"level\":\"info\", \"since\":10000}");
|
||||||
}
|
}
|
||||||
|
|
||||||
getProjectStats(id: number) {
|
getProjectStats(id: number) {
|
||||||
return this.http.get(this.url + "/project/stats/" + id)
|
return this.http.get(this.url + "/project/stats/" + id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getProjects() {
|
||||||
|
return this.http.get(this.url + "/project/stats")
|
||||||
|
}
|
||||||
|
|
||||||
|
getProject(id: number) {
|
||||||
|
return this.http.get(this.url + "/project/get/" + id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
8
web/angular/src/app/app-routing.module.ts
Normal file → Executable file
8
web/angular/src/app/app-routing.module.ts
Normal file → Executable file
@ -2,10 +2,16 @@ import {NgModule} from '@angular/core';
|
|||||||
import {RouterModule, Routes} from '@angular/router';
|
import {RouterModule, Routes} from '@angular/router';
|
||||||
import {LogsComponent} from "./logs/logs.component";
|
import {LogsComponent} from "./logs/logs.component";
|
||||||
import {ProjectDashboardComponent} from "./project-dashboard/project-dashboard.component";
|
import {ProjectDashboardComponent} from "./project-dashboard/project-dashboard.component";
|
||||||
|
import {ProjectListComponent} from "./project-list/project-list.component";
|
||||||
|
import {CreateProjectComponent} from "./create-project/create-project.component";
|
||||||
|
import {UpdateProjectComponent} from "./update-project/update-project.component";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{path: "log", component: LogsComponent},
|
{path: "log", component: LogsComponent},
|
||||||
{path: "project", component: ProjectDashboardComponent}
|
{path: "projects", component: ProjectListComponent},
|
||||||
|
{path: "project/:id", component: ProjectDashboardComponent},
|
||||||
|
{path: "project/:id/update", component: UpdateProjectComponent},
|
||||||
|
{path: "new_project", component: CreateProjectComponent}
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
0
web/angular/src/app/app.component.css
Normal file → Executable file
0
web/angular/src/app/app.component.css
Normal file → Executable file
14
web/angular/src/app/app.component.html
Normal file → Executable file
14
web/angular/src/app/app.component.html
Normal file → Executable file
@ -1,8 +1,12 @@
|
|||||||
<mat-toolbar>
|
<!--<mat-toolbar>-->
|
||||||
<a [routerLink]="''">Index</a>
|
<ul>
|
||||||
<a [routerLink]="'log'">Logs</a>
|
<li><a [routerLink]="''">Index</a></li>
|
||||||
<a [routerLink]="'project'">Project</a>
|
<li><a [routerLink]="'log'">Logs</a></li>
|
||||||
</mat-toolbar>
|
<li><a [routerLink]="'project'">Project</a></li>
|
||||||
|
<li><a [routerLink]="'projects'">list</a></li>
|
||||||
|
<li><a [routerLink]="'new_project'">new project</a></li>
|
||||||
|
</ul>
|
||||||
|
<!--</mat-toolbar>-->
|
||||||
|
|
||||||
|
|
||||||
<router-outlet></router-outlet>
|
<router-outlet></router-outlet>
|
||||||
|
15
web/angular/src/app/app.module.ts
Normal file → Executable file
15
web/angular/src/app/app.module.ts
Normal file → Executable file
@ -7,6 +7,8 @@ import {LogsComponent} from './logs/logs.component';
|
|||||||
|
|
||||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||||
import {
|
import {
|
||||||
|
MatAutocompleteModule,
|
||||||
|
MatButtonModule,
|
||||||
MatCardModule,
|
MatCardModule,
|
||||||
MatExpansionModule,
|
MatExpansionModule,
|
||||||
MatFormFieldModule,
|
MatFormFieldModule,
|
||||||
@ -22,12 +24,19 @@ import {
|
|||||||
import {ApiService} from "./api.service";
|
import {ApiService} from "./api.service";
|
||||||
import {HttpClientModule} from "@angular/common/http";
|
import {HttpClientModule} from "@angular/common/http";
|
||||||
import {ProjectDashboardComponent} from './project-dashboard/project-dashboard.component';
|
import {ProjectDashboardComponent} from './project-dashboard/project-dashboard.component';
|
||||||
|
import {ProjectListComponent} from './project-list/project-list.component';
|
||||||
|
import {CreateProjectComponent} from './create-project/create-project.component';
|
||||||
|
import {FormsModule, ReactiveFormsModule} from "@angular/forms";
|
||||||
|
import {UpdateProjectComponent} from './update-project/update-project.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
LogsComponent,
|
LogsComponent,
|
||||||
ProjectDashboardComponent
|
ProjectDashboardComponent,
|
||||||
|
ProjectListComponent,
|
||||||
|
CreateProjectComponent,
|
||||||
|
UpdateProjectComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
@ -41,6 +50,10 @@ import {ProjectDashboardComponent} from './project-dashboard/project-dashboard.c
|
|||||||
MatInputModule,
|
MatInputModule,
|
||||||
MatToolbarModule,
|
MatToolbarModule,
|
||||||
MatCardModule,
|
MatCardModule,
|
||||||
|
MatButtonModule,
|
||||||
|
MatAutocompleteModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
FormsModule,
|
||||||
MatExpansionModule,
|
MatExpansionModule,
|
||||||
MatTreeModule,
|
MatTreeModule,
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
.mat-form-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
<mat-card>
|
||||||
|
<mat-card-title>Create new project</mat-card-title>
|
||||||
|
<mat-card-subtitle></mat-card-subtitle>
|
||||||
|
|
||||||
|
<mat-card-content>
|
||||||
|
<form>
|
||||||
|
<mat-form-field>
|
||||||
|
<input type="text" matInput [(ngModel)]="project.name" name="name" placeholder="Name">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field>
|
||||||
|
<input type="text" matInput [(ngModel)]="project.clone_url" name="clone_url"
|
||||||
|
placeholder="Git clone url">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<input type="text" matInput [(ngModel)]="project.git_repo" name="clone_url"
|
||||||
|
placeholder='Full repository name (e.g. "simon987/task_tracker")'>
|
||||||
|
<mat-hint align="start">Changes on the <strong>master</strong> branch will be tracked if webhooks are
|
||||||
|
enabled
|
||||||
|
</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
<input type="hidden" name="version" value="{{project.version}}">
|
||||||
|
</form>
|
||||||
|
</mat-card-content>
|
||||||
|
|
||||||
|
<mat-card-actions>
|
||||||
|
<button>Create</button>
|
||||||
|
</mat-card-actions>
|
||||||
|
|
||||||
|
</mat-card>
|
@ -0,0 +1,20 @@
|
|||||||
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {Project} from "../models/project";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-create-project',
|
||||||
|
templateUrl: './create-project.component.html',
|
||||||
|
styleUrls: ['./create-project.component.css']
|
||||||
|
})
|
||||||
|
export class CreateProjectComponent implements OnInit {
|
||||||
|
|
||||||
|
private project = new Project();
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.project.name = "test"
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,25 +0,0 @@
|
|||||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
|
||||||
|
|
||||||
import {LogsComponent} from './logs.component';
|
|
||||||
|
|
||||||
describe('LogsComponent', () => {
|
|
||||||
let component: LogsComponent;
|
|
||||||
let fixture: ComponentFixture<LogsComponent>;
|
|
||||||
|
|
||||||
beforeEach(async(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
declarations: [LogsComponent]
|
|
||||||
})
|
|
||||||
.compileComponents();
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(LogsComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create', () => {
|
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
@ -40,7 +40,7 @@ export class LogsComponent implements OnInit {
|
|||||||
private getLogs() {
|
private getLogs() {
|
||||||
this.apiService.getLogs().subscribe(
|
this.apiService.getLogs().subscribe(
|
||||||
data => {
|
data => {
|
||||||
this.data.data = _.map(data, (entry) => {
|
this.data.data = _.map(data["logs"], (entry) => {
|
||||||
return <LogEntry>{
|
return <LogEntry>{
|
||||||
message: entry.message,
|
message: entry.message,
|
||||||
timestamp: moment.unix(entry.timestamp).toISOString(),
|
timestamp: moment.unix(entry.timestamp).toISOString(),
|
||||||
|
9
web/angular/src/app/models/project.ts
Normal file
9
web/angular/src/app/models/project.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export class Project {
|
||||||
|
|
||||||
|
public priority: number;
|
||||||
|
public motd: string;
|
||||||
|
public name: string;
|
||||||
|
public clone_url: string;
|
||||||
|
public git_repo: string;
|
||||||
|
public version: string;
|
||||||
|
}
|
@ -4,6 +4,7 @@ import * as d3 from "d3"
|
|||||||
import * as _ from "lodash"
|
import * as _ from "lodash"
|
||||||
import {interval} from "rxjs";
|
import {interval} from "rxjs";
|
||||||
import {ApiService} from "../api.service";
|
import {ApiService} from "../api.service";
|
||||||
|
import {ActivatedRoute} from "@angular/router";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-project-dashboard',
|
selector: 'app-project-dashboard',
|
||||||
@ -12,7 +13,9 @@ import {ApiService} from "../api.service";
|
|||||||
})
|
})
|
||||||
export class ProjectDashboardComponent implements OnInit {
|
export class ProjectDashboardComponent implements OnInit {
|
||||||
|
|
||||||
|
private projectId;
|
||||||
projectStats;
|
projectStats;
|
||||||
|
|
||||||
private pieWidth = 360;
|
private pieWidth = 360;
|
||||||
private pieHeight = 360;
|
private pieHeight = 360;
|
||||||
private pieRadius = Math.min(this.pieWidth, this.pieHeight) / 2;
|
private pieRadius = Math.min(this.pieWidth, this.pieHeight) / 2;
|
||||||
@ -47,7 +50,7 @@ export class ProjectDashboardComponent implements OnInit {
|
|||||||
private assigneesPath: any;
|
private assigneesPath: any;
|
||||||
private assigneesSvg: any;
|
private assigneesSvg: any;
|
||||||
|
|
||||||
constructor(private apiService: ApiService) {
|
constructor(private apiService: ApiService, private route: ActivatedRoute) {
|
||||||
}
|
}
|
||||||
|
|
||||||
setupStatusPieChart() {
|
setupStatusPieChart() {
|
||||||
@ -165,7 +168,7 @@ export class ProjectDashboardComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getStats() {
|
getStats() {
|
||||||
this.apiService.getProjectStats(2).subscribe((data) => {
|
this.apiService.getProjectStats(this.projectId).subscribe((data) => {
|
||||||
|
|
||||||
this.projectStats = data["stats"];
|
this.projectStats = data["stats"];
|
||||||
|
|
||||||
@ -259,9 +262,13 @@ export class ProjectDashboardComponent implements OnInit {
|
|||||||
this.setupAssigneesPieChart();
|
this.setupAssigneesPieChart();
|
||||||
this.setupLine();
|
this.setupLine();
|
||||||
|
|
||||||
|
this.route.params.subscribe(params => {
|
||||||
|
this.projectId = params["id"];
|
||||||
this.getStats();
|
this.getStats();
|
||||||
interval(1000).subscribe(() => {
|
interval(1000).subscribe(() => {
|
||||||
this.getStats()
|
// this.getStats()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
0
web/angular/src/app/project-list/project-list.component.css
Executable file
0
web/angular/src/app/project-list/project-list.component.css
Executable file
19
web/angular/src/app/project-list/project-list.component.html
Executable file
19
web/angular/src/app/project-list/project-list.component.html
Executable file
@ -0,0 +1,19 @@
|
|||||||
|
<mat-card>
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title>Projects</mat-card-title>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<mat-accordion>
|
||||||
|
<mat-expansion-panel *ngFor="let stats of projects">
|
||||||
|
<mat-expansion-panel-header>
|
||||||
|
<mat-panel-title>{{stats.project.id}}: {{stats.project.name}}</mat-panel-title>
|
||||||
|
<mat-panel-description>{{stats.project.motd}}</mat-panel-description>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
<pre>{{stats.project | json}}</pre>
|
||||||
|
<div style="display: flex;">
|
||||||
|
<a [routerLink]="'/project/' + stats.project.id">Dashboard</a>
|
||||||
|
</div>
|
||||||
|
</mat-expansion-panel>
|
||||||
|
</mat-accordion>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
24
web/angular/src/app/project-list/project-list.component.ts
Executable file
24
web/angular/src/app/project-list/project-list.component.ts
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {ApiService} from "../api.service";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-project-list',
|
||||||
|
templateUrl: './project-list.component.html',
|
||||||
|
styleUrls: ['./project-list.component.css']
|
||||||
|
})
|
||||||
|
export class ProjectListComponent implements OnInit {
|
||||||
|
|
||||||
|
constructor(private apiService: ApiService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
projects: any[];
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.getProjects()
|
||||||
|
}
|
||||||
|
|
||||||
|
getProjects() {
|
||||||
|
this.apiService.getProjects().subscribe(data => this.projects = data["stats"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"module": "commonjs",
|
|
||||||
"target": "es5",
|
|
||||||
"sourceMap": true
|
|
||||||
},
|
|
||||||
"exclude": [
|
|
||||||
"node_modules"
|
|
||||||
]
|
|
||||||
}
|
|
@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
.mat-form-field {
|
||||||
|
width: 100%;
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
<mat-card>
|
||||||
|
<mat-card-title>Update project</mat-card-title>
|
||||||
|
<mat-card-subtitle>Changes are saved in real time</mat-card-subtitle>
|
||||||
|
|
||||||
|
<mat-card-content>
|
||||||
|
<form>
|
||||||
|
<mat-form-field>
|
||||||
|
<input type="text" matInput [(ngModel)]="project.name" name="name" placeholder="Name">
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field>
|
||||||
|
<textarea matInput placeholder="Message of the day"></textarea>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field>
|
||||||
|
<input type="text" matInput [(ngModel)]="project.clone_url" name="clone_url"
|
||||||
|
placeholder="Git clone url">
|
||||||
|
</mat-form-field>
|
||||||
|
<mat-form-field>
|
||||||
|
<input type="text" matInput [(ngModel)]="project.git_repo" name="clone_url"
|
||||||
|
placeholder='Full repository name (e.g. "simon987/task_tracker")'>
|
||||||
|
<mat-hint align="start">Changes on the <strong>master</strong> branch will be tracked if webhooks are
|
||||||
|
enabled
|
||||||
|
</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
<input type="hidden" name="version" value="{{project.version}}">
|
||||||
|
</form>
|
||||||
|
</mat-card-content>
|
||||||
|
</mat-card>
|
@ -0,0 +1,40 @@
|
|||||||
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {Project} from "../models/project";
|
||||||
|
import {ApiService} from "../api.service";
|
||||||
|
import {ActivatedRoute} from "@angular/router";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-update-project',
|
||||||
|
templateUrl: './update-project.component.html',
|
||||||
|
styleUrls: ['./update-project.component.css']
|
||||||
|
})
|
||||||
|
export class UpdateProjectComponent implements OnInit {
|
||||||
|
|
||||||
|
constructor(private apiService: ApiService, private route: ActivatedRoute) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private project: Project;
|
||||||
|
private projectId: number;
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.route.params.subscribe(params => {
|
||||||
|
this.projectId = params["id"];
|
||||||
|
this.getProject();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private getProject() {
|
||||||
|
this.apiService.getProject(this.projectId).subscribe(data => {
|
||||||
|
this.project = <Project>{
|
||||||
|
name: data["project"]["name"],
|
||||||
|
clone_url: data["project"]["clone_url"],
|
||||||
|
git_repo: data["project"]["git_repo"],
|
||||||
|
motd: data["project"]["motd"],
|
||||||
|
priority: data["project"]["priority"],
|
||||||
|
version: data["project"]["version"]
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user