complete badges
This commit is contained in:
parent
d83eef15c0
commit
da80eb2d78
16 changed files with 73 additions and 21 deletions
|
@ -120,11 +120,13 @@ export default class AdminController_Auth$Admin extends Controller {
|
|||
adminBadgeViewModel.description = badge.Description;
|
||||
adminBadgeViewModel.imageUrl = badge.ImageUrl;
|
||||
adminBadgeViewModel.forUrl = badge.ForUrl;
|
||||
adminBadgeViewModel.isSecret = badge.IsSecret;
|
||||
} else {
|
||||
adminBadgeViewModel.name = "";
|
||||
adminBadgeViewModel.description = "";
|
||||
adminBadgeViewModel.imageUrl = "";
|
||||
adminBadgeViewModel.forUrl = "";
|
||||
adminBadgeViewModel.isSecret = false;
|
||||
}
|
||||
|
||||
return this.view(adminBadgeViewModel);
|
||||
|
@ -135,7 +137,7 @@ export default class AdminController_Auth$Admin extends Controller {
|
|||
return this.badRequest();
|
||||
}
|
||||
|
||||
await BadgeService.SaveBadge(this.session.userId, parseInt(adminBadgeViewModel.id), adminBadgeViewModel.name ?? "", adminBadgeViewModel.description ?? "", adminBadgeViewModel.imageUrl ?? "", adminBadgeViewModel.forUrl ?? "");
|
||||
await BadgeService.SaveBadge(this.session.userId, parseInt(adminBadgeViewModel.id), adminBadgeViewModel.name ?? "", adminBadgeViewModel.description ?? "", adminBadgeViewModel.imageUrl ?? "", adminBadgeViewModel.forUrl ?? "", (adminBadgeViewModel.isSecret?.toString() ?? "") === "on");
|
||||
|
||||
return this.redirectToAction("badges");
|
||||
}
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import Controller from "./Controller";
|
||||
import FunkyArray from "funky-array";
|
||||
import UserService from "../services/UserService";
|
||||
import HomeViewModel from "../models/home/HomeViewModel";
|
||||
import PartyService from "../services/PartyService";
|
||||
import BadgeService from "../services/BadgeService";
|
||||
import Badge from "../entities/Badge";
|
||||
import UserBadge from "../entities/UserBadge";
|
||||
|
||||
export default class HomeController extends Controller {
|
||||
public async Index_Get_AllowAnonymous() {
|
||||
|
@ -14,12 +18,23 @@ export default class HomeController extends Controller {
|
|||
const parties = await PartyService.GetUserParties(this.session.userId);
|
||||
const activeUserParty = await UserService.GetActiveParty(this.session.userId);
|
||||
const unlockedBadges = await UserService.LoadUserBadges(this.session.userId);
|
||||
const unlockedBadgesById = new FunkyArray<number, UserBadge>();
|
||||
for (const unlockedBadge of unlockedBadges) {
|
||||
unlockedBadgesById.set(unlockedBadge.BadgeId, unlockedBadge);
|
||||
}
|
||||
const badges = await BadgeService.LoadAll();
|
||||
const badgeById = new FunkyArray<number, Badge>();
|
||||
for (const badge of badges) {
|
||||
badgeById.set(badge.Id, badge);
|
||||
}
|
||||
|
||||
const homeViewModel: HomeViewModel = {
|
||||
user,
|
||||
parties,
|
||||
activeUserParty,
|
||||
unlockedBadges
|
||||
unlockedBadges,
|
||||
unlockedBadgesById,
|
||||
badgeById
|
||||
};
|
||||
|
||||
return this.view("home", homeViewModel);
|
||||
|
|
|
@ -4,6 +4,7 @@ export default class Badge {
|
|||
public Description: string;
|
||||
public ImageUrl: string;
|
||||
public ForUrl: string;
|
||||
public IsSecret: boolean;
|
||||
public CreatedByUserId: number;
|
||||
public CreatedDatetime: Date;
|
||||
public LastModifiedByUserId?: number;
|
||||
|
@ -18,6 +19,7 @@ export default class Badge {
|
|||
this.Description = "";
|
||||
this.ImageUrl = "";
|
||||
this.ForUrl = "";
|
||||
this.IsSecret = false;
|
||||
this.CreatedByUserId = Number.MIN_VALUE;
|
||||
this.CreatedDatetime = new Date(0);
|
||||
this.IsDeleted = false;
|
||||
|
|
|
@ -3,5 +3,6 @@ export default interface AdminBadgeViewModel {
|
|||
name?: string,
|
||||
description?: string,
|
||||
imageUrl?: string,
|
||||
forUrl?: string
|
||||
forUrl?: string,
|
||||
isSecret?: boolean
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
import Badge from "../../entities/Badge";
|
||||
import FunkyArray from "funky-array";
|
||||
import Party from "../../entities/Party";
|
||||
import User from "../../entities/User";
|
||||
import UserBadge from "../../entities/UserBadge";
|
||||
|
@ -7,5 +9,7 @@ export default interface HomeViewModel {
|
|||
user: User,
|
||||
parties: Array<Party>,
|
||||
activeUserParty: UserParty | null,
|
||||
unlockedBadges: Array<UserBadge>
|
||||
unlockedBadges: Array<UserBadge>,
|
||||
unlockedBadgesById: FunkyArray<number, UserBadge>,
|
||||
badgeById: FunkyArray<number, Badge>
|
||||
}
|
|
@ -35,12 +35,12 @@ export default abstract class BadgeRepo {
|
|||
|
||||
public static async insertUpdate(badge:Badge) {
|
||||
if (badge.Id === Number.MIN_VALUE) {
|
||||
badge.Id = (await Database.Instance.query("INSERT Badge (Name, Description, ImageUrl, ForUrl, CreatedByUserId, CreatedDatetime, LastModifiedByUserId, LastModifiedDatetime, DeletedByUserId, DeletedDatetime, IsDeleted) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING Id;", [
|
||||
badge.Name, badge.Description, badge.ImageUrl, badge.ForUrl, badge.CreatedByUserId, badge.CreatedDatetime.getTime(), badge.LastModifiedByUserId ?? null, badge.LastModifiedDatetime?.getTime() ?? null, badge.DeletedByUserId ?? null, badge.DeletedDatetime?.getTime() ?? null, Number(badge.IsDeleted)
|
||||
badge.Id = (await Database.Instance.query("INSERT Badge (Name, Description, ImageUrl, ForUrl, IsSecret, CreatedByUserId, CreatedDatetime, LastModifiedByUserId, LastModifiedDatetime, DeletedByUserId, DeletedDatetime, IsDeleted) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING Id;", [
|
||||
badge.Name, badge.Description, badge.ImageUrl, badge.ForUrl, Number(badge.IsSecret), badge.CreatedByUserId, badge.CreatedDatetime.getTime(), badge.LastModifiedByUserId ?? null, badge.LastModifiedDatetime?.getTime() ?? null, badge.DeletedByUserId ?? null, badge.DeletedDatetime?.getTime() ?? null, Number(badge.IsDeleted)
|
||||
]))[0]["Id"];
|
||||
} else {
|
||||
await Database.Instance.query(`UPDATE Badge SET Name = ?, Description = ?, ImageUrl = ?, ForUrl = ?, CreatedByUserId = ?, CreatedDatetime = ?, LastModifiedByUserId = ?, LastModifiedDatetime = ?, DeletedByUserId = ?, DeletedDatetime = ?, IsDeleted = ? WHERE Id = ?`, [
|
||||
badge.Name, badge.Description, badge.ImageUrl, badge.ForUrl, badge.CreatedByUserId, badge.CreatedDatetime.getTime(), badge.LastModifiedByUserId ?? null, badge.LastModifiedDatetime?.getTime() ?? null, badge.DeletedByUserId ?? null, badge.DeletedDatetime?.getTime() ?? null, Number(badge.IsDeleted), badge.Id
|
||||
await Database.Instance.query(`UPDATE Badge SET Name = ?, Description = ?, ImageUrl = ?, ForUrl = ?, IsSecret = ?, CreatedByUserId = ?, CreatedDatetime = ?, LastModifiedByUserId = ?, LastModifiedDatetime = ?, DeletedByUserId = ?, DeletedDatetime = ?, IsDeleted = ? WHERE Id = ?`, [
|
||||
badge.Name, badge.Description, badge.ImageUrl, badge.ForUrl, Number(badge.IsSecret), badge.CreatedByUserId, badge.CreatedDatetime.getTime(), badge.LastModifiedByUserId ?? null, badge.LastModifiedDatetime?.getTime() ?? null, badge.DeletedByUserId ?? null, badge.DeletedDatetime?.getTime() ?? null, Number(badge.IsDeleted), badge.Id
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -54,11 +54,12 @@ function populateBadgeFromDB(badge:Badge, dbBadge:any) {
|
|||
badge.Description = dbBadge.Description;
|
||||
badge.ImageUrl = dbBadge.ImageUrl;
|
||||
badge.ForUrl = dbBadge.ForUrl;
|
||||
badge.IsSecret = dbBadge.IsSecret[0] === 1;
|
||||
badge.CreatedByUserId = dbBadge.CreatedByUserId;
|
||||
badge.CreatedDatetime = new Date(dbBadge.CreatedDatetime);
|
||||
badge.LastModifiedByUserId = dbBadge.LastModifiedByUserId;
|
||||
badge.LastModifiedDatetime = RepoBase.convertNullableDatetimeIntToDate(dbBadge.LastModifiedDatetime);
|
||||
badge.DeletedByUserId = dbBadge.DeletedByUserId;
|
||||
badge.DeletedDatetime = RepoBase.convertNullableDatetimeIntToDate(dbBadge.DeletedDatetime);
|
||||
badge.IsDeleted = dbBadge.IsDeleted === 1;
|
||||
badge.IsDeleted = dbBadge.IsDeleted[0] === 1;
|
||||
}
|
|
@ -86,5 +86,5 @@ function populatePartyFromDB(party:Party, dbParty:any) {
|
|||
party.LastModifiedDatetime = RepoBase.convertNullableDatetimeIntToDate(dbParty.LastModifiedDatetime);
|
||||
party.DeletedByUserId = dbParty.DeletedByUserId;
|
||||
party.DeletedDatetime = RepoBase.convertNullableDatetimeIntToDate(dbParty.DeletedDatetime);
|
||||
party.IsDeleted = dbParty.IsDeleted === 1;
|
||||
party.IsDeleted = dbParty.IsDeleted[0] === 1;
|
||||
}
|
|
@ -15,7 +15,7 @@ export default abstract class UserBadgeRepo {
|
|||
}
|
||||
|
||||
public static async selectByUserId(userId:number) {
|
||||
const dbUserBadge = await Database.Instance.query("SELECT * FROM UserBadge WHERE UserId = ? AND IsDeleted = 0 LIMIT 1", [ userId ]);
|
||||
const dbUserBadge = await Database.Instance.query("SELECT * FROM UserBadge WHERE UserId = ? AND IsDeleted = 0", [ userId ]);
|
||||
const userBadges = new Array<UserBadge>();
|
||||
|
||||
for (const row of dbUserBadge) {
|
||||
|
@ -63,5 +63,5 @@ function populateUserBadgeFromDB(userBadge:UserBadge, dbUser:any) {
|
|||
userBadge.LastModifiedDatetime = RepoBase.convertNullableDatetimeIntToDate(dbUser.LastModifiedDatetime);
|
||||
userBadge.DeletedByUserId = dbUser.DeletedByUserId;
|
||||
userBadge.DeletedDatetime = RepoBase.convertNullableDatetimeIntToDate(dbUser.DeletedDatetime);
|
||||
userBadge.IsDeleted = dbUser.IsDeleted === 1;
|
||||
userBadge.IsDeleted = dbUser.IsDeleted[0] === 1;
|
||||
}
|
|
@ -84,5 +84,5 @@ function populateUserPartyFromDB(userParty:UserParty, dbUserParty:any) {
|
|||
userParty.LastModifiedDatetime = RepoBase.convertNullableDatetimeIntToDate(dbUserParty.LastModifiedDatetime);
|
||||
userParty.DeletedByUserId = dbUserParty.DeletedByUserId;
|
||||
userParty.DeletedDatetime = RepoBase.convertNullableDatetimeIntToDate(dbUserParty.DeletedDatetime);
|
||||
userParty.IsDeleted = dbUserParty.IsDeleted === 1;
|
||||
userParty.IsDeleted = dbUserParty.IsDeleted[0] === 1;
|
||||
}
|
|
@ -77,12 +77,12 @@ function populateUserFromDB(user:User, dbUser:any) {
|
|||
user.PasswordHash = dbUser.PasswordHash;
|
||||
user.PasswordSalt = dbUser.PasswordSalt;
|
||||
user.APIKey = dbUser.APIKey;
|
||||
user.HasUsedClient = dbUser.HasUsedClient === 1;
|
||||
user.HasUsedClient = dbUser.HasUsedClient[0] === 1;
|
||||
user.CreatedByUserId = dbUser.CreatedByUserId;
|
||||
user.CreatedDatetime = new Date(dbUser.CreatedDatetime);
|
||||
user.LastModifiedByUserId = dbUser.LastModifiedByUserId;
|
||||
user.LastModifiedDatetime = RepoBase.convertNullableDatetimeIntToDate(dbUser.LastModifiedDatetime);
|
||||
user.DeletedByUserId = dbUser.DeletedByUserId;
|
||||
user.DeletedDatetime = RepoBase.convertNullableDatetimeIntToDate(dbUser.DeletedDatetime);
|
||||
user.IsDeleted = dbUser.IsDeleted === 1;
|
||||
user.IsDeleted = dbUser.IsDeleted[0] === 1;
|
||||
}
|
|
@ -4,7 +4,7 @@ import BadgeRepo from "../repos/BadgeRepo";
|
|||
import BadgeCache from "../objects/BadgeCache";
|
||||
|
||||
export default abstract class BadgeService {
|
||||
public static async SaveBadge(currentUserId:number, id:number | undefined, name:string, description:string, imageUrl: string, forUrl:string) {
|
||||
public static async SaveBadge(currentUserId:number, id:number | undefined, name:string, description:string, imageUrl: string, forUrl:string, isSecret: boolean) {
|
||||
try {
|
||||
let badge = id ? await BadgeRepo.selectById(id) : null;
|
||||
if (badge === null) {
|
||||
|
@ -16,10 +16,13 @@ export default abstract class BadgeService {
|
|||
badge.LastModifiedDatetime = new Date();
|
||||
}
|
||||
|
||||
console.log(isSecret);
|
||||
|
||||
badge.Name = name;
|
||||
badge.Description = description;
|
||||
badge.ImageUrl = imageUrl;
|
||||
badge.ForUrl = forUrl;
|
||||
badge.IsSecret = isSecret;
|
||||
|
||||
badge = await BadgeRepo.insertUpdate(badge);
|
||||
|
||||
|
|
|
@ -42,12 +42,20 @@
|
|||
<img id="imageImg" style="image-rendering:pixelated;margin-top: 36px" class="mb-3" src="<%= imageUrl.trim().length === 0 ? "/img/missing.png" : imageUrl %>" onerror="if (this.src != '/img/missing.png') this.src = '/img/missing.png';" width="32" height="32" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-5">
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
<label for="forUrl" class="form-label">For URL</label>
|
||||
<input class="form-control" id="forUrl" name="forUrl" value="<%= typeof(forUrl) === "undefined" ? "" : forUrl %>" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-5">
|
||||
<div class="col">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" class="form-check-input" id="isSecret" name="isSecret" <%= isSecret ? "checked" : "" %> />
|
||||
<label for="isSecret" class="form-check-label">Is Secret</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col text-center">
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
<th> </th>
|
||||
<th>Name</th>
|
||||
<th>For URL</th>
|
||||
<th style="white-space:nowrap">Secret</th>
|
||||
<th> </th>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -35,6 +36,7 @@
|
|||
<td class="align-middle"><img style="image-rendering:pixelated" src="<%= badge.ImageUrl.trim().length === 0 ? "/img/missing.png" : badge.ImageUrl %>" width="32" height="32" /></td>
|
||||
<td class="align-middle"><%= badge.Name %></td>
|
||||
<td class="align-middle"><a href="<%= badge.ForUrl %>"><%= badge.ForUrl %></a></td>
|
||||
<td class="align-middle text-center"><%= badge.IsSecret ? "Yes" : "No" %></td>
|
||||
<td class="text-end text-nowrap align-middle">
|
||||
<a class="btn btn-sm btn-primary" href="/admin/badge?id=<%= badge.Id %>">Edit</a>
|
||||
<a class="btn btn-sm btn-danger" href="/admin/deletebadge?id=<%= badge.Id %>" onclick="return confirm(`Are you sure you want to delete '<%= badge.Name %>'?`)">Delete</a>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
</div>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.bundle.min.js" integrity="sha512-7Pi/otdlbbCR+LnW+F7PwFcSDJOuUJB3OxtEHbg4vSMvzvJjde4Po1v4BR9Gdc9aXNUNFVUY+SK51wWT8WF0Gg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/cookieconsent/3.1.1/cookieconsent.min.js" integrity="sha512-yXXqOFjdjHNH1GND+1EO0jbvvebABpzGKD66djnUfiKlYME5HGMUJHoCaeE4D5PTG2YsSJf6dwqyUUvQvS0vaA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script>
|
||||
(() => {
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
</style>
|
||||
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js" integrity="sha512-v2CJ7UaYy4JwqLDIrZUI/4hqeoQieOmAZNXBeQyjo21dadnwR+8ZaIJVT8EE2iyI61OV8e6M8PP2/4hpQINQ/g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.bundle.min.js" integrity="sha512-7Pi/otdlbbCR+LnW+F7PwFcSDJOuUJB3OxtEHbg4vSMvzvJjde4Po1v4BR9Gdc9aXNUNFVUY+SK51wWT8WF0Gg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand bg-body-tertiary">
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="col pb-5">
|
||||
<h3>Your Parties</h3>
|
||||
<% if (parties.length > 0) { %>
|
||||
<table class="table table-striped">
|
||||
|
@ -66,11 +66,25 @@
|
|||
<h3>Badges</h3>
|
||||
<div class="row mt-4">
|
||||
<div class="col">
|
||||
<% for (let i = 0; i < 71; i++) { %>
|
||||
<img class="mb-3" src="https://eusv.net/Mu6LedXkxrZDUB">
|
||||
<% for (const badgeKey of badgeById.keys) { %>
|
||||
<% const badge = badgeById.get(badgeKey); const unlockedBadge = unlockedBadgesById.get(badgeKey); %>
|
||||
<% if (unlockedBadge) { %>
|
||||
<span class="d-inline-block mb-3" tabindex="0" data-bs-toggle="popover" data-bs-trigger="hover focus" data-bs-placement="top" data-bs-title="<%= badge.Name %>" data-bs-content="<%= badge.Description %><br><small>Unlocked on <%= badge.CreatedDatetime.toString().split(" ").slice(1, 4).join(" ") %></small>">
|
||||
<img width="32" height="32" src="<%= badge.ImageUrl %>">
|
||||
</span>
|
||||
<% } else if (!badge.IsSecret) { %>
|
||||
<span class="d-inline-block mb-3" tabindex="0" data-bs-toggle="popover" data-bs-trigger="hover focus" data-bs-placement="top" data-bs-title="<%= badge.Name %>" data-bs-content="<small>Not yet unlocked</small>">
|
||||
<img src="https://eusv.net/Mu6LedXkxrZDUB">
|
||||
</span>
|
||||
<% } %>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]');
|
||||
const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl, { html: true }));
|
||||
</script>
|
||||
<%- include("../base/footer") %>
|
Loading…
Reference in a new issue