list: cleanup, include offset in DateAttempt

included UTC offset in minutes in DateAttempt, will be used shortly.
Also moved this stuff (ParsedDate, DateAttempt) to the common d.ts, and
the method for parsing from a string (now parseDateString) to common.
Also pre-emptively load the user cache when the admin page loads.
This commit is contained in:
Harvey Tindall
2025-05-27 14:20:17 +01:00
parent 18f8921eba
commit b40abafb95
8 changed files with 53 additions and 46 deletions

View File

@@ -7,8 +7,6 @@ import { SearchConfiguration, QueryType, SearchableItem, SearchableItemDataAttri
import { HiddenInputField } from "./ui"
import { PaginatedList } from "./list"
// FIXME: Find and define a threshold after which searches are no longer performed on input (or just in general by the browser).
declare var window: GlobalWindow;
const USER_DEFAULT_SORT_FIELD = "name";
@@ -976,9 +974,6 @@ export class accountsList extends PaginatedList {
});
this._populateNumbers();
// FIXME: Remove!
(window as any).acc = this;
let searchConfig: SearchConfiguration = {
filterArea: this._c.filterArea,
sortingByButton: this._sortingByButton,

View File

@@ -533,9 +533,6 @@ export class activityList extends PaginatedList {
}
});
// FIXME: Remove!
(window as any).act = this;
this._container = document.getElementById("activity-card-list")
document.addEventListener("activity-reload", () => this.reload());
@@ -583,7 +580,7 @@ export class activityList extends PaginatedList {
// Setting default sort makes sense, since this is the only sort ever being done.
this._c.defaultSortAscending = this.ascending;
this._sortDirection.innerHTML = `${window.lang.strings("sortDirection")} <i class="ri-arrow-${v ? "up" : "down"}-s-line ml-2"></i>`;
// FIXME?: We don't actually re-sort the list here, instead just use setOrdering to apply this.ascending before a reload.
// NOTE: We don't actually re-sort the list here, instead just use setOrdering to apply this.ascending before a reload.
this._search.setOrdering(this._search.ordering, this._c.defaultSortField, this.ascending);
if (this._hasLoaded) {
if (this._search.inServerSearch) {

View File

@@ -1,4 +1,5 @@
declare var window: GlobalWindow;
import dateParser from "any-date-parser";
export function toDateString(date: Date): string {
const locale = window.language || (window as any).navigator.userLanguage || window.navigator.language;
@@ -21,6 +22,20 @@ export function toDateString(date: Date): string {
return date.toLocaleDateString(locale, args1) + " " + date.toLocaleString(locale, args2);
}
export const parseDateString = (value: string): ParsedDate => {
let out: ParsedDate = {
text: value,
// Used just to tell use what fields the user passed.
attempt: dateParser.attempt(value),
// note Date.fromString is also provided by dateParser.
date: (Date as any).fromString(value) as Date
};
out.attempt.offsetMinutesFromUTC = out.date.getTimezoneOffset();
// Month in Date objects is 0-based, so make our parsed date that way too
if ("month" in out.attempt) out.attempt.month -= 1;
return out;
}
export const _get = (url: string, data: Object, onreadystatechange: (req: XMLHttpRequest) => void, noConnectionError: boolean = false): void => {
let req = new XMLHttpRequest();
if (window.pages) { url = window.pages.Base + url; }

View File

@@ -4,6 +4,10 @@ import "@af-utils/scrollend-polyfill";
declare var window: GlobalWindow;
export interface ListItem {
asElement: () => HTMLElement;
};
export class RecordCounter {
private _container: HTMLElement;
private _totalRecords: HTMLElement;
@@ -342,7 +346,7 @@ export abstract class PaginatedList {
this.lastPage = resp.last_page;
appendFunc(resp);
this._counter.loaded = this._search.ordering.length;
if (post) post(resp);

View File

@@ -1,4 +1,5 @@
const dateParser = require("any-date-parser");
import { ListItem } from "./list";
import { parseDateString } from "./common";
declare var window: GlobalWindow;
@@ -176,20 +177,6 @@ export class StringQuery extends Query {
}
}
export interface DateAttempt {
year?: number;
month?: number;
day?: number;
hour?: number;
minute?: number
}
export interface ParsedDate {
attempt: DateAttempt;
date: Date;
text: string;
};
const dateGetters: Map<string, () => number> = (() => {
let m = new Map<string, () => number>();
m.set("year", Date.prototype.getFullYear);
@@ -222,24 +209,15 @@ export class DateQuery extends Query {
<span class="font-bold mr-2">${subject.name}:</span> ${dateText != "" ? dateText+" " : ""}${value.text}
`;
}
public static paramsFromString(valueString: string): [ParsedDate, QueryOperator, boolean] {
// FIXME: Validate this!
let op = QueryOperator.Equal;
if ((Object.values(QueryOperator) as string[]).includes(valueString.charAt(0))) {
op = valueString.charAt(0) as QueryOperator;
// Trim the operator from the string
valueString = valueString.substring(1);
}
let out: ParsedDate = {
text: valueString,
// Used just to tell use what fields the user passed.
attempt: dateParser.attempt(valueString),
// note Date.fromString is also provided by dateParser.
date: (Date as any).fromString(valueString) as Date
};
// Month in Date objects is 0-based, so make our parsed date that way too
if ("month" in out.attempt) out.attempt.month -= 1;
let out = parseDateString(valueString);
let isValid = true;
if ("invalid" in (out.date as any)) { isValid = false; };
@@ -278,10 +256,8 @@ export class DateQuery extends Query {
}
}
export interface SearchableItem {
export interface SearchableItem extends ListItem {
matchesSearch: (query: string) => boolean;
// FIXME: SearchableItem should really be ListItem or something, this isn't for search!
asElement: () => HTMLElement;
}
export const SearchableItemDataAttribute = "data-search-item";
@@ -598,7 +574,6 @@ export class Search {
this._c.search.oninput(null as any);
};
// FIXME: Make XQuery classes less specifically for in-progress searches, and include this code for making info button things.
generateFilterList = () => {
// Generate filter buttons
for (let queryName of Object.keys(this._c.queries)) {

View File

@@ -161,5 +161,20 @@ interface PaginatedReqDTO {
ascending: boolean;
};
interface DateAttempt {
year?: number;
month?: number;
day?: number;
hour?: number;
minute?: number;
offsetMinutesFromUTC?: number;
}
interface ParsedDate {
attempt: DateAttempt;
date: Date;
text: string;
};
declare var config: Object;
declare var modifiedConfig: Object;

View File

@@ -235,11 +235,12 @@ const (
}*/
type DateAttempt struct {
Year *int `json:"year,omitempty"`
Month *int `json:"month,omitempty"`
Day *int `json:"day,omitempty"`
Hour *int `json:"hour,omitempty"`
Minute *int `json:"minute,omitempty"`
Year *int `json:"year,omitempty"`
Month *int `json:"month,omitempty"`
Day *int `json:"day,omitempty"`
Hour *int `json:"hour,omitempty"`
Minute *int `json:"minute,omitempty"`
OffsetMinutesFromUTC *int `json:"offsetMinutesFromUTC,omitempty"`
}
// Compare roughly compares a time.Time to a DateAttempt.

View File

@@ -67,9 +67,10 @@ func (app *appContext) pushResources(gc *gin.Context, page Page) {
default:
toPush = []string{}
}
urlBase := app.getURLBase(gc)
if pusher := gc.Writer.Pusher(); pusher != nil {
for _, f := range toPush {
if err := pusher.Push(PAGES.Base+f, nil); err != nil {
if err := pusher.Push(urlBase+f, nil); err != nil {
app.debug.Printf(lm.FailedServerPush, err)
}
}
@@ -172,6 +173,10 @@ func (app *appContext) getLang(gc *gin.Context, page Page, chosen string) string
func (app *appContext) AdminPage(gc *gin.Context) {
app.pushResources(gc, AdminPage)
// Pre-emptively (maybe) generate user cache
go app.userCache.MaybeSync(app)
lang := app.getLang(gc, AdminPage, app.storage.lang.chosenAdminLang)
jfAdminOnly := app.config.Section("ui").Key("admin_only").MustBool(true)
jfAllowAll := app.config.Section("ui").Key("allow_all").MustBool(false)