Veebirakenduste loomine API päringute abil

Tööristad: Visual Studio 2022, Visual Studio Code, Postman, Node.js

valime VS-s

Lõpplik Solution Explorer välimus

Esimene avalik API otspunkt, milleks on “hello-world”.

using Microsoft.AspNetCore.Mvc;

namespace veeb.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class PrimitiividController : ControllerBase
    {

        // GET: primitiivid/hello-world
        [HttpGet("hello-world")]
        public string HelloWorld()
        {
            return "Hello world at " + DateTime.Now;
        }

    }
}

Suuname kontrollerinimele ja metoodile, ning näeme sisu

Avalik API mis kuvab sisestatud nimi

[Route("[controller]")]
[ApiController]
public class PrimitiividController : ControllerBase
{

    // GET: primitiivid/hello-world
    [HttpGet("hello-world")]
    public string HelloWorld()
    {
        return "Hello world at " + DateTime.Now;
    }

    // GET: primitiivid/hello-variable/mari
    [HttpGet("hello-variable/{nimi}")]
    public string HelloVariable(string nimi)
    {
        return "Hello " + nimi;
    }
}

Pärast metoodi nimet anname tähenduse  /oma nimi

API mis loob logid käsureas

// GET: primitiivid/do-logs/5
[HttpGet("do-logs/{arv}")]
public void DoLogs(int arv)
{
    for (int i = 0; i < arv; i++)
    {
        Console.WriteLine("See on logi nr " + i);
    }
}

Loob käsureal logid antud arvuga pärast metoodit

Iseseisev harjutus: API, mis näitab sisestatud arvuga aasta – teie kategooria

// GET: primitiivid/aasta/2000
[HttpGet("aasta/{nr1}")]
public string Synniaasta(string nr1)
{
    string AA1970 = "Sina oled vana";
    string AA2000 = "Sina oled noor";
    string AA2010 = "Sina oled väga noor";
    string NoData = "Palun, sisesta arv";

    int numericvalue;
    bool isNumber = int.TryParse(nr1, out numericvalue);

    if (!isNumber)
    {
        return NoData;
    }

    if (numericvalue < 1970)
    {
        return AA1970;
    }
    else if (numericvalue < 2000)
    {
        return AA2000;
    }
    else if (numericvalue < 2010)
    {
        return AA2010;
    }
    else
    {
        return NoData;
    }
}

Sisestame oma sündimisaasta ja näeme järgmise tulemuse

Iseseisev harjutus: API, mis genereerib kaks arvu

// GET: primitiivid/paar-arvud
[HttpGet("paar-arvud")]
public string Paararvud()
{
    Random rand = new Random();

    int Nr1 = rand.Next(1,100);
    int Nr2 = rand.Next(1,100);

    return $"Arvud on: {Nr1} ja {Nr2}";

}

Metood genereerib paar arvudele  1-100 väärtuse

Mudelite loomine

Iseseisev harjutus: Luua kasutaja mudel

Teeme kontrollerile API otspunkti, mis annab eerakendusele vastloodud toote.

[Route("[controller]")]
[ApiController]
public class ToodeController : ControllerBase
{
    private static Toode _toode = new Toode(1, "Koola", 1.5, true);

    // GET: toode
    [HttpGet]
    public Toode GetToode()
    {
        return _toode;
    }

    // GET: toode/suurenda-hinda
    [HttpGet("suurenda-hinda")]
    public Toode SuurendaHinda()
    {
        _toode.Price = _toode.Price + 1;
        return _toode;
    }
}

Metood suurendab hinne ühele

Iseseisev harjutus: API otspunkt, mis käivitades muudab toote aktiivsust

// GET: toode/false-true-false
[HttpGet("false-true-false")]
public Toode FalseTrueFalse()
{
    if(_toode.IsActive == true)
    {
        _toode.IsActive = _toode.IsActive = false;
    }
    else
    {
        _toode.IsActive = _toode.IsActive = true;
    }               
    return _toode;
}

Näeme, et algusel on isActive true

Pärast metoodi käivitamist näeme, et isActive muutub false -le

Iseseisev harjutus: API otspunkt, mis käivitades muudab toote nime

// GET: toode/ümbernimetamine
[HttpGet("ümbernimetamine")]
public Toode FalseTrueFalse(string Name)
{
    _toode.Name = Name;

    return _toode;
}

Toode nimi oli Koka, muudame selle Coca -ks

Iseseisev harjutus: API otspunkt, mis käivitades muudab toote hinda muutujana antud numbri kordseks

// GET: toode/uushind
[HttpGet("uushind")]
public Toode Uushind(float Xkorda)
{
    _toode.Price *= Xkorda;

    return _toode;
}

Muudame hind väärtusele  korrutada 1.5 hind

API otspunkti, mis annab eesrakendusele vastloodud tooted.

// https://localhost:port/tooted
[HttpGet]
public List<Toode> Get()
{
    return _tooted;
}

Näitab kõik tooded

API otspunkti “kustuta/”, millele lisatakse järjekorranumber

[HttpGet("kustuta/{index}")]
public List<Toode> Delete(int index)
{
    _tooted.RemoveAt(index);
    return _tooted;
}

Kustutame toode index -i järgi

API, mille pärast toote kustutamist tagastada eesrakendusele sõnum – kui eesrakendus selle sõnumi kätte saab, siis on kustutamine olnud edukas.

[HttpGet("kustuta2/{index}")]
public string Delete2(int index)
{
    _tooted.RemoveAt(index);
    return "Kustutatud!";
}

Kustutame toode ja saame tulemusena Kustutatud!

API otspunkt, millega on võimalik ühte toodet juurde lisada.

[HttpGet("lisa/{id}/{nimi}/{hind}/{aktiivne}")]
public List<Toode> Add(int id, string nimi, double hind, bool aktiivne)
{
    Toode toode = new Toode(id, nimi, hind, aktiivne);
    _tooted.Add(toode);
    return _tooted;
}

Loome uue toode /järgi

Päringu parameetritena, mis nõuab API otspunkti järel ? (küsimärgi) taga võtit ja väärtust, mis on & märgiga eraldatud

[HttpGet("lisa")] // GET /tooted/lisa?id=1&nimi=Koola&hind=1.5&aktiivne=true
public List<Toode> Add2([FromQuery] int id, [FromQuery] string nimi, [FromQuery] double hind, [FromQuery] bool aktiivne)
{
    Toode toode = new Toode(id, nimi, hind, aktiivne);
    _tooted.Add(toode);
    return _tooted;
}

loome toode läbi päringu

API, milles muudame eurost dollaritesse for tsükliga

[HttpGet("hind-dollaritesse/{kurss}")] // GET /tooted/hind-dollaritesse/1.5
public List<Toode> Dollaritesse(double kurss)
{
    for (int i = 0; i < _tooted.Count; i++)
    {
        _tooted[i].Price = _tooted[i].Price * kurss;
    }
    return _tooted;
}

Muudame hind korrutada määratud kurssile

API, milles muudame eurost dollaritesse foreach tsükliga

// või foreachina:

[HttpGet("hind-dollaritesse2/{kurss}")] // GET /tooted/hind-dollaritesse2/1.5
public List<Toode> Dollaritesse2(double kurss)
{
    foreach (var t in _tooted)
    {
        t.Price = t.Price * kurss;
    }

    return _tooted;
}

Läbi foreach võib ka seda teha (kurssi meetod)

Iseseisev harjutus: API otspunkt, mis kustutab korraga kõik tooted

[HttpGet("kustuta-tooded")] // GET /tooted/kustuta-tooded
public List&lt;Toode> Kustutakoik()
{
    _tooted.Clear();
    return _tooted;
}

Näeme kõik tooded
Pärast metoodi käivitamist, enam toodeid pole

Iseseisev harjutus: API otspunkt, mis muudab kõikide toodete aktiivsuse väära peale

[HttpGet("aktiivsus-väär")]
public List&lt;Toode> Aktiivsus_vaar()
{
    foreach (var t in _tooted)
    {
        t.IsActive = false;
    }
    return _tooted;
}

Määrame IsActive false-le

Iseseisev harjutus: API otspunkt, mis tagastab ühe toote – vastavalt kelle järjekorranumber on lisatud URL muutujasse

[HttpGet("näita/{id}")] // GET /tooted/näita/id
public ActionResult<Toode> Naita(int id)
{
    var toode = _tooted.Find(t => t.Id == id);
    return toode != null ? toode : NotFound();
}

Näitame toode id-ga

Iseseisev harjutus: API otspunkt, mis tagastab ühe toote – kõige suurema hinnaga toote

[HttpGet("max-hind")] // GET /tooted/max-hind
public ActionResult<Toode> Maxhind()
{
    var toode = _tooted.OrderByDescending(t => t.Price).First();
    return toode != null ? toode : NotFound();
}

Sorteerime kahanevalt ja näitame ainult üks andme

Eesliidese implementeerimine

1. Kui meil pole Node.js siis laadime alla seda.
2. Pärast käsureas meie projektide kaustas loome uus FrontEnd React app.
3. Avame seda uue React appi kausta Visual Studio Code-s
4. Leiame App.js ja muudame seda NB! vaata localhost BackEnd pordi ja peame käivitama seda HTTP.
5. npm start – käivitab Node.js
6. CORS veaga lahendame läbi VS Program.cs lisatud sisuga
app.UseCors(options => options
.WithOrigins(“*”)
.AllowAnyMethod()
.AllowAnyHeader()
);

9. Taaskäivitame seda
10. Nüüd on võimalik tegeleda FrontEnd-ga

Postmanis toode lisamine läbi POST päringu

{
  "id": 321,
  "name": "Red bull",
  "price": 5,
  "isActive": true
}

Iseseisev harjutus: API otspunkt, mis muudab toodet

{
        "name": "Aura vichy",
        "price": 1.5,
        "isActive": false
}

https://localhost:7279/Tooted/uuenda/4
// PUT https://localhost:port/tooted/uuenda/{id}
[HttpPut("uuenda/{id}")]
public ActionResult<Toode> Update(int id, [FromBody] Toode updatedToode)
{
    var Toode = _tooted.Find(t => t.Id == id);
    if (Toode == null)
    {
        return NotFound();
    }

    Toode.Name = updatedToode.Name;
    Toode.Price = updatedToode.Price;
    Toode.IsActive = updatedToode.IsActive;

    return Toode;
}

API päring teise rakendusse: pakiautomaatide kättesaamine

FrontEnd-s näeme röpploend omniva pakkiautomaadide kohad ning api.js määrame oma localhost:number
[Route("[controller]")]
[ApiController]
public class ParcelMachineController : ControllerBase
{
    private readonly HttpClient _httpClient;

    public ParcelMachineController(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    [HttpGet("omniva")]
    public async Task<IActionResult> GetParcelMachinesOmniva()
    {
        var response = await _httpClient.GetAsync("https://www.omniva.ee/locations.json");
        var responseBody = await response.Content.ReadAsStringAsync();
        return Content(responseBody, "application/json");
    }
}

//https://localhost:7279/parcelmachine/omniva

Iseseisev harjutus: API otspunktis SmartPost pakkiautomaatide kohad

SmartPost ei ava ning me ei saa kuvada andmed
[Route("[controller]")]
[ApiController]
public class ParcelMachineController : ControllerBase
{
    private readonly HttpClient _httpClient;

    public ParcelMachineController(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    [HttpGet("omniva")]
    public async Task<IActionResult> GetParcelMachinesOmniva()
    {
        var response = await _httpClient.GetAsync("https://www.omniva.ee/locations.json");
        var responseBody = await response.Content.ReadAsStringAsync();
        return Content(responseBody, "application/json");
    }

    [HttpGet("smartpost")]
    public async Task<IActionResult> GetParcelMachinesSmartPost()
    {
        var response = await _httpClient.GetAsync("https://www.smartpost.ee/places.json");
        var responseBody = await response.Content.ReadAsStringAsync();
        return Content(responseBody, "application/json");
    }
}

API päring teise rakendusse: Elering hindade kättesaamine

Nordpool andmed / https://localhost:7279/nordpool

Eesrakendusel, võttes eesrakendusest vastu kuupäeva ning saate selle Elering API otspunktile.

Nordpool andmed valimisega: riik + päevast – päevani / https://localhost:7279/nordpool

API päring teise rakendusse: Makse

Käivitame päringu https://localhost:7279/Payment/0.1 ning saame lingi maksmisele, suuname sellele ja näeme tulemuse maksmisele

Anname metoodile /summa maksimele ja saame pärast lingi maksmisele

Iseseisev harjutus: API, mis kuvab kasutajad

private static List&lt;Kasutaja> _kasutaja = new()
{
    new Kasutaja(1,"martinkem", "meknitram", "Martin", "Kemppi"),
    new Kasutaja(2,"maksimtse", "estmiskam", "Maksim", "Tsepeleivits"),
    new Kasutaja(3,"darjamil", "limajrad", "Darja", "Miljukova"),
    new Kasutaja(4,"lucagluh", "hulgacul", "Luca", "Gluhhov"),
    new Kasutaja(5,"oleksboha", "ohabskelo", "Oleksandr", "Bohatyrov")
};

// GET https://localhost:4444/Kasutajad
[HttpGet]
public List&lt;Kasutaja> Get()
{
    return _kasutaja;
}

Saame kõik kasutajad

Iseseisev harjutus: API, mis kustutab kasutaja index järgi

// DELETE https://localhost:4444/api/kasutajad/kustuta/0
[HttpDelete("kustuta/{index}")]
public List<Kasutaja> Delete(int index)
{
    _kasutaja.RemoveAt(index);
    return _kasutaja;
}

Oleme kustutatud neljas index ning näeme, et 4+1 Id-ga andmet pole

Iseseisev harjutus: API, mille läbi POST käsu lisame uue kasutaja

// POST https://localhost:4444/api/kasutajad/lisa/1/kasutaja/parool1/mina/tema
[HttpPost("lisa/{id}/{kasutajanimi}/{salasona}/{eesnimi}/{perekonnanimi}")]
public List<Kasutaja> Add(int id, string kasutajanimi, string salasona, string eesnimi, string perekonnanimi)
{
    Kasutaja kasutaja = new Kasutaja(id, kasutajanimi, salasona, eesnimi, perekonnanimi);
    _kasutaja.Add(kasutaja);
    return _kasutaja;
}

Ülevalt võetud andmed sisestamiseks oleme loonud uue kasutaja

Iseseisev harjutus: API, mille läbi POST päringu lisame uue kasutaja

[HttpPost("lisa2")]
public List<Kasutaja> Add2(int id, string kasutajanimi, string salasona, string eesnimi, string perekonnanimi)
{
    Kasutaja kasutaja = new Kasutaja(id, kasutajanimi, salasona, eesnimi, perekonnanimi);
    _kasutaja.Add(kasutaja);
    return _kasutaja;
}

Uue kasutaja loomine

Iseseisev harjutus: Postmanis kasutaja lisamine läbi POST päringu JSON vormis

[HttpPost("lisa")]
public List<Kasutaja> Add([FromBody] Kasutaja kasutaja)
{
    _kasutaja.Add(kasutaja);
    return _kasutaja;
}

Iseseisev harjutus: API, mis kustutab kõik kasutajad

[HttpGet("kustuta-kasutajad")] // GET /kasutajad/kustuta-tooded
public List<Kasutaja> Kustutakoik()
{
    _kasutaja.Clear();
    return _kasutaja;
}

Kustutamise päringu pärast näeme, et andmeid enam pole, aga üleval on näha, et olid.

Iseseisev harjutus: API, mis tagastab kasutaja sisestatud ID-ga

[HttpGet("näita/{id}")] // GET /kasutajad/näita/id
public ActionResult<Kasutaja> Naita(int id)
{
    var kasutaja = _kasutaja.Find(t => t.Id == id);
    return kasutaja != null ? kasutaja : NotFound();
}

Kuvame kasutaja ID-ga

Iseseisev harjutus: Postmanis kasutajate uuendamine läbi PUT päringu JSON vormis

// PUT https://localhost:port/kasutajad/uuenda/{id}
[HttpPut("uuenda/{id}")]
public ActionResult<Kasutaja> Update(int id, [FromBody] Kasutaja uuendatudkasutaja)
{
    var Kasutaja = _kasutaja.Find(t => t.Id == id);
    if (Kasutaja == null)
    {
        return NotFound();
    }

    Kasutaja.Username = uuendatudkasutaja.Username;
    Kasutaja.Password = uuendatudkasutaja.Password;
    Kasutaja.Firstname = uuendatudkasutaja.Firstname;
    Kasutaja.Lastname = uuendatudkasutaja.Lastname;

    return Kasutaja;
}

Töötlemine andmebaasiga

lisame uus kaust “Data” ja Context AB jaoks.

Võtame meie mudelite kaustast klassid ja loome nendest andmebaasi tabelid

public class DBContext : DbContext
{
    public DbSet<Kasutaja> Kasutajad { get; set; }
    public DbSet<Toode> Tooded { get; set; }
    public DbSet<Tellimus> Tellimused { get; set; }

    public DBContext(DbContextOptions<DBContext> options) : base(options)
    {
    }        
}

Ühendame andmebaasiga

{
  "ConnectionStrings": {
    "DefaultConnection": "server=(localdb)\\MSSQLLocalDB;database=WEBAPI;"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Program.cs lisame mõned väljad juurde, näitan enda oma

using Microsoft.EntityFrameworkCore;
using Veebirakenduste_loomine_API_MartinKemppi.Data;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddHttpClient();
builder.Services.AddDbContext<DBContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseCors(options => options
    .WithOrigins("*")
    .AllowAnyMethod()
    .AllowAnyHeader()
);

app.UseAuthorization();

app.MapControllers();

// Ensure the database is created
using (var scope = app.Services.CreateScope())
{
    var context = scope.ServiceProvider.GetRequiredService<DBContext>();
    context.Database.EnsureCreated();
}


app.UseRouting();
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});

app.Run();

Nüüd peame teha migratsiooni ja uuendama andmebaasi (Tools – NUget Package Manager – Package Manager Console)

add-migration "nimi"
update-database

Andmebaas peaks luua, saame seda näha (View – SQL Server Object Explorer – SQL Server – (localdb) – Databases – meie andmebaas)

WEBAPI – meie loodud andmebaas koos AB- tabelitega

Nüüd teeme kontrolleri seotud meie andmebaasiga:

[Route("api/[controller]")]
[ApiController]
public class KTMController : ControllerBase
{
    private readonly DBContext _context;

    public KTMController(DBContext context)
    {
        _context = context;
    }

    // Toode
    [HttpGet("toode")]
    public List<Toode> GetToode()
    {
        return _context.Tooded.ToList();
    }

    [HttpPost("toode")]
    public List<Toode> PostToode([FromBody] Toode toode)
    {
        _context.Tooded.Add(toode);
        _context.SaveChanges();
        return _context.Tooded.ToList();
    }

    [HttpDelete("toode/{id}")]
    public List<Toode> DeleteToode(int id)
    {
        var toode = _context.Tooded.Find(id);
        if (toode != null)
        {
            _context.Tooded.Remove(toode);
            _context.SaveChanges();
        }
        return _context.Tooded.ToList();
    }

    [HttpGet("toode/{id}")]
    public ActionResult<Toode> GetToodeID(int id)
    {
        var toode = _context.Tooded.Find(id);
        if (toode == null)
        {
            return NotFound();
        }
        return toode;
    }

    [HttpPut("toode/{id}")]
    public ActionResult<List<Toode>> PutToode(int id, [FromBody] Toode updatedToode)
    {
        var toode = _context.Tooded.Find(id);
        if (toode == null)
        {
            return NotFound();
        }

        toode.Name = updatedToode.Name;
        toode.Price = updatedToode.Price;
        toode.IsActive = updatedToode.IsActive;

        _context.Tooded.Update(toode);
        _context.SaveChanges();

        return Ok(_context.Tooded);
    }

    // Kasutaja
    [HttpGet("kasutaja")]
    public List<Kasutaja> GetKasutaja()
    {
        return _context.Kasutajad.ToList();
    }

    [HttpPost("kasutaja")]
    public List<Kasutaja> PostKasutaja([FromBody] Kasutaja kasutaja)
    {
        _context.Kasutajad.Add(kasutaja);
        _context.SaveChanges();
        return _context.Kasutajad.ToList();
    }

    [HttpDelete("kasutaja/{id}")]
    public List<Kasutaja> DeleteKasutaja(int id)
    {
        var kasutaja = _context.Kasutajad.Find(id);
        if (kasutaja != null)
        {
            _context.Kasutajad.Remove(kasutaja);
            _context.SaveChanges();
        }
        return _context.Kasutajad.ToList();
    }

    [HttpGet("kasutaja/{id}")]
    public ActionResult<Kasutaja> GetKasutajaID(int id)
    {
        var kasutaja = _context.Kasutajad.Find(id);
        if (kasutaja == null)
        {
            return NotFound();
        }
        return kasutaja;
    }

    [HttpPut("kasutaja/{id}")]
    public ActionResult<List<Kasutaja>> PutKasutaja(int id, [FromBody] Kasutaja updatedKasutaja)
    {
        var kasutaja = _context.Kasutajad.Find(id);
        if (kasutaja == null)
        {
            return NotFound();
        }

        kasutaja.Username = updatedKasutaja.Username;
        kasutaja.Password = updatedKasutaja.Password;
        kasutaja.Firstname = updatedKasutaja.Firstname;
        kasutaja.Lastname = updatedKasutaja.Lastname;

        _context.Kasutajad.Update(kasutaja);
        _context.SaveChanges();

        return Ok(_context.Kasutajad);
    }

    // Tellimus
    [HttpGet("tellimus")]
    public List<Tellimus> GetTellimus()
    {
        return _context.Tellimused
        .Select(t => new Tellimus(
            t.Id,
            t.Kasutaja,
            t.Timestamp,
            t.TooteNimed,
            t.Kogused,
            (float)t.Hind))
        .ToList();
    }

    [HttpPost("tellimus")]
    public IActionResult PostTellimus([FromBody] Tellimus tellimus)
    {
        if (tellimus == null)
        {
            return BadRequest("Invalid order data.");
        }

        Console.WriteLine($"Kasutaja: {tellimus.Kasutaja}, Timestamp: {tellimus.Timestamp}, Hind: {tellimus.Hind}");

        _context.Tellimused.Add(tellimus);

        try
        {
            _context.SaveChanges();
        }
        catch (Exception ex)
        {
            return StatusCode(500, $"Internal server error: {ex.Message}");
        }

        return Ok(_context.Tellimused.ToList());
    }


    [HttpDelete("tellimus/{id}")]
    public List<Tellimus> DeleteTellimus(int id)
    {
        var tellimus = _context.Tellimused.Find(id);
        if (tellimus != null)
        {
            _context.Tellimused.Remove(tellimus);
            _context.SaveChanges();
        }
        return _context.Tellimused.ToList();
    }

    [HttpGet("tellimus/{id}")]
    public ActionResult<Tellimus> GetTellimusID(int id)
    {
        var tellimus = _context.Tellimused.Find(id);
        if (tellimus == null)
        {
            return NotFound();
        }
        return tellimus;
    }

    [HttpPut("tellimus/{id}")]
    public ActionResult<List<Tellimus>> PutTellimus(int id, [FromBody] Tellimus updatedTellimus)
    {
        var tellimus = _context.Tellimused.Find(id);
        if (tellimus == null)
        {
            return NotFound();
        }

        tellimus.Kasutaja = updatedTellimus.Kasutaja;
        tellimus.TooteNimed = updatedTellimus.TooteNimed;
        tellimus.Kogused = updatedTellimus.Kogused;
        tellimus.Hind = updatedTellimus.Hind;

        _context.Tellimused.Update(tellimus);
        _context.SaveChanges();

        return Ok(_context.Tellimused);
    }
}

BackEnd osa lõppenud, läheme FrontEnd-i

FrontEnd andmebaasiga

meil on 4 pea faili: App.js, MainPage.js, OrderPage.js, ProductsPage.js.
Seletus:
App.js – pea leht, kus me näeme registreerimis/logi sisse vormi – juhul kui oleme loginud sisse siis -> Pood, Tellimused, Logi välja
MainPage.js – osa, kus võime registreerida/logida sisse
Productspage.js – Pood, kus me võime näha tooded, lisada, redigeerida, kustutada. Näeme ka meie ostukorv, kus asuvad lisatud ostukorvisse tooded ning vormista tellimus -> Tahame need tooded ära osta.
OrderPage.js – Näeme meie vormistatud tellimused, koos sisuga (tooded, mida oleme valinud)

import React, { useState, useEffect } from 'react';
import { BrowserRouter as Router, Route, Routes, Navigate, Link } from 'react-router-dom';
import MainPage from './MainPage';
import ProductsPage from './ProductsPage';
import OrderPage from './OrderPage';
import 'bootstrap/dist/css/bootstrap.min.css';
import styles from './App.module.css';

function App() {
    const [user, setUser] = useState(null);

    useEffect(() => {
        const storedUser = localStorage.getItem('user');
        if (storedUser) {
            setUser(JSON.parse(storedUser));
        }
    }, []);

    const handleLogin = (userData) => {
        setUser(userData);
        localStorage.setItem('user', JSON.stringify(userData));
    };

    const handleLogout = () => {
        setUser(null);
        localStorage.removeItem('user');
    };

    return (
        <Router>
            <div>
                <header className={styles.header}>
                    <h1>KTM Pood</h1>
                    <nav>
                        <ul className={styles.navList}>
                            {user ? (
                                <>
                                    <li><Link to="/products">Pood</Link></li>
                                    <li><Link to="/order">Tellimused</Link></li>
                                    <li>
                                        <Link to="#" onClick={handleLogout}>Logi välja</Link>
                                    </li>
                                </>
                            ) : null}
                        </ul>
                    </nav>
                </header>
                <main>
                    <Routes>
                        <Route path="/" element={<MainPage onLogin={handleLogin} />} />
                        <Route path="/products" element={user ? <ProductsPage user={user} /> : <Navigate to="/" />} />
                        <Route path="/order" element={user ? <OrderPage user={user} /> : <Navigate to="/" />} />
                    </Routes>
                </main>
            </div>
        </Router>
    );
}

export default App;

import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import styles from './MainPage.module.css';

function MainPage({ onLogin }) {
    const [username, setUsername] = useState('');
    const [password, setPassword] = useState('');
    const [firstname, setFirstname] = useState('');
    const [lastname, setLastname] = useState('');
    const [isRegistering, setIsRegistering] = useState(false);
    const navigate = useNavigate();

    const handleLogin = async (e) => {
        e.preventDefault();
        const response = await fetch('https://localhost:7279/api/ktm/kasutaja');
        const users = await response.json();

        const userData = users.find(user => user.username === username && user.password === password);
        if (userData) {
            onLogin(userData);
            navigate('/products');
        } else {
            alert('Vale kasutajanimi või salasõna');
        }
    };

    const handleRegister = async (e) => {
        e.preventDefault();
        const newUser = { username, password, firstname, lastname };
        const response = await fetch('https://localhost:7279/api/ktm/kasutaja', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(newUser),
        });

        if (response.ok) {
            onLogin(newUser);
            navigate('/products');
        } else {
            alert('Registreerimine pole õnnestunud');
        }
    };

    return (
        <div className={styles.container}>
            <div className={styles.titleContainer}>
                <h1>KTM Pood</h1>
            </div>
            <form onSubmit={isRegistering ? handleRegister : handleLogin} className={styles.formContainer}>
                {isRegistering && (
                    <>
                        <input
                            placeholder="Eesnimi"
                            value={firstname}
                            onChange={(e) => setFirstname(e.target.value)}
                            required
                        />
                        <input
                            placeholder="Perekonnanimi"
                            value={lastname}
                            onChange={(e) => setLastname(e.target.value)}
                            required
                        />
                    </>
                )}
                <input
                    placeholder="Kasutajanimi"
                    value={username}
                    onChange={(e) => setUsername(e.target.value)}
                    required
                />
                <input
                    type="password"
                    placeholder="Salasõna"
                    value={password}
                    onChange={(e) => setPassword(e.target.value)}
                    required
                />
                <button type="submit" className="btn btn-primary">
                    {isRegistering ? 'Registreeri' : 'Logi sisse'}
                </button>
            </form>
            <button onClick={() => setIsRegistering(!isRegistering)} className="btn btn-link">
                {isRegistering ? 'Juba olemas konto? Logi sisse' : 'Vaja uut kontot? Registreeri'}
            </button>
        </div>
    );
}

export default MainPage;

import React, { useState, useEffect } from 'react';
import styles from './ProductsPage.module.css';

const ProductsPage = () => {
    const [products, setProducts] = useState([]);
    const [newProduct, setNewProduct] = useState({ name: '', price: 0, isActive: true });
    const [editProductId, setEditProductId] = useState(null);
    const [cart, setCart] = useState([]);
    const [username, setUsername] = useState('martinkem');

    useEffect(() => {
        fetchProducts();
    }, []);

    const fetchProducts = async () => {
        try {
            const response = await fetch('https://localhost:7279/api/KTM/toode');
            if (!response.ok) {
                throw new Error('Tekkis viga internetiga');
            }
            const data = await response.json();
            console.log('Kättesaadud tooded:', data);
            setProducts(data);
        } catch (error) {
            console.error('Tekkis viga toodete kättesaamisel:', error);
        }
    };

    const handleAddOrUpdateProduct = async () => {
        if (!newProduct.name || newProduct.price <= 0) {
            alert('Palun täida kõik väljad');
            return;
        }

        try {
            const url = editProductId
                ? `https://localhost:7279/api/KTM/toode/${editProductId}`
                : 'https://localhost:7279/api/KTM/toode';

            const method = editProductId ? 'PUT' : 'POST';

            const response = await fetch(url, {
                method: method,
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify(newProduct),
            });

            if (response.ok) {
                await fetchProducts();
                setNewProduct({ name: '', price: 0, isActive: true });
                setEditProductId(null);
            } else {
                alert('Tekkis viga toodete salvestamisel.');
            }
        } catch (error) {
            console.error('Tekkis viga toodete salvestamisel:', error);
        }
    };

    const handleEditProduct = (product) => {
        setEditProductId(product.id);
        setNewProduct({ name: product.name, price: product.price, isActive: product.isActive });
    };

    const handleDeleteProduct = async (productId) => {
        if (window.confirm('Kas ikka soovite kustutada seda toodet?')) {
            try {
                const response = await fetch(`https://localhost:7279/api/KTM/toode/${productId}`, {
                    method: 'DELETE',
                });

                if (response.ok) {
                    await fetchProducts();
                    alert('Toode on kustutatud!');
                } else {
                    alert('Tekkis viga toodete kustutamisel.');
                }
            } catch (error) {
                console.error('Tekkis viga toodete kustutamisel:', error);
            }
        }
    };

    const addToCart = (product) => {
        setCart((prevCart) => {
            const existingProduct = prevCart.find(item => item.id === product.id);
            if (existingProduct) {
                return prevCart.map(item =>
                    item.id === product.id
                        ? { ...item, quantity: item.quantity + 1 }
                        : item
                );
            } else {
                return [...prevCart, { ...product, quantity: 1 }];
            }
        });
    };

    const calculateTotalPrice = () => {
        return cart.reduce((total, item) => {
            const price = item.price || 0;
            return total + price * item.quantity;
        }, 0).toFixed(2);
    };

    const handleRemoveFromCart = (productId) => {
        setCart((prevCart) => prevCart.filter(item => item.id !== productId));
    };

    const placeOrder = async () => {
        if (cart.length === 0) {
            alert('Ostukorv on tühi. Lisage toode ostukorvi tellimuste vormistamiseks.');
            return;
        }

        const order = {
            Id: 0,
            Kasutaja: username,
            Timestamp: new Date().toISOString(),
            TooteNimed: cart.map(item => item.name),
            Kogused: cart.map(item => item.quantity),
            Hind: calculateTotalPrice()
        };

        try {
            const response = await fetch('https://localhost:7279/api/KTM/tellimus', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify(order),
            });

            if (response.ok) {
                alert('Tellimuste vormistamine õnnestus!');
                setCart([]);
            } else {
                alert('Tekkis viga tellimuste koostamise.');
            }
        } catch (error) {
            console.error('Tekkis viga tellimuste koostamisel:', error);
        }
    };

    return (
        <div className={styles.container}>
            <h1>Tooted</h1>
            <ul className={styles.productList}>
                {products.map(product => (
                    <li key={product.id} className={styles.productItem}>
                        {product.name} - €{product.price ? product.price.toFixed(2) : '0.00'}
                        <button onClick={() => addToCart(product)}>Lisa korvi</button>
                        <button onClick={() => handleEditProduct(product)}>Redigeeri</button>
                        <button onClick={() => handleDeleteProduct(product.id)}>Kustuta</button>
                    </li>
                ))}
            </ul>
            <h2>{editProductId ? 'Redigeeri toode' : 'Lisa uus toode'}</h2>
            <input
                type="text"
                value={newProduct.name}
                onChange={(e) => setNewProduct({ ...newProduct, name: e.target.value })}
                placeholder="Toote nimi"
            />
            <input
                type="number"
                value={newProduct.price}
                onChange={(e) => setNewProduct({ ...newProduct, price: parseFloat(e.target.value) || 0 })}
                placeholder="Toote hind"
            />
            <button onClick={handleAddOrUpdateProduct}>{editProductId ? 'Redigeeri toode' : 'Lisa toode'}</button>

            <h2>Ostukorv</h2>
            {cart.length === 0 ? (
                <p>Ostukorv on tühi.</p>
            ) : (
                <ul>
                    {cart.map(item => (
                        <li key={item.id}>
                            {item.name} - {item.quantity} - €{(item.price * item.quantity).toFixed(2)}
                            <button onClick={() => handleRemoveFromCart(item.id)}>Eemalda</button>
                        </li>
                    ))}
                </ul>
            )}
            <h3>Kokku: €{calculateTotalPrice()}</h3>
            <button onClick={placeOrder}>Vormista tellimus</button>
        </div>
    );
};

export default ProductsPage;

import React, { useState, useEffect } from 'react';
import styles from './OrderPage.module.css';

function OrderPage({ user }) {
    const [orders, setOrders] = useState([]);
    const [selectedOrder, setSelectedOrder] = useState(null);
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState(null);

    useEffect(() => {
        const fetchOrders = async () => {
            setLoading(true);
            setError(null);
            try {
                const response = await fetch(`https://localhost:7279/api/KTM/tellimus?username=${user.username}`);
                if (!response.ok) {
                    throw new Error('Tekkis viga tellimuste kättesaamisel');
                }
                const data = await response.json();
                const userOrders = data.filter(order => order.kasutaja === user.username);
                setOrders(userOrders);
            } catch (error) {
                setError(error.message);
                console.error('Tekkis viga tellimuste kättesaamisel:', error);
            } finally {
                setLoading(false);
            }
        };

        fetchOrders();
    }, [user.username]);

    const toggleOrderDetails = (orderId) => {
        if (selectedOrder === orderId) {
            setSelectedOrder(null);
        } else {
            setSelectedOrder(orderId);
        }
    };

    const initiatePayment = async (amount) => {
        try {
            const response = await fetch(`https://localhost:7279/api/payment/${amount}`);
            if (!response.ok) {
                throw new Error('Tekkis viga maksmise initsialiseerimisel. Palun proovi uuesti.');
            }

            const paymentLink = await response.text();
            console.log("Payment link:", paymentLink);

            const cleanedLink = paymentLink.replace(/['"]+/g, '');

            window.open(cleanedLink, "_blank");
        } catch (error) {
            console.error('Tekkis viga maksmise initsialiseerimisel:', error);
            alert('Tekkis viga maksmise initsialiseerimisel. Palun proovi uuesti.');
        }
    };

    return (
        <div className={styles.orderContainer}>
            <h2>Tellimuste ajalugu</h2>
            {loading && <p>Loading orders...</p>}
            {error && <p style={{ color: 'red' }}>{error}</p>}
            {orders.length === 0 && !loading ? (
                <p>Tellimusi ei ole leitud</p>
            ) : (
                <div>
                    {orders.map(order => (
                        <div key={order.id} className={styles.orderCard}>
                            <div onClick={() => toggleOrderDetails(order.id)} style={{ cursor: 'pointer' }}>
                                <h3>Tellimuse nr: {new Date(order.timestamp).toLocaleString()}</h3>
                                <p>Kokku: €{order.hind.toFixed(2)}</p>
                                <button onClick={() => initiatePayment(order.hind.toFixed(2))}>
                                    Maksa tellimus
                                </button>
                            </div>
                            {selectedOrder === order.id && (
                                <div className={styles.orderDetails}>
                                    <h4>Tooted:</h4>
                                    <ul>
                                        {order.tooteNimed.map((productName, index) => (
                                            <li key={index}>
                                                {productName} {order.kogused[index]}TK
                                            </li>
                                        ))}
                                    </ul>
                                    <p><strong>Kokku: €{order.hind.toFixed(2)}</strong></p>
                                </div>
                            )}
                        </div>
                    ))}
                </div>
            )}
        </div>
    );
}

export default OrderPage;

olemas ka MainDashboard.js, aga see ei mõju millegile ja ei ole kasutusel.

import React from 'react';
import { Link } from 'react-router-dom';

function MainDashboard() {
    return (
        <div>
            <h1>Tervist!</h1>
            <p>Vali leht:</p>
            <ul>
                <li>
                    <Link to="/products">Tooded</Link>
                </li>
                <li>
                    <Link to="/order">Tellimused</Link>
                </li>
            </ul>
        </div>
    );
}

export default MainDashboard;

Lisaks me võime lisada bootstrap ja .css failid välimusele.

Lisatud ostukorvi tooded

Vormista tellimus -> tellimus andmebaasi kirjutamine

Tellimuste maksmisel suuname lehele, kus peame maksma enda tellimus