Added a few controllers and views
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,3 @@
|
||||
bin
|
||||
obj
|
||||
*.db
|
||||
*.db*
|
||||
|
||||
69
Controllers/AuthController.cs
Normal file
69
Controllers/AuthController.cs
Normal file
@@ -0,0 +1,69 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace OneForMe.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class AuthController : ControllerBase
|
||||
{
|
||||
private readonly UserManager<IdentityUser> _userManager;
|
||||
private readonly SignInManager<IdentityUser> _signInManager;
|
||||
|
||||
public AuthController(UserManager<IdentityUser> userManager, SignInManager<IdentityUser> signInManager)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a new user
|
||||
/// </summary>
|
||||
[HttpPost("register")]
|
||||
public async Task<IActionResult> Register([FromBody] RegisterRequest request)
|
||||
{
|
||||
var user = new IdentityUser { UserName = request.Email, Email = request.Email };
|
||||
var result = await _userManager.CreateAsync(user, request.Password);
|
||||
|
||||
if (result.Succeeded)
|
||||
return Ok(new { message = "User registered successfully" });
|
||||
|
||||
return BadRequest(new { errors = result.Errors.Select(e => e.Description) });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Login user
|
||||
/// </summary>
|
||||
[HttpPost("login")]
|
||||
public async Task<IActionResult> Login([FromBody] LoginRequest request)
|
||||
{
|
||||
var result = await _signInManager.PasswordSignInAsync(request.Email, request.Password, false, false);
|
||||
|
||||
if (result.Succeeded)
|
||||
return Ok(new { message = "Login successful" });
|
||||
|
||||
return Unauthorized(new { message = "Invalid email or password" });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logout user
|
||||
/// </summary>
|
||||
[HttpPost("logout")]
|
||||
public async Task<IActionResult> Logout()
|
||||
{
|
||||
await _signInManager.SignOutAsync();
|
||||
return Ok(new { message = "Logout successful" });
|
||||
}
|
||||
}
|
||||
|
||||
public class RegisterRequest
|
||||
{
|
||||
public string Email { get; set; } = string.Empty;
|
||||
public string Password { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class LoginRequest
|
||||
{
|
||||
public string Email { get; set; } = string.Empty;
|
||||
public string Password { get; set; } = string.Empty;
|
||||
}
|
||||
35
Controllers/AuthViewController.cs
Normal file
35
Controllers/AuthViewController.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System.CodeDom.Compiler;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace OneForMe.Controllers;
|
||||
|
||||
[Route("[controller]")]
|
||||
public class AuthViewController : Controller
|
||||
{
|
||||
private readonly SignInManager<IdentityUser> _signInManager;
|
||||
|
||||
public AuthViewController(SignInManager<IdentityUser> signInManager)
|
||||
{
|
||||
_signInManager = signInManager;
|
||||
}
|
||||
|
||||
[HttpGet("Login")]
|
||||
public IActionResult Login()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpGet("Register")]
|
||||
public IActionResult Register()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost("Logout")]
|
||||
public async Task<IActionResult> Logout()
|
||||
{
|
||||
await _signInManager.SignOutAsync();
|
||||
return RedirectToAction("Index", "Home");
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,56 @@
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using OneForMe.Data;
|
||||
using OneForMe.Models;
|
||||
|
||||
namespace OneForMe.Controllers;
|
||||
|
||||
[Authorize]
|
||||
public class HomeController : Controller
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
public HomeController(ApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public IActionResult Index()
|
||||
{
|
||||
return View();
|
||||
if (User.Identity?.IsAuthenticated == true)
|
||||
{
|
||||
return RedirectToAction("Dashboard");
|
||||
}
|
||||
return RedirectToAction("Login", "AuthViewController");
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Dashboard()
|
||||
{
|
||||
var userEmail = User.Identity?.Name;
|
||||
|
||||
var createdOrders = await _context.Orders
|
||||
.Where(o => o.CreatorName == userEmail)
|
||||
.Include(o => o.MenuItems)
|
||||
.Include(o => o.OrderItems)
|
||||
.ThenInclude(oi => oi.MenuItem)
|
||||
.ToListAsync();
|
||||
|
||||
var joinedOrders = await _context.Orders
|
||||
.Where(o => o.OrderItems.Any(oi => oi.ParticipantEmail == userEmail || oi.ParticipantName == userEmail))
|
||||
.Include(o => o.MenuItems)
|
||||
.Include(o => o.OrderItems)
|
||||
.ThenInclude(oi => oi.MenuItem)
|
||||
.ToListAsync();
|
||||
|
||||
var viewModel = new DashboardViewModel
|
||||
{
|
||||
CreatedOrders = createdOrders,
|
||||
JoinedOrders = joinedOrders
|
||||
};
|
||||
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
public IActionResult Privacy()
|
||||
|
||||
118
Controllers/OrderController.cs
Normal file
118
Controllers/OrderController.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using OneForMe.Data;
|
||||
using OneForMe.Models;
|
||||
|
||||
namespace OneForMe.Controllers;
|
||||
|
||||
[Authorize]
|
||||
public class OrderController : Controller
|
||||
{
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
public OrderController(ApplicationDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
// GET: Order/Create
|
||||
public IActionResult Create()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
// POST: Order/Create
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Create(Order order, string[] itemNames, decimal[] itemPrices)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
return View();
|
||||
|
||||
order.OrderCode = GenerateOrderCode();
|
||||
order.CreatorName = User.Identity?.Name ?? "Unknown";
|
||||
|
||||
_context.Orders.Add(order);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
// Add menu items
|
||||
for (int i = 0; i < itemNames.Length; i++)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(itemNames[i]) && itemPrices[i] > 0)
|
||||
{
|
||||
_context.MenuItems.Add(new MenuItem
|
||||
{
|
||||
OrderId = order.Id,
|
||||
Name = itemNames[i],
|
||||
Price = itemPrices[i]
|
||||
});
|
||||
}
|
||||
}
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return RedirectToAction("Details", new { code = order.OrderCode });
|
||||
}
|
||||
|
||||
// GET: Order/Join
|
||||
public async Task<IActionResult> Join(string code)
|
||||
{
|
||||
var order = await _context.Orders
|
||||
.Include(o => o.MenuItems)
|
||||
.Include(o => o.OrderItems)
|
||||
.FirstOrDefaultAsync(o => o.OrderCode == code);
|
||||
|
||||
if (order == null)
|
||||
return NotFound("Order not found");
|
||||
|
||||
if (order.IsClosed)
|
||||
return BadRequest("This order is closed");
|
||||
|
||||
return View(order);
|
||||
}
|
||||
|
||||
// POST: Order/AddItem
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> AddItem(int orderId, int menuItemId, int quantity, string participantName, string? participantEmail)
|
||||
{
|
||||
var order = await _context.Orders.FindAsync(orderId);
|
||||
if (order == null || order.IsClosed)
|
||||
return BadRequest("Order not found or is closed");
|
||||
|
||||
var menuItem = await _context.MenuItems.FindAsync(menuItemId);
|
||||
if (menuItem == null)
|
||||
return BadRequest("Menu item not found");
|
||||
|
||||
var orderItem = new OrderItem
|
||||
{
|
||||
OrderId = orderId,
|
||||
MenuItemId = menuItemId,
|
||||
Quantity = quantity,
|
||||
ParticipantName = participantName,
|
||||
ParticipantEmail = participantEmail
|
||||
};
|
||||
|
||||
_context.OrderItems.Add(orderItem);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
return RedirectToAction("Join", new { code = order.OrderCode });
|
||||
}
|
||||
|
||||
// GET: Order/Details/{code}
|
||||
public async Task<IActionResult> Details(string code)
|
||||
{
|
||||
var order = await _context.Orders
|
||||
.Include(o => o.MenuItems)
|
||||
.Include(o => o.OrderItems)
|
||||
.FirstOrDefaultAsync(o => o.OrderCode == code);
|
||||
|
||||
if (order == null)
|
||||
return NotFound();
|
||||
|
||||
return View(order);
|
||||
}
|
||||
|
||||
private string GenerateOrderCode()
|
||||
{
|
||||
return Guid.NewGuid().ToString().Substring(0, 8).ToUpper();
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using OneForMe.Models;
|
||||
|
||||
namespace OneForMe.Data;
|
||||
|
||||
public class ApplicationDbContext : DbContext
|
||||
public class ApplicationDbContext : IdentityDbContext<IdentityUser>
|
||||
{
|
||||
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
|
||||
{
|
||||
|
||||
398
Migrations/20251129195023_InitialCreate.Designer.cs
generated
Normal file
398
Migrations/20251129195023_InitialCreate.Designer.cs
generated
Normal file
@@ -0,0 +1,398 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using OneForMe.Data;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace OneForMe.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20251129195023_InitialCreate")]
|
||||
partial class InitialCreate
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107");
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OneForMe.Models.MenuItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("OrderId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<decimal>("Price")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("OrderId");
|
||||
|
||||
b.ToTable("MenuItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OneForMe.Models.Order", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("ClosedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CreatorName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsClosed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("OrderCode")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("OrderCode")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Orders");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OneForMe.Models.OrderItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MenuItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("OrderId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("OrderedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ParticipantEmail")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ParticipantName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MenuItemId");
|
||||
|
||||
b.HasIndex("OrderId");
|
||||
|
||||
b.ToTable("OrderItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OneForMe.Models.MenuItem", b =>
|
||||
{
|
||||
b.HasOne("OneForMe.Models.Order", "Order")
|
||||
.WithMany("MenuItems")
|
||||
.HasForeignKey("OrderId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Order");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OneForMe.Models.OrderItem", b =>
|
||||
{
|
||||
b.HasOne("OneForMe.Models.MenuItem", "MenuItem")
|
||||
.WithMany("OrderItems")
|
||||
.HasForeignKey("MenuItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("OneForMe.Models.Order", "Order")
|
||||
.WithMany("OrderItems")
|
||||
.HasForeignKey("OrderId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("MenuItem");
|
||||
|
||||
b.Navigation("Order");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OneForMe.Models.MenuItem", b =>
|
||||
{
|
||||
b.Navigation("OrderItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OneForMe.Models.Order", b =>
|
||||
{
|
||||
b.Navigation("MenuItems");
|
||||
|
||||
b.Navigation("OrderItems");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
320
Migrations/20251129195023_InitialCreate.cs
Normal file
320
Migrations/20251129195023_InitialCreate.cs
Normal file
@@ -0,0 +1,320 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace OneForMe.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class InitialCreate : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetRoles",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Name = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
|
||||
NormalizedName = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUsers",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||
UserName = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
|
||||
NormalizedUserName = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
|
||||
Email = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
|
||||
NormalizedEmail = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
|
||||
EmailConfirmed = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
PasswordHash = table.Column<string>(type: "TEXT", nullable: true),
|
||||
SecurityStamp = table.Column<string>(type: "TEXT", nullable: true),
|
||||
ConcurrencyStamp = table.Column<string>(type: "TEXT", nullable: true),
|
||||
PhoneNumber = table.Column<string>(type: "TEXT", nullable: true),
|
||||
PhoneNumberConfirmed = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
TwoFactorEnabled = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
LockoutEnd = table.Column<DateTimeOffset>(type: "TEXT", nullable: true),
|
||||
LockoutEnabled = table.Column<bool>(type: "INTEGER", nullable: false),
|
||||
AccessFailedCount = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Orders",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
OrderCode = table.Column<string>(type: "TEXT", nullable: false),
|
||||
CreatorName = table.Column<string>(type: "TEXT", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "TEXT", nullable: false),
|
||||
ClosedAt = table.Column<DateTime>(type: "TEXT", nullable: true),
|
||||
IsClosed = table.Column<bool>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Orders", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetRoleClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
RoleId = table.Column<string>(type: "TEXT", nullable: false),
|
||||
ClaimType = table.Column<string>(type: "TEXT", nullable: true),
|
||||
ClaimValue = table.Column<string>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "AspNetRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserClaims",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
UserId = table.Column<string>(type: "TEXT", nullable: false),
|
||||
ClaimType = table.Column<string>(type: "TEXT", nullable: true),
|
||||
ClaimValue = table.Column<string>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserLogins",
|
||||
columns: table => new
|
||||
{
|
||||
LoginProvider = table.Column<string>(type: "TEXT", nullable: false),
|
||||
ProviderKey = table.Column<string>(type: "TEXT", nullable: false),
|
||||
ProviderDisplayName = table.Column<string>(type: "TEXT", nullable: true),
|
||||
UserId = table.Column<string>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserRoles",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(type: "TEXT", nullable: false),
|
||||
RoleId = table.Column<string>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
|
||||
column: x => x.RoleId,
|
||||
principalTable: "AspNetRoles",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "AspNetUserTokens",
|
||||
columns: table => new
|
||||
{
|
||||
UserId = table.Column<string>(type: "TEXT", nullable: false),
|
||||
LoginProvider = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Name = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Value = table.Column<string>(type: "TEXT", nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
|
||||
table.ForeignKey(
|
||||
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
|
||||
column: x => x.UserId,
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "MenuItems",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
OrderId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Name = table.Column<string>(type: "TEXT", nullable: false),
|
||||
Price = table.Column<decimal>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_MenuItems", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_MenuItems_Orders_OrderId",
|
||||
column: x => x.OrderId,
|
||||
principalTable: "Orders",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "OrderItems",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
OrderId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
MenuItemId = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
ParticipantName = table.Column<string>(type: "TEXT", nullable: false),
|
||||
ParticipantEmail = table.Column<string>(type: "TEXT", nullable: true),
|
||||
Quantity = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
OrderedAt = table.Column<DateTime>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_OrderItems", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_OrderItems_MenuItems_MenuItemId",
|
||||
column: x => x.MenuItemId,
|
||||
principalTable: "MenuItems",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
table.ForeignKey(
|
||||
name: "FK_OrderItems_Orders_OrderId",
|
||||
column: x => x.OrderId,
|
||||
principalTable: "Orders",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetRoleClaims_RoleId",
|
||||
table: "AspNetRoleClaims",
|
||||
column: "RoleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "RoleNameIndex",
|
||||
table: "AspNetRoles",
|
||||
column: "NormalizedName",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserClaims_UserId",
|
||||
table: "AspNetUserClaims",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserLogins_UserId",
|
||||
table: "AspNetUserLogins",
|
||||
column: "UserId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AspNetUserRoles_RoleId",
|
||||
table: "AspNetUserRoles",
|
||||
column: "RoleId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "EmailIndex",
|
||||
table: "AspNetUsers",
|
||||
column: "NormalizedEmail");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "UserNameIndex",
|
||||
table: "AspNetUsers",
|
||||
column: "NormalizedUserName",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_MenuItems_OrderId",
|
||||
table: "MenuItems",
|
||||
column: "OrderId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OrderItems_MenuItemId",
|
||||
table: "OrderItems",
|
||||
column: "MenuItemId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_OrderItems_OrderId",
|
||||
table: "OrderItems",
|
||||
column: "OrderId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Orders_OrderCode",
|
||||
table: "Orders",
|
||||
column: "OrderCode",
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetRoleClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserClaims");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserLogins");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserRoles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUserTokens");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "OrderItems");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetRoles");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "AspNetUsers");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "MenuItems");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Orders");
|
||||
}
|
||||
}
|
||||
}
|
||||
395
Migrations/ApplicationDbContextModelSnapshot.cs
Normal file
395
Migrations/ApplicationDbContextModelSnapshot.cs
Normal file
@@ -0,0 +1,395 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using OneForMe.Data;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace OneForMe.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "10.0.0-rc.2.25502.107");
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("RoleNameIndex");
|
||||
|
||||
b.ToTable("AspNetRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetRoleClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("AccessFailedCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ConcurrencyStamp")
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Email")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("EmailConfirmed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("LockoutEnabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTimeOffset?>("LockoutEnd")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedEmail")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("NormalizedUserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PasswordHash")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PhoneNumber")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("PhoneNumberConfirmed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SecurityStamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("TwoFactorEnabled")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("UserName")
|
||||
.HasMaxLength(256)
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("NormalizedEmail")
|
||||
.HasDatabaseName("EmailIndex");
|
||||
|
||||
b.HasIndex("NormalizedUserName")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("UserNameIndex");
|
||||
|
||||
b.ToTable("AspNetUsers", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("ClaimType")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ClaimValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserClaims", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderKey")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ProviderDisplayName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("UserId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("LoginProvider", "ProviderKey");
|
||||
|
||||
b.HasIndex("UserId");
|
||||
|
||||
b.ToTable("AspNetUserLogins", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("RoleId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("UserId", "RoleId");
|
||||
|
||||
b.HasIndex("RoleId");
|
||||
|
||||
b.ToTable("AspNetUserRoles", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.Property<string>("UserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("LoginProvider")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("UserId", "LoginProvider", "Name");
|
||||
|
||||
b.ToTable("AspNetUserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OneForMe.Models.MenuItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("OrderId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<decimal>("Price")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("OrderId");
|
||||
|
||||
b.ToTable("MenuItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OneForMe.Models.Order", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime?>("ClosedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CreatorName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsClosed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("OrderCode")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("OrderCode")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Orders");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OneForMe.Models.OrderItem", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("MenuItemId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("OrderId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("OrderedAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ParticipantEmail")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ParticipantName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Quantity")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("MenuItemId");
|
||||
|
||||
b.HasIndex("OrderId");
|
||||
|
||||
b.ToTable("OrderItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("RoleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||
{
|
||||
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser", null)
|
||||
.WithMany()
|
||||
.HasForeignKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OneForMe.Models.MenuItem", b =>
|
||||
{
|
||||
b.HasOne("OneForMe.Models.Order", "Order")
|
||||
.WithMany("MenuItems")
|
||||
.HasForeignKey("OrderId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Order");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OneForMe.Models.OrderItem", b =>
|
||||
{
|
||||
b.HasOne("OneForMe.Models.MenuItem", "MenuItem")
|
||||
.WithMany("OrderItems")
|
||||
.HasForeignKey("MenuItemId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("OneForMe.Models.Order", "Order")
|
||||
.WithMany("OrderItems")
|
||||
.HasForeignKey("OrderId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("MenuItem");
|
||||
|
||||
b.Navigation("Order");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OneForMe.Models.MenuItem", b =>
|
||||
{
|
||||
b.Navigation("OrderItems");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("OneForMe.Models.Order", b =>
|
||||
{
|
||||
b.Navigation("MenuItems");
|
||||
|
||||
b.Navigation("OrderItems");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
9
Models/DashboardViewModel.cs
Normal file
9
Models/DashboardViewModel.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using OneForMe.Models;
|
||||
|
||||
namespace OneForMe.Models;
|
||||
|
||||
public class DashboardViewModel
|
||||
{
|
||||
public List<Order> CreatedOrders { get; set; } = new();
|
||||
public List<Order> JoinedOrders { get; set; } = new();
|
||||
}
|
||||
@@ -10,5 +10,12 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.0-rc.2.25502.107" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.0-rc.2.25502.107" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="10.0.0-rc.2.25502.107" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
43
Program.cs
43
Program.cs
@@ -1,36 +1,72 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using OneForMe.Data;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddControllersWithViews();
|
||||
|
||||
// Add Swagger/OpenAPI
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen(options =>
|
||||
{
|
||||
options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
|
||||
{
|
||||
Title = "OneForMe API",
|
||||
Version = "v1",
|
||||
Description = "Group Order Management API"
|
||||
});
|
||||
});
|
||||
|
||||
// Add Entity Framework Core with SQLite
|
||||
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
||||
options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection") ??
|
||||
"Data Source=OneForMe.db"));
|
||||
|
||||
// Add Identity
|
||||
builder.Services.AddIdentity<IdentityUser, IdentityRole>(options =>
|
||||
{
|
||||
options.Password.RequiredLength = 6;
|
||||
options.Password.RequireNonAlphanumeric = false;
|
||||
options.Password.RequireUppercase = false;
|
||||
options.Password.RequireLowercase = false;
|
||||
options.Password.RequireDigit = false;
|
||||
})
|
||||
.AddEntityFrameworkStores<ApplicationDbContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Create database on startup
|
||||
// Create and migrate database on startup
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
|
||||
db.Database.EnsureCreated();
|
||||
db.Database.Migrate();
|
||||
}
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI(options =>
|
||||
{
|
||||
options.SwaggerEndpoint("/swagger/v1/swagger.json", "OneForMe API v1");
|
||||
options.RoutePrefix = "swagger";
|
||||
});
|
||||
}
|
||||
|
||||
if (!app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseExceptionHandler("/Home/Error");
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapStaticAssets();
|
||||
@@ -40,5 +76,6 @@ app.MapControllerRoute(
|
||||
pattern: "{controller=Home}/{action=Index}/{id?}")
|
||||
.WithStaticAssets();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
||||
|
||||
67
Views/AuthView/Login.cshtml
Normal file
67
Views/AuthView/Login.cshtml
Normal file
@@ -0,0 +1,67 @@
|
||||
@{
|
||||
ViewData["Title"] = "Login";
|
||||
}
|
||||
|
||||
<div class="container mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card shadow">
|
||||
<div class="card-body p-5">
|
||||
<h2 class="text-center mb-4">Login</h2>
|
||||
|
||||
<form id="loginForm">
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Email</label>
|
||||
<input type="email" class="form-control" id="email" name="email" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Password</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary w-100">Login</button>
|
||||
</form>
|
||||
|
||||
<hr>
|
||||
|
||||
<p class="text-center">Don't have an account? <a asp-controller="AuthView" asp-action="Register">Register here</a></p>
|
||||
|
||||
<div id="message" class="mt-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('loginForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const email = document.getElementById('email').value;
|
||||
const password = document.getElementById('password').value;
|
||||
const messageDiv = document.getElementById('message');
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/auth/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ email, password })
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
messageDiv.innerHTML = '<div class="alert alert-success">Login successful! Redirecting...</div>';
|
||||
setTimeout(() => {
|
||||
window.location.href = '/';
|
||||
}, 1500);
|
||||
} else {
|
||||
const error = await response.json();
|
||||
messageDiv.innerHTML = `<div class="alert alert-danger">${error.message}</div>`;
|
||||
}
|
||||
} catch (error) {
|
||||
messageDiv.innerHTML = `<div class="alert alert-danger">An error occurred: ${error.message}</div>`;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
79
Views/AuthView/Register.cshtml
Normal file
79
Views/AuthView/Register.cshtml
Normal file
@@ -0,0 +1,79 @@
|
||||
@{
|
||||
ViewData["Title"] = "Register";
|
||||
}
|
||||
|
||||
<div class="container mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card shadow">
|
||||
<div class="card-body p-5">
|
||||
<h2 class="text-center mb-4">Register</h2>
|
||||
|
||||
<form id="registerForm">
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Email</label>
|
||||
<input type="email" class="form-control" id="email" name="email" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Password</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="confirmPassword" class="form-label">Confirm Password</label>
|
||||
<input type="password" class="form-control" id="confirmPassword" name="confirmPassword" required>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary w-100">Register</button>
|
||||
</form>
|
||||
|
||||
<hr>
|
||||
|
||||
<p class="text-center">Already have an account? <a href="/auth/login">Login here</a></p>
|
||||
|
||||
<div id="message" class="mt-3"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('registerForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const email = document.getElementById('email').value;
|
||||
const password = document.getElementById('password').value;
|
||||
const confirmPassword = document.getElementById('confirmPassword').value;
|
||||
const messageDiv = document.getElementById('message');
|
||||
|
||||
if (password !== confirmPassword) {
|
||||
messageDiv.innerHTML = '<div class="alert alert-danger">Passwords do not match!</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/auth/register', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({ email, password })
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
messageDiv.innerHTML = '<div class="alert alert-success">Registration successful! Redirecting to login...</div>';
|
||||
setTimeout(() => {
|
||||
window.location.href = '/AuthView/Login';
|
||||
}, 1500);
|
||||
} else {
|
||||
const error = await response.json();
|
||||
const errorMessages = error.errors.join('<br>');
|
||||
messageDiv.innerHTML = `<div class="alert alert-danger">${errorMessages}</div>`;
|
||||
}
|
||||
} catch (error) {
|
||||
messageDiv.innerHTML = `<div class="alert alert-danger">An error occurred: ${error.message}</div>`;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
130
Views/Home/Dashboard.cshtml
Normal file
130
Views/Home/Dashboard.cshtml
Normal file
@@ -0,0 +1,130 @@
|
||||
@using OneForMe.Controllers
|
||||
@model DashboardViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Dashboard";
|
||||
}
|
||||
|
||||
<div class="container mt-5">
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-12">
|
||||
<h1>Welcome to OneForMe</h1>
|
||||
<p>Logged in as: <strong>@User.Identity?.Name</strong></p>
|
||||
<a href="/AuthView/Logout" class="btn btn-danger">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<div class="card shadow">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Create New Order</h5>
|
||||
<p class="card-text">Start a new group order and invite friends</p>
|
||||
<a href="/order/create" class="btn btn-primary">Create Order</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="card shadow">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Join Order</h5>
|
||||
<p class="card-text">Join an existing order using the order code</p>
|
||||
<form method="get" action="/order/join" class="mb-3">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" name="code" placeholder="Enter order code" required>
|
||||
<button class="btn btn-success" type="submit">Join</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- Created Orders -->
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-12">
|
||||
<h3>My Created Orders</h3>
|
||||
@if (Model.CreatedOrders.Any())
|
||||
{
|
||||
<div class="row">
|
||||
@foreach (var order in Model.CreatedOrders)
|
||||
{
|
||||
<div class="col-md-6 mb-3">
|
||||
<div class="card shadow">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">@order.CreatorName</h5>
|
||||
<p class="card-text">
|
||||
<small class="text-muted">Code: <strong>@order.OrderCode</strong></small><br>
|
||||
<small class="text-muted">Created: @order.CreatedAt.ToString("MMM dd, yyyy HH:mm")</small><br>
|
||||
<small>Items: @order.MenuItems.Count | Orders: @order.OrderItems.Count</small>
|
||||
</p>
|
||||
<p class="text-success"><strong>Total: $@order.OrderItems.Sum(oi => oi.MenuItem.Price * oi.Quantity).ToString("F2")</strong></p>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="/order/details?code=@order.OrderCode" class="btn btn-sm btn-primary">View</a>
|
||||
@if (!order.IsClosed)
|
||||
{
|
||||
<a href="/order/close?code=@order.OrderCode" class="btn btn-sm btn-danger">Close Order</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="badge bg-secondary">Closed</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="alert alert-info">You haven't created any orders yet.</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- Joined Orders -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h3>Orders I Joined</h3>
|
||||
@if (Model.JoinedOrders.Any())
|
||||
{
|
||||
<div class="row">
|
||||
@foreach (var order in Model.JoinedOrders)
|
||||
{
|
||||
var myItems = order.OrderItems.Where(oi => oi.ParticipantEmail == User.Identity?.Name || oi.ParticipantName == User.Identity?.Name).ToList();
|
||||
<div class="col-md-6 mb-3">
|
||||
<div class="card shadow">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">@order.CreatorName</h5>
|
||||
<p class="card-text">
|
||||
<small class="text-muted">Code: <strong>@order.OrderCode</strong></small><br>
|
||||
<small class="text-muted">Created by: @order.CreatorName</small><br>
|
||||
<small>My items: @myItems.Count</small>
|
||||
</p>
|
||||
<p class="text-warning"><strong>I owe: $@myItems.Sum(oi => oi.MenuItem.Price * oi.Quantity).ToString("F2")</strong></p>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="/order/join?code=@order.OrderCode" class="btn btn-sm btn-primary">View</a>
|
||||
@if (!order.IsClosed)
|
||||
{
|
||||
<a href="/order/join?code=@order.OrderCode" class="btn btn-sm btn-success">Add More</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="alert alert-info">You haven't joined any orders yet.</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
84
Views/Order/Create.cshtml
Normal file
84
Views/Order/Create.cshtml
Normal file
@@ -0,0 +1,84 @@
|
||||
@{
|
||||
ViewData["Title"] = "Create Order";
|
||||
}
|
||||
|
||||
<div class="container mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card shadow">
|
||||
<div class="card-body p-5">
|
||||
<h2 class="mb-4">Create New Order</h2>
|
||||
|
||||
<form method="post" id="createOrderForm">
|
||||
<div class="mb-3">
|
||||
<label for="creatorName" class="form-label">Order Name</label>
|
||||
<input type="text" class="form-control" id="creatorName" name="creatorName" placeholder="e.g., Pizza Party" required>
|
||||
<small class="text-muted">Give your order a name</small>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<h5 class="mb-3">Menu Items</h5>
|
||||
<p class="text-muted">Add items that people can order</p>
|
||||
|
||||
<div id="itemsContainer">
|
||||
<div class="item-row mb-3">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<input type="text" class="form-control" placeholder="Item name (e.g., Margherita Pizza)" name="itemNames[]">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">$</span>
|
||||
<input type="number" class="form-control" placeholder="Price" name="itemPrices[]" step="0.01" min="0">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-secondary mb-3" id="addItemBtn">+ Add Another Item</button>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">Create Order</button>
|
||||
<a href="/Home/Dashboard" class="btn btn-secondary">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById('addItemBtn').addEventListener('click', function() {
|
||||
const container = document.getElementById('itemsContainer');
|
||||
const newRow = document.createElement('div');
|
||||
newRow.className = 'item-row mb-3';
|
||||
newRow.innerHTML = `
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<input type="text" class="form-control" placeholder="Item name" name="itemNames[]">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">$</span>
|
||||
<input type="number" class="form-control" placeholder="Price" name="itemPrices[]" step="0.01" min="0">
|
||||
<button type="button" class="btn btn-outline-danger" onclick="this.closest('.item-row').remove()">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
container.appendChild(newRow);
|
||||
});
|
||||
|
||||
// Add first item row on page load
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const container = document.getElementById('itemsContainer');
|
||||
if (container.children.length === 0) {
|
||||
document.getElementById('addItemBtn').click();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
124
Views/Order/Details.cshtml
Normal file
124
Views/Order/Details.cshtml
Normal file
@@ -0,0 +1,124 @@
|
||||
@model OneForMe.Models.Order
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Order Details";
|
||||
}
|
||||
|
||||
<div class="container mt-5">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-body">
|
||||
<h2>@Model.CreatorName</h2>
|
||||
<p class="text-muted">Order Code: <strong>@Model.OrderCode</strong></p>
|
||||
<p>Created by: <strong>@Model.CreatorName</strong></p>
|
||||
<p>Status: <span class="badge @(Model.IsClosed ? "bg-danger" : "bg-success")">@(Model.IsClosed ? "Closed" : "Open")</span></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h5 class="mb-0">Menu Items</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if (Model.MenuItems.Any())
|
||||
{
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Item</th>
|
||||
<th>Price</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var item in Model.MenuItems)
|
||||
{
|
||||
<tr>
|
||||
<td>@item.Name</td>
|
||||
<td>$@item.Price.ToString("F2")</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-muted">No items added yet</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow">
|
||||
<div class="card-header bg-info text-white">
|
||||
<h5 class="mb-0">Orders (@Model.OrderItems.Count)</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if (Model.OrderItems.Any())
|
||||
{
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Participant</th>
|
||||
<th>Item</th>
|
||||
<th>Qty</th>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var orderItem in Model.OrderItems)
|
||||
{
|
||||
<tr>
|
||||
<td>@orderItem.ParticipantName</td>
|
||||
<td>@orderItem.MenuItem?.Name</td>
|
||||
<td>@orderItem.Quantity</td>
|
||||
<td>$@(orderItem.MenuItem?.Price * orderItem.Quantity ?? 0).ToString("F2")</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-muted">No orders yet</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow mb-4 bg-light">
|
||||
<div class="card-body">
|
||||
<h5>Share Order</h5>
|
||||
<p>Send this link to others:</p>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="shareLink" value="@(Context.Request.Scheme)://@(Context.Request.Host)/order/join?code=@Model.OrderCode" readonly>
|
||||
<button class="btn btn-outline-primary" onclick="copyToClipboard()">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-body">
|
||||
<h5>Quick Stats</h5>
|
||||
<p>Total Items: <strong>@Model.MenuItems.Count</strong></p>
|
||||
<p>Total Orders: <strong>@Model.OrderItems.Count</strong></p>
|
||||
<p>Total Revenue: <strong>$@Model.OrderItems.Sum(oi => oi.MenuItem.Price * oi.Quantity).ToString("F2")</strong></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (!Model.IsClosed)
|
||||
{
|
||||
<a href="#" class="btn btn-danger w-100">Close Order</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function copyToClipboard() {
|
||||
const link = document.getElementById('shareLink');
|
||||
link.select();
|
||||
document.execCommand('copy');
|
||||
alert('Link copied to clipboard!');
|
||||
}
|
||||
</script>
|
||||
151
Views/Order/Join.cshtml
Normal file
151
Views/Order/Join.cshtml
Normal file
@@ -0,0 +1,151 @@
|
||||
@model OneForMe.Models.Order
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Join Order";
|
||||
}
|
||||
|
||||
<div class="container mt-5">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-body">
|
||||
<h2>@Model.CreatorName</h2>
|
||||
<p class="text-muted">Order Code: <strong>@Model.OrderCode</strong></p>
|
||||
<p>Created by: <strong>@Model.CreatorName</strong></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h5 class="mb-0">Available Items</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if (Model.MenuItems.Any())
|
||||
{
|
||||
<form method="post" action="/order/additem">
|
||||
<input type="hidden" name="orderId" value="@Model.Id">
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="participantName" class="form-label">Your Name</label>
|
||||
<input type="text" class="form-control" id="participantName" name="participantName" placeholder="Enter your name" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="participantEmail" class="form-label">Your Email (optional)</label>
|
||||
<input type="email" class="form-control" id="participantEmail" name="participantEmail" placeholder="Enter your email">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="menuItemId" class="form-label">Select Item</label>
|
||||
<select class="form-select" id="menuItemId" name="menuItemId" required onchange="updatePrice()">
|
||||
<option value="">-- Choose an item --</option>
|
||||
@foreach (var item in Model.MenuItems)
|
||||
{
|
||||
<option value="@item.Id" data-price="@item.Price.ToString("F2")">@item.Name - $@item.Price.ToString("F2")</option>
|
||||
}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="quantity" class="form-label">Quantity</label>
|
||||
<input type="number" class="form-control" id="quantity" name="quantity" value="1" min="1" required>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<strong>Total:</strong> $<span id="totalPrice">0.00</span>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success">Add to Order</button>
|
||||
<a href="/Home/Dashboard" class="btn btn-secondary">Back to Dashboard</a>
|
||||
</form>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-muted">No items available for this order</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header bg-info text-white">
|
||||
<h5 class="mb-0">Current Orders (@Model.OrderItems.Count)</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if (Model.OrderItems.Any())
|
||||
{
|
||||
<table class="table table-sm table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Item</th>
|
||||
<th>Qty</th>
|
||||
<th>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var orderItem in Model.OrderItems)
|
||||
{
|
||||
<tr>
|
||||
<td>@orderItem.ParticipantName</td>
|
||||
<td>@orderItem.MenuItem?.Name</td>
|
||||
<td>@orderItem.Quantity</td>
|
||||
<td>$@(orderItem.MenuItem?.Price * orderItem.Quantity ?? 0).ToString("F2")</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Person</th>
|
||||
<th>Owes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var person in Model.OrderItems.GroupBy(oi => oi.ParticipantName))
|
||||
{
|
||||
<tr>
|
||||
<td>@person.Key</td>
|
||||
<td>$@person.Sum(oi => oi.MenuItem.Price * oi.Quantity).ToString("F2")</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<p class="text-muted">No one has ordered yet</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow">
|
||||
<div class="card-body">
|
||||
<h5>Order Total</h5>
|
||||
<h3 class="text-success">$@Model.OrderItems.Sum(oi => oi.MenuItem.Price * oi.Quantity).ToString("F2")</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function updatePrice() {
|
||||
const select = document.getElementById('menuItemId');
|
||||
const quantity = document.getElementById('quantity').value;
|
||||
const selectedOption = select.options[select.selectedIndex];
|
||||
const price = parseFloat(selectedOption.dataset.price) || 0;
|
||||
const total = (price * quantity).toFixed(2);
|
||||
document.getElementById('totalPrice').textContent = total;
|
||||
}
|
||||
|
||||
document.getElementById('quantity').addEventListener('change', updatePrice);
|
||||
document.getElementById('menuItemId').addEventListener('change', updatePrice);
|
||||
</script>
|
||||
Reference in New Issue
Block a user