Commit e498b470 authored by Dmitriy's avatar Dmitriy

graph-painter

parents
node_modules/
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div class="container">
<div class="desktop">
<div class="search-panel">
<input type="text" placeholder="Введите id ошибки" id="bugId">
<button>Отправить запрос</button>
</div>
<div class="graph">
</div>
<div class="preloader hide">
<div class="loader">
<div class="ball"></div>
<div class="ball"></div>
<div class="ball"></div>
</div>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<filter id="gooey">
<feGaussianBlur in="SourceGraphic" stdDeviation="12" result="blur"></feGaussianBlur>
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -8" result="goo"></feColorMatrix>
<feBlend in="SourceGraphic" in2="goo"></feBlend>
</filter>
</defs>
</svg>
</div>
</div>
<div class="errors-message">
</div>
</div>
<script src="dist/main.js"></script>
</body>
</html>
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "etersoft_task",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --mode production",
"dev": "webpack-dev-server --mode development --open"
},
"author": "Dmitriy Kaurov",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.2.2",
"@babel/plugin-transform-runtime": "^7.2.0",
"@babel/preset-env": "^7.2.3",
"babel-loader": "^8.0.4",
"css-loader": "^2.1.0",
"d3": "^5.7.0",
"d3-force": "^2.0.0",
"style-loader": "^0.23.1",
"webpack": "^4.28.2",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.14"
},
"dependencies": {
"@babel/runtime": "^7.2.0"
}
}
.preloader {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
height: 60vh;
}
.preloader svg {
position: absolute;
}
.loader {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-filter: url("#gooey");
filter: url("#gooey");
-webkit-animation: rotate 2s ease infinite;
animation: rotate 2s ease infinite;
}
.loader .ball {
width: 80px;
height: 80px;
background-color: mediumaquamarine;
border-radius: 100%;
}
.loader .ball:nth-child(1) {
-webkit-animation: pull-left 2s ease infinite;
animation: pull-left 2s ease infinite;
}
.loader .ball:nth-child(2) {
-webkit-animation: shimmy 2s ease infinite;
animation: shimmy 2s ease infinite;
}
.loader .ball:nth-child(3) {
-webkit-animation: pull-right 2s ease infinite;
animation: pull-right 2s ease infinite;
}
@-webkit-keyframes rotate {
from {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
to {
-webkit-transform: rotate(720deg);
transform: rotate(720deg);
}
}
@keyframes rotate {
from {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
to {
-webkit-transform: rotate(720deg);
transform: rotate(720deg);
}
}
@-webkit-keyframes shimmy {
0%, 55% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
60%, 65% {
-webkit-transform: translate(-12px, 12px);
transform: translate(-12px, 12px);
}
80%, 85% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
90% {
-webkit-transform: scaleX(1.2) translate(-24px, 0);
transform: scaleX(1.2) translate(-24px, 0);
}
100% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
}
@keyframes shimmy {
0%, 55% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
60%, 65% {
-webkit-transform: translate(-12px, 12px);
transform: translate(-12px, 12px);
}
80%, 85% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
90% {
-webkit-transform: scaleX(1.2) translate(-24px, 0);
transform: scaleX(1.2) translate(-24px, 0);
}
100% {
-webkit-transform: translate(0, 0);
transform: translate(0, 0);
}
}
@-webkit-keyframes pull-left {
0% {
-webkit-transform: scale(0.5) translateX(150%);
transform: scale(0.5) translateX(150%);
}
50% {
-webkit-transform: scale(1) translateX(-25%);
transform: scale(1) translateX(-25%);
}
75% {
-webkit-transform: scale(1) translateX(300%);
transform: scale(1) translateX(300%);
}
100% {
-webkit-transform: scale(0.5) translateX(150%);
transform: scale(0.5) translateX(150%);
}
}
@keyframes pull-left {
0% {
-webkit-transform: scale(0.5) translateX(150%);
transform: scale(0.5) translateX(150%);
}
50% {
-webkit-transform: scale(1) translateX(-25%);
transform: scale(1) translateX(-25%);
}
75% {
-webkit-transform: scale(1) translateX(300%);
transform: scale(1) translateX(300%);
}
100% {
-webkit-transform: scale(0.5) translateX(150%);
transform: scale(0.5) translateX(150%);
}
}
@-webkit-keyframes pull-right {
0% {
-webkit-transform: scale(0.5) translateX(-150%);
transform: scale(0.5) translateX(-150%);
}
50% {
-webkit-transform: scale(1) translateX(25%);
transform: scale(1) translateX(25%);
}
80% {
-webkit-transform: scale(0.5) translateX(-150%);
transform: scale(0.5) translateX(-150%);
}
90% {
-webkit-transform: scale(0.5) translateX(-300%);
transform: scale(0.5) translateX(-300%);
}
100% {
-webkit-transform: scale(0.5) translateX(-150%);
transform: scale(0.5) translateX(-150%);
}
}
@keyframes pull-right {
0% {
-webkit-transform: scale(0.5) translateX(-150%);
transform: scale(0.5) translateX(-150%);
}
50% {
-webkit-transform: scale(1) translateX(25%);
transform: scale(1) translateX(25%);
}
80% {
-webkit-transform: scale(0.5) translateX(-150%);
transform: scale(0.5) translateX(-150%);
}
90% {
-webkit-transform: scale(0.5) translateX(-300%);
transform: scale(0.5) translateX(-300%);
}
100% {
-webkit-transform: scale(0.5) translateX(-150%);
transform: scale(0.5) translateX(-150%);
}
}
.hide {
display: none;
}
\ No newline at end of file
body {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
height: 100vh;
}
* {
padding: 0;
margin: 0;
}
.container {
width: 100%;
height: 100vh;
display: flex;
justify-content: space-between;
}
.desktop {
flex: 1;
}
.errors-message {
max-width: 25%;
flex: 1;
padding: 20px;
border: 4px solid #ccc;
font-family: Arial;
font-size: 15px;
}
.search-panel {
height: 10vh;
padding-top: 30px;
padding-left: 50px;
background-color: rgba(126,234,180,0.7);
/*border: 1px solid black;*/
}
.search-panel > input {
height: 25px;
padding-left: 15px;
outline: none;
background-color: rgba(255,255,255,0.9);
border: 2px solid rgb(255,255,255);;
}
.search-panel > button {
height: 29px;
padding: 0 10px;
text-align: center;
border: 2px solid #3265f2;
background-color: #4f76e2;
border-radius: 5px;
color: #fff;
outline: none;
cursor: pointer;
}
.graph {
width: 100%;
background-color: rgba(51,51,51,0.2);
}
.links line {
stroke-width: 2px;
}
.nodes circle {
pointer-events: all;
stroke: cadetblue;
stroke-width: 2px;
r:9px;
fill: #fff;
}
.myText text {
font-weight: bold;
font-family: Arial;
}
.nodes circle:first-child {
r:15px;
fill: crimson;
}
\ No newline at end of file
import * as d3 from "d3";
function drowGraph(data) {
let w = 1000;
let h = 550;
let graph = data;
let svg = d3.select(".graph")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.style('fill', 'rgba(51,51,51,0.2)');
let simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody().strength(-100))
.force("center", d3.forceCenter(w / 2, h / 2));
let link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line");
link.attr('stroke', function (d) {return d.value});
let node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr('fill', function (d) {if(d.index == 0){return 'red';}})
.attr('r', function (d) {if(d.index == 0){return '8';}})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
let myText = svg.selectAll(".mytext")
.data(graph.nodes)
.enter()
.append("text");
//the rest of your code
node
.append("title")
.text(function(d) { return d.id; });
myText
.style("fill", "#333")
.attr("width", "6")
.attr("height", "6")
.attr('font-size', '10px')
.attr("font-weight","bold")
.attr("font-family","Arial")
.text(function(d) { return 'id-' + d.id; });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
function ticked() {
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
myText
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; });
}
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
}
export default drowGraph;
\ No newline at end of file
import './css/style.css';
import './css/preloader.css';
import request from './request';
console.log(1);
function runApp(input) {
let links = [];
let arr = [];
let id = parseInt(input.value);
if (!id) {return false;}
let graphArea = document.querySelector('.graph');
let errors = document.querySelector('.errors-message');
graphArea.innerHTML = '';
errors.innerHTML = '';
let bugs = [id];
request(id,0,bugs,links,arr);
}
let btn = document.querySelector('button');
let input = document.getElementById('bugId');
btn.addEventListener('click', runApp.bind(this,input));
import drowGraph from './drowGraph'
function request(id, count, bugs, links, requests) {
let preloader = document.querySelector('.preloader');
let graph = document.querySelector('.graph');
let xhr = new XMLHttpRequest();
xhr.open("GET", 'https://bugs.etersoft.ru/rest/bug/' + id + '?include_fields=depends_on,blocks', true);
requests.push(xhr);
xhr.send();
console.log('id- ', id);
console.log('count', count);
preloader.classList.remove('hide');
graph.classList.add('hide');
xhr.onreadystatechange = function () {
if(xhr.status === 401) {
alertMsg(id);
}
if (xhr.readyState == 4 && xhr.status == 200) {
let bagText = JSON.parse(xhr.responseText);
//получем боги от которых зависит запрашивемый баг
let bagBlock = bagText.bugs[0].blocks;
//получем зависящие от него баги
let bagDepends = bagText.bugs[0].depends_on;
//создаем динаимический массив зависимостей бага
let childBugs = [...returnElem(bagBlock), ...returnElem(bagDepends)];
console.log('это текущие', childBugs);
//добавляем узлы графа
for (let i = 0; i < childBugs.length; i++) {
if (bugs.indexOf(childBugs[i]) === -1) {
bugs.push(childBugs[i]);
}
}
//создаине массива зависимостей
let i = 4 >= bagBlock.length ? bagBlock.length - 1 : 3;
for (; i >= 0; i--) {
links.push(createLinks(id, bagBlock[i], 'green'));
}
let g = 4 >= bagDepends.length ? bagDepends.length - 1 : 3;
for (; g >= 0; g--) {
links.push(createLinks(id, bagDepends[g], 'red'));
}
if (count < 3) {
//отправляем запрос для зависимотей бага
childBugs.forEach(function (value) {
return request(value, count + 1, bugs, links, requests)
});
//если все запросы выполненны
} else if(requests.every(cgeckReq)) {
//преобразуем массив узлов
let nodes = createNodes(bugs)
preloader.classList.add('hide');
graph.classList.remove('hide');
//отрисовываем граф
drowGraph({'nodes': nodes, 'links': links});
return;
}
}
}
}
function createNodes(arr) {
let nodes = []
arr.forEach(function (id) {
nodes.push({'id':id })
});
return nodes;
}
function createLinks(id, target, value) {
return {'source':id, 'target': target, 'value': value}
}
function returnElem(arr) {
let firstElem = [];
let i = 4 >= arr.length ? arr.length-1 : 3;
for(; i >= 0; i--){
firstElem.push(arr[i]);
}
return firstElem;
}
function cgeckReq(item) {
return item.readyState == 4;
}
function alertMsg(id) {
let p = document.createElement('p');
p.innerHTML = `Информация об ошибке с id ${id} недоступна`;
let container = document.querySelector('.errors-message');
container.appendChild(p);
}
export default request;
\ No newline at end of file
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: "main.js",
path: path.resolve(__dirname, 'dist'),
publicPath: 'dist/'
},
module: {
rules: [
{
test: /\.js/,
exclude: /node_modules/,
use: [
{
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"],
plugins: ["@babel/plugin-transform-runtime"]
}
}
]
},
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
}
]
}
};
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment