From 0fc3bb0c94972b6fab5fd533556e53bdd2fe9eab Mon Sep 17 00:00:00 2001 From: Haelnorr Date: Sat, 14 Feb 2026 14:54:06 +1100 Subject: [PATCH] admin page updates --- cmd/oslstats/routes.go | 32 +- internal/db/auditlog.go | 32 +- internal/db/getlist.go | 11 +- internal/db/role.go | 11 + .../embedfs/web/css/flatpickr-catppuccin.css | 151 ++++ internal/embedfs/web/css/input.css | 71 ++ internal/embedfs/web/css/output.css | 78 +- internal/embedfs/web/js/admin.js | 265 +++++- internal/embedfs/web/js/pagination.js | 13 + .../vendored/flatpickr-dark@4.6.13.min.css | 795 ++++++++++++++++++ .../web/vendored/flatpickr@4.6.13.min.css | 13 + .../web/vendored/flatpickr@4.6.13.min.js | 2 + internal/handlers/admin_audit.go | 114 ++- internal/handlers/admin_roles.go | 49 +- internal/handlers/admin_users.go | 12 +- .../view/adminview/audit_log_detail.templ | 193 +++-- internal/view/adminview/audit_logs_list.templ | 341 +++++--- .../view/adminview/dashboard_layout.templ | 7 +- internal/view/adminview/roles_list.templ | 115 ++- internal/view/adminview/roles_page.templ | 2 +- internal/view/adminview/user_list.templ | 103 ++- internal/view/pagination/table.templ | 44 + 22 files changed, 2136 insertions(+), 318 deletions(-) create mode 100644 internal/embedfs/web/css/flatpickr-catppuccin.css create mode 100644 internal/embedfs/web/vendored/flatpickr-dark@4.6.13.min.css create mode 100644 internal/embedfs/web/vendored/flatpickr@4.6.13.min.css create mode 100644 internal/embedfs/web/vendored/flatpickr@4.6.13.min.js create mode 100644 internal/view/pagination/table.templ diff --git a/cmd/oslstats/routes.go b/cmd/oslstats/routes.go index c461f5a..877f847 100644 --- a/cmd/oslstats/routes.go +++ b/cmd/oslstats/routes.go @@ -30,7 +30,7 @@ func addRoutes( audit *auditlog.Logger, ) error { // Create the routes - pageroutes := []hws.Route{ + baseRoutes := []hws.Route{ { Path: "/static/", Method: hws.MethodGET, @@ -41,6 +41,9 @@ func addRoutes( Method: hws.MethodGET, Handler: handlers.Index(s), }, + } + + authRoutes := []hws.Route{ { Path: "/login", Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, @@ -61,11 +64,9 @@ func addRoutes( Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, Handler: auth.LoginReq(handlers.Logout(s, auth, conn, discordAPI)), }, - { - Path: "/notification-tester", - Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, - Handler: perms.RequireAdmin(s)(handlers.NotifyTester(s)), - }, + } + + seasonRoutes := []hws.Route{ { Path: "/seasons", Method: hws.MethodGET, @@ -121,6 +122,9 @@ func addRoutes( Method: hws.MethodPOST, Handler: perms.RequirePermission(s, permissions.TeamsAddToLeague)(handlers.SeasonLeagueAddTeam(s, conn, audit)), }, + } + + leagueRoutes := []hws.Route{ { Path: "/leagues", Method: hws.MethodGET, @@ -136,6 +140,9 @@ func addRoutes( Method: hws.MethodPOST, Handler: perms.RequirePermission(s, permissions.LeaguesCreate)(handlers.NewLeagueSubmit(s, conn, audit)), }, + } + + teamRoutes := []hws.Route{ { Path: "/teams", Method: hws.MethodGET, @@ -206,6 +213,11 @@ func addRoutes( // Admin routes adminRoutes := []hws.Route{ + { + Path: "/notification-tester", + Methods: []hws.Method{hws.MethodGET, hws.MethodPOST}, + Handler: perms.RequireAdmin(s)(handlers.NotifyTester(s)), + }, // Full page routes (for direct navigation and refreshes) { Path: "/admin", @@ -288,13 +300,11 @@ func addRoutes( Method: hws.MethodPOST, Handler: perms.RequireActualAdmin(s)(handlers.AdminPreviewRoleStop(s)), }, - // Audit log filtering (returns only results table, no URL push) { Path: "/admin/audit/filter", Method: hws.MethodPOST, Handler: perms.RequireAdmin(s)(handlers.AdminAuditLogsFilter(s, conn)), }, - // Audit log detail modal { Path: "/admin/audit/{id}", Method: hws.MethodGET, @@ -302,9 +312,13 @@ func addRoutes( }, } - routes := append(pageroutes, htmxRoutes...) + routes := append(baseRoutes, htmxRoutes...) routes = append(routes, wsRoutes...) + routes = append(routes, authRoutes...) routes = append(routes, adminRoutes...) + routes = append(routes, seasonRoutes...) + routes = append(routes, leagueRoutes...) + routes = append(routes, teamRoutes...) // Register the routes with the server err := s.AddRoutes(routes...) diff --git a/internal/db/auditlog.go b/internal/db/auditlog.go index 2a702d5..f615e93 100644 --- a/internal/db/auditlog.go +++ b/internal/db/auditlog.go @@ -69,6 +69,34 @@ func (a *AuditLogFilter) Result(result string) *AuditLogFilter { return a } +func (a *AuditLogFilter) UserIDs(ids []int) *AuditLogFilter { + if len(ids) > 0 { + a.In("al.user_id", ids) + } + return a +} + +func (a *AuditLogFilter) Actions(actions []string) *AuditLogFilter { + if len(actions) > 0 { + a.In("al.action", actions) + } + return a +} + +func (a *AuditLogFilter) ResourceTypes(resourceTypes []string) *AuditLogFilter { + if len(resourceTypes) > 0 { + a.In("al.resource_type", resourceTypes) + } + return a +} + +func (a *AuditLogFilter) Results(results []string) *AuditLogFilter { + if len(results) > 0 { + a.In("al.result", results) + } + return a +} + func (a *AuditLogFilter) DateRange(start, end int64) *AuditLogFilter { if start > 0 { a.GreaterEqualThan("al.created_at", start) @@ -83,7 +111,7 @@ func (a *AuditLogFilter) DateRange(start, end int64) *AuditLogFilter { func GetAuditLogs(ctx context.Context, tx bun.Tx, pageOpts *PageOpts, filters *AuditLogFilter) (*List[AuditLog], error) { defaultPageOpts := &PageOpts{ Page: 1, - PerPage: 15, + PerPage: 10, Order: bun.OrderDesc, OrderBy: "created_at", } @@ -119,7 +147,7 @@ func GetAuditLogByID(ctx context.Context, tx bun.Tx, id int) (*AuditLog, error) if id <= 0 { return nil, errors.New("id must be positive") } - return GetByID[AuditLog](tx, id).Relation("User").Get(ctx) + return GetByField[AuditLog](tx, "al.id", id).Relation("User").Get(ctx) } // GetUniqueActions retrieves a list of all unique actions in the audit log diff --git a/internal/db/getlist.go b/internal/db/getlist.go index 1768fde..2b96b9a 100644 --- a/internal/db/getlist.go +++ b/internal/db/getlist.go @@ -33,6 +33,7 @@ const ( LessEqual Comparator = "<=" Greater Comparator = ">" GreaterEqual Comparator = ">=" + In Comparator = "IN" ) type ListFilter struct { @@ -63,6 +64,10 @@ func (f *ListFilter) GreaterEqualThan(field string, value any) { f.filters = append(f.filters, Filter{field, value, GreaterEqual}) } +func (f *ListFilter) In(field string, values any) { + f.filters = append(f.filters, Filter{field, values, In}) +} + func GetList[T any](tx bun.Tx) *listgetter[T] { l := &listgetter[T]{ items: new([]*T), @@ -89,7 +94,11 @@ func (l *listgetter[T]) Relation(name string, apply ...func(*bun.SelectQuery) *b func (l *listgetter[T]) Filter(filters ...Filter) *listgetter[T] { for _, filter := range filters { - l.q = l.q.Where("? ? ?", bun.Ident(filter.Field), bun.Safe(filter.Comparator), filter.Value) + if filter.Comparator == In { + l.q = l.q.Where("? IN (?)", bun.Ident(filter.Field), bun.In(filter.Value)) + } else { + l.q = l.q.Where("? ? ?", bun.Ident(filter.Field), bun.Safe(filter.Comparator), filter.Value) + } } return l } diff --git a/internal/db/role.go b/internal/db/role.go index b018fbe..9ed2957 100644 --- a/internal/db/role.go +++ b/internal/db/role.go @@ -61,6 +61,17 @@ func ListAllRoles(ctx context.Context, tx bun.Tx) ([]*Role, error) { return GetList[Role](tx).GetAll(ctx) } +// GetRoles returns a paginated list of roles +func GetRoles(ctx context.Context, tx bun.Tx, pageOpts *PageOpts) (*List[Role], error) { + defaults := &PageOpts{ + Page: 1, + PerPage: 25, + Order: bun.OrderAsc, + OrderBy: "display_name", + } + return GetList[Role](tx).GetPaged(ctx, pageOpts, defaults) +} + // CreateRole creates a new role func CreateRole(ctx context.Context, tx bun.Tx, role *Role) error { if role == nil { diff --git a/internal/embedfs/web/css/flatpickr-catppuccin.css b/internal/embedfs/web/css/flatpickr-catppuccin.css new file mode 100644 index 0000000..5746d6c --- /dev/null +++ b/internal/embedfs/web/css/flatpickr-catppuccin.css @@ -0,0 +1,151 @@ +/* Flatpickr Catppuccin Mocha Theme */ +/* Override flatpickr colors to match our custom theme */ + +.flatpickr-calendar { + background: #1e1e2e; /* mantle */ + border: 1px solid #45475a; /* surface1 */ + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3); +} + +.flatpickr-months { + background: #181825; /* base */ + border-bottom: 1px solid #45475a; /* surface1 */ +} + +.flatpickr-month { + color: #cdd6f4; /* text */ +} + +.flatpickr-current-month .flatpickr-monthDropdown-months { + background: #1e1e2e; /* mantle */ + color: #cdd6f4; /* text */ + border: 1px solid #45475a; /* surface1 */ +} + +.flatpickr-current-month .flatpickr-monthDropdown-months:hover { + background: #313244; /* surface0 */ +} + +.flatpickr-current-month input.cur-year { + color: #cdd6f4; /* text */ + background: #1e1e2e; /* mantle */ +} + +.flatpickr-current-month input.cur-year:hover { + background: #313244; /* surface0 */ +} + +.flatpickr-prev-month, +.flatpickr-next-month { + color: #cdd6f4; /* text */ +} + +.flatpickr-prev-month:hover, +.flatpickr-next-month:hover { + color: #89b4fa; /* blue */ +} + +.flatpickr-weekdays { + background: #181825; /* base */ + border-bottom: 1px solid #45475a; /* surface1 */ +} + +span.flatpickr-weekday { + color: #bac2de; /* subtext0 */ + font-weight: 600; +} + +.flatpickr-days { + background: #1e1e2e; /* mantle */ +} + +.flatpickr-day { + color: #cdd6f4; /* text */ + border: 1px solid transparent; +} + +.flatpickr-day.today { + border-color: #89b4fa; /* blue */ + background: #89b4fa20; /* blue with transparency */ + color: #89b4fa; /* blue */ +} + +.flatpickr-day.today:hover { + background: #89b4fa40; /* blue with more transparency */ + border-color: #89b4fa; /* blue */ + color: #89b4fa; /* blue */ +} + +.flatpickr-day.selected, +.flatpickr-day.startRange, +.flatpickr-day.endRange { + background: #89b4fa; /* blue */ + border-color: #89b4fa; /* blue */ + color: #181825; /* base */ +} + +.flatpickr-day.selected:hover, +.flatpickr-day.startRange:hover, +.flatpickr-day.endRange:hover { + background: #74a7f9; /* slightly lighter blue */ + border-color: #74a7f9; +} + +.flatpickr-day:hover { + background: #313244; /* surface0 */ + border-color: #45475a; /* surface1 */ +} + +.flatpickr-day.prevMonthDay, +.flatpickr-day.nextMonthDay { + color: #585b70; /* surface2 */ +} + +.flatpickr-day.flatpickr-disabled, +.flatpickr-day.flatpickr-disabled:hover { + color: #585b70; /* surface2 */ + cursor: not-allowed; +} + +.flatpickr-day.inRange { + background: #89b4fa30; /* blue with light transparency */ + border-color: transparent; + box-shadow: -5px 0 0 #89b4fa30, 5px 0 0 #89b4fa30; +} + +.flatpickr-time { + background: #181825; /* base */ + border-top: 1px solid #45475a; /* surface1 */ +} + +.flatpickr-time input { + color: #cdd6f4; /* text */ + background: #1e1e2e; /* mantle */ +} + +.flatpickr-time input:hover, +.flatpickr-time input:focus { + background: #313244; /* surface0 */ +} + +.flatpickr-time .flatpickr-time-separator, +.flatpickr-time .flatpickr-am-pm { + color: #cdd6f4; /* text */ +} + +.flatpickr-time .flatpickr-am-pm:hover, +.flatpickr-time .flatpickr-am-pm:focus { + background: #313244; /* surface0 */ +} + +.flatpickr-time .numInputWrapper span.arrowUp:after { + border-bottom-color: #cdd6f4; /* text */ +} + +.flatpickr-time .numInputWrapper span.arrowDown:after { + border-top-color: #cdd6f4; /* text */ +} + +.flatpickr-time .numInputWrapper span:hover { + background: #313244; /* surface0 */ +} diff --git a/internal/embedfs/web/css/input.css b/internal/embedfs/web/css/input.css index 12af588..2686382 100644 --- a/internal/embedfs/web/css/input.css +++ b/internal/embedfs/web/css/input.css @@ -127,3 +127,74 @@ font-weight: 700; font-style: italic; } + +/* Custom Scrollbar Styles - Catppuccin Theme */ + +/* Firefox */ +* { + scrollbar-width: thin; + scrollbar-color: var(--surface1) var(--mantle); +} + +/* Webkit browsers (Chrome, Safari, Edge) */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: var(--mantle); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb { + background: var(--surface1); + border-radius: 4px; + border: 2px solid var(--mantle); +} + +::-webkit-scrollbar-thumb:hover { + background: var(--surface2); +} + +::-webkit-scrollbar-thumb:active { + background: var(--overlay0); +} + +/* Specific styling for multi-select dropdowns */ +.multi-select-dropdown::-webkit-scrollbar { + width: 6px; +} + +.multi-select-dropdown::-webkit-scrollbar-track { + background: var(--base); + border-radius: 3px; +} + +.multi-select-dropdown::-webkit-scrollbar-thumb { + background: var(--surface2); + border-radius: 3px; + border: 1px solid var(--base); +} + +.multi-select-dropdown::-webkit-scrollbar-thumb:hover { + background: var(--overlay0); +} + +/* Specific styling for modal content */ +.modal-scrollable::-webkit-scrollbar { + width: 8px; +} + +.modal-scrollable::-webkit-scrollbar-track { + background: var(--base); +} + +.modal-scrollable::-webkit-scrollbar-thumb { + background: var(--surface1); + border-radius: 4px; +} + +.modal-scrollable::-webkit-scrollbar-thumb:hover { + background: var(--surface2); +} diff --git a/internal/embedfs/web/css/output.css b/internal/embedfs/web/css/output.css index 5c6b35a..44ea2f9 100644 --- a/internal/embedfs/web/css/output.css +++ b/internal/embedfs/web/css/output.css @@ -37,6 +37,7 @@ --text-6xl--line-height: 1; --text-9xl: 8rem; --text-9xl--line-height: 1; + --font-weight-normal: 400; --font-weight-medium: 500; --font-weight-semibold: 600; --font-weight-bold: 700; @@ -379,6 +380,9 @@ .ml-2 { margin-left: calc(var(--spacing) * 2); } + .ml-4 { + margin-left: calc(var(--spacing) * 4); + } .line-clamp-2 { overflow: hidden; display: -webkit-box; @@ -440,6 +444,9 @@ .h-screen { height: 100vh; } + .max-h-60 { + max-height: calc(var(--spacing) * 60); + } .max-h-96 { max-height: calc(var(--spacing) * 96); } @@ -664,6 +671,9 @@ .gap-x-2 { column-gap: calc(var(--spacing) * 2); } + .gap-x-6 { + column-gap: calc(var(--spacing) * 6); + } .space-x-2 { :where(& > :not(:last-child)) { --tw-space-x-reverse: 0; @@ -685,6 +695,9 @@ margin-inline-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-x-reverse))); } } + .gap-y-3 { + row-gap: calc(var(--spacing) * 3); + } .gap-y-4 { row-gap: calc(var(--spacing) * 4); } @@ -913,9 +926,6 @@ .p-2\.5 { padding: calc(var(--spacing) * 2.5); } - .p-3 { - padding: calc(var(--spacing) * 3); - } .p-4 { padding: calc(var(--spacing) * 4); } @@ -1051,6 +1061,10 @@ --tw-font-weight: var(--font-weight-medium); font-weight: var(--font-weight-medium); } + .font-normal { + --tw-font-weight: var(--font-weight-normal); + font-weight: var(--font-weight-normal); + } .font-semibold { --tw-font-weight: var(--font-weight-semibold); font-weight: var(--font-weight-semibold); @@ -1066,6 +1080,9 @@ .text-wrap { text-wrap: wrap; } + .break-words { + overflow-wrap: break-word; + } .break-all { word-break: break-all; } @@ -1144,6 +1161,10 @@ --tw-shadow: 0 20px 25px -5px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 8px 10px -6px var(--tw-shadow-color, rgb(0 0 0 / 0.1)); box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); } + .blur { + --tw-blur: blur(8px); + filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); + } .filter { filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); } @@ -1816,6 +1837,57 @@ font-weight: 700; font-style: italic; } +* { + scrollbar-width: thin; + scrollbar-color: var(--surface1) var(--mantle); +} +::-webkit-scrollbar { + width: 8px; + height: 8px; +} +::-webkit-scrollbar-track { + background: var(--mantle); + border-radius: 4px; +} +::-webkit-scrollbar-thumb { + background: var(--surface1); + border-radius: 4px; + border: 2px solid var(--mantle); +} +::-webkit-scrollbar-thumb:hover { + background: var(--surface2); +} +::-webkit-scrollbar-thumb:active { + background: var(--overlay0); +} +.multi-select-dropdown::-webkit-scrollbar { + width: 6px; +} +.multi-select-dropdown::-webkit-scrollbar-track { + background: var(--base); + border-radius: 3px; +} +.multi-select-dropdown::-webkit-scrollbar-thumb { + background: var(--surface2); + border-radius: 3px; + border: 1px solid var(--base); +} +.multi-select-dropdown::-webkit-scrollbar-thumb:hover { + background: var(--overlay0); +} +.modal-scrollable::-webkit-scrollbar { + width: 8px; +} +.modal-scrollable::-webkit-scrollbar-track { + background: var(--base); +} +.modal-scrollable::-webkit-scrollbar-thumb { + background: var(--surface1); + border-radius: 4px; +} +.modal-scrollable::-webkit-scrollbar-thumb:hover { + background: var(--surface2); +} @property --tw-translate-x { syntax: "*"; inherits: false; diff --git a/internal/embedfs/web/js/admin.js b/internal/embedfs/web/js/admin.js index 5a494af..04a17cd 100644 --- a/internal/embedfs/web/js/admin.js +++ b/internal/embedfs/web/js/admin.js @@ -10,8 +10,236 @@ function formatJSON(json) { } } -// Handle HTMX navigation for admin sections +// Initialize flatpickr for all date inputs +function initFlatpickr() { + document.querySelectorAll(".flatpickr-date").forEach(function (input) { + if (!input._flatpickr) { + flatpickr(input, { + dateFormat: "d/m/Y", + allowInput: true, + }); + } + }); +} + +// Submit the audit filter form with specific page/perPage/order params +function submitAuditFilter(page, perPage, order, orderBy) { + const form = document.getElementById("audit-filters-form"); + if (!form) return; + + // Create hidden inputs for pagination/sorting if they don't exist + let pageInput = form.querySelector('input[name="page"]'); + if (!pageInput) { + pageInput = document.createElement("input"); + pageInput.type = "hidden"; + pageInput.name = "page"; + form.appendChild(pageInput); + } + pageInput.value = page; + + let perPageInput = form.querySelector('input[name="per_page"]'); + if (!perPageInput) { + perPageInput = document.createElement("input"); + perPageInput.type = "hidden"; + perPageInput.name = "per_page"; + form.appendChild(perPageInput); + } + perPageInput.value = perPage; + + let orderInput = form.querySelector('input[name="order"]'); + if (!orderInput) { + orderInput = document.createElement("input"); + orderInput.type = "hidden"; + orderInput.name = "order"; + form.appendChild(orderInput); + } + orderInput.value = order; + + let orderByInput = form.querySelector('input[name="order_by"]'); + if (!orderByInput) { + orderByInput = document.createElement("input"); + orderByInput.type = "hidden"; + orderByInput.name = "order_by"; + form.appendChild(orderByInput); + } + orderByInput.value = orderBy; + + htmx.trigger(form, "submit"); +} + +// Sort by column - toggle direction if same column +function sortAuditColumn(field, currentOrder, currentOrderBy) { + const page = 1; // Reset to first page when sorting + const perPageSelect = document.getElementById("per-page-select"); + const perPage = perPageSelect ? parseInt(perPageSelect.value) || 25 : 25; + let newOrder, newOrderBy; + + if (currentOrderBy === field) { + // Toggle order + newOrder = currentOrder === "ASC" ? "DESC" : "ASC"; + newOrderBy = field; + } else { + // New column, default to DESC + newOrder = "DESC"; + newOrderBy = field; + } + + submitAuditFilter(page, perPage, newOrder, newOrderBy); +} + +// Clear all audit filters +function clearAuditFilters() { + const form = document.getElementById("audit-filters-form"); + if (!form) return; + + form.reset(); + + // Clear flatpickr instances + document.querySelectorAll(".flatpickr-date").forEach(function (input) { + var fp = input._flatpickr; + if (fp) fp.clear(); + }); + + // Clear multi-select dropdowns + document.querySelectorAll(".multi-select-container").forEach(function (container) { + var hiddenInput = container.querySelector('input[type="hidden"]'); + if (hiddenInput) hiddenInput.value = ""; + var selectedDisplay = container.querySelector(".multi-select-selected"); + if (selectedDisplay) + selectedDisplay.innerHTML = 'Select...'; + container.querySelectorAll(".multi-select-option").forEach(function (opt) { + opt.classList.remove("bg-blue", "text-mantle"); + opt.classList.add("hover:bg-surface1"); + }); + }); + + // Trigger form submission with reset pagination + submitAuditFilter(1, 25, "DESC", "created_at"); +} + +// Toggle multi-select dropdown visibility +function toggleMultiSelect(containerId) { + var dropdown = document.getElementById(containerId + "-dropdown"); + if (dropdown) { + dropdown.classList.toggle("hidden"); + } +} + +// Toggle multi-select option selection +function toggleMultiSelectOption(containerId, value, label) { + var container = document.getElementById(containerId); + var hiddenInput = container.querySelector('input[type="hidden"]'); + var selectedDisplay = container.querySelector(".multi-select-selected"); + + var values = hiddenInput.value ? hiddenInput.value.split(",") : []; + var index = values.indexOf(value); + + if (index > -1) { + values.splice(index, 1); + } else { + values.push(value); + } + + hiddenInput.value = values.join(","); + + var option = container.querySelector('[data-value="' + value + '"]'); + if (option) { + if (index > -1) { + option.classList.remove("bg-blue", "text-mantle"); + option.classList.add("hover:bg-surface1"); + } else { + option.classList.add("bg-blue", "text-mantle"); + option.classList.remove("hover:bg-surface1"); + } + } + + if (values.length === 0) { + selectedDisplay.innerHTML = 'Select...'; + } else if (values.length === 1) { + selectedDisplay.innerHTML = "" + label + ""; + } else { + selectedDisplay.innerHTML = "" + values.length + " selected"; + } + + // Trigger form submission + document.getElementById("audit-filters-form").requestSubmit(); +} + +// Submit the users page with specific page/perPage/order params +function submitUsersPage(page, perPage, order, orderBy) { + const formData = new FormData(); + formData.append("page", page); + formData.append("per_page", perPage); + formData.append("order", order); + formData.append("order_by", orderBy); + + htmx.ajax("POST", "/admin/users", { + target: "#users-list-container", + swap: "outerHTML", + values: Object.fromEntries(formData), + }); +} + +// Sort users column - toggle direction if same column +function sortUsersColumn(field, currentOrder, currentOrderBy) { + const page = 1; // Reset to first page when sorting + const perPageSelect = document.getElementById("users-per-page-select"); + const perPage = perPageSelect ? parseInt(perPageSelect.value) || 25 : 25; + let newOrder, newOrderBy; + + if (currentOrderBy === field) { + // Toggle order + newOrder = currentOrder === "ASC" ? "DESC" : "ASC"; + newOrderBy = field; + } else { + // New column, default to ASC + newOrder = "ASC"; + newOrderBy = field; + } + + submitUsersPage(page, perPage, newOrder, newOrderBy); +} + +// Submit the roles page with specific page/perPage/order params +function submitRolesPage(page, perPage, order, orderBy) { + const formData = new FormData(); + formData.append("page", page); + formData.append("per_page", perPage); + formData.append("order", order); + formData.append("order_by", orderBy); + + htmx.ajax("POST", "/admin/roles", { + target: "#roles-list-container", + swap: "outerHTML", + values: Object.fromEntries(formData), + }); +} + +// Sort roles column - toggle direction if same column +function sortRolesColumn(field, currentOrder, currentOrderBy) { + const page = 1; // Reset to first page when sorting + const perPageSelect = document.getElementById("roles-per-page-select"); + const perPage = perPageSelect ? parseInt(perPageSelect.value) || 25 : 25; + let newOrder, newOrderBy; + + if (currentOrderBy === field) { + // Toggle order + newOrder = currentOrder === "ASC" ? "DESC" : "ASC"; + newOrderBy = field; + } else { + // New column, default to ASC + newOrder = "ASC"; + newOrderBy = field; + } + + submitRolesPage(page, perPage, newOrder, newOrderBy); +} + +// Handle HTMX navigation and initialization document.addEventListener("DOMContentLoaded", function () { + // Initialize flatpickr on page load + initFlatpickr(); + // Update active nav item after HTMX navigation document.body.addEventListener("htmx:afterSwap", function (event) { if (event.detail.target.id === "admin-content") { @@ -23,13 +251,40 @@ document.addEventListener("DOMContentLoaded", function () { document.querySelectorAll("nav a").forEach(function (link) { const href = link.getAttribute("href"); if (href && href.includes("/" + section)) { - link.classList.remove("text-subtext0", "hover:bg-surface1", "hover:text-text"); - link.classList.add("bg-blue", "text-mantle", "font-semibold"); + link.classList.remove( + "border-transparent", + "text-subtext0", + "hover:text-text", + "hover:border-surface2" + ); + link.classList.add("border-blue", "text-blue", "font-semibold"); } else { - link.classList.remove("bg-blue", "text-mantle", "font-semibold"); - link.classList.add("text-subtext0", "hover:bg-surface1", "hover:text-text"); + link.classList.remove("border-blue", "text-blue", "font-semibold"); + link.classList.add( + "border-transparent", + "text-subtext0", + "hover:text-text", + "hover:border-surface2" + ); } }); + + // Re-initialize flatpickr after content swap + initFlatpickr(); + } + + // Re-initialize flatpickr when audit results are updated + if (event.detail.target.id === "audit-results-container") { + initFlatpickr(); + } + }); + + // Close multi-select dropdowns when clicking outside + document.addEventListener("click", function (evt) { + if (!evt.target.closest(".multi-select-container")) { + document.querySelectorAll(".multi-select-dropdown").forEach(function (d) { + d.classList.add("hidden"); + }); } }); }); diff --git a/internal/embedfs/web/js/pagination.js b/internal/embedfs/web/js/pagination.js index 10c5173..3174797 100644 --- a/internal/embedfs/web/js/pagination.js +++ b/internal/embedfs/web/js/pagination.js @@ -25,6 +25,19 @@ function paginateData( this.submit(); }, + sortByColumn(field) { + if (this.orderBy === field) { + // Toggle order if same column + this.order = this.order === "ASC" ? "DESC" : "ASC"; + } else { + // New column, default to DESC + this.orderBy = field; + this.order = "DESC"; + } + this.page = 1; // Reset to first page when sorting + this.submit(); + }, + setPerPage(n) { this.perPage = n; this.page = 1; // Reset to first page when changing per page diff --git a/internal/embedfs/web/vendored/flatpickr-dark@4.6.13.min.css b/internal/embedfs/web/vendored/flatpickr-dark@4.6.13.min.css new file mode 100644 index 0000000..dda3075 --- /dev/null +++ b/internal/embedfs/web/vendored/flatpickr-dark@4.6.13.min.css @@ -0,0 +1,795 @@ +.flatpickr-calendar { + background: transparent; + opacity: 0; + display: none; + text-align: center; + visibility: hidden; + padding: 0; + -webkit-animation: none; + animation: none; + direction: ltr; + border: 0; + font-size: 14px; + line-height: 24px; + border-radius: 5px; + position: absolute; + width: 307.875px; + -webkit-box-sizing: border-box; + box-sizing: border-box; + -ms-touch-action: manipulation; + touch-action: manipulation; + background: #3f4458; + -webkit-box-shadow: 1px 0 0 #20222c, -1px 0 0 #20222c, 0 1px 0 #20222c, 0 -1px 0 #20222c, 0 3px 13px rgba(0,0,0,0.08); + box-shadow: 1px 0 0 #20222c, -1px 0 0 #20222c, 0 1px 0 #20222c, 0 -1px 0 #20222c, 0 3px 13px rgba(0,0,0,0.08); +} +.flatpickr-calendar.open, +.flatpickr-calendar.inline { + opacity: 1; + max-height: 640px; + visibility: visible; +} +.flatpickr-calendar.open { + display: inline-block; + z-index: 99999; +} +.flatpickr-calendar.animate.open { + -webkit-animation: fpFadeInDown 300ms cubic-bezier(0.23, 1, 0.32, 1); + animation: fpFadeInDown 300ms cubic-bezier(0.23, 1, 0.32, 1); +} +.flatpickr-calendar.inline { + display: block; + position: relative; + top: 2px; +} +.flatpickr-calendar.static { + position: absolute; + top: calc(100% + 2px); +} +.flatpickr-calendar.static.open { + z-index: 999; + display: block; +} +.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+1) .flatpickr-day.inRange:nth-child(7n+7) { + -webkit-box-shadow: none !important; + box-shadow: none !important; +} +.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+2) .flatpickr-day.inRange:nth-child(7n+1) { + -webkit-box-shadow: -2px 0 0 #e6e6e6, 5px 0 0 #e6e6e6; + box-shadow: -2px 0 0 #e6e6e6, 5px 0 0 #e6e6e6; +} +.flatpickr-calendar .hasWeeks .dayContainer, +.flatpickr-calendar .hasTime .dayContainer { + border-bottom: 0; + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; +} +.flatpickr-calendar .hasWeeks .dayContainer { + border-left: 0; +} +.flatpickr-calendar.hasTime .flatpickr-time { + height: 40px; + border-top: 1px solid #20222c; +} +.flatpickr-calendar.noCalendar.hasTime .flatpickr-time { + height: auto; +} +.flatpickr-calendar:before, +.flatpickr-calendar:after { + position: absolute; + display: block; + pointer-events: none; + border: solid transparent; + content: ''; + height: 0; + width: 0; + left: 22px; +} +.flatpickr-calendar.rightMost:before, +.flatpickr-calendar.arrowRight:before, +.flatpickr-calendar.rightMost:after, +.flatpickr-calendar.arrowRight:after { + left: auto; + right: 22px; +} +.flatpickr-calendar.arrowCenter:before, +.flatpickr-calendar.arrowCenter:after { + left: 50%; + right: 50%; +} +.flatpickr-calendar:before { + border-width: 5px; + margin: 0 -5px; +} +.flatpickr-calendar:after { + border-width: 4px; + margin: 0 -4px; +} +.flatpickr-calendar.arrowTop:before, +.flatpickr-calendar.arrowTop:after { + bottom: 100%; +} +.flatpickr-calendar.arrowTop:before { + border-bottom-color: #20222c; +} +.flatpickr-calendar.arrowTop:after { + border-bottom-color: #3f4458; +} +.flatpickr-calendar.arrowBottom:before, +.flatpickr-calendar.arrowBottom:after { + top: 100%; +} +.flatpickr-calendar.arrowBottom:before { + border-top-color: #20222c; +} +.flatpickr-calendar.arrowBottom:after { + border-top-color: #3f4458; +} +.flatpickr-calendar:focus { + outline: 0; +} +.flatpickr-wrapper { + position: relative; + display: inline-block; +} +.flatpickr-months { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; +} +.flatpickr-months .flatpickr-month { + background: #3f4458; + color: #fff; + fill: #fff; + height: 34px; + line-height: 1; + text-align: center; + position: relative; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + overflow: hidden; + -webkit-box-flex: 1; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; +} +.flatpickr-months .flatpickr-prev-month, +.flatpickr-months .flatpickr-next-month { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + text-decoration: none; + cursor: pointer; + position: absolute; + top: 0; + height: 34px; + padding: 10px; + z-index: 3; + color: #fff; + fill: #fff; +} +.flatpickr-months .flatpickr-prev-month.flatpickr-disabled, +.flatpickr-months .flatpickr-next-month.flatpickr-disabled { + display: none; +} +.flatpickr-months .flatpickr-prev-month i, +.flatpickr-months .flatpickr-next-month i { + position: relative; +} +.flatpickr-months .flatpickr-prev-month.flatpickr-prev-month, +.flatpickr-months .flatpickr-next-month.flatpickr-prev-month { +/* + /*rtl:begin:ignore*/ +/* + */ + left: 0; +/* + /*rtl:end:ignore*/ +/* + */ +} +/* + /*rtl:begin:ignore*/ +/* + /*rtl:end:ignore*/ +.flatpickr-months .flatpickr-prev-month.flatpickr-next-month, +.flatpickr-months .flatpickr-next-month.flatpickr-next-month { +/* + /*rtl:begin:ignore*/ +/* + */ + right: 0; +/* + /*rtl:end:ignore*/ +/* + */ +} +/* + /*rtl:begin:ignore*/ +/* + /*rtl:end:ignore*/ +.flatpickr-months .flatpickr-prev-month:hover, +.flatpickr-months .flatpickr-next-month:hover { + color: #eee; +} +.flatpickr-months .flatpickr-prev-month:hover svg, +.flatpickr-months .flatpickr-next-month:hover svg { + fill: #f64747; +} +.flatpickr-months .flatpickr-prev-month svg, +.flatpickr-months .flatpickr-next-month svg { + width: 14px; + height: 14px; +} +.flatpickr-months .flatpickr-prev-month svg path, +.flatpickr-months .flatpickr-next-month svg path { + -webkit-transition: fill 0.1s; + transition: fill 0.1s; + fill: inherit; +} +.numInputWrapper { + position: relative; + height: auto; +} +.numInputWrapper input, +.numInputWrapper span { + display: inline-block; +} +.numInputWrapper input { + width: 100%; +} +.numInputWrapper input::-ms-clear { + display: none; +} +.numInputWrapper input::-webkit-outer-spin-button, +.numInputWrapper input::-webkit-inner-spin-button { + margin: 0; + -webkit-appearance: none; +} +.numInputWrapper span { + position: absolute; + right: 0; + width: 14px; + padding: 0 4px 0 2px; + height: 50%; + line-height: 50%; + opacity: 0; + cursor: pointer; + border: 1px solid rgba(255,255,255,0.15); + -webkit-box-sizing: border-box; + box-sizing: border-box; +} +.numInputWrapper span:hover { + background: rgba(192,187,167,0.1); +} +.numInputWrapper span:active { + background: rgba(192,187,167,0.2); +} +.numInputWrapper span:after { + display: block; + content: ""; + position: absolute; +} +.numInputWrapper span.arrowUp { + top: 0; + border-bottom: 0; +} +.numInputWrapper span.arrowUp:after { + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-bottom: 4px solid rgba(255,255,255,0.6); + top: 26%; +} +.numInputWrapper span.arrowDown { + top: 50%; +} +.numInputWrapper span.arrowDown:after { + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 4px solid rgba(255,255,255,0.6); + top: 40%; +} +.numInputWrapper span svg { + width: inherit; + height: auto; +} +.numInputWrapper span svg path { + fill: rgba(255,255,255,0.5); +} +.numInputWrapper:hover { + background: rgba(192,187,167,0.05); +} +.numInputWrapper:hover span { + opacity: 1; +} +.flatpickr-current-month { + font-size: 135%; + line-height: inherit; + font-weight: 300; + color: inherit; + position: absolute; + width: 75%; + left: 12.5%; + padding: 7.48px 0 0 0; + line-height: 1; + height: 34px; + display: inline-block; + text-align: center; + -webkit-transform: translate3d(0px, 0px, 0px); + transform: translate3d(0px, 0px, 0px); +} +.flatpickr-current-month span.cur-month { + font-family: inherit; + font-weight: 700; + color: inherit; + display: inline-block; + margin-left: 0.5ch; + padding: 0; +} +.flatpickr-current-month span.cur-month:hover { + background: rgba(192,187,167,0.05); +} +.flatpickr-current-month .numInputWrapper { + width: 6ch; + width: 7ch\0; + display: inline-block; +} +.flatpickr-current-month .numInputWrapper span.arrowUp:after { + border-bottom-color: #fff; +} +.flatpickr-current-month .numInputWrapper span.arrowDown:after { + border-top-color: #fff; +} +.flatpickr-current-month input.cur-year { + background: transparent; + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: inherit; + cursor: text; + padding: 0 0 0 0.5ch; + margin: 0; + display: inline-block; + font-size: inherit; + font-family: inherit; + font-weight: 300; + line-height: inherit; + height: auto; + border: 0; + border-radius: 0; + vertical-align: initial; + -webkit-appearance: textfield; + -moz-appearance: textfield; + appearance: textfield; +} +.flatpickr-current-month input.cur-year:focus { + outline: 0; +} +.flatpickr-current-month input.cur-year[disabled], +.flatpickr-current-month input.cur-year[disabled]:hover { + font-size: 100%; + color: rgba(255,255,255,0.5); + background: transparent; + pointer-events: none; +} +.flatpickr-current-month .flatpickr-monthDropdown-months { + appearance: menulist; + background: #3f4458; + border: none; + border-radius: 0; + box-sizing: border-box; + color: inherit; + cursor: pointer; + font-size: inherit; + font-family: inherit; + font-weight: 300; + height: auto; + line-height: inherit; + margin: -1px 0 0 0; + outline: none; + padding: 0 0 0 0.5ch; + position: relative; + vertical-align: initial; + -webkit-box-sizing: border-box; + -webkit-appearance: menulist; + -moz-appearance: menulist; + width: auto; +} +.flatpickr-current-month .flatpickr-monthDropdown-months:focus, +.flatpickr-current-month .flatpickr-monthDropdown-months:active { + outline: none; +} +.flatpickr-current-month .flatpickr-monthDropdown-months:hover { + background: rgba(192,187,167,0.05); +} +.flatpickr-current-month .flatpickr-monthDropdown-months .flatpickr-monthDropdown-month { + background-color: #3f4458; + outline: none; + padding: 0; +} +.flatpickr-weekdays { + background: transparent; + text-align: center; + overflow: hidden; + width: 100%; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-align: center; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + height: 28px; +} +.flatpickr-weekdays .flatpickr-weekdaycontainer { + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-flex: 1; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; +} +span.flatpickr-weekday { + cursor: default; + font-size: 90%; + background: #3f4458; + color: #fff; + line-height: 1; + margin: 0; + text-align: center; + display: block; + -webkit-box-flex: 1; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + font-weight: bolder; +} +.dayContainer, +.flatpickr-weeks { + padding: 1px 0 0 0; +} +.flatpickr-days { + position: relative; + overflow: hidden; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-align: start; + -webkit-align-items: flex-start; + -ms-flex-align: start; + align-items: flex-start; + width: 307.875px; +} +.flatpickr-days:focus { + outline: 0; +} +.dayContainer { + padding: 0; + outline: 0; + text-align: left; + width: 307.875px; + min-width: 307.875px; + max-width: 307.875px; + -webkit-box-sizing: border-box; + box-sizing: border-box; + display: inline-block; + display: -ms-flexbox; + display: -webkit-box; + display: -webkit-flex; + display: flex; + -webkit-flex-wrap: wrap; + flex-wrap: wrap; + -ms-flex-wrap: wrap; + -ms-flex-pack: justify; + -webkit-justify-content: space-around; + justify-content: space-around; + -webkit-transform: translate3d(0px, 0px, 0px); + transform: translate3d(0px, 0px, 0px); + opacity: 1; +} +.dayContainer + .dayContainer { + -webkit-box-shadow: -1px 0 0 #20222c; + box-shadow: -1px 0 0 #20222c; +} +.flatpickr-day { + background: none; + border: 1px solid transparent; + border-radius: 150px; + -webkit-box-sizing: border-box; + box-sizing: border-box; + color: rgba(255,255,255,0.95); + cursor: pointer; + font-weight: 400; + width: 14.2857143%; + -webkit-flex-basis: 14.2857143%; + -ms-flex-preferred-size: 14.2857143%; + flex-basis: 14.2857143%; + max-width: 39px; + height: 39px; + line-height: 39px; + margin: 0; + display: inline-block; + position: relative; + -webkit-box-pack: center; + -webkit-justify-content: center; + -ms-flex-pack: center; + justify-content: center; + text-align: center; +} +.flatpickr-day.inRange, +.flatpickr-day.prevMonthDay.inRange, +.flatpickr-day.nextMonthDay.inRange, +.flatpickr-day.today.inRange, +.flatpickr-day.prevMonthDay.today.inRange, +.flatpickr-day.nextMonthDay.today.inRange, +.flatpickr-day:hover, +.flatpickr-day.prevMonthDay:hover, +.flatpickr-day.nextMonthDay:hover, +.flatpickr-day:focus, +.flatpickr-day.prevMonthDay:focus, +.flatpickr-day.nextMonthDay:focus { + cursor: pointer; + outline: 0; + background: #646c8c; + border-color: #646c8c; +} +.flatpickr-day.today { + border-color: #eee; +} +.flatpickr-day.today:hover, +.flatpickr-day.today:focus { + border-color: #eee; + background: #eee; + color: #3f4458; +} +.flatpickr-day.selected, +.flatpickr-day.startRange, +.flatpickr-day.endRange, +.flatpickr-day.selected.inRange, +.flatpickr-day.startRange.inRange, +.flatpickr-day.endRange.inRange, +.flatpickr-day.selected:focus, +.flatpickr-day.startRange:focus, +.flatpickr-day.endRange:focus, +.flatpickr-day.selected:hover, +.flatpickr-day.startRange:hover, +.flatpickr-day.endRange:hover, +.flatpickr-day.selected.prevMonthDay, +.flatpickr-day.startRange.prevMonthDay, +.flatpickr-day.endRange.prevMonthDay, +.flatpickr-day.selected.nextMonthDay, +.flatpickr-day.startRange.nextMonthDay, +.flatpickr-day.endRange.nextMonthDay { + background: #80cbc4; + -webkit-box-shadow: none; + box-shadow: none; + color: #fff; + border-color: #80cbc4; +} +.flatpickr-day.selected.startRange, +.flatpickr-day.startRange.startRange, +.flatpickr-day.endRange.startRange { + border-radius: 50px 0 0 50px; +} +.flatpickr-day.selected.endRange, +.flatpickr-day.startRange.endRange, +.flatpickr-day.endRange.endRange { + border-radius: 0 50px 50px 0; +} +.flatpickr-day.selected.startRange + .endRange:not(:nth-child(7n+1)), +.flatpickr-day.startRange.startRange + .endRange:not(:nth-child(7n+1)), +.flatpickr-day.endRange.startRange + .endRange:not(:nth-child(7n+1)) { + -webkit-box-shadow: -10px 0 0 #80cbc4; + box-shadow: -10px 0 0 #80cbc4; +} +.flatpickr-day.selected.startRange.endRange, +.flatpickr-day.startRange.startRange.endRange, +.flatpickr-day.endRange.startRange.endRange { + border-radius: 50px; +} +.flatpickr-day.inRange { + border-radius: 0; + -webkit-box-shadow: -5px 0 0 #646c8c, 5px 0 0 #646c8c; + box-shadow: -5px 0 0 #646c8c, 5px 0 0 #646c8c; +} +.flatpickr-day.flatpickr-disabled, +.flatpickr-day.flatpickr-disabled:hover, +.flatpickr-day.prevMonthDay, +.flatpickr-day.nextMonthDay, +.flatpickr-day.notAllowed, +.flatpickr-day.notAllowed.prevMonthDay, +.flatpickr-day.notAllowed.nextMonthDay { + color: rgba(255,255,255,0.3); + background: transparent; + border-color: transparent; + cursor: default; +} +.flatpickr-day.flatpickr-disabled, +.flatpickr-day.flatpickr-disabled:hover { + cursor: not-allowed; + color: rgba(255,255,255,0.1); +} +.flatpickr-day.week.selected { + border-radius: 0; + -webkit-box-shadow: -5px 0 0 #80cbc4, 5px 0 0 #80cbc4; + box-shadow: -5px 0 0 #80cbc4, 5px 0 0 #80cbc4; +} +.flatpickr-day.hidden { + visibility: hidden; +} +.rangeMode .flatpickr-day { + margin-top: 1px; +} +.flatpickr-weekwrapper { + float: left; +} +.flatpickr-weekwrapper .flatpickr-weeks { + padding: 0 12px; + -webkit-box-shadow: 1px 0 0 #20222c; + box-shadow: 1px 0 0 #20222c; +} +.flatpickr-weekwrapper .flatpickr-weekday { + float: none; + width: 100%; + line-height: 28px; +} +.flatpickr-weekwrapper span.flatpickr-day, +.flatpickr-weekwrapper span.flatpickr-day:hover { + display: block; + width: 100%; + max-width: none; + color: rgba(255,255,255,0.3); + background: transparent; + cursor: default; + border: none; +} +.flatpickr-innerContainer { + display: block; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-box-sizing: border-box; + box-sizing: border-box; + overflow: hidden; +} +.flatpickr-rContainer { + display: inline-block; + padding: 0; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} +.flatpickr-time { + text-align: center; + outline: 0; + display: block; + height: 0; + line-height: 40px; + max-height: 40px; + -webkit-box-sizing: border-box; + box-sizing: border-box; + overflow: hidden; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; +} +.flatpickr-time:after { + content: ""; + display: table; + clear: both; +} +.flatpickr-time .numInputWrapper { + -webkit-box-flex: 1; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + width: 40%; + height: 40px; + float: left; +} +.flatpickr-time .numInputWrapper span.arrowUp:after { + border-bottom-color: rgba(255,255,255,0.95); +} +.flatpickr-time .numInputWrapper span.arrowDown:after { + border-top-color: rgba(255,255,255,0.95); +} +.flatpickr-time.hasSeconds .numInputWrapper { + width: 26%; +} +.flatpickr-time.time24hr .numInputWrapper { + width: 49%; +} +.flatpickr-time input { + background: transparent; + -webkit-box-shadow: none; + box-shadow: none; + border: 0; + border-radius: 0; + text-align: center; + margin: 0; + padding: 0; + height: inherit; + line-height: inherit; + color: rgba(255,255,255,0.95); + font-size: 14px; + position: relative; + -webkit-box-sizing: border-box; + box-sizing: border-box; + -webkit-appearance: textfield; + -moz-appearance: textfield; + appearance: textfield; +} +.flatpickr-time input.flatpickr-hour { + font-weight: bold; +} +.flatpickr-time input.flatpickr-minute, +.flatpickr-time input.flatpickr-second { + font-weight: 400; +} +.flatpickr-time input:focus { + outline: 0; + border: 0; +} +.flatpickr-time .flatpickr-time-separator, +.flatpickr-time .flatpickr-am-pm { + height: inherit; + float: left; + line-height: inherit; + color: rgba(255,255,255,0.95); + font-weight: bold; + width: 2%; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-align-self: center; + -ms-flex-item-align: center; + align-self: center; +} +.flatpickr-time .flatpickr-am-pm { + outline: 0; + width: 18%; + cursor: pointer; + text-align: center; + font-weight: 400; +} +.flatpickr-time input:hover, +.flatpickr-time .flatpickr-am-pm:hover, +.flatpickr-time input:focus, +.flatpickr-time .flatpickr-am-pm:focus { + background: #6a7395; +} +.flatpickr-input[readonly] { + cursor: pointer; +} +@-webkit-keyframes fpFadeInDown { + from { + opacity: 0; + -webkit-transform: translate3d(0, -20px, 0); + transform: translate3d(0, -20px, 0); + } + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} +@keyframes fpFadeInDown { + from { + opacity: 0; + -webkit-transform: translate3d(0, -20px, 0); + transform: translate3d(0, -20px, 0); + } + to { + opacity: 1; + -webkit-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); + } +} diff --git a/internal/embedfs/web/vendored/flatpickr@4.6.13.min.css b/internal/embedfs/web/vendored/flatpickr@4.6.13.min.css new file mode 100644 index 0000000..a10acc6 --- /dev/null +++ b/internal/embedfs/web/vendored/flatpickr@4.6.13.min.css @@ -0,0 +1,13 @@ +.flatpickr-calendar{background:transparent;opacity:0;display:none;text-align:center;visibility:hidden;padding:0;-webkit-animation:none;animation:none;direction:ltr;border:0;font-size:14px;line-height:24px;border-radius:5px;position:absolute;width:307.875px;-webkit-box-sizing:border-box;box-sizing:border-box;-ms-touch-action:manipulation;touch-action:manipulation;background:#fff;-webkit-box-shadow:1px 0 0 #e6e6e6,-1px 0 0 #e6e6e6,0 1px 0 #e6e6e6,0 -1px 0 #e6e6e6,0 3px 13px rgba(0,0,0,0.08);box-shadow:1px 0 0 #e6e6e6,-1px 0 0 #e6e6e6,0 1px 0 #e6e6e6,0 -1px 0 #e6e6e6,0 3px 13px rgba(0,0,0,0.08)}.flatpickr-calendar.open,.flatpickr-calendar.inline{opacity:1;max-height:640px;visibility:visible}.flatpickr-calendar.open{display:inline-block;z-index:99999}.flatpickr-calendar.animate.open{-webkit-animation:fpFadeInDown 300ms cubic-bezier(.23,1,.32,1);animation:fpFadeInDown 300ms cubic-bezier(.23,1,.32,1)}.flatpickr-calendar.inline{display:block;position:relative;top:2px}.flatpickr-calendar.static{position:absolute;top:calc(100% + 2px)}.flatpickr-calendar.static.open{z-index:999;display:block}.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+1) .flatpickr-day.inRange:nth-child(7n+7){-webkit-box-shadow:none !important;box-shadow:none !important}.flatpickr-calendar.multiMonth .flatpickr-days .dayContainer:nth-child(n+2) .flatpickr-day.inRange:nth-child(7n+1){-webkit-box-shadow:-2px 0 0 #e6e6e6,5px 0 0 #e6e6e6;box-shadow:-2px 0 0 #e6e6e6,5px 0 0 #e6e6e6}.flatpickr-calendar .hasWeeks .dayContainer,.flatpickr-calendar .hasTime .dayContainer{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.flatpickr-calendar .hasWeeks .dayContainer{border-left:0}.flatpickr-calendar.hasTime .flatpickr-time{height:40px;border-top:1px solid #e6e6e6}.flatpickr-calendar.noCalendar.hasTime .flatpickr-time{height:auto}.flatpickr-calendar:before,.flatpickr-calendar:after{position:absolute;display:block;pointer-events:none;border:solid transparent;content:'';height:0;width:0;left:22px}.flatpickr-calendar.rightMost:before,.flatpickr-calendar.arrowRight:before,.flatpickr-calendar.rightMost:after,.flatpickr-calendar.arrowRight:after{left:auto;right:22px}.flatpickr-calendar.arrowCenter:before,.flatpickr-calendar.arrowCenter:after{left:50%;right:50%}.flatpickr-calendar:before{border-width:5px;margin:0 -5px}.flatpickr-calendar:after{border-width:4px;margin:0 -4px}.flatpickr-calendar.arrowTop:before,.flatpickr-calendar.arrowTop:after{bottom:100%}.flatpickr-calendar.arrowTop:before{border-bottom-color:#e6e6e6}.flatpickr-calendar.arrowTop:after{border-bottom-color:#fff}.flatpickr-calendar.arrowBottom:before,.flatpickr-calendar.arrowBottom:after{top:100%}.flatpickr-calendar.arrowBottom:before{border-top-color:#e6e6e6}.flatpickr-calendar.arrowBottom:after{border-top-color:#fff}.flatpickr-calendar:focus{outline:0}.flatpickr-wrapper{position:relative;display:inline-block}.flatpickr-months{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.flatpickr-months .flatpickr-month{background:transparent;color:rgba(0,0,0,0.9);fill:rgba(0,0,0,0.9);height:34px;line-height:1;text-align:center;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;overflow:hidden;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}.flatpickr-months .flatpickr-prev-month,.flatpickr-months .flatpickr-next-month{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;text-decoration:none;cursor:pointer;position:absolute;top:0;height:34px;padding:10px;z-index:3;color:rgba(0,0,0,0.9);fill:rgba(0,0,0,0.9)}.flatpickr-months .flatpickr-prev-month.flatpickr-disabled,.flatpickr-months .flatpickr-next-month.flatpickr-disabled{display:none}.flatpickr-months .flatpickr-prev-month i,.flatpickr-months .flatpickr-next-month i{position:relative}.flatpickr-months .flatpickr-prev-month.flatpickr-prev-month,.flatpickr-months .flatpickr-next-month.flatpickr-prev-month{/* + /*rtl:begin:ignore*/left:0/* + /*rtl:end:ignore*/}/* + /*rtl:begin:ignore*/ +/* + /*rtl:end:ignore*/ +.flatpickr-months .flatpickr-prev-month.flatpickr-next-month,.flatpickr-months .flatpickr-next-month.flatpickr-next-month{/* + /*rtl:begin:ignore*/right:0/* + /*rtl:end:ignore*/}/* + /*rtl:begin:ignore*/ +/* + /*rtl:end:ignore*/ +.flatpickr-months .flatpickr-prev-month:hover,.flatpickr-months .flatpickr-next-month:hover{color:#959ea9}.flatpickr-months .flatpickr-prev-month:hover svg,.flatpickr-months .flatpickr-next-month:hover svg{fill:#f64747}.flatpickr-months .flatpickr-prev-month svg,.flatpickr-months .flatpickr-next-month svg{width:14px;height:14px}.flatpickr-months .flatpickr-prev-month svg path,.flatpickr-months .flatpickr-next-month svg path{-webkit-transition:fill .1s;transition:fill .1s;fill:inherit}.numInputWrapper{position:relative;height:auto}.numInputWrapper input,.numInputWrapper span{display:inline-block}.numInputWrapper input{width:100%}.numInputWrapper input::-ms-clear{display:none}.numInputWrapper input::-webkit-outer-spin-button,.numInputWrapper input::-webkit-inner-spin-button{margin:0;-webkit-appearance:none}.numInputWrapper span{position:absolute;right:0;width:14px;padding:0 4px 0 2px;height:50%;line-height:50%;opacity:0;cursor:pointer;border:1px solid rgba(57,57,57,0.15);-webkit-box-sizing:border-box;box-sizing:border-box}.numInputWrapper span:hover{background:rgba(0,0,0,0.1)}.numInputWrapper span:active{background:rgba(0,0,0,0.2)}.numInputWrapper span:after{display:block;content:"";position:absolute}.numInputWrapper span.arrowUp{top:0;border-bottom:0}.numInputWrapper span.arrowUp:after{border-left:4px solid transparent;border-right:4px solid transparent;border-bottom:4px solid rgba(57,57,57,0.6);top:26%}.numInputWrapper span.arrowDown{top:50%}.numInputWrapper span.arrowDown:after{border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid rgba(57,57,57,0.6);top:40%}.numInputWrapper span svg{width:inherit;height:auto}.numInputWrapper span svg path{fill:rgba(0,0,0,0.5)}.numInputWrapper:hover{background:rgba(0,0,0,0.05)}.numInputWrapper:hover span{opacity:1}.flatpickr-current-month{font-size:135%;line-height:inherit;font-weight:300;color:inherit;position:absolute;width:75%;left:12.5%;padding:7.48px 0 0 0;line-height:1;height:34px;display:inline-block;text-align:center;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.flatpickr-current-month span.cur-month{font-family:inherit;font-weight:700;color:inherit;display:inline-block;margin-left:.5ch;padding:0}.flatpickr-current-month span.cur-month:hover{background:rgba(0,0,0,0.05)}.flatpickr-current-month .numInputWrapper{width:6ch;width:7ch\0;display:inline-block}.flatpickr-current-month .numInputWrapper span.arrowUp:after{border-bottom-color:rgba(0,0,0,0.9)}.flatpickr-current-month .numInputWrapper span.arrowDown:after{border-top-color:rgba(0,0,0,0.9)}.flatpickr-current-month input.cur-year{background:transparent;-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;cursor:text;padding:0 0 0 .5ch;margin:0;display:inline-block;font-size:inherit;font-family:inherit;font-weight:300;line-height:inherit;height:auto;border:0;border-radius:0;vertical-align:initial;-webkit-appearance:textfield;-moz-appearance:textfield;appearance:textfield}.flatpickr-current-month input.cur-year:focus{outline:0}.flatpickr-current-month input.cur-year[disabled],.flatpickr-current-month input.cur-year[disabled]:hover{font-size:100%;color:rgba(0,0,0,0.5);background:transparent;pointer-events:none}.flatpickr-current-month .flatpickr-monthDropdown-months{appearance:menulist;background:transparent;border:none;border-radius:0;box-sizing:border-box;color:inherit;cursor:pointer;font-size:inherit;font-family:inherit;font-weight:300;height:auto;line-height:inherit;margin:-1px 0 0 0;outline:none;padding:0 0 0 .5ch;position:relative;vertical-align:initial;-webkit-box-sizing:border-box;-webkit-appearance:menulist;-moz-appearance:menulist;width:auto}.flatpickr-current-month .flatpickr-monthDropdown-months:focus,.flatpickr-current-month .flatpickr-monthDropdown-months:active{outline:none}.flatpickr-current-month .flatpickr-monthDropdown-months:hover{background:rgba(0,0,0,0.05)}.flatpickr-current-month .flatpickr-monthDropdown-months .flatpickr-monthDropdown-month{background-color:transparent;outline:none;padding:0}.flatpickr-weekdays{background:transparent;text-align:center;overflow:hidden;width:100%;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;height:28px}.flatpickr-weekdays .flatpickr-weekdaycontainer{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1}span.flatpickr-weekday{cursor:default;font-size:90%;background:transparent;color:rgba(0,0,0,0.54);line-height:1;margin:0;text-align:center;display:block;-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;font-weight:bolder}.dayContainer,.flatpickr-weeks{padding:1px 0 0 0}.flatpickr-days{position:relative;overflow:hidden;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:start;-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start;width:307.875px}.flatpickr-days:focus{outline:0}.dayContainer{padding:0;outline:0;text-align:left;width:307.875px;min-width:307.875px;max-width:307.875px;-webkit-box-sizing:border-box;box-sizing:border-box;display:inline-block;display:-ms-flexbox;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-wrap:wrap;-ms-flex-pack:justify;-webkit-justify-content:space-around;justify-content:space-around;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);opacity:1}.dayContainer + .dayContainer{-webkit-box-shadow:-1px 0 0 #e6e6e6;box-shadow:-1px 0 0 #e6e6e6}.flatpickr-day{background:none;border:1px solid transparent;border-radius:150px;-webkit-box-sizing:border-box;box-sizing:border-box;color:#393939;cursor:pointer;font-weight:400;width:14.2857143%;-webkit-flex-basis:14.2857143%;-ms-flex-preferred-size:14.2857143%;flex-basis:14.2857143%;max-width:39px;height:39px;line-height:39px;margin:0;display:inline-block;position:relative;-webkit-box-pack:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;text-align:center}.flatpickr-day.inRange,.flatpickr-day.prevMonthDay.inRange,.flatpickr-day.nextMonthDay.inRange,.flatpickr-day.today.inRange,.flatpickr-day.prevMonthDay.today.inRange,.flatpickr-day.nextMonthDay.today.inRange,.flatpickr-day:hover,.flatpickr-day.prevMonthDay:hover,.flatpickr-day.nextMonthDay:hover,.flatpickr-day:focus,.flatpickr-day.prevMonthDay:focus,.flatpickr-day.nextMonthDay:focus{cursor:pointer;outline:0;background:#e6e6e6;border-color:#e6e6e6}.flatpickr-day.today{border-color:#959ea9}.flatpickr-day.today:hover,.flatpickr-day.today:focus{border-color:#959ea9;background:#959ea9;color:#fff}.flatpickr-day.selected,.flatpickr-day.startRange,.flatpickr-day.endRange,.flatpickr-day.selected.inRange,.flatpickr-day.startRange.inRange,.flatpickr-day.endRange.inRange,.flatpickr-day.selected:focus,.flatpickr-day.startRange:focus,.flatpickr-day.endRange:focus,.flatpickr-day.selected:hover,.flatpickr-day.startRange:hover,.flatpickr-day.endRange:hover,.flatpickr-day.selected.prevMonthDay,.flatpickr-day.startRange.prevMonthDay,.flatpickr-day.endRange.prevMonthDay,.flatpickr-day.selected.nextMonthDay,.flatpickr-day.startRange.nextMonthDay,.flatpickr-day.endRange.nextMonthDay{background:#569ff7;-webkit-box-shadow:none;box-shadow:none;color:#fff;border-color:#569ff7}.flatpickr-day.selected.startRange,.flatpickr-day.startRange.startRange,.flatpickr-day.endRange.startRange{border-radius:50px 0 0 50px}.flatpickr-day.selected.endRange,.flatpickr-day.startRange.endRange,.flatpickr-day.endRange.endRange{border-radius:0 50px 50px 0}.flatpickr-day.selected.startRange + .endRange:not(:nth-child(7n+1)),.flatpickr-day.startRange.startRange + .endRange:not(:nth-child(7n+1)),.flatpickr-day.endRange.startRange + .endRange:not(:nth-child(7n+1)){-webkit-box-shadow:-10px 0 0 #569ff7;box-shadow:-10px 0 0 #569ff7}.flatpickr-day.selected.startRange.endRange,.flatpickr-day.startRange.startRange.endRange,.flatpickr-day.endRange.startRange.endRange{border-radius:50px}.flatpickr-day.inRange{border-radius:0;-webkit-box-shadow:-5px 0 0 #e6e6e6,5px 0 0 #e6e6e6;box-shadow:-5px 0 0 #e6e6e6,5px 0 0 #e6e6e6}.flatpickr-day.flatpickr-disabled,.flatpickr-day.flatpickr-disabled:hover,.flatpickr-day.prevMonthDay,.flatpickr-day.nextMonthDay,.flatpickr-day.notAllowed,.flatpickr-day.notAllowed.prevMonthDay,.flatpickr-day.notAllowed.nextMonthDay{color:rgba(57,57,57,0.3);background:transparent;border-color:transparent;cursor:default}.flatpickr-day.flatpickr-disabled,.flatpickr-day.flatpickr-disabled:hover{cursor:not-allowed;color:rgba(57,57,57,0.1)}.flatpickr-day.week.selected{border-radius:0;-webkit-box-shadow:-5px 0 0 #569ff7,5px 0 0 #569ff7;box-shadow:-5px 0 0 #569ff7,5px 0 0 #569ff7}.flatpickr-day.hidden{visibility:hidden}.rangeMode .flatpickr-day{margin-top:1px}.flatpickr-weekwrapper{float:left}.flatpickr-weekwrapper .flatpickr-weeks{padding:0 12px;-webkit-box-shadow:1px 0 0 #e6e6e6;box-shadow:1px 0 0 #e6e6e6}.flatpickr-weekwrapper .flatpickr-weekday{float:none;width:100%;line-height:28px}.flatpickr-weekwrapper span.flatpickr-day,.flatpickr-weekwrapper span.flatpickr-day:hover{display:block;width:100%;max-width:none;color:rgba(57,57,57,0.3);background:transparent;cursor:default;border:none}.flatpickr-innerContainer{display:block;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden}.flatpickr-rContainer{display:inline-block;padding:0;-webkit-box-sizing:border-box;box-sizing:border-box}.flatpickr-time{text-align:center;outline:0;display:block;height:0;line-height:40px;max-height:40px;-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex}.flatpickr-time:after{content:"";display:table;clear:both}.flatpickr-time .numInputWrapper{-webkit-box-flex:1;-webkit-flex:1;-ms-flex:1;flex:1;width:40%;height:40px;float:left}.flatpickr-time .numInputWrapper span.arrowUp:after{border-bottom-color:#393939}.flatpickr-time .numInputWrapper span.arrowDown:after{border-top-color:#393939}.flatpickr-time.hasSeconds .numInputWrapper{width:26%}.flatpickr-time.time24hr .numInputWrapper{width:49%}.flatpickr-time input{background:transparent;-webkit-box-shadow:none;box-shadow:none;border:0;border-radius:0;text-align:center;margin:0;padding:0;height:inherit;line-height:inherit;color:#393939;font-size:14px;position:relative;-webkit-box-sizing:border-box;box-sizing:border-box;-webkit-appearance:textfield;-moz-appearance:textfield;appearance:textfield}.flatpickr-time input.flatpickr-hour{font-weight:bold}.flatpickr-time input.flatpickr-minute,.flatpickr-time input.flatpickr-second{font-weight:400}.flatpickr-time input:focus{outline:0;border:0}.flatpickr-time .flatpickr-time-separator,.flatpickr-time .flatpickr-am-pm{height:inherit;float:left;line-height:inherit;color:#393939;font-weight:bold;width:2%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-align-self:center;-ms-flex-item-align:center;align-self:center}.flatpickr-time .flatpickr-am-pm{outline:0;width:18%;cursor:pointer;text-align:center;font-weight:400}.flatpickr-time input:hover,.flatpickr-time .flatpickr-am-pm:hover,.flatpickr-time input:focus,.flatpickr-time .flatpickr-am-pm:focus{background:#eee}.flatpickr-input[readonly]{cursor:pointer}@-webkit-keyframes fpFadeInDown{from{opacity:0;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes fpFadeInDown{from{opacity:0;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}to{opacity:1;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}} \ No newline at end of file diff --git a/internal/embedfs/web/vendored/flatpickr@4.6.13.min.js b/internal/embedfs/web/vendored/flatpickr@4.6.13.min.js new file mode 100644 index 0000000..b0f59ec --- /dev/null +++ b/internal/embedfs/web/vendored/flatpickr@4.6.13.min.js @@ -0,0 +1,2 @@ +/* flatpickr v4.6.13,, @license MIT */ +!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?module.exports=n():"function"==typeof define&&define.amd?define(n):(e="undefined"!=typeof globalThis?globalThis:e||self).flatpickr=n()}(this,(function(){"use strict";var e=function(){return(e=Object.assign||function(e){for(var n,t=1,a=arguments.length;t",noCalendar:!1,now:new Date,onChange:[],onClose:[],onDayCreate:[],onDestroy:[],onKeyDown:[],onMonthChange:[],onOpen:[],onParseConfig:[],onReady:[],onValueUpdate:[],onYearChange:[],onPreCalendarPosition:[],plugins:[],position:"auto",positionElement:void 0,prevArrow:"",shorthandCurrentMonth:!1,showMonths:1,static:!1,time_24hr:!1,weekNumbers:!1,wrap:!1},i={weekdays:{shorthand:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],longhand:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]},months:{shorthand:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],longhand:["January","February","March","April","May","June","July","August","September","October","November","December"]},daysInMonth:[31,28,31,30,31,30,31,31,30,31,30,31],firstDayOfWeek:0,ordinal:function(e){var n=e%100;if(n>3&&n<21)return"th";switch(n%10){case 1:return"st";case 2:return"nd";case 3:return"rd";default:return"th"}},rangeSeparator:" to ",weekAbbreviation:"Wk",scrollTitle:"Scroll to increment",toggleTitle:"Click to toggle",amPM:["AM","PM"],yearAriaLabel:"Year",monthAriaLabel:"Month",hourAriaLabel:"Hour",minuteAriaLabel:"Minute",time_24hr:!1},o=function(e,n){return void 0===n&&(n=2),("000"+e).slice(-1*n)},r=function(e){return!0===e?1:0};function l(e,n){var t;return function(){var a=this,i=arguments;clearTimeout(t),t=setTimeout((function(){return e.apply(a,i)}),n)}}var c=function(e){return e instanceof Array?e:[e]};function s(e,n,t){if(!0===t)return e.classList.add(n);e.classList.remove(n)}function d(e,n,t){var a=window.document.createElement(e);return n=n||"",t=t||"",a.className=n,void 0!==t&&(a.textContent=t),a}function u(e){for(;e.firstChild;)e.removeChild(e.firstChild)}function f(e,n){return n(e)?e:e.parentNode?f(e.parentNode,n):void 0}function m(e,n){var t=d("div","numInputWrapper"),a=d("input","numInput "+e),i=d("span","arrowUp"),o=d("span","arrowDown");if(-1===navigator.userAgent.indexOf("MSIE 9.0")?a.type="number":(a.type="text",a.pattern="\\d*"),void 0!==n)for(var r in n)a.setAttribute(r,n[r]);return t.appendChild(a),t.appendChild(i),t.appendChild(o),t}function g(e){try{return"function"==typeof e.composedPath?e.composedPath()[0]:e.target}catch(n){return e.target}}var p=function(){},h=function(e,n,t){return t.months[n?"shorthand":"longhand"][e]},v={D:p,F:function(e,n,t){e.setMonth(t.months.longhand.indexOf(n))},G:function(e,n){e.setHours((e.getHours()>=12?12:0)+parseFloat(n))},H:function(e,n){e.setHours(parseFloat(n))},J:function(e,n){e.setDate(parseFloat(n))},K:function(e,n,t){e.setHours(e.getHours()%12+12*r(new RegExp(t.amPM[1],"i").test(n)))},M:function(e,n,t){e.setMonth(t.months.shorthand.indexOf(n))},S:function(e,n){e.setSeconds(parseFloat(n))},U:function(e,n){return new Date(1e3*parseFloat(n))},W:function(e,n,t){var a=parseInt(n),i=new Date(e.getFullYear(),0,2+7*(a-1),0,0,0,0);return i.setDate(i.getDate()-i.getDay()+t.firstDayOfWeek),i},Y:function(e,n){e.setFullYear(parseFloat(n))},Z:function(e,n){return new Date(n)},d:function(e,n){e.setDate(parseFloat(n))},h:function(e,n){e.setHours((e.getHours()>=12?12:0)+parseFloat(n))},i:function(e,n){e.setMinutes(parseFloat(n))},j:function(e,n){e.setDate(parseFloat(n))},l:p,m:function(e,n){e.setMonth(parseFloat(n)-1)},n:function(e,n){e.setMonth(parseFloat(n)-1)},s:function(e,n){e.setSeconds(parseFloat(n))},u:function(e,n){return new Date(parseFloat(n))},w:p,y:function(e,n){e.setFullYear(2e3+parseFloat(n))}},D={D:"",F:"",G:"(\\d\\d|\\d)",H:"(\\d\\d|\\d)",J:"(\\d\\d|\\d)\\w+",K:"",M:"",S:"(\\d\\d|\\d)",U:"(.+)",W:"(\\d\\d|\\d)",Y:"(\\d{4})",Z:"(.+)",d:"(\\d\\d|\\d)",h:"(\\d\\d|\\d)",i:"(\\d\\d|\\d)",j:"(\\d\\d|\\d)",l:"",m:"(\\d\\d|\\d)",n:"(\\d\\d|\\d)",s:"(\\d\\d|\\d)",u:"(.+)",w:"(\\d\\d|\\d)",y:"(\\d{2})"},w={Z:function(e){return e.toISOString()},D:function(e,n,t){return n.weekdays.shorthand[w.w(e,n,t)]},F:function(e,n,t){return h(w.n(e,n,t)-1,!1,n)},G:function(e,n,t){return o(w.h(e,n,t))},H:function(e){return o(e.getHours())},J:function(e,n){return void 0!==n.ordinal?e.getDate()+n.ordinal(e.getDate()):e.getDate()},K:function(e,n){return n.amPM[r(e.getHours()>11)]},M:function(e,n){return h(e.getMonth(),!0,n)},S:function(e){return o(e.getSeconds())},U:function(e){return e.getTime()/1e3},W:function(e,n,t){return t.getWeek(e)},Y:function(e){return o(e.getFullYear(),4)},d:function(e){return o(e.getDate())},h:function(e){return e.getHours()%12?e.getHours()%12:12},i:function(e){return o(e.getMinutes())},j:function(e){return e.getDate()},l:function(e,n){return n.weekdays.longhand[e.getDay()]},m:function(e){return o(e.getMonth()+1)},n:function(e){return e.getMonth()+1},s:function(e){return e.getSeconds()},u:function(e){return e.getTime()},w:function(e){return e.getDay()},y:function(e){return String(e.getFullYear()).substring(2)}},b=function(e){var n=e.config,t=void 0===n?a:n,o=e.l10n,r=void 0===o?i:o,l=e.isMobile,c=void 0!==l&&l;return function(e,n,a){var i=a||r;return void 0===t.formatDate||c?n.split("").map((function(n,a,o){return w[n]&&"\\"!==o[a-1]?w[n](e,i,t):"\\"!==n?n:""})).join(""):t.formatDate(e,n,i)}},C=function(e){var n=e.config,t=void 0===n?a:n,o=e.l10n,r=void 0===o?i:o;return function(e,n,i,o){if(0===e||e){var l,c=o||r,s=e;if(e instanceof Date)l=new Date(e.getTime());else if("string"!=typeof e&&void 0!==e.toFixed)l=new Date(e);else if("string"==typeof e){var d=n||(t||a).dateFormat,u=String(e).trim();if("today"===u)l=new Date,i=!0;else if(t&&t.parseDate)l=t.parseDate(e,d);else if(/Z$/.test(u)||/GMT$/.test(u))l=new Date(e);else{for(var f=void 0,m=[],g=0,p=0,h="";g=0?new Date:new Date(w.config.minDate.getTime()),t=E(w.config);n.setHours(t.hours,t.minutes,t.seconds,n.getMilliseconds()),w.selectedDates=[n],w.latestSelectedDateObj=n}void 0!==e&&"blur"!==e.type&&function(e){e.preventDefault();var n="keydown"===e.type,t=g(e),a=t;void 0!==w.amPM&&t===w.amPM&&(w.amPM.textContent=w.l10n.amPM[r(w.amPM.textContent===w.l10n.amPM[0])]);var i=parseFloat(a.getAttribute("min")),l=parseFloat(a.getAttribute("max")),c=parseFloat(a.getAttribute("step")),s=parseInt(a.value,10),d=e.delta||(n?38===e.which?1:-1:0),u=s+c*d;if(void 0!==a.value&&2===a.value.length){var f=a===w.hourElement,m=a===w.minuteElement;ul&&(u=a===w.hourElement?u-l-r(!w.amPM):i,m&&L(void 0,1,w.hourElement)),w.amPM&&f&&(1===c?u+s===23:Math.abs(u-s)>c)&&(w.amPM.textContent=w.l10n.amPM[r(w.amPM.textContent===w.l10n.amPM[0])]),a.value=o(u)}}(e);var a=w._input.value;O(),ye(),w._input.value!==a&&w._debouncedChange()}function O(){if(void 0!==w.hourElement&&void 0!==w.minuteElement){var e,n,t=(parseInt(w.hourElement.value.slice(-2),10)||0)%24,a=(parseInt(w.minuteElement.value,10)||0)%60,i=void 0!==w.secondElement?(parseInt(w.secondElement.value,10)||0)%60:0;void 0!==w.amPM&&(e=t,n=w.amPM.textContent,t=e%12+12*r(n===w.l10n.amPM[1]));var o=void 0!==w.config.minTime||w.config.minDate&&w.minDateHasTime&&w.latestSelectedDateObj&&0===M(w.latestSelectedDateObj,w.config.minDate,!0),l=void 0!==w.config.maxTime||w.config.maxDate&&w.maxDateHasTime&&w.latestSelectedDateObj&&0===M(w.latestSelectedDateObj,w.config.maxDate,!0);if(void 0!==w.config.maxTime&&void 0!==w.config.minTime&&w.config.minTime>w.config.maxTime){var c=y(w.config.minTime.getHours(),w.config.minTime.getMinutes(),w.config.minTime.getSeconds()),s=y(w.config.maxTime.getHours(),w.config.maxTime.getMinutes(),w.config.maxTime.getSeconds()),d=y(t,a,i);if(d>s&&d=12)]),void 0!==w.secondElement&&(w.secondElement.value=o(t)))}function N(e){var n=g(e),t=parseInt(n.value)+(e.delta||0);(t/1e3>1||"Enter"===e.key&&!/[^\d]/.test(t.toString()))&&ee(t)}function P(e,n,t,a){return n instanceof Array?n.forEach((function(n){return P(e,n,t,a)})):e instanceof Array?e.forEach((function(e){return P(e,n,t,a)})):(e.addEventListener(n,t,a),void w._handlers.push({remove:function(){return e.removeEventListener(n,t,a)}}))}function Y(){De("onChange")}function j(e,n){var t=void 0!==e?w.parseDate(e):w.latestSelectedDateObj||(w.config.minDate&&w.config.minDate>w.now?w.config.minDate:w.config.maxDate&&w.config.maxDate=0&&M(e,w.selectedDates[1])<=0)}(n)&&!be(n)&&o.classList.add("inRange"),w.weekNumbers&&1===w.config.showMonths&&"prevMonthDay"!==e&&a%7==6&&w.weekNumbers.insertAdjacentHTML("beforeend",""+w.config.getWeek(n)+""),De("onDayCreate",o),o}function W(e){e.focus(),"range"===w.config.mode&&oe(e)}function B(e){for(var n=e>0?0:w.config.showMonths-1,t=e>0?w.config.showMonths:-1,a=n;a!=t;a+=e)for(var i=w.daysContainer.children[a],o=e>0?0:i.children.length-1,r=e>0?i.children.length:-1,l=o;l!=r;l+=e){var c=i.children[l];if(-1===c.className.indexOf("hidden")&&ne(c.dateObj))return c}}function J(e,n){var t=k(),a=te(t||document.body),i=void 0!==e?e:a?t:void 0!==w.selectedDateElem&&te(w.selectedDateElem)?w.selectedDateElem:void 0!==w.todayDateElem&&te(w.todayDateElem)?w.todayDateElem:B(n>0?1:-1);void 0===i?w._input.focus():a?function(e,n){for(var t=-1===e.className.indexOf("Month")?e.dateObj.getMonth():w.currentMonth,a=n>0?w.config.showMonths:-1,i=n>0?1:-1,o=t-w.currentMonth;o!=a;o+=i)for(var r=w.daysContainer.children[o],l=t-w.currentMonth===o?e.$i+n:n<0?r.children.length-1:0,c=r.children.length,s=l;s>=0&&s0?c:-1);s+=i){var d=r.children[s];if(-1===d.className.indexOf("hidden")&&ne(d.dateObj)&&Math.abs(e.$i-s)>=Math.abs(n))return W(d)}w.changeMonth(i),J(B(i),0)}(i,n):W(i)}function K(e,n){for(var t=(new Date(e,n,1).getDay()-w.l10n.firstDayOfWeek+7)%7,a=w.utils.getDaysInMonth((n-1+12)%12,e),i=w.utils.getDaysInMonth(n,e),o=window.document.createDocumentFragment(),r=w.config.showMonths>1,l=r?"prevMonthDay hidden":"prevMonthDay",c=r?"nextMonthDay hidden":"nextMonthDay",s=a+1-t,u=0;s<=a;s++,u++)o.appendChild(R("flatpickr-day "+l,new Date(e,n-1,s),0,u));for(s=1;s<=i;s++,u++)o.appendChild(R("flatpickr-day",new Date(e,n,s),0,u));for(var f=i+1;f<=42-t&&(1===w.config.showMonths||u%7!=0);f++,u++)o.appendChild(R("flatpickr-day "+c,new Date(e,n+1,f%i),0,u));var m=d("div","dayContainer");return m.appendChild(o),m}function U(){if(void 0!==w.daysContainer){u(w.daysContainer),w.weekNumbers&&u(w.weekNumbers);for(var e=document.createDocumentFragment(),n=0;n1||"dropdown"!==w.config.monthSelectorType)){var e=function(e){return!(void 0!==w.config.minDate&&w.currentYear===w.config.minDate.getFullYear()&&ew.config.maxDate.getMonth())};w.monthsDropdownContainer.tabIndex=-1,w.monthsDropdownContainer.innerHTML="";for(var n=0;n<12;n++)if(e(n)){var t=d("option","flatpickr-monthDropdown-month");t.value=new Date(w.currentYear,n).getMonth().toString(),t.textContent=h(n,w.config.shorthandCurrentMonth,w.l10n),t.tabIndex=-1,w.currentMonth===n&&(t.selected=!0),w.monthsDropdownContainer.appendChild(t)}}}function $(){var e,n=d("div","flatpickr-month"),t=window.document.createDocumentFragment();w.config.showMonths>1||"static"===w.config.monthSelectorType?e=d("span","cur-month"):(w.monthsDropdownContainer=d("select","flatpickr-monthDropdown-months"),w.monthsDropdownContainer.setAttribute("aria-label",w.l10n.monthAriaLabel),P(w.monthsDropdownContainer,"change",(function(e){var n=g(e),t=parseInt(n.value,10);w.changeMonth(t-w.currentMonth),De("onMonthChange")})),q(),e=w.monthsDropdownContainer);var a=m("cur-year",{tabindex:"-1"}),i=a.getElementsByTagName("input")[0];i.setAttribute("aria-label",w.l10n.yearAriaLabel),w.config.minDate&&i.setAttribute("min",w.config.minDate.getFullYear().toString()),w.config.maxDate&&(i.setAttribute("max",w.config.maxDate.getFullYear().toString()),i.disabled=!!w.config.minDate&&w.config.minDate.getFullYear()===w.config.maxDate.getFullYear());var o=d("div","flatpickr-current-month");return o.appendChild(e),o.appendChild(a),t.appendChild(o),n.appendChild(t),{container:n,yearElement:i,monthElement:e}}function V(){u(w.monthNav),w.monthNav.appendChild(w.prevMonthNav),w.config.showMonths&&(w.yearElements=[],w.monthElements=[]);for(var e=w.config.showMonths;e--;){var n=$();w.yearElements.push(n.yearElement),w.monthElements.push(n.monthElement),w.monthNav.appendChild(n.container)}w.monthNav.appendChild(w.nextMonthNav)}function z(){w.weekdayContainer?u(w.weekdayContainer):w.weekdayContainer=d("div","flatpickr-weekdays");for(var e=w.config.showMonths;e--;){var n=d("div","flatpickr-weekdaycontainer");w.weekdayContainer.appendChild(n)}return G(),w.weekdayContainer}function G(){if(w.weekdayContainer){var e=w.l10n.firstDayOfWeek,t=n(w.l10n.weekdays.shorthand);e>0&&e\n "+t.join("")+"\n \n "}}function Z(e,n){void 0===n&&(n=!0);var t=n?e:e-w.currentMonth;t<0&&!0===w._hidePrevMonthArrow||t>0&&!0===w._hideNextMonthArrow||(w.currentMonth+=t,(w.currentMonth<0||w.currentMonth>11)&&(w.currentYear+=w.currentMonth>11?1:-1,w.currentMonth=(w.currentMonth+12)%12,De("onYearChange"),q()),U(),De("onMonthChange"),Ce())}function Q(e){return w.calendarContainer.contains(e)}function X(e){if(w.isOpen&&!w.config.inline){var n=g(e),t=Q(n),a=!(n===w.input||n===w.altInput||w.element.contains(n)||e.path&&e.path.indexOf&&(~e.path.indexOf(w.input)||~e.path.indexOf(w.altInput)))&&!t&&!Q(e.relatedTarget),i=!w.config.ignoredFocusElements.some((function(e){return e.contains(n)}));a&&i&&(w.config.allowInput&&w.setDate(w._input.value,!1,w.config.altInput?w.config.altFormat:w.config.dateFormat),void 0!==w.timeContainer&&void 0!==w.minuteElement&&void 0!==w.hourElement&&""!==w.input.value&&void 0!==w.input.value&&_(),w.close(),w.config&&"range"===w.config.mode&&1===w.selectedDates.length&&w.clear(!1))}}function ee(e){if(!(!e||w.config.minDate&&ew.config.maxDate.getFullYear())){var n=e,t=w.currentYear!==n;w.currentYear=n||w.currentYear,w.config.maxDate&&w.currentYear===w.config.maxDate.getFullYear()?w.currentMonth=Math.min(w.config.maxDate.getMonth(),w.currentMonth):w.config.minDate&&w.currentYear===w.config.minDate.getFullYear()&&(w.currentMonth=Math.max(w.config.minDate.getMonth(),w.currentMonth)),t&&(w.redraw(),De("onYearChange"),q())}}function ne(e,n){var t;void 0===n&&(n=!0);var a=w.parseDate(e,void 0,n);if(w.config.minDate&&a&&M(a,w.config.minDate,void 0!==n?n:!w.minDateHasTime)<0||w.config.maxDate&&a&&M(a,w.config.maxDate,void 0!==n?n:!w.maxDateHasTime)>0)return!1;if(!w.config.enable&&0===w.config.disable.length)return!0;if(void 0===a)return!1;for(var i=!!w.config.enable,o=null!==(t=w.config.enable)&&void 0!==t?t:w.config.disable,r=0,l=void 0;r=l.from.getTime()&&a.getTime()<=l.to.getTime())return i}return!i}function te(e){return void 0!==w.daysContainer&&(-1===e.className.indexOf("hidden")&&-1===e.className.indexOf("flatpickr-disabled")&&w.daysContainer.contains(e))}function ae(e){var n=e.target===w._input,t=w._input.value.trimEnd()!==Me();!n||!t||e.relatedTarget&&Q(e.relatedTarget)||w.setDate(w._input.value,!0,e.target===w.altInput?w.config.altFormat:w.config.dateFormat)}function ie(e){var n=g(e),t=w.config.wrap?p.contains(n):n===w._input,a=w.config.allowInput,i=w.isOpen&&(!a||!t),o=w.config.inline&&t&&!a;if(13===e.keyCode&&t){if(a)return w.setDate(w._input.value,!0,n===w.altInput?w.config.altFormat:w.config.dateFormat),w.close(),n.blur();w.open()}else if(Q(n)||i||o){var r=!!w.timeContainer&&w.timeContainer.contains(n);switch(e.keyCode){case 13:r?(e.preventDefault(),_(),fe()):me(e);break;case 27:e.preventDefault(),fe();break;case 8:case 46:t&&!w.config.allowInput&&(e.preventDefault(),w.clear());break;case 37:case 39:if(r||t)w.hourElement&&w.hourElement.focus();else{e.preventDefault();var l=k();if(void 0!==w.daysContainer&&(!1===a||l&&te(l))){var c=39===e.keyCode?1:-1;e.ctrlKey?(e.stopPropagation(),Z(c),J(B(1),0)):J(void 0,c)}}break;case 38:case 40:e.preventDefault();var s=40===e.keyCode?1:-1;w.daysContainer&&void 0!==n.$i||n===w.input||n===w.altInput?e.ctrlKey?(e.stopPropagation(),ee(w.currentYear-s),J(B(1),0)):r||J(void 0,7*s):n===w.currentYearElement?ee(w.currentYear-s):w.config.enableTime&&(!r&&w.hourElement&&w.hourElement.focus(),_(e),w._debouncedChange());break;case 9:if(r){var d=[w.hourElement,w.minuteElement,w.secondElement,w.amPM].concat(w.pluginElements).filter((function(e){return e})),u=d.indexOf(n);if(-1!==u){var f=d[u+(e.shiftKey?-1:1)];e.preventDefault(),(f||w._input).focus()}}else!w.config.noCalendar&&w.daysContainer&&w.daysContainer.contains(n)&&e.shiftKey&&(e.preventDefault(),w._input.focus())}}if(void 0!==w.amPM&&n===w.amPM)switch(e.key){case w.l10n.amPM[0].charAt(0):case w.l10n.amPM[0].charAt(0).toLowerCase():w.amPM.textContent=w.l10n.amPM[0],O(),ye();break;case w.l10n.amPM[1].charAt(0):case w.l10n.amPM[1].charAt(0).toLowerCase():w.amPM.textContent=w.l10n.amPM[1],O(),ye()}(t||Q(n))&&De("onKeyDown",e)}function oe(e,n){if(void 0===n&&(n="flatpickr-day"),1===w.selectedDates.length&&(!e||e.classList.contains(n)&&!e.classList.contains("flatpickr-disabled"))){for(var t=e?e.dateObj.getTime():w.days.firstElementChild.dateObj.getTime(),a=w.parseDate(w.selectedDates[0],void 0,!0).getTime(),i=Math.min(t,w.selectedDates[0].getTime()),o=Math.max(t,w.selectedDates[0].getTime()),r=!1,l=0,c=0,s=i;si&&sl)?l=s:s>a&&(!c||s ."+n)).forEach((function(n){var i,o,s,d=n.dateObj.getTime(),u=l>0&&d0&&d>c;if(u)return n.classList.add("notAllowed"),void["inRange","startRange","endRange"].forEach((function(e){n.classList.remove(e)}));r&&!u||(["startRange","inRange","endRange","notAllowed"].forEach((function(e){n.classList.remove(e)})),void 0!==e&&(e.classList.add(t<=w.selectedDates[0].getTime()?"startRange":"endRange"),at&&d===a&&n.classList.add("endRange"),d>=l&&(0===c||d<=c)&&(o=a,s=t,(i=d)>Math.min(o,s)&&i0||t.getMinutes()>0||t.getSeconds()>0),w.selectedDates&&(w.selectedDates=w.selectedDates.filter((function(e){return ne(e)})),w.selectedDates.length||"min"!==e||F(t),ye()),w.daysContainer&&(ue(),void 0!==t?w.currentYearElement[e]=t.getFullYear().toString():w.currentYearElement.removeAttribute(e),w.currentYearElement.disabled=!!a&&void 0!==t&&a.getFullYear()===t.getFullYear())}}function ce(){return w.config.wrap?p.querySelector("[data-input]"):p}function se(){"object"!=typeof w.config.locale&&void 0===I.l10ns[w.config.locale]&&w.config.errorHandler(new Error("flatpickr: invalid locale "+w.config.locale)),w.l10n=e(e({},I.l10ns.default),"object"==typeof w.config.locale?w.config.locale:"default"!==w.config.locale?I.l10ns[w.config.locale]:void 0),D.D="("+w.l10n.weekdays.shorthand.join("|")+")",D.l="("+w.l10n.weekdays.longhand.join("|")+")",D.M="("+w.l10n.months.shorthand.join("|")+")",D.F="("+w.l10n.months.longhand.join("|")+")",D.K="("+w.l10n.amPM[0]+"|"+w.l10n.amPM[1]+"|"+w.l10n.amPM[0].toLowerCase()+"|"+w.l10n.amPM[1].toLowerCase()+")",void 0===e(e({},v),JSON.parse(JSON.stringify(p.dataset||{}))).time_24hr&&void 0===I.defaultConfig.time_24hr&&(w.config.time_24hr=w.l10n.time_24hr),w.formatDate=b(w),w.parseDate=C({config:w.config,l10n:w.l10n})}function de(e){if("function"!=typeof w.config.position){if(void 0!==w.calendarContainer){De("onPreCalendarPosition");var n=e||w._positionElement,t=Array.prototype.reduce.call(w.calendarContainer.children,(function(e,n){return e+n.offsetHeight}),0),a=w.calendarContainer.offsetWidth,i=w.config.position.split(" "),o=i[0],r=i.length>1?i[1]:null,l=n.getBoundingClientRect(),c=window.innerHeight-l.bottom,d="above"===o||"below"!==o&&ct,u=window.pageYOffset+l.top+(d?-t-2:n.offsetHeight+2);if(s(w.calendarContainer,"arrowTop",!d),s(w.calendarContainer,"arrowBottom",d),!w.config.inline){var f=window.pageXOffset+l.left,m=!1,g=!1;"center"===r?(f-=(a-l.width)/2,m=!0):"right"===r&&(f-=a-l.width,g=!0),s(w.calendarContainer,"arrowLeft",!m&&!g),s(w.calendarContainer,"arrowCenter",m),s(w.calendarContainer,"arrowRight",g);var p=window.document.body.offsetWidth-(window.pageXOffset+l.right),h=f+a>window.document.body.offsetWidth,v=p+a>window.document.body.offsetWidth;if(s(w.calendarContainer,"rightMost",h),!w.config.static)if(w.calendarContainer.style.top=u+"px",h)if(v){var D=function(){for(var e=null,n=0;nw.currentMonth+w.config.showMonths-1)&&"range"!==w.config.mode;if(w.selectedDateElem=t,"single"===w.config.mode)w.selectedDates=[a];else if("multiple"===w.config.mode){var o=be(a);o?w.selectedDates.splice(parseInt(o),1):w.selectedDates.push(a)}else"range"===w.config.mode&&(2===w.selectedDates.length&&w.clear(!1,!1),w.latestSelectedDateObj=a,w.selectedDates.push(a),0!==M(a,w.selectedDates[0],!0)&&w.selectedDates.sort((function(e,n){return e.getTime()-n.getTime()})));if(O(),i){var r=w.currentYear!==a.getFullYear();w.currentYear=a.getFullYear(),w.currentMonth=a.getMonth(),r&&(De("onYearChange"),q()),De("onMonthChange")}if(Ce(),U(),ye(),i||"range"===w.config.mode||1!==w.config.showMonths?void 0!==w.selectedDateElem&&void 0===w.hourElement&&w.selectedDateElem&&w.selectedDateElem.focus():W(t),void 0!==w.hourElement&&void 0!==w.hourElement&&w.hourElement.focus(),w.config.closeOnSelect){var l="single"===w.config.mode&&!w.config.enableTime,c="range"===w.config.mode&&2===w.selectedDates.length&&!w.config.enableTime;(l||c)&&fe()}Y()}}w.parseDate=C({config:w.config,l10n:w.l10n}),w._handlers=[],w.pluginElements=[],w.loadedPlugins=[],w._bind=P,w._setHoursFromDate=F,w._positionCalendar=de,w.changeMonth=Z,w.changeYear=ee,w.clear=function(e,n){void 0===e&&(e=!0);void 0===n&&(n=!0);w.input.value="",void 0!==w.altInput&&(w.altInput.value="");void 0!==w.mobileInput&&(w.mobileInput.value="");w.selectedDates=[],w.latestSelectedDateObj=void 0,!0===n&&(w.currentYear=w._initialDate.getFullYear(),w.currentMonth=w._initialDate.getMonth());if(!0===w.config.enableTime){var t=E(w.config),a=t.hours,i=t.minutes,o=t.seconds;A(a,i,o)}w.redraw(),e&&De("onChange")},w.close=function(){w.isOpen=!1,w.isMobile||(void 0!==w.calendarContainer&&w.calendarContainer.classList.remove("open"),void 0!==w._input&&w._input.classList.remove("active"));De("onClose")},w.onMouseOver=oe,w._createElement=d,w.createDay=R,w.destroy=function(){void 0!==w.config&&De("onDestroy");for(var e=w._handlers.length;e--;)w._handlers[e].remove();if(w._handlers=[],w.mobileInput)w.mobileInput.parentNode&&w.mobileInput.parentNode.removeChild(w.mobileInput),w.mobileInput=void 0;else if(w.calendarContainer&&w.calendarContainer.parentNode)if(w.config.static&&w.calendarContainer.parentNode){var n=w.calendarContainer.parentNode;if(n.lastChild&&n.removeChild(n.lastChild),n.parentNode){for(;n.firstChild;)n.parentNode.insertBefore(n.firstChild,n);n.parentNode.removeChild(n)}}else w.calendarContainer.parentNode.removeChild(w.calendarContainer);w.altInput&&(w.input.type="text",w.altInput.parentNode&&w.altInput.parentNode.removeChild(w.altInput),delete w.altInput);w.input&&(w.input.type=w.input._type,w.input.classList.remove("flatpickr-input"),w.input.removeAttribute("readonly"));["_showTimeInput","latestSelectedDateObj","_hideNextMonthArrow","_hidePrevMonthArrow","__hideNextMonthArrow","__hidePrevMonthArrow","isMobile","isOpen","selectedDateElem","minDateHasTime","maxDateHasTime","days","daysContainer","_input","_positionElement","innerContainer","rContainer","monthNav","todayDateElem","calendarContainer","weekdayContainer","prevMonthNav","nextMonthNav","monthsDropdownContainer","currentMonthElement","currentYearElement","navigationCurrentMonth","selectedDateElem","config"].forEach((function(e){try{delete w[e]}catch(e){}}))},w.isEnabled=ne,w.jumpToDate=j,w.updateValue=ye,w.open=function(e,n){void 0===n&&(n=w._positionElement);if(!0===w.isMobile){if(e){e.preventDefault();var t=g(e);t&&t.blur()}return void 0!==w.mobileInput&&(w.mobileInput.focus(),w.mobileInput.click()),void De("onOpen")}if(w._input.disabled||w.config.inline)return;var a=w.isOpen;w.isOpen=!0,a||(w.calendarContainer.classList.add("open"),w._input.classList.add("active"),De("onOpen"),de(n));!0===w.config.enableTime&&!0===w.config.noCalendar&&(!1!==w.config.allowInput||void 0!==e&&w.timeContainer.contains(e.relatedTarget)||setTimeout((function(){return w.hourElement.select()}),50))},w.redraw=ue,w.set=function(e,n){if(null!==e&&"object"==typeof e)for(var a in Object.assign(w.config,e),e)void 0!==ge[a]&&ge[a].forEach((function(e){return e()}));else w.config[e]=n,void 0!==ge[e]?ge[e].forEach((function(e){return e()})):t.indexOf(e)>-1&&(w.config[e]=c(n));w.redraw(),ye(!0)},w.setDate=function(e,n,t){void 0===n&&(n=!1);void 0===t&&(t=w.config.dateFormat);if(0!==e&&!e||e instanceof Array&&0===e.length)return w.clear(n);pe(e,t),w.latestSelectedDateObj=w.selectedDates[w.selectedDates.length-1],w.redraw(),j(void 0,n),F(),0===w.selectedDates.length&&w.clear(!1);ye(n),n&&De("onChange")},w.toggle=function(e){if(!0===w.isOpen)return w.close();w.open(e)};var ge={locale:[se,G],showMonths:[V,S,z],minDate:[j],maxDate:[j],positionElement:[ve],clickOpens:[function(){!0===w.config.clickOpens?(P(w._input,"focus",w.open),P(w._input,"click",w.open)):(w._input.removeEventListener("focus",w.open),w._input.removeEventListener("click",w.open))}]};function pe(e,n){var t=[];if(e instanceof Array)t=e.map((function(e){return w.parseDate(e,n)}));else if(e instanceof Date||"number"==typeof e)t=[w.parseDate(e,n)];else if("string"==typeof e)switch(w.config.mode){case"single":case"time":t=[w.parseDate(e,n)];break;case"multiple":t=e.split(w.config.conjunction).map((function(e){return w.parseDate(e,n)}));break;case"range":t=e.split(w.l10n.rangeSeparator).map((function(e){return w.parseDate(e,n)}))}else w.config.errorHandler(new Error("Invalid date supplied: "+JSON.stringify(e)));w.selectedDates=w.config.allowInvalidPreload?t:t.filter((function(e){return e instanceof Date&&ne(e,!1)})),"range"===w.config.mode&&w.selectedDates.sort((function(e,n){return e.getTime()-n.getTime()}))}function he(e){return e.slice().map((function(e){return"string"==typeof e||"number"==typeof e||e instanceof Date?w.parseDate(e,void 0,!0):e&&"object"==typeof e&&e.from&&e.to?{from:w.parseDate(e.from,void 0),to:w.parseDate(e.to,void 0)}:e})).filter((function(e){return e}))}function ve(){w._positionElement=w.config.positionElement||w._input}function De(e,n){if(void 0!==w.config){var t=w.config[e];if(void 0!==t&&t.length>0)for(var a=0;t[a]&&a1||"static"===w.config.monthSelectorType?w.monthElements[n].textContent=h(t.getMonth(),w.config.shorthandCurrentMonth,w.l10n)+" ":w.monthsDropdownContainer.value=t.getMonth().toString(),e.value=t.getFullYear().toString()})),w._hidePrevMonthArrow=void 0!==w.config.minDate&&(w.currentYear===w.config.minDate.getFullYear()?w.currentMonth<=w.config.minDate.getMonth():w.currentYearw.config.maxDate.getMonth():w.currentYear>w.config.maxDate.getFullYear()))}function Me(e){var n=e||(w.config.altInput?w.config.altFormat:w.config.dateFormat);return w.selectedDates.map((function(e){return w.formatDate(e,n)})).filter((function(e,n,t){return"range"!==w.config.mode||w.config.enableTime||t.indexOf(e)===n})).join("range"!==w.config.mode?w.config.conjunction:w.l10n.rangeSeparator)}function ye(e){void 0===e&&(e=!0),void 0!==w.mobileInput&&w.mobileFormatStr&&(w.mobileInput.value=void 0!==w.latestSelectedDateObj?w.formatDate(w.latestSelectedDateObj,w.mobileFormatStr):""),w.input.value=Me(w.config.dateFormat),void 0!==w.altInput&&(w.altInput.value=Me(w.config.altFormat)),!1!==e&&De("onValueUpdate")}function xe(e){var n=g(e),t=w.prevMonthNav.contains(n),a=w.nextMonthNav.contains(n);t||a?Z(t?-1:1):w.yearElements.indexOf(n)>=0?n.select():n.classList.contains("arrowUp")?w.changeYear(w.currentYear+1):n.classList.contains("arrowDown")&&w.changeYear(w.currentYear-1)}return function(){w.element=w.input=p,w.isOpen=!1,function(){var n=["wrap","weekNumbers","allowInput","allowInvalidPreload","clickOpens","time_24hr","enableTime","noCalendar","altInput","shorthandCurrentMonth","inline","static","enableSeconds","disableMobile"],i=e(e({},JSON.parse(JSON.stringify(p.dataset||{}))),v),o={};w.config.parseDate=i.parseDate,w.config.formatDate=i.formatDate,Object.defineProperty(w.config,"enable",{get:function(){return w.config._enable},set:function(e){w.config._enable=he(e)}}),Object.defineProperty(w.config,"disable",{get:function(){return w.config._disable},set:function(e){w.config._disable=he(e)}});var r="time"===i.mode;if(!i.dateFormat&&(i.enableTime||r)){var l=I.defaultConfig.dateFormat||a.dateFormat;o.dateFormat=i.noCalendar||r?"H:i"+(i.enableSeconds?":S":""):l+" H:i"+(i.enableSeconds?":S":"")}if(i.altInput&&(i.enableTime||r)&&!i.altFormat){var s=I.defaultConfig.altFormat||a.altFormat;o.altFormat=i.noCalendar||r?"h:i"+(i.enableSeconds?":S K":" K"):s+" h:i"+(i.enableSeconds?":S":"")+" K"}Object.defineProperty(w.config,"minDate",{get:function(){return w.config._minDate},set:le("min")}),Object.defineProperty(w.config,"maxDate",{get:function(){return w.config._maxDate},set:le("max")});var d=function(e){return function(n){w.config["min"===e?"_minTime":"_maxTime"]=w.parseDate(n,"H:i:S")}};Object.defineProperty(w.config,"minTime",{get:function(){return w.config._minTime},set:d("min")}),Object.defineProperty(w.config,"maxTime",{get:function(){return w.config._maxTime},set:d("max")}),"time"===i.mode&&(w.config.noCalendar=!0,w.config.enableTime=!0);Object.assign(w.config,o,i);for(var u=0;u-1?w.config[m]=c(f[m]).map(T).concat(w.config[m]):void 0===i[m]&&(w.config[m]=f[m])}i.altInputClass||(w.config.altInputClass=ce().className+" "+w.config.altInputClass);De("onParseConfig")}(),se(),function(){if(w.input=ce(),!w.input)return void w.config.errorHandler(new Error("Invalid input element specified"));w.input._type=w.input.type,w.input.type="text",w.input.classList.add("flatpickr-input"),w._input=w.input,w.config.altInput&&(w.altInput=d(w.input.nodeName,w.config.altInputClass),w._input=w.altInput,w.altInput.placeholder=w.input.placeholder,w.altInput.disabled=w.input.disabled,w.altInput.required=w.input.required,w.altInput.tabIndex=w.input.tabIndex,w.altInput.type="text",w.input.setAttribute("type","hidden"),!w.config.static&&w.input.parentNode&&w.input.parentNode.insertBefore(w.altInput,w.input.nextSibling));w.config.allowInput||w._input.setAttribute("readonly","readonly");ve()}(),function(){w.selectedDates=[],w.now=w.parseDate(w.config.now)||new Date;var e=w.config.defaultDate||("INPUT"!==w.input.nodeName&&"TEXTAREA"!==w.input.nodeName||!w.input.placeholder||w.input.value!==w.input.placeholder?w.input.value:null);e&&pe(e,w.config.dateFormat);w._initialDate=w.selectedDates.length>0?w.selectedDates[0]:w.config.minDate&&w.config.minDate.getTime()>w.now.getTime()?w.config.minDate:w.config.maxDate&&w.config.maxDate.getTime()0&&(w.latestSelectedDateObj=w.selectedDates[0]);void 0!==w.config.minTime&&(w.config.minTime=w.parseDate(w.config.minTime,"H:i"));void 0!==w.config.maxTime&&(w.config.maxTime=w.parseDate(w.config.maxTime,"H:i"));w.minDateHasTime=!!w.config.minDate&&(w.config.minDate.getHours()>0||w.config.minDate.getMinutes()>0||w.config.minDate.getSeconds()>0),w.maxDateHasTime=!!w.config.maxDate&&(w.config.maxDate.getHours()>0||w.config.maxDate.getMinutes()>0||w.config.maxDate.getSeconds()>0)}(),w.utils={getDaysInMonth:function(e,n){return void 0===e&&(e=w.currentMonth),void 0===n&&(n=w.currentYear),1===e&&(n%4==0&&n%100!=0||n%400==0)?29:w.l10n.daysInMonth[e]}},w.isMobile||function(){var e=window.document.createDocumentFragment();if(w.calendarContainer=d("div","flatpickr-calendar"),w.calendarContainer.tabIndex=-1,!w.config.noCalendar){if(e.appendChild((w.monthNav=d("div","flatpickr-months"),w.yearElements=[],w.monthElements=[],w.prevMonthNav=d("span","flatpickr-prev-month"),w.prevMonthNav.innerHTML=w.config.prevArrow,w.nextMonthNav=d("span","flatpickr-next-month"),w.nextMonthNav.innerHTML=w.config.nextArrow,V(),Object.defineProperty(w,"_hidePrevMonthArrow",{get:function(){return w.__hidePrevMonthArrow},set:function(e){w.__hidePrevMonthArrow!==e&&(s(w.prevMonthNav,"flatpickr-disabled",e),w.__hidePrevMonthArrow=e)}}),Object.defineProperty(w,"_hideNextMonthArrow",{get:function(){return w.__hideNextMonthArrow},set:function(e){w.__hideNextMonthArrow!==e&&(s(w.nextMonthNav,"flatpickr-disabled",e),w.__hideNextMonthArrow=e)}}),w.currentYearElement=w.yearElements[0],Ce(),w.monthNav)),w.innerContainer=d("div","flatpickr-innerContainer"),w.config.weekNumbers){var n=function(){w.calendarContainer.classList.add("hasWeeks");var e=d("div","flatpickr-weekwrapper");e.appendChild(d("span","flatpickr-weekday",w.l10n.weekAbbreviation));var n=d("div","flatpickr-weeks");return e.appendChild(n),{weekWrapper:e,weekNumbers:n}}(),t=n.weekWrapper,a=n.weekNumbers;w.innerContainer.appendChild(t),w.weekNumbers=a,w.weekWrapper=t}w.rContainer=d("div","flatpickr-rContainer"),w.rContainer.appendChild(z()),w.daysContainer||(w.daysContainer=d("div","flatpickr-days"),w.daysContainer.tabIndex=-1),U(),w.rContainer.appendChild(w.daysContainer),w.innerContainer.appendChild(w.rContainer),e.appendChild(w.innerContainer)}w.config.enableTime&&e.appendChild(function(){w.calendarContainer.classList.add("hasTime"),w.config.noCalendar&&w.calendarContainer.classList.add("noCalendar");var e=E(w.config);w.timeContainer=d("div","flatpickr-time"),w.timeContainer.tabIndex=-1;var n=d("span","flatpickr-time-separator",":"),t=m("flatpickr-hour",{"aria-label":w.l10n.hourAriaLabel});w.hourElement=t.getElementsByTagName("input")[0];var a=m("flatpickr-minute",{"aria-label":w.l10n.minuteAriaLabel});w.minuteElement=a.getElementsByTagName("input")[0],w.hourElement.tabIndex=w.minuteElement.tabIndex=-1,w.hourElement.value=o(w.latestSelectedDateObj?w.latestSelectedDateObj.getHours():w.config.time_24hr?e.hours:function(e){switch(e%24){case 0:case 12:return 12;default:return e%12}}(e.hours)),w.minuteElement.value=o(w.latestSelectedDateObj?w.latestSelectedDateObj.getMinutes():e.minutes),w.hourElement.setAttribute("step",w.config.hourIncrement.toString()),w.minuteElement.setAttribute("step",w.config.minuteIncrement.toString()),w.hourElement.setAttribute("min",w.config.time_24hr?"0":"1"),w.hourElement.setAttribute("max",w.config.time_24hr?"23":"12"),w.hourElement.setAttribute("maxlength","2"),w.minuteElement.setAttribute("min","0"),w.minuteElement.setAttribute("max","59"),w.minuteElement.setAttribute("maxlength","2"),w.timeContainer.appendChild(t),w.timeContainer.appendChild(n),w.timeContainer.appendChild(a),w.config.time_24hr&&w.timeContainer.classList.add("time24hr");if(w.config.enableSeconds){w.timeContainer.classList.add("hasSeconds");var i=m("flatpickr-second");w.secondElement=i.getElementsByTagName("input")[0],w.secondElement.value=o(w.latestSelectedDateObj?w.latestSelectedDateObj.getSeconds():e.seconds),w.secondElement.setAttribute("step",w.minuteElement.getAttribute("step")),w.secondElement.setAttribute("min","0"),w.secondElement.setAttribute("max","59"),w.secondElement.setAttribute("maxlength","2"),w.timeContainer.appendChild(d("span","flatpickr-time-separator",":")),w.timeContainer.appendChild(i)}w.config.time_24hr||(w.amPM=d("span","flatpickr-am-pm",w.l10n.amPM[r((w.latestSelectedDateObj?w.hourElement.value:w.config.defaultHour)>11)]),w.amPM.title=w.l10n.toggleTitle,w.amPM.tabIndex=-1,w.timeContainer.appendChild(w.amPM));return w.timeContainer}());s(w.calendarContainer,"rangeMode","range"===w.config.mode),s(w.calendarContainer,"animate",!0===w.config.animate),s(w.calendarContainer,"multiMonth",w.config.showMonths>1),w.calendarContainer.appendChild(e);var i=void 0!==w.config.appendTo&&void 0!==w.config.appendTo.nodeType;if((w.config.inline||w.config.static)&&(w.calendarContainer.classList.add(w.config.inline?"inline":"static"),w.config.inline&&(!i&&w.element.parentNode?w.element.parentNode.insertBefore(w.calendarContainer,w._input.nextSibling):void 0!==w.config.appendTo&&w.config.appendTo.appendChild(w.calendarContainer)),w.config.static)){var l=d("div","flatpickr-wrapper");w.element.parentNode&&w.element.parentNode.insertBefore(l,w.element),l.appendChild(w.element),w.altInput&&l.appendChild(w.altInput),l.appendChild(w.calendarContainer)}w.config.static||w.config.inline||(void 0!==w.config.appendTo?w.config.appendTo:window.document.body).appendChild(w.calendarContainer)}(),function(){w.config.wrap&&["open","close","toggle","clear"].forEach((function(e){Array.prototype.forEach.call(w.element.querySelectorAll("[data-"+e+"]"),(function(n){return P(n,"click",w[e])}))}));if(w.isMobile)return void function(){var e=w.config.enableTime?w.config.noCalendar?"time":"datetime-local":"date";w.mobileInput=d("input",w.input.className+" flatpickr-mobile"),w.mobileInput.tabIndex=1,w.mobileInput.type=e,w.mobileInput.disabled=w.input.disabled,w.mobileInput.required=w.input.required,w.mobileInput.placeholder=w.input.placeholder,w.mobileFormatStr="datetime-local"===e?"Y-m-d\\TH:i:S":"date"===e?"Y-m-d":"H:i:S",w.selectedDates.length>0&&(w.mobileInput.defaultValue=w.mobileInput.value=w.formatDate(w.selectedDates[0],w.mobileFormatStr));w.config.minDate&&(w.mobileInput.min=w.formatDate(w.config.minDate,"Y-m-d"));w.config.maxDate&&(w.mobileInput.max=w.formatDate(w.config.maxDate,"Y-m-d"));w.input.getAttribute("step")&&(w.mobileInput.step=String(w.input.getAttribute("step")));w.input.type="hidden",void 0!==w.altInput&&(w.altInput.type="hidden");try{w.input.parentNode&&w.input.parentNode.insertBefore(w.mobileInput,w.input.nextSibling)}catch(e){}P(w.mobileInput,"change",(function(e){w.setDate(g(e).value,!1,w.mobileFormatStr),De("onChange"),De("onClose")}))}();var e=l(re,50);w._debouncedChange=l(Y,300),w.daysContainer&&!/iPhone|iPad|iPod/i.test(navigator.userAgent)&&P(w.daysContainer,"mouseover",(function(e){"range"===w.config.mode&&oe(g(e))}));P(w._input,"keydown",ie),void 0!==w.calendarContainer&&P(w.calendarContainer,"keydown",ie);w.config.inline||w.config.static||P(window,"resize",e);void 0!==window.ontouchstart?P(window.document,"touchstart",X):P(window.document,"mousedown",X);P(window.document,"focus",X,{capture:!0}),!0===w.config.clickOpens&&(P(w._input,"focus",w.open),P(w._input,"click",w.open));void 0!==w.daysContainer&&(P(w.monthNav,"click",xe),P(w.monthNav,["keyup","increment"],N),P(w.daysContainer,"click",me));if(void 0!==w.timeContainer&&void 0!==w.minuteElement&&void 0!==w.hourElement){var n=function(e){return g(e).select()};P(w.timeContainer,["increment"],_),P(w.timeContainer,"blur",_,{capture:!0}),P(w.timeContainer,"click",H),P([w.hourElement,w.minuteElement],["focus","click"],n),void 0!==w.secondElement&&P(w.secondElement,"focus",(function(){return w.secondElement&&w.secondElement.select()})),void 0!==w.amPM&&P(w.amPM,"click",(function(e){_(e)}))}w.config.allowInput&&P(w._input,"blur",ae)}(),(w.selectedDates.length||w.config.noCalendar)&&(w.config.enableTime&&F(w.config.noCalendar?w.latestSelectedDateObj:void 0),ye(!1)),S();var n=/^((?!chrome|android).)*safari/i.test(navigator.userAgent);!w.isMobile&&n&&de(),De("onReady")}(),w}function T(e,n){for(var t=Array.prototype.slice.call(e).filter((function(e){return e instanceof HTMLElement})),a=[],i=0;i 0 { - filters.UserID(userID) + if len(userIDs) > 0 { + filters.UserIDs(userIDs) + } + if len(actions) > 0 { + filters.Actions(actions) + } + if len(resourceTypes) > 0 { + filters.ResourceTypes(resourceTypes) + } + if len(results) > 0 { + filters.Results(results) } - if action != "" { - filters.Action(action) + if !startDate.IsZero() { + filters.DateRange(startDate.Unix(), 0) } - - if resourceType != "" { - filters.ResourceType(resourceType) - } - - if result != "" { - filters.Result(result) - } - - // Parse and apply date range - if startDateStr != "" { - if startDate, err := time.Parse("2006-01-02", startDateStr); err == nil { - filters.DateRange(startDate.Unix(), 0) - } - } - - if endDateStr != "" { - if endDate, err := time.Parse("2006-01-02", endDateStr); err == nil { - // Set to end of day - endOfDay := endDate.Add(23*time.Hour + 59*time.Minute + 59*time.Second) - filters.DateRange(0, endOfDay.Unix()) - } + if !endDate.IsZero() { + endOfDay := endDate.Add(23*time.Hour + 59*time.Minute + 59*time.Second) + filters.DateRange(0, endOfDay.Unix()) } return filters, true diff --git a/internal/handlers/admin_roles.go b/internal/handlers/admin_roles.go index 2c8e335..7d68014 100644 --- a/internal/handlers/admin_roles.go +++ b/internal/handlers/admin_roles.go @@ -21,12 +21,22 @@ import ( // AdminRoles renders the full admin dashboard page with roles section func AdminRoles(s *hws.Server, conn *bun.DB) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - var rolesList []*db.Role + var pageOpts *db.PageOpts + if r.Method == "GET" { + pageOpts = pageOptsFromQuery(s, w, r) + } else { + pageOpts = pageOptsFromForm(s, w, r) + } + if pageOpts == nil { + return + } + + var rolesList *db.List[db.Role] if ok := db.WithReadTx(s, w, r, conn, func(ctx context.Context, tx bun.Tx) (bool, error) { var err error - rolesList, err = db.ListAllRoles(ctx, tx) + rolesList, err = db.GetRoles(ctx, tx, pageOpts) if err != nil { - return false, errors.Wrap(err, "db.ListAllRoles") + return false, errors.Wrap(err, "db.GetRoles") } return true, nil }); !ok { @@ -64,7 +74,12 @@ func AdminRoleCreate(s *hws.Server, conn *bun.DB, audit *auditlog.Logger) http.H return } - var rolesList []*db.Role + pageOpts := pageOptsFromForm(s, w, r) + if pageOpts == nil { + return + } + + var rolesList *db.List[db.Role] var newRole *db.Role if ok := db.WithNotifyTx(s, w, r, conn, func(ctx context.Context, tx bun.Tx) (bool, error) { newRole = &db.Role{ @@ -80,9 +95,9 @@ func AdminRoleCreate(s *hws.Server, conn *bun.DB, audit *auditlog.Logger) http.H return false, errors.Wrap(err, "db.Insert") } - rolesList, err = db.ListAllRoles(ctx, tx) + rolesList, err = db.GetRoles(ctx, tx, pageOpts) if err != nil { - return false, errors.Wrap(err, "db.ListAllRoles") + return false, errors.Wrap(err, "db.GetRoles") } return true, nil @@ -162,7 +177,12 @@ func AdminRoleDelete(s *hws.Server, conn *bun.DB, audit *auditlog.Logger) http.H return } - var rolesList []*db.Role + pageOpts := pageOptsFromForm(s, w, r) + if pageOpts == nil { + return + } + + var rolesList *db.List[db.Role] if ok := db.WithNotifyTx(s, w, r, conn, func(ctx context.Context, tx bun.Tx) (bool, error) { // First check if role exists and get its details role, err := db.GetRoleByID(ctx, tx, roleID) @@ -185,9 +205,9 @@ func AdminRoleDelete(s *hws.Server, conn *bun.DB, audit *auditlog.Logger) http.H } // Reload roles - rolesList, err = db.ListAllRoles(ctx, tx) + rolesList, err = db.GetRoles(ctx, tx, pageOpts) if err != nil { - return false, errors.Wrap(err, "db.ListAllRoles") + return false, errors.Wrap(err, "db.GetRoles") } return true, nil @@ -289,7 +309,12 @@ func AdminRolePermissionsUpdate(s *hws.Server, conn *bun.DB, audit *auditlog.Log selectedPermIDs[id] = true } - var rolesList []*db.Role + pageOpts := pageOptsFromForm(s, w, r) + if pageOpts == nil { + return + } + + var rolesList *db.List[db.Role] if ok := db.WithWriteTx(s, w, r, conn, func(ctx context.Context, tx bun.Tx) (bool, error) { // Get role with current permissions role, err := db.GetRoleWithPermissions(ctx, tx, roleID) @@ -356,9 +381,9 @@ func AdminRolePermissionsUpdate(s *hws.Server, conn *bun.DB, audit *auditlog.Log } // Reload roles - rolesList, err = db.ListAllRoles(ctx, tx) + rolesList, err = db.GetRoles(ctx, tx, pageOpts) if err != nil { - return false, errors.Wrap(err, "db.ListAllRoles") + return false, errors.Wrap(err, "db.GetRoles") } return true, nil diff --git a/internal/handlers/admin_users.go b/internal/handlers/admin_users.go index 95f3656..e1827de 100644 --- a/internal/handlers/admin_users.go +++ b/internal/handlers/admin_users.go @@ -14,10 +14,20 @@ import ( // AdminUsersPage renders the full admin dashboard page with users section func AdminUsersPage(s *hws.Server, conn *bun.DB) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var pageOpts *db.PageOpts + if r.Method == "GET" { + pageOpts = pageOptsFromQuery(s, w, r) + } else { + pageOpts = pageOptsFromForm(s, w, r) + } + if pageOpts == nil { + return + } + var users *db.List[db.User] if ok := db.WithReadTx(s, w, r, conn, func(ctx context.Context, tx bun.Tx) (bool, error) { var err error - users, err = db.GetUsersWithRoles(ctx, tx, nil) + users, err = db.GetUsersWithRoles(ctx, tx, pageOpts) if err != nil { return false, errors.Wrap(err, "db.GetUsersWithRoles") } diff --git a/internal/view/adminview/audit_log_detail.templ b/internal/view/adminview/audit_log_detail.templ index 49525fe..6d54503 100644 --- a/internal/view/adminview/audit_log_detail.templ +++ b/internal/view/adminview/audit_log_detail.templ @@ -21,7 +21,7 @@ templ AuditLogDetail(log *db.AuditLog) { >
-
-

Audit Log Details

- +
+
+
+
+

Audit Log #{ fmt.Sprintf("%d", log.ID) }

+ @resultBadge(log.Result) +
+

{ formatDetailTimestamp(log.CreatedAt) }

+
+ +
-
- -
- -

{ fmt.Sprintf("%d", log.ID) }

-
- -
- -

- if log.User != nil { - { log.User.Username } (ID: { fmt.Sprintf("%d", log.UserID) }) - } else { - Unknown User (ID: { fmt.Sprintf("%d", log.UserID) }) - } -

-
- -
- -

{ formatDetailTimestamp(log.CreatedAt) }

-
- -
- -

{ log.Action }

-
- -
- -

{ log.ResourceType }

-
- -
- -

- if log.ResourceID != nil { - { *log.ResourceID } - } else { - N/A - } -

-
- -
- -
- @resultBadge(log.Result) +
+ +
+
+ +
+ + + +
+
User
+
+ if log.User != nil { + { log.User.Username } + ({ fmt.Sprintf("%d", log.UserID) }) + } else { + Unknown ({ fmt.Sprintf("%d", log.UserID) }) + } +
+
+
+ +
+ + + +
+
Action
+
{ log.Action }
+
+
+ +
+ + + +
+
Resource Type
+
{ log.ResourceType }
+
+
+ +
+ + + +
+
Resource ID
+
+ if log.ResourceID != nil { + { *log.ResourceID } + } else { + N/A + } +
+
+
+ +
+ + + +
+
IP Address
+
{ log.IPAddress }
+
+
if log.ErrorMessage != nil && *log.ErrorMessage != "" { -
- -
-

{ *log.ErrorMessage }

+
+
+ + + +
+
Error Message
+

{ *log.ErrorMessage }

+
} - -
- -

{ log.IPAddress }

-
-
- -

{ log.UserAgent }

+
+
+ + + +
+
User Agent
+

{ log.UserAgent }

+
+
if log.Details != nil && len(log.Details) > 0 && string(log.Details) != "null" { -
- -
+
+
+
+ + + + Details (JSON) +
+
+
{ formatJSON(log.Details) }
}
-
+
diff --git a/internal/view/adminview/audit_logs_list.templ b/internal/view/adminview/audit_logs_list.templ index be257ef..2cb54be 100644 --- a/internal/view/adminview/audit_logs_list.templ +++ b/internal/view/adminview/audit_logs_list.templ @@ -1,101 +1,65 @@ package adminview import ( - "git.haelnorr.com/h/oslstats/internal/db" "fmt" + "git.haelnorr.com/h/oslstats/internal/db" "time" ) templ AuditLogsList(logs *db.List[db.AuditLog], users []*db.User, actions []string, resourceTypes []string) {
-
+

Audit Logs

-
+
- -
- - -
- -
- - -
- -
- - -
- -
- - -
- + + @multiSelectDropdown("user_id", "Users", userOptions(users)) + + @multiSelectDropdown("action", "Actions", stringOptions(actions)) + + @multiSelectDropdown("resource_type", "Resource Types", stringOptions(resourceTypes)) + + @multiSelectDropdown("result", "Results", []selectOption{ + {Value: "success", Label: "Success"}, + {Value: "denied", Label: "Denied"}, + {Value: "error", Label: "Error"}, + }) +
- +
-
+
- -
+ +
@AuditLogsResults(logs)
+ +
- - } templ AuditLogsResults(logs *db.List[db.AuditLog]) { - if len(logs.Items) == 0 { -
-

No audit logs found

-
- } else { +
+ + if len(logs.Items) == 0 { +
+

No audit logs found

+
+ } else {
- - - - - - + @auditTableHeader("created_at", "Timestamp", logs.PageOpts.OrderBy, string(logs.PageOpts.Order)) + @auditTableHeader("user_id", "User", logs.PageOpts.OrderBy, string(logs.PageOpts.Order)) + @auditTableHeader("action", "Action", logs.PageOpts.OrderBy, string(logs.PageOpts.Order)) + @auditTableHeader("resource_type", "Resource", logs.PageOpts.OrderBy, string(logs.PageOpts.Order)) + @auditTableHeader("resource_id", "Resource ID", logs.PageOpts.OrderBy, string(logs.PageOpts.Order)) + @auditTableHeader("result", "Result", logs.PageOpts.OrderBy, string(logs.PageOpts.Order)) @@ -145,7 +117,7 @@ templ AuditLogsResults(logs *db.List[db.AuditLog]) { Unknown } -
TimestampUserActionResourceResource IDResultActions
+ { log.Action } @@ -176,45 +148,188 @@ templ AuditLogsResults(logs *db.List[db.AuditLog]) {
- - {{ - totalPages := (logs.Total + logs.PageOpts.PerPage - 1) / logs.PageOpts.PerPage - if logs.PageOpts.PerPage == 0 { - totalPages = 1 - } - }} - if totalPages > 1 { -
- if logs.PageOpts.Page > 1 { - + + + + for _, pageNum := range logs.PageOpts.GetPageRange(logs.Total, 7) { + + } + + + + +
+ } +
+ } +
+} + +templ auditTableHeader(field string, label string, currentField string, currentOrder string) { + {{ + isActive := currentField == field + baseClasses := "px-4 py-3 text-left text-sm font-semibold text-text cursor-pointer select-none hover:text-blue transition-colors" + arrow := "" + if isActive { + if currentOrder == "ASC" { + arrow = " ↑" + } else { + arrow = " ↓" } + } + }} + + { label } + if arrow != "" { + { arrow } + } + +} + +type selectOption struct { + Value string + Label string +} + +func userOptions(users []*db.User) []selectOption { + opts := make([]selectOption, len(users)) + for i, u := range users { + opts[i] = selectOption{Value: fmt.Sprintf("%d", u.ID), Label: u.Username} } + return opts +} + +func stringOptions(strs []string) []selectOption { + opts := make([]selectOption, len(strs)) + for i, s := range strs { + opts[i] = selectOption{Value: s, Label: s} + } + return opts +} + +templ multiSelectDropdown(name string, label string, options []selectOption) { + {{ containerId := name + "-container" }} +
+ +
+ + + + + + +
+
} templ resultBadge(result string) { - {{ + {{ var classes string switch result { case "success": diff --git a/internal/view/adminview/dashboard_layout.templ b/internal/view/adminview/dashboard_layout.templ index f89d30a..b9636c5 100644 --- a/internal/view/adminview/dashboard_layout.templ +++ b/internal/view/adminview/dashboard_layout.templ @@ -4,6 +4,11 @@ import "git.haelnorr.com/h/oslstats/internal/view/baseview" templ DashboardLayout(activeSection string) { @baseview.Layout("Admin Dashboard") { + + + + +

Admin Dashboard

@@ -25,7 +30,7 @@ templ DashboardLayout(activeSection string) {
- + } } diff --git a/internal/view/adminview/roles_list.templ b/internal/view/adminview/roles_list.templ index 3428c1c..b45a2f0 100644 --- a/internal/view/adminview/roles_list.templ +++ b/internal/view/adminview/roles_list.templ @@ -5,13 +5,20 @@ import ( "git.haelnorr.com/h/oslstats/internal/db" ) -templ RolesList(roles []*db.Role) { -
+templ RolesList(roles *db.List[db.Role]) { +
-
+

Role Management

-
- - - - - - - - - - - if len(roles) == 0 { + if len(roles.Items) == 0 { +
+

No roles found

+
+ } else { +
+
NameDescriptionTypeActions
+ - + @roleTableHeader("display_name", "Name", roles.PageOpts.OrderBy, string(roles.PageOpts.Order)) + + @roleTableHeader("is_system", "Type", roles.PageOpts.OrderBy, string(roles.PageOpts.Order)) + - } else { - for _, role := range roles { + + + for _, role := range roles.Items { @roleRow(role) } - } - -
No roles foundDescriptionActions
-
+ + +
+ +
+ +
+
+ if roles.Total > 0 { + Showing { fmt.Sprintf("%d", roles.PageOpts.StartItem()) } - { fmt.Sprintf("%d", roles.PageOpts.EndItem(roles.Total)) } of { fmt.Sprintf("%d", roles.Total) } roles + } else { + No roles + } +
+
+ + +
+
+ + if roles.Total > 0 && roles.PageOpts.TotalPages(roles.Total) > 1 { +
+ @paginationButton("First", "<<", fmt.Sprintf("submitRolesPage(1, perPage, order, orderBy)"), !roles.PageOpts.HasPrevPage()) + @paginationButton("Previous", "<", fmt.Sprintf("submitRolesPage(%d, perPage, order, orderBy)", roles.PageOpts.Page-1), !roles.PageOpts.HasPrevPage()) + for _, pageNum := range roles.PageOpts.GetPageRange(roles.Total, 7) { + @pageNumberButton(pageNum, roles.PageOpts.Page, "submitRolesPage") + } + @paginationButton("Next", ">", fmt.Sprintf("submitRolesPage(%d, perPage, order, orderBy)", roles.PageOpts.Page+1), !roles.PageOpts.HasNextPage(roles.Total)) + @paginationButton("Last", ">>", fmt.Sprintf("submitRolesPage(%d, perPage, order, orderBy)", roles.PageOpts.TotalPages(roles.Total)), !roles.PageOpts.HasNextPage(roles.Total)) +
+ } +
+ }
@@ -57,6 +103,27 @@ templ RolesList(roles []*db.Role) {
} +templ roleTableHeader(field string, label string, currentField string, currentOrder string) { + {{ + isActive := currentField == field + baseClasses := "px-6 py-3 text-left text-sm font-semibold text-text cursor-pointer select-none hover:text-blue transition-colors" + arrow := "" + if isActive { + if currentOrder == "ASC" { + arrow = " ↑" + } else { + arrow = " ↓" + } + } + }} + + { label } + if arrow != "" { + { arrow } + } + +} + templ roleRow(role *db.Role) { { role.DisplayName } @@ -70,7 +137,7 @@ templ roleRow(role *db.Role) {
} +templ userTableHeader(field string, label string, currentField string, currentOrder string) { + {{ + isActive := currentField == field + baseClasses := "px-4 py-3 text-left text-sm font-semibold text-text cursor-pointer select-none hover:text-blue transition-colors" + arrow := "" + if isActive { + if currentOrder == "ASC" { + arrow = " ↑" + } else { + arrow = " ↓" + } + } + }} + + { label } + if arrow != "" { + { arrow } + } + +} + +templ paginationButton(longText string, shortText string, onClick string, disabled bool) { + +} + +templ pageNumberButton(pageNum int, currentPage int, funcName string) { + +} + func formatTimestamp(unixTime int64) string { t := time.Unix(unixTime, 0) return t.Format("Jan 2, 2006") diff --git a/internal/view/pagination/table.templ b/internal/view/pagination/table.templ new file mode 100644 index 0000000..6c25b36 --- /dev/null +++ b/internal/view/pagination/table.templ @@ -0,0 +1,44 @@ +package pagination + +import "git.haelnorr.com/h/oslstats/internal/db" +import "fmt" + +// TableColumn defines a sortable column in a paginated table +type TableColumn struct { + Field string // database field name for sorting (e.g., "created_at") + Label string // display label (e.g., "Timestamp") + Sortable bool // whether this column can be sorted +} + +// TableHeader renders a table header with sortable columns +// Use this inside ... +templ TableHeader(opts db.PageOpts, columns []TableColumn) { + for _, col := range columns { + if col.Sortable { + @sortableHeaderCell(col.Field, col.Label, opts.OrderBy, string(opts.Order)) + } else { + { col.Label } + } + } +} + +templ sortableHeaderCell(field string, label string, currentField string, currentOrder string) { + {{ + isActive := currentField == field + baseClasses := "px-4 py-3 text-left text-sm font-semibold text-text cursor-pointer select-none hover:text-blue transition-colors" + arrow := "" + if isActive { + if currentOrder == "ASC" { + arrow = " ↑" + } else { + arrow = " ↓" + } + } + }} + + { label } + if arrow != "" { + { arrow } + } + +}