Spinwheel using .NET Core and Winsheel.js

 Hi All,

Recently I had request from one of my friends to create a small application to distribute 1000 gifts among employees of a certain company.

1) There were 5 types of gifts

2) Each type had 200 gifts

3) Gifts must be given in a random way

4) Every employee must get a gift

5) They should not get a gift which is over

So created a small application with a .NET Core backend, html, javascript. I used Winwheel.js to create the spinwheel. Hope you like it.

1) Solution 










2) SQL Tables









3) HomeController.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using SpinWheelAppCore.Models;

namespace SpinWheelAppCore.Controllers
{
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;
        private readonly IConfiguration _configuration;
        private readonly PrizeContext _entities;

        public int MaxPrizeCount
        {
            get
            {
                return int.Parse(_configuration["MaxPrizeCount"]);
            }
        }
        public int MaxPrizeTotalCount
        {
            get
            {
                return int.Parse(_configuration["MaxPrizeTotalCount"]);

            }
        }
        public int PrizeCategoryCount
        {
            get
            {
                return int.Parse(_configuration["PrizeCategoryCount"]);

            }
        }

        public HomeController(ILogger<HomeController> logger,
            IConfiguration configuration,
            PrizeContext entities)
        {
            _logger = logger;
            _configuration = configuration;
            _entities = entities;
        }

        public IActionResult Index()
        {
            return View(new HomeViewModel() { MaxPrizeTotalCount = MaxPrizeTotalCount,PrizeCategoryCount=PrizeCategoryCount });
        }

        public IActionResult Reset()
        {
            return View();
        }

        [HttpPost]
        public IActionResult Reset(string submitButton)
        {
            if (!string.IsNullOrEmpty(submitButton) && submitButton == "Yes")
            {
                _entities.Prize.RemoveRange(_entities.Prize);
                _entities.SaveChanges();
            }
               




            

            return RedirectToAction("Index");
        }



        public ReturnDataMsg GetResults()
        {
            ReturnDataMsg returnDataMsg = new ReturnDataMsg();

            try
            {
                int count = _entities.Prize.Count();


                var prizeList = (from pc in _entities.PrizeCategory
                                 select new PrizeCount() { PrizeName = pc.Name, Count = MaxPrizeCount }).ToList();

                var prizeWonList = (from p in _entities.Prize
                                    orderby p.Prize1
                                    group p by p.Prize1 into grp
                                    select new PrizeCount() { PrizeName = grp.Key, Count = MaxPrizeCount - grp.Count() }).ToList();

                returnDataMsg.TotalZeroCount = prizeWonList.Where(p => p.Count == 0).Count();

                PrizeCount objFound = null;
                foreach (var prize in prizeList)
                {
                    objFound = prizeWonList.Where(p => p.PrizeName == prize.PrizeName).SingleOrDefault();
                    if (objFound != null)
                    {
                        prize.Count = objFound.Count;
                    }
                    objFound = null;
                }

                returnDataMsg.PrizeCounts = prizeList;
                returnDataMsg.TotalCount = count;
            }
            catch (Exception ex)
            {


            }

            return returnDataMsg;

        }

        [HttpPost]
        public ReturnDataMsg PostPrize(string id)
        {
            ReturnDataMsg returnDataMsg = new ReturnDataMsg();

            try
            {
                if (!string.IsNullOrEmpty(id))
                {
                    int count = _entities.Prize.Count();

                    List<PrizeCount> prizeWonList = (from p in _entities.Prize
                                                     orderby p.Prize1
                                                     group p by p.Prize1 into grp
                                                     select new PrizeCount() { PrizeName = grp.Key, Count = MaxPrizeCount - grp.Count() }).ToList();


                    //bool havePrize = prizeWonList.Where(l => l.PrizeName == value && l.Count > 0).SingleOrDefault() != null;

                    if (count < MaxPrizeTotalCount)
                    {

                        _entities.Prize.Add(new Prize() { Prize1 = id, CreatedOn = DateTime.Now });
                        _entities.SaveChanges();
                        count++;
                    }

                    prizeWonList = (from p in _entities.Prize
                                    orderby p.Prize1
                                    group p by p.Prize1 into grp
                                    select new PrizeCount() { PrizeName = grp.Key, Count = MaxPrizeCount - grp.Count() }).ToList();

                    var prizeList = (from pc in _entities.PrizeCategory
                                     select new PrizeCount() { PrizeName = pc.Name, Count = MaxPrizeCount }).ToList();


                    returnDataMsg.TotalZeroCount = prizeWonList.Where(p => p.Count == 0).Count();

                    PrizeCount objFound = null;
                    foreach (var prize in prizeList)
                    {
                        objFound = prizeWonList.Where(p => p.PrizeName == prize.PrizeName).SingleOrDefault();
                        if (objFound != null)
                        {
                            prize.Count = objFound.Count;
                        }
                        objFound = null;
                    }

                    returnDataMsg.PrizeCounts = prizeList;
                    returnDataMsg.TotalCount = count;

                }

            }
            catch (Exception ex)
            {

            }

            return returnDataMsg;
        }
    }
}

4) Models.cs

using System;
using System.Collections.Generic;

namespace SpinWheelAppCore.Models
{
    public class ErrorViewModel
    {
        public string RequestId { get; set; }

        public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
    }

    public class HomeViewModel {
        public int MaxPrizeTotalCount
        {
            get;set;
        }

        public int PrizeCategoryCount
        {
            get; set;
        }

    }

    public class PrizeCount
    {
        public string PrizeName { get; set; }
        public int Count { get; set; }

    }

    public class ReturnDataMsg
    {
        public List<PrizeCount> PrizeCounts { get; set; }
        public int TotalCount { get; set; }
        public int TotalZeroCount { get; set; }
    }
}

5) Index View (Index.cshtml)

@model HomeViewModel


@{

    ViewData["Title"] = "Click on the Wheel to Play & Win";

}


< <div id="completeModal" class="modal" tabindex="-1" role="dialog">

    <div class="modal-dialog" role="document">

        <div class="modal-content">

            <div class="modal-header">

                <h5 class="modal-title">Competition Finished</h5>


            </div>

            <div class="modal-body">

                <p>Congratulations to all Prize Winners. Have a nice day!</p>

            </div>

            <div class="modal-footer">


            </div>

        </div>

    </div>

</div>


<div class="row">


    <div class="col-md-8">

        <table cellpadding="0" cellspacing="0" align="center" valign="center" border="0">

            <tr>

                <td height="580" class="the_wheel">

                    <canvas onclick="startSpin();" id="canvas" width="434" height="434">

                        <p style="{color: white}" align="center">Sorry, your browser doesn't support canvas. Please try another.</p>

                    </canvas>

                </td>

            </tr>


        </table>


    </div>

    <div class="col-md-4">

        <div class="container">

            <div class="card-deck mb-3 text-center">

                <div class="card mb-4 box-shadow">

                    <div class="card-header">

                        <h4 class="my-0 font-weight-normal">Results</h4>

                    </div>

                    <div id="resultBody1" class="card-body">





                    </div>

                    

                    <div id="resultFooter" style="display:none" class="card-footer">

                        <a asp-action="Reset" class="btn btn-lg btn-block btn-primary">Reset</a>

                    </div>

                </div>


            </div>



        </div>




    </div>

</div>



<script type="text/javascript">






    // Create new wheel object specifying the parameters at creation time.

    let theWheel = new Winwheel({

        'numSegments': 5,     // Specify number of segments.

        'outerRadius': 212,   // Set outer radius so wheel fits inside the background.

        'textFontSize': 28,    // Set font size as desired.

        //'segments'     :        // Define segments including colour and text.

        //[

        //   {'fillStyle' : '#888888', 'text' : 'Prize 1'},

        //   {'fillStyle' : '#89f26e', 'text' : 'Prize 2'},

        //   {'fillStyle' : '#7de6ef', 'text' : 'Prize 3'},

        //   {'fillStyle' : '#e7706f', 'text' : 'Prize 4'},

        //   {'fillStyle' : '#eae56f', 'text' : 'Prize 5'}


        //],

        'animation':           // Specify the animation to use.

        {

            'type': 'spinToStop',

            'duration': 5,     // Duration in seconds.

            'spins': 12,     // Number of complete spins.

            'callbackFinished': alertPrize

        }

    });


    // Vars used by the code in this page to do power controls.

    let wheelPower = 0;

    let wheelSpinning = false;


    // -------------------------------------------------------

    // Function to handle the onClick on the power buttons.

    // -------------------------------------------------------

    function powerSelected(powerLevel) {

        // Ensure that power can't be changed while wheel is spinning.

        if (wheelSpinning == false) {

            // Reset all to grey incase this is not the first time the user has selected the power.



            // Now light up all cells below-and-including the one selected by changing the class.



            // Set wheelPower var used when spin button is clicked.

            wheelPower = powerLevel;



        }

    }


    // -------------------------------------------------------

    // Click handler for spin button.

    // -------------------------------------------------------

    function startSpin() {

        // Ensure that spinning can't be clicked again while already running.

        if (wheelSpinning == false) {

            // Based on the power level selected adjust the number of spins for the wheel, the more times is has

            // to rotate with the duration of the animation the quicker the wheel spins.

            if (wheelPower == 1) {

                theWheel.animation.spins = 3;

            } else if (wheelPower == 2) {

                theWheel.animation.spins = 8;

            } else if (wheelPower == 3) {

                theWheel.animation.spins = 15;

            }


            // Disable the spin button so can't click again while wheel is spinning.

            //document.getElementById('spin_button').src       = "spin_off.png";

            //document.getElementById('spin_button').className = "";


            // Begin the spin animation by calling startAnimation on the wheel object.

            theWheel.startAnimation();


            // Set to true so that power can't be changed and spin button re-enabled during

            // the current animation. The user will have to reset before spinning again.

            wheelSpinning = true;

        }

    }


    // -------------------------------------------------------

    // Function for reset button.

    // -------------------------------------------------------

    function resetWheel() {

        theWheel.stopAnimation(false);  // Stop the animation, false as param so does not call callback function.

        theWheel.rotationAngle = 0;     // Re-set the wheel angle to 0 degrees.

        theWheel.draw();                // Call draw to render changes to the wheel.




        wheelSpinning = false;          // Reset to false to power buttons and spin can be clicked again.

    }


    // -------------------------------------------------------

    // Called when the spin animation has finished by the callback feature of the wheel because I specified callback in the parameters

    // note the indicated segment is passed in as a parmeter as 99% of the time you will want to know this to inform the user of their prize.

    // -------------------------------------------------------

    function alertPrize(indicatedSegment) {


        // Do basic alert of the segment text. You would probably want to do something more interesting with this information.

        bootbox.alert("Congratulations!!!  You have won " + indicatedSegment.text, function () {


            postResult(indicatedSegment.text);

        });



    }



    function getResult() {

        $.get("../Home/GetResults", function (data) {

            

            if (data) {



                initWheel(data);



            }

        });

    }


    function drawInitialWheel() {


        var segments = [

            { 'fillStyle': '#888888', 'text': 'Prize 1' },

            { 'fillStyle': '#89f26e', 'text': 'Prize 2' },

            { 'fillStyle': '#7de6ef', 'text': 'Prize 3' },

            { 'fillStyle': '#e7706f', 'text': 'Prize 4' },

            { 'fillStyle': '#eae56f', 'text': 'Prize 5' }


        ];


        for (var l = 0; l < theWheel.segments.length; l++) {

            theWheel.deleteSegment(l);


        }



        for (var i = 0; i < segments.length; i++) {

            theWheel.addSegment({ 'fillStyle': segments[i]["fillStyle"], 'text': segments[i]["text"] });

        }

    }


    function initWheel(data) {


        $("#resultBody1").empty();

        $("#resultBody1").append("<h1 class='totalCard card-title pricing-card-title'> " + data.totalCount + "/@Model.MaxPrizeTotalCount" + " <small class='text-muted'>Prizes Won</small> </h1>");


        if (data.totalCount > 0) {

            $("#resultFooter").attr("style", "display:normal");


        }


        if (data.prizeCounts) {



           




            let tempSegments = theWheel.segments.map((x) => x);



            for (var l = 1; l < theWheel.segments.length; l++) {

                theWheel.deleteSegment(l);


            }


            //theWheel.deleteAllSegments();


            //theWheel.deleteSegment();


            //console.log(theWheel.numSegments);



            var segIndex = 1;

            var addedSegments = false;


            for (var i = 0; i < data.prizeCounts.length; i++) {

                $("#resultBody1").append("<h3 class='resultCard card-title pricing-card-title'>" + data.prizeCounts[i].count + " <small class='text-muted'>" + data.prizeCounts[i].prizeName + " Remaining </small> </h3>");




                let segment = null;

                let dataPrize = null;

                for (var l = 0; l < tempSegments.length; l++) {

                    if (tempSegments[l] && tempSegments[l].text === data.prizeCounts[i].prizeName && data.prizeCounts[i].count > 0) {

                        segment = tempSegments[l];

                        dataPrize = data.prizeCounts[i];

                        break;

                    }


                }


            


                if (segment) {


                    if (i === 0) {

                        segment.startAngle = 0;

                        segment.endAngle = 360 / (5 - data.TotalZeroCount);

                        theWheel.addSegment(segment, segIndex++);


                        addedSegments = true;

                    }

                    else {


                        let exSegment = null;


                        for (var l = 0; l < theWheel.segments.length; l++) {

                            if (theWheel.segments[l] && theWheel.segments[l].text === segment.text) {

                                exSegment = theWheel.segments[l];

                                break;

                            }


                        }



                        if (exSegment === null && data.TotalZeroCount != @Model.PrizeCategoryCount -1) {


                            theWheel.addSegment({ 'fillStyle': segment.fillStyle, 'text': segment.text }, segIndex++);


                            addedSegments = true;

                        }


                                              else {

                            segment.startAngle = 0;

                            segment.endAngle = 360 / (@Model.PrizeCategoryCount - data.totalZeroCount);


                            theWheel.addSegment(segment, segIndex++);

                        }



                                         


                                     }



                }


            }




            if (addedSegments || (data.totalZeroCount === @Model.PrizeCategoryCount  -1))

            theWheel.deleteSegment(segIndex);


            theWheel.draw();


        }


        if (data.totalCount === @Model.MaxPrizeTotalCount) {

            $("#completeModal").modal({

                backdrop: 'static',

                keyboard: false

            });


        }

    }


    function postResult(value) {



        


        $.post("../Home/PostPrize/" + value, function (data) {


            if (data) {



                initWheel(data);

            }



        });



        resetWheel();

    }

    drawInitialWheel();

    powerSelected(3);

    getResult();

</script>

6) In Action







Comments