Compare commits

..

3 Commits

Author SHA1 Message Date
Elias Jansson
3b0ea79748 Merge
Some checks failed
continuous-integration/drone/push Build is failing
2025-06-02 09:21:56 +02:00
Elias Jansson
600df026d5 Merge 2025-06-02 09:21:48 +02:00
Elias Jansson
801e21842a Dev branch
All checks were successful
continuous-integration/drone/push Build is passing
2025-05-31 22:44:15 +02:00
8 changed files with 835 additions and 100 deletions

View File

@@ -3,9 +3,6 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Aberwyn.Models; using Aberwyn.Models;
using Aberwyn.Data; using Aberwyn.Data;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
namespace Aberwyn.Controllers namespace Aberwyn.Controllers
{ {
@@ -17,28 +14,22 @@ namespace Aberwyn.Controllers
private readonly RoleManager<IdentityRole> _roleManager; private readonly RoleManager<IdentityRole> _roleManager;
private readonly IConfiguration _configuration; private readonly IConfiguration _configuration;
private readonly IHostEnvironment _env; private readonly IHostEnvironment _env;
private readonly MenuService _menuService;
private readonly ApplicationDbContext _context; private readonly ApplicationDbContext _context;
public AdminController( public AdminController(
UserManager<ApplicationUser> userManager,
RoleManager<IdentityRole> roleManager,
IConfiguration configuration, IConfiguration configuration,
IHostEnvironment env, IHostEnvironment env,
MenuService menuService, ApplicationDbContext context)
ApplicationDbContext context,
UserManager<ApplicationUser> userManager,
RoleManager<IdentityRole> roleManager)
{ {
_configuration = configuration;
_env = env;
_menuService = menuService;
_context = context;
_userManager = userManager; _userManager = userManager;
_roleManager = roleManager; _roleManager = roleManager;
_configuration = configuration;
_env = env;
_context = context;
} }
public async Task<IActionResult> Index() public async Task<IActionResult> Index()
{ {
var users = _userManager.Users.ToList(); var users = _userManager.Users.ToList();
@@ -148,8 +139,6 @@ public IActionResult ImportMealsFromProd()
} }
[HttpPost] [HttpPost]
[Authorize(Roles = "Admin")] [Authorize(Roles = "Admin")]
[HttpPost]
[Authorize(Roles = "Admin")]
public IActionResult ImportMenusFromProd() public IActionResult ImportMenusFromProd()
{ {
var prodService = MenuService.CreateWithConfig(_configuration, _env, useProdDb: true); var prodService = MenuService.CreateWithConfig(_configuration, _env, useProdDb: true);
@@ -158,36 +147,11 @@ public IActionResult ImportMenusFromProd()
var allProdMenus = prodService.GetAllWeeklyMenus(); var allProdMenus = prodService.GetAllWeeklyMenus();
var allMeals = devService.GetMeals(); var allMeals = devService.GetMeals();
foreach (var menu in allProdMenus)
{
var newMenu = new WeeklyMenu
{
DayOfWeek = menu.DayOfWeek,
BreakfastMealId = menu.BreakfastMealName != null
? allMeals.FirstOrDefault(m => m.Name == menu.BreakfastMealName)?.Id
: null,
LunchMealId = menu.LunchMealName != null
? allMeals.FirstOrDefault(m => m.Name == menu.LunchMealName)?.Id
: null,
DinnerMealId = menu.DinnerMealName != null
? allMeals.FirstOrDefault(m => m.Name == menu.DinnerMealName)?.Id
: null,
WeekNumber = menu.WeekNumber,
Year = menu.Year,
Cook = menu.Cook
};
_context.WeeklyMenus.Add(newMenu);
}
_context.SaveChanges();
TempData["Message"] = "Import av veckomenyer klar.";
return RedirectToAction("Index"); return RedirectToAction("Index");
} }
} }
public class AdminUserViewModel public class AdminUserViewModel
{ {
public string UserId { get; set; } public string UserId { get; set; }

View File

@@ -27,10 +27,6 @@ namespace Aberwyn.Data
public DbSet<AppSetting> AppSettings { get; set; } public DbSet<AppSetting> AppSettings { get; set; }
public DbSet<BudgetItemDefinition> BudgetItemDefinitions { get; set; } public DbSet<BudgetItemDefinition> BudgetItemDefinitions { get; set; }
public DbSet<BudgetCategoryDefinition> BudgetCategoryDefinitions { get; set; } public DbSet<BudgetCategoryDefinition> BudgetCategoryDefinitions { get; set; }
public DbSet<Meal> Meals { get; set; }
public DbSet<Ingredient> Ingredients { get; set; }
public DbSet<WeeklyMenu> WeeklyMenus { get; set; }
} }

View File

@@ -157,6 +157,12 @@ public List<WeeklyMenu> GetAllWeeklyMenus()
.OrderBy(m => m.Name) .OrderBy(m => m.Name)
.ToList(); .ToList();
} }
public List<WeeklyMenu> GetWeeklyMenu(int weekNumber, int year)
{
return _context.WeeklyMenus
.Where(m => m.WeekNumber == weekNumber && m.Year == year)
.ToList();
}
public List<WeeklyMenu> GetMenuEntriesByDateRange(DateTime startDate, DateTime endDate) public List<WeeklyMenu> GetMenuEntriesByDateRange(DateTime startDate, DateTime endDate)
{ {

View File

@@ -0,0 +1,541 @@
// <auto-generated />
using System;
using Aberwyn.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace Aberwyn.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20250529183339_CreateTodoTaskTable")]
partial class CreateTodoTaskTable
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.0.36")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
modelBuilder.Entity("Aberwyn.Models.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("varchar(255)");
b.Property<int>("AccessFailedCount")
.HasColumnType("int");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("longtext");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("varchar(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("tinyint(1)");
b.Property<bool>("LockoutEnabled")
.HasColumnType("tinyint(1)");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("datetime(6)");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("varchar(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("varchar(256)");
b.Property<string>("PasswordHash")
.HasColumnType("longtext");
b.Property<string>("PhoneNumber")
.HasColumnType("longtext");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("tinyint(1)");
b.Property<string>("SecurityStamp")
.HasColumnType("longtext");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("tinyint(1)");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("varchar(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Aberwyn.Models.AppSetting", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Key")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("AppSettings");
});
modelBuilder.Entity("Aberwyn.Models.BudgetCategory", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int?>("BudgetCategoryDefinitionId")
.HasColumnType("int");
b.Property<int>("BudgetPeriodId")
.HasColumnType("int");
b.Property<string>("Color")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("Order")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("BudgetCategoryDefinitionId");
b.HasIndex("BudgetPeriodId");
b.ToTable("BudgetCategories");
});
modelBuilder.Entity("Aberwyn.Models.BudgetCategoryDefinition", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Color")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("BudgetCategoryDefinitions");
});
modelBuilder.Entity("Aberwyn.Models.BudgetItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<decimal>("Amount")
.HasColumnType("decimal(65,30)");
b.Property<int>("BudgetCategoryId")
.HasColumnType("int");
b.Property<int?>("BudgetItemDefinitionId")
.HasColumnType("int");
b.Property<bool>("IncludeInSummary")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsExpense")
.HasColumnType("tinyint(1)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.Property<int>("Order")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("BudgetCategoryId");
b.HasIndex("BudgetItemDefinitionId");
b.ToTable("BudgetItems");
});
modelBuilder.Entity("Aberwyn.Models.BudgetItemDefinition", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("DefaultCategory")
.HasColumnType("longtext");
b.Property<bool>("IncludeInSummary")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsExpense")
.HasColumnType("tinyint(1)");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("BudgetItemDefinitions");
});
modelBuilder.Entity("Aberwyn.Models.BudgetPeriod", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int>("Month")
.HasColumnType("int");
b.Property<int>("Order")
.HasColumnType("int");
b.Property<int>("Year")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("BudgetPeriods");
});
modelBuilder.Entity("Aberwyn.Models.PizzaOrder", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("CustomerName")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("IngredientsJson")
.HasColumnType("longtext");
b.Property<DateTime>("OrderedAt")
.HasColumnType("datetime(6)");
b.Property<string>("PizzaName")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Status")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("PizzaOrders");
});
modelBuilder.Entity("Aberwyn.Models.PushSubscriber", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("Auth")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Endpoint")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("P256DH")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("PushSubscribers");
});
modelBuilder.Entity("Aberwyn.Models.TodoTask", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<int>("Priority")
.HasColumnType("int");
b.Property<string>("Status")
.IsRequired()
.HasColumnType("longtext");
b.Property<string>("Title")
.IsRequired()
.HasColumnType("longtext");
b.HasKey("Id");
b.ToTable("TodoTasks");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.HasColumnType("varchar(255)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("longtext");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("varchar(256)");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("varchar(256)");
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("int");
b.Property<string>("ClaimType")
.HasColumnType("longtext");
b.Property<string>("ClaimValue")
.HasColumnType("longtext");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("varchar(255)");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("ClaimType")
.HasColumnType("longtext");
b.Property<string>("ClaimValue")
.HasColumnType("longtext");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("varchar(255)");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("varchar(255)");
b.Property<string>("ProviderKey")
.HasColumnType("varchar(255)");
b.Property<string>("ProviderDisplayName")
.HasColumnType("longtext");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("varchar(255)");
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("varchar(255)");
b.Property<string>("RoleId")
.HasColumnType("varchar(255)");
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("varchar(255)");
b.Property<string>("LoginProvider")
.HasColumnType("varchar(255)");
b.Property<string>("Name")
.HasColumnType("varchar(255)");
b.Property<string>("Value")
.HasColumnType("longtext");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Aberwyn.Models.BudgetCategory", b =>
{
b.HasOne("Aberwyn.Models.BudgetCategoryDefinition", "Definition")
.WithMany()
.HasForeignKey("BudgetCategoryDefinitionId");
b.HasOne("Aberwyn.Models.BudgetPeriod", "BudgetPeriod")
.WithMany("Categories")
.HasForeignKey("BudgetPeriodId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("BudgetPeriod");
b.Navigation("Definition");
});
modelBuilder.Entity("Aberwyn.Models.BudgetItem", b =>
{
b.HasOne("Aberwyn.Models.BudgetCategory", "BudgetCategory")
.WithMany("Items")
.HasForeignKey("BudgetCategoryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Aberwyn.Models.BudgetItemDefinition", "BudgetItemDefinition")
.WithMany()
.HasForeignKey("BudgetItemDefinitionId");
b.Navigation("BudgetCategory");
b.Navigation("BudgetItemDefinition");
});
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("Aberwyn.Models.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Aberwyn.Models.ApplicationUser", 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("Aberwyn.Models.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Aberwyn.Models.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Aberwyn.Models.BudgetCategory", b =>
{
b.Navigation("Items");
});
modelBuilder.Entity("Aberwyn.Models.BudgetPeriod", b =>
{
b.Navigation("Categories");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,39 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Aberwyn.Migrations
{
public partial class CreateTodoTaskTable : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "TodoTasks",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
Title = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
Status = table.Column<string>(type: "longtext", nullable: false)
.Annotation("MySql:CharSet", "utf8mb4"),
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
Priority = table.Column<int>(type: "int", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_TodoTasks", x => x.Id);
})
.Annotation("MySql:CharSet", "utf8mb4");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "TodoTasks");
}
}
}

View File

@@ -358,50 +358,6 @@ namespace Aberwyn.Migrations
b.ToTable("PushSubscribers"); b.ToTable("PushSubscribers");
}); });
modelBuilder.Entity("Aberwyn.Models.WeeklyMenu", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<int?>("BreakfastMealId")
.HasColumnType("int");
b.Property<string>("BreakfastMealName")
.HasColumnType("longtext");
b.Property<string>("Cook")
.HasColumnType("longtext");
b.Property<DateTime>("Date")
.HasColumnType("datetime(6)");
b.Property<int>("DayOfWeek")
.HasColumnType("int");
b.Property<int?>("DinnerMealId")
.HasColumnType("int");
b.Property<string>("DinnerMealName")
.HasColumnType("longtext");
b.Property<int?>("LunchMealId")
.HasColumnType("int");
b.Property<string>("LunchMealName")
.HasColumnType("longtext");
b.Property<int>("WeekNumber")
.HasColumnType("int");
b.Property<int>("Year")
.HasColumnType("int");
b.HasKey("Id");
b.ToTable("WeeklyMenu", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{ {
b.Property<string>("Id") b.Property<string>("Id")

View File

@@ -7,4 +7,14 @@
public string Value { get; set; } public string Value { get; set; }
} }
public class TodoTask
{
public int Id { get; set; }
public string Title { get; set; }
public string Status { get; set; } // "ideas", "doing", "done"
public DateTime CreatedAt { get; set; }
public int Priority { get; set; }
}
} }

View File

@@ -0,0 +1,223 @@
@{
ViewData["Title"] = "Todo";
}
<!DOCTYPE html>
<html lang="sv" ng-app="todoApp">
<head>
<meta charset="utf-8" />
<title>@ViewData["Title"]</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
<style>
.todo-board {
padding: 20px;
font-family: sans-serif;
}
.todo-columns {
display: flex;
gap: 16px;
flex-wrap: wrap;
}
.todo-column {
background: #f1f5f9;
border-radius: 8px;
padding: 10px;
width: 250px;
min-height: 200px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.todo-column h2 {
margin-top: 0;
}
.todo-task {
background: white;
padding: 8px;
border-radius: 6px;
margin-bottom: 6px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
cursor: move;
}
input[type="text"] {
width: 100%;
margin-top: 8px;
padding: 6px;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
margin-top: 6px;
padding: 6px 10px;
background-color: #3b82f6;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.priority-low {
border-left: 4px solid #10b981;
}
.priority-medium {
border-left: 4px solid #f59e0b;
}
.priority-high {
border-left: 4px solid #ef4444;
}
</style>
</head>
<body ng-controller="TodoController" class="todo-board" ng-init="loadTasks()">
<h1>🗂️ Min idé-board</h1>
<div class="todo-columns">
<div class="todo-column" ng-repeat="col in columns">
<h2>{{ col.title }}</h2>
<div class="todo-task {{ priorityClass(task.priority) }}"
ng-repeat="task in filteredTasks(col.id) track by task.id"
draggable-task
task="task"
on-task-drop="moveTask(task, col.id)">
{{ task.title }}
<div>
<small>Prioritet:
<select ng-model="task.priority" ng-change="updatePriority(task)">
<option value="1">Låg</option>
<option value="2">Medel</option>
<option value="3">Hög</option>
</select>
</small>
</div>
</div>
<div ng-if="col.id === 'ideas'">
<input type="text" ng-model="newTask.title" placeholder="Ny idé..." />
<select ng-model="newTask.priority">
<option value="1">Låg</option>
<option value="2">Medel</option>
<option value="3">Hög</option>
</select>
<button type="button" ng-click="addTask()">Lägg till</button>
</div>
</div>
</div>
<script>
var app = angular.module("todoApp", []);
app.controller("TodoController", function ($scope, $http) {
$scope.columns = [
{ id: "ideas", title: "💡 Idéer" },
{ id: "doing", title: "🔧 Pågående" },
{ id: "done", title: "✅ Klart" }
];
$scope.tasks = [];
$scope.newTask = {
title: '',
priority: 2
};
$scope.loadTasks = function () {
$http.get("/Admin/GetTodoTasks").then(res => {
$scope.tasks = res.data;
});
};
$scope.addTask = function () {
console.log("addTask:");
const title = $scope.newTask.title;
const priority = $scope.newTask.priority;
if (typeof title !== 'string' || !title.trim()) {
console.warn("Titel är tom eller ogiltig:", title);
return;
}
console.log("post check");
const task = {
title: title.trim(),
status: "ideas",
priority: priority
};
console.log("post title trim");
$http.post("/Admin/AddTodoTask", task, {
headers: { 'Content-Type': 'application/json' }
}).then(res => {
$scope.tasks.push(res.data);
$scope.newTask.title = '';
$scope.newTask.priority = 2;
console.log("Created task");
}, err => {
console.error("Fel vid POST:", err);
});
};
$scope.moveTask = function (task, newStatus) {
task.status = newStatus;
$http.post("/Admin/UpdateTodoTask", task);
};
$scope.updatePriority = function (task) {
$http.post("/Admin/UpdateTodoTask", task);
};
$scope.priorityClass = function (priority) {
return {
1: 'priority-low',
2: 'priority-medium',
3: 'priority-high'
}[priority];
};
$scope.filteredTasks = function (status) {
if (!$scope.tasks || !$scope.tasks.length) return [];
// För säkerhets skull jämför lowercase
return $scope.tasks.filter(t => (t.status || '').toLowerCase() === status.toLowerCase());
};
$scope.loadTasks();
});
app.directive("draggableTask", function () {
return {
restrict: "A",
scope: {
task: "=",
onTaskDrop: "&"
},
link: function (scope, element) {
element.attr("draggable", true);
element.on("dragstart", function (e) {
e.dataTransfer.setData("text/plain", JSON.stringify(scope.task));
});
}
};
});
app.directive("ngRepeat", function () {
return {
restrict: "A",
link: function (scope, element) {
element.on("dragover", function (e) {
e.preventDefault();
element.css("background-color", "#e0f2fe");
});
element.on("dragleave", function () {
element.css("background-color", "");
});
element.on("drop", function (e) {
e.preventDefault();
const data = e.dataTransfer.getData("text/plain");
const task = JSON.parse(data);
scope.onTaskDrop({ task });
element.css("background-color", "");
scope.$apply();
});
}
};
});
</script>
</body>
</html>