Skip to content

Instantly share code, notes, and snippets.

@cemo
Forked from nyilmaz/data.js
Created January 29, 2019 12:19
Show Gist options
  • Select an option

  • Save cemo/c486baeac339472fc54677fcdd6af9df to your computer and use it in GitHub Desktop.

Select an option

Save cemo/c486baeac339472fc54677fcdd6af9df to your computer and use it in GitHub Desktop.

Revisions

  1. cemo revised this gist Jan 29, 2019. 1 changed file with 6 additions and 1 deletion.
    7 changes: 6 additions & 1 deletion data.js
    Original file line number Diff line number Diff line change
    @@ -8,10 +8,15 @@ window._data = [
    "filled": 10
    },
    {
    "timeslot": "10:00 - 11:00",
    "timeslot": "10:30 - 11:00",
    "capacity": 10,
    "filled": 9
    },
    {
    "timeslot": "11:00 - 11:30",
    "capacity": 14,
    "filled": 9
    },
    {
    "timeslot": "11:00 - 12:00",
    "capacity": 10,
  2. @nyilmaz nyilmaz created this gist Jan 29, 2019.
    72 changes: 72 additions & 0 deletions data.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,72 @@
    window._data = [
    {
    "store": "Atasehir",
    "quotainfo" :[
    {
    "timeslot": "09:00 - 10:00",
    "capacity": 10,
    "filled": 10
    },
    {
    "timeslot": "10:00 - 11:00",
    "capacity": 10,
    "filled": 9
    },
    {
    "timeslot": "11:00 - 12:00",
    "capacity": 10,
    "filled": 8
    },
    {
    "timeslot": "12:00 - 13:00",
    "capacity": 10,
    "filled": 6
    },
    {
    "timeslot": "13:00 - 14:00",
    "capacity": 10,
    "filled": 4
    },
    {
    "timeslot": "14:00 - 15:00",
    "capacity": 10,
    "filled": 0
    }
    ]
    },
    {
    "store": "Novada",
    "quotainfo" :[
    {
    "timeslot": "09:00 - 10:00",
    "capacity": 10,
    "filled": 8
    },
    {
    "timeslot": "10:00 - 11:00",
    "capacity": 10,
    "filled": 7
    },
    {
    "timeslot": "11:00 - 12:00",
    "capacity": 10,
    "filled": 10
    },
    {
    "timeslot": "12:00 - 13:00",
    "capacity": 10,
    "filled": 2
    },
    {
    "timeslot": "13:00 - 14:00",
    "capacity": 10,
    "filled": 8
    },
    {
    "timeslot": "14:00 - 15:00",
    "capacity": 10,
    "filled": 9
    }
    ]
    }
    ];
    56 changes: 56 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,56 @@
    <!DOCTYPE html>
    <meta charset="utf-8">
    <html>
    <head>
    <style>
    .cell {
    fill: black;
    box-sizing: border-box;
    border: 1px solid red;
    }

    rect {
    fill: cadetblue
    }

    rect.selected-timeslot {
    fill: orange;
    box-sizing: content-box;
    border: 2px solid red;
    }

    .capacity-normal {
    fill: green;
    }

    .capacity-danger {
    fill: orange;
    }

    .capacity-full {
    fill: red;
    }

    div.tooltip {
    position: absolute;
    text-align: left;
    width: 100px;
    height: 42px;
    padding: 2px;
    font: 12px sans-serif;
    background: lightsteelblue;
    border: 0px;
    border-radius: 8px;
    pointer-events: none;
    }

    </style>
    <script src="https://d3js.org/d3.v5.min.js"></script>
    </head>
    <body>
    <div id="chart"></div>
    <script src="./padStart.polyfill.js"></script>
    <script src="./data.js"></script>
    <script src="./script.js"></script>
    </body>
    </html>
    15 changes: 15 additions & 0 deletions padStart.polyfill.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,15 @@
    if (!String.prototype.padStart) {
    String.prototype.padStart = function padStart(targetLength, padString) {
    targetLength = targetLength >> 0; //truncate if number, or convert non-number to 0;
    padString = String(typeof padString !== 'undefined' ? padString : ' ');
    if (this.length >= targetLength) {
    return String(this);
    } else {
    targetLength = targetLength - this.length;
    if (targetLength > padString.length) {
    padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed
    }
    return padString.slice(0, targetLength) + String(this);
    }
    };
    }
    107 changes: 107 additions & 0 deletions script.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,107 @@
    const parseTimeslot = timeslot => {
    const [start, end] = timeslot.split('-').map(ts => ts.trim());
    const [startHour, startMin] = start.split(':').map(s => parseInt(s));
    const [endHour, endMin] = end.split(':').map(s => parseInt(s));
    return [startHour * 60 + startMin, endHour * 60 + endMin];
    };

    const formatTimeslot = minute => {
    const hourStart = (Math.floor(minute / 60) + '').padStart(2, '0');
    const minuteStart = ((minute % 60) + '').padStart(2, '0');
    return `${hourStart}:${minuteStart}`;
    };

    const load = function () {
    const mins = Array.from(new Array(48), (x, i) => i * 30);
    const capacityData = window._data;
    const allData = [];

    capacityData.forEach((capacity, i) => {
    mins.forEach((min, j) => {
    const quotainfo = capacity.quotainfo.filter(quota => {
    const [start, end] = parseTimeslot(quota.timeslot);
    return start <= min && end > min;
    });
    allData.push({
    x: j,
    y: i,
    quotainfo,
    min
    });
    });
    });

    const svg = d3.select('#chart')
    .append('svg')
    .attr('width', 1400).attr('height', 800)
    .append('g')
    .attr('transform', 'translate(30, 50)');

    const offset = 15;
    svg.selectAll('.hours')
    .data(mins).enter().append('text')
    .attr('x', (d, i) => 75 + i * 25)
    .attr('y', offset - 5)
    .attr('transform', function() {
    return `rotate(-75, ${d3.select(this).attr('x')}, ${d3.select(this).attr('y')})`;
    })
    .text(formatTimeslot);

    svg.selectAll('.store-name')
    .data(capacityData).enter().append('text')
    .attr('x', () => 5)
    .attr('y', (d, i) => 30 + i * 25)
    .text(d => d.store);

    const tooltip = d3.select("body").append("div")
    .attr("class", "tooltip")
    .style("opacity", 0);

    // console.log(allData);

    svg.selectAll('.cell')
    .data(allData).enter().append('rect')
    .attr('x', d => 70 + d.x * 25)
    .attr('y', d => offset + d.y * 25)
    .attr('width', d => {
    const { quotainfo } = d;
    if (quotainfo.length > 1) {
    throw new Error('found more than one matching slot', d);
    }
    if (quotainfo.length === 0) {
    return 20;
    }
    const [start, end] = parseTimeslot(quotainfo[0].timeslot);
    return end === d.min + 30 ? 20 : 25;
    })
    .attr('height', 20)
    .attr('class', d => {
    const { quotainfo } = d;
    if (quotainfo.length > 1) {
    throw new Error('found more than one matching slot', d);
    }
    if (quotainfo.length === 0) {
    return;
    }
    const fullness = quotainfo[0].filled / quotainfo[0].capacity;
    return fullness < 0.80 ? 'capacity-normal' : (fullness < 0.96 ? 'capacity-danger' : 'capacity-full');
    })
    .on('mouseover', function(d) {
    const { quotainfo } = d;
    if (quotainfo.length) {
    tooltip.transition().duration(200).style('opacity', .9);
    tooltip.html(`${quotainfo[0].timeslot}<br/>Kapasite:${quotainfo[0].capacity}<br/>Dolu:${quotainfo[0].filled}`)
    .style('left', `${d3.event.pageX}px`)
    .style('top', `${d3.event.pageY - 28}px`);
    }
    d3.select(this).classed('selected-timeslot', true);
    })
    .on('mouseout', function() {
    tooltip.transition().duration(300).style('opacity', 0);
    d3.select(this).classed('selected-timeslot', false);
    });


    }

    load();