Upgrade/migrate tailwind v3 to v4

This commit is contained in:
Glenn de Haan
2025-02-25 20:14:12 +01:00
parent 4e6b5187af
commit 04ccba25c9
13 changed files with 946 additions and 1189 deletions

View File

@@ -1,3 +1,33 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import 'tailwindcss';
@plugin '@tailwindcss/forms';
/*
The default border color has changed to `currentColor` in Tailwind CSS v4,
so we've added these compatibility styles to make sure everything still
looks the same as it did with Tailwind CSS v3.
If we ever want to remove these styles, we need to add an explicit border
color utility to any element that depends on these defaults.
*/
/*
The default cursor has changed to `default` in Tailwind CSS v4,
so we've added these compatibility styles to make sure everything still
looks the same as it did with Tailwind CSS v3.
If we ever want to remove these styles, we need to add an cursor
utility to any element that depends on these defaults.
*/
@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentColor);
}
button:not(:disabled),
[role="button"]:not(:disabled) {
cursor: pointer;
}
}

1974
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -39,7 +39,8 @@
"qrcode": "^1.5.4"
},
"devDependencies": {
"@tailwindcss/cli": "^4.0.9",
"@tailwindcss/forms": "^0.5.10",
"tailwindcss": "^3.4.17"
"tailwindcss": "^4.0.9"
}
}

View File

@@ -1,14 +1,4 @@
module.exports = {
mode: 'jit',
content: ["./template/**/*.{html,js,ejs}"],
darkMode: 'media',
theme: {
extend: {}
},
variants: {
extend: {}
},
plugins: [
require('@tailwindcss/forms')
]
};
/*
This file is present to satisfy a requirement of the Tailwind CSS IntelliSense
The rest of this file is intentionally empty.
*/

View File

@@ -34,7 +34,7 @@
<h1 class="mt-5 text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-5xl">Page not found</h1>
<p class="mt-4 text-base leading-7 text-gray-600 dark:text-gray-400">Sorry, we couldnt find the page youre looking for.</p>
<div class="mt-10 flex items-center justify-center gap-x-6">
<a href="<%= baseUrl %>/" class="rounded-md bg-sky-700 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-sky-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700">Go back home</a>
<a href="<%= baseUrl %>/" class="rounded-md bg-sky-700 px-3.5 py-2.5 text-sm font-semibold text-white shadow-xs hover:bg-sky-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700">Go back home</a>
</div>
<p class="mt-10 text-center text-sm text-gray-500 dark:text-gray-400">
<a href="https://github.com/glenndehaan/unifi-voucher-site" aria-label="GitHub Project Link" target="_blank" rel="noreferrer noopener" class="hover:text-gray-600 dark:hover:text-gray-500">

View File

@@ -33,11 +33,11 @@
<img class="mx-auto h-24 w-auto" width="96" height="96" alt="UniFi Voucher Site Logo" src="<%= baseUrl %>/images/logo.png">
<h1 class="mt-5 text-3xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-5xl">Error</h1>
<p class="mt-4 text-base leading-7 text-gray-600 dark:text-gray-400">Sorry, an unexpected error occurred.</p>
<pre class="max-w-72 sm:max-w-lg mt-4 text-left text-xs text-gray-600 dark:text-gray-400 overflow-x-auto rounded-md border bg-white dark:bg-white/5 border-gray-300 dark:border-white/10 px-3 py-2 placeholder-gray-400 shadow-sm">
<pre class="max-w-72 sm:max-w-lg mt-4 text-left text-xs text-gray-600 dark:text-gray-400 overflow-x-auto rounded-md border bg-white dark:bg-white/5 border-gray-300 dark:border-white/10 px-3 py-2 placeholder-gray-400 shadow-xs">
<%= error %>
</pre>
<div class="mt-10 flex items-center justify-center gap-x-6">
<a href="<%= baseUrl %>/" class="rounded-md bg-sky-700 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-sky-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700">Go back home</a>
<a href="<%= baseUrl %>/" class="rounded-md bg-sky-700 px-3.5 py-2.5 text-sm font-semibold text-white shadow-xs hover:bg-sky-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700">Go back home</a>
</div>
<p class="mt-10 text-center text-sm text-gray-500 dark:text-gray-400">
<a href="https://github.com/glenndehaan/unifi-voucher-site" aria-label="GitHub Project Link" target="_blank" rel="noreferrer noopener" class="hover:text-gray-600 dark:hover:text-gray-500">

View File

@@ -1,5 +1,5 @@
<div class="relative z-40" role="dialog" aria-modal="true">
<div id="overlay" class="fixed inset-0 bg-gray-500 bg-opacity-75"></div>
<div id="overlay" class="fixed inset-0 bg-gray-500/75"></div>
<div class="fixed overflow-hidden">
<div class="absolute inset-0 overflow-hidden">
@@ -21,7 +21,7 @@
<div>
<label for="language" class="block text-sm font-medium leading-6 text-gray-900 dark:text-white">Language</label>
<div class="mt-2">
<select id="language" name="language" class="mt-2 block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 dark:text-white dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-sky-600 sm:text-sm sm:leading-6 [&_*]:text-black">
<select id="language" name="language" class="mt-2 block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 dark:text-white dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-sky-600 sm:text-sm sm:leading-6 **:text-black">
<% Object.keys(languages).forEach((language) => { %>
<option value="<%= language %>"<%= language === defaultLanguage ? ' selected' : '' %>><%= languages[language] %> (<%= language %>)</option>
<% }); %>
@@ -33,7 +33,7 @@
<ul role="list" class="max-h-96 h-96 overflow-y-auto mt-2 rounded-md border-0 divide-y divide-black/5 dark:divide-white/5 dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10">
<% vouchers.forEach((voucher) => { %>
<li class="relative flex items-center space-x-4 px-4 py-4">
<input id="voucher-<%= voucher._id %>" aria-describedby="voucher-<%= voucher._id %>" name="vouchers" type="checkbox" value="<%= voucher._id %>" class="col-start-1 row-start-1 appearance-none rounded border-0 dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-sky-600 focus:ring-offset-0">
<input id="voucher-<%= voucher._id %>" aria-describedby="voucher-<%= voucher._id %>" name="vouchers" type="checkbox" value="<%= voucher._id %>" class="col-start-1 row-start-1 appearance-none rounded-sm border-0 dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-sky-600 focus:ring-offset-0">
<label for="voucher-<%= voucher._id %>" class="voucher min-w-0 flex-auto">
<div class="flex items-center gap-x-3">
<h2 class="min-w-0 text-sm font-semibold leading-6 text-gray-900 dark:text-white">
@@ -96,9 +96,9 @@
</div>
</div>
</div>
<div class="flex flex-shrink-0 justify-end px-4 py-4">
<button id="close" type="button" class="rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50">Cancel</button>
<button type="submit" class="ml-4 inline-flex justify-center rounded-md bg-sky-700 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-sky-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700">Print</button>
<div class="flex shrink-0 justify-end px-4 py-4">
<button id="close" type="button" class="rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-xs ring-1 ring-inset ring-gray-300 hover:bg-gray-50">Cancel</button>
<button type="submit" class="ml-4 inline-flex justify-center rounded-md bg-sky-700 px-3 py-2 text-sm font-semibold text-white shadow-xs hover:bg-sky-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700">Print</button>
</div>
</form>
</div>

View File

@@ -1,5 +1,5 @@
<div class="relative z-40" role="dialog" aria-modal="true">
<div id="overlay" class="fixed inset-0 bg-gray-500 bg-opacity-75"></div>
<div id="overlay" class="fixed inset-0 bg-gray-500/75"></div>
<div class="fixed overflow-hidden">
<div class="absolute inset-0 overflow-hidden">
@@ -97,7 +97,7 @@
<% } else { %>
<% guests.forEach((guest) => { %>
<div class="lg:col-start-3 lg:row-end-1">
<div class="rounded-lg bg-gray-50 dark:bg-gray-800 shadow-sm ring-1 ring-gray-900/5 dark:ring-gray-50/25">
<div class="rounded-lg bg-gray-50 dark:bg-gray-800 shadow-xs ring-1 ring-gray-900/5 dark:ring-gray-50/25">
<dl class="flex flex-wrap">
<div class="flex-auto pl-6 pt-6">
<dt class="text-sm font-semibold leading-6 text-gray-900 dark:text-white"><%= guest.mac %></dt>
@@ -158,8 +158,8 @@
</div>
</div>
</div>
<div class="flex flex-shrink-0 justify-end px-4 py-4">
<button id="close" type="button" class="rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50">Close</button>
<div class="flex shrink-0 justify-end px-4 py-4">
<button id="close" type="button" class="rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-xs ring-1 ring-inset ring-gray-300 hover:bg-gray-50">Close</button>
</div>
</div>
</div>

View File

@@ -1,5 +1,5 @@
<div class="relative z-40" role="dialog" aria-modal="true">
<div id="overlay" class="fixed inset-0 bg-gray-500 bg-opacity-75"></div>
<div id="overlay" class="fixed inset-0 bg-gray-500/75"></div>
<div class="fixed overflow-hidden">
<div class="absolute inset-0 overflow-hidden">
@@ -27,7 +27,7 @@
<div>
<label for="language" class="block text-sm font-medium leading-6 text-gray-900 dark:text-white">Language</label>
<div class="mt-2">
<select id="language" name="language" class="mt-2 block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 dark:text-white dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-sky-600 sm:text-sm sm:leading-6 [&_*]:text-black">
<select id="language" name="language" class="mt-2 block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 dark:text-white dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-sky-600 sm:text-sm sm:leading-6 **:text-black">
<% Object.keys(languages).forEach((language) => { %>
<option value="<%= language %>"<%= language === defaultLanguage ? ' selected' : '' %>><%= languages[language] %> (<%= language %>)</option>
<% }); %>
@@ -38,9 +38,9 @@
</div>
</div>
</div>
<div class="flex flex-shrink-0 justify-end px-4 py-4">
<button id="close" type="button" class="rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50">Cancel</button>
<button type="submit" class="ml-4 inline-flex justify-center rounded-md bg-sky-700 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-sky-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700">Send</button>
<div class="flex shrink-0 justify-end px-4 py-4">
<button id="close" type="button" class="rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-xs ring-1 ring-inset ring-gray-300 hover:bg-gray-50">Cancel</button>
<button type="submit" class="ml-4 inline-flex justify-center rounded-md bg-sky-700 px-3 py-2 text-sm font-semibold text-white shadow-xs hover:bg-sky-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700">Send</button>
</div>
</form>
</div>

View File

@@ -1,5 +1,5 @@
<div class="relative z-40" role="dialog" aria-modal="true">
<div id="overlay" class="fixed inset-0 bg-gray-500 bg-opacity-75"></div>
<div id="overlay" class="fixed inset-0 bg-gray-500/75"></div>
<div class="fixed overflow-hidden">
<div class="absolute inset-0 overflow-hidden">
@@ -21,7 +21,7 @@
<div>
<label for="language" class="block text-sm font-medium leading-6 text-gray-900 dark:text-white">Language</label>
<div class="mt-2">
<select id="language" name="language" class="mt-2 block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 dark:text-white dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-sky-600 sm:text-sm sm:leading-6 [&_*]:text-black">
<select id="language" name="language" class="mt-2 block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 dark:text-white dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-sky-600 sm:text-sm sm:leading-6 **:text-black">
<% Object.keys(languages).forEach((language) => { %>
<option value="<%= language %>"<%= language === defaultLanguage ? ' selected' : '' %>><%= languages[language] %> (<%= language %>)</option>
<% }); %>
@@ -32,9 +32,9 @@
</div>
</div>
</div>
<div class="flex flex-shrink-0 justify-end px-4 py-4">
<button id="close" type="button" class="rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50">Cancel</button>
<button type="submit" class="ml-4 inline-flex justify-center rounded-md bg-sky-700 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-sky-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700">Print</button>
<div class="flex shrink-0 justify-end px-4 py-4">
<button id="close" type="button" class="rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-xs ring-1 ring-inset ring-gray-300 hover:bg-gray-50">Cancel</button>
<button type="submit" class="ml-4 inline-flex justify-center rounded-md bg-sky-700 px-3 py-2 text-sm font-semibold text-white shadow-xs hover:bg-sky-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700">Print</button>
</div>
</form>
</div>

View File

@@ -36,7 +36,7 @@
<% if(error) { %>
<div class="mt-5 rounded-md bg-red-700 p-4">
<div class="flex">
<div class="flex-shrink-0">
<div class="shrink-0">
<svg class="h-5 w-5 text-white" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd" />
</svg>
@@ -55,12 +55,12 @@
<div>
<label for="password" class="block text-sm font-medium leading-6 text-gray-900 dark:text-white">Password</label>
<div class="mt-2">
<input type="password" id="password" name="password" required class="block w-full rounded-md border-0 dark:bg-white/5 py-1.5 text-gray-900 dark:text-white shadow-sm ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-inset focus:ring-sky-700 sm:text-sm sm:leading-6">
<input type="password" id="password" name="password" required class="block w-full rounded-md border-0 dark:bg-white/5 py-1.5 text-gray-900 dark:text-white shadow-xs ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-inset focus:ring-sky-700 sm:text-sm sm:leading-6">
</div>
</div>
<div>
<button type="submit" class="flex w-full justify-center rounded-md bg-sky-700 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-sky-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700">Sign in</button>
<button type="submit" class="flex w-full justify-center rounded-md bg-sky-700 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-xs hover:bg-sky-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700">Sign in</button>
</div>
</form>
<% } %>
@@ -79,7 +79,7 @@
<% } %>
<div class="<%= internalAuth ? 'mt-6' : '' %>">
<a href="<%= baseUrl %>/oidc/login" class="flex w-full items-center justify-center gap-3 rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus-visible:ring-transparent">
<a href="<%= baseUrl %>/oidc/login" class="flex w-full items-center justify-center gap-3 rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-xs ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus-visible:ring-transparent">
<svg class="h-5 w-5" viewBox="0 0 64 64" aria-hidden="true">
<path fill="#f7931e" d="M29.1 6.4v54.5l9.7-4.5V1.7l-9.7 4.7z"></path>
<path fill="#b2b2b2" d="M62.7 22.4L64 36.3l-18.7-4.1"></path>

View File

@@ -1,15 +1,15 @@
<nav class="sticky top-0 z-40 bg-white shadow-sm dark:bg-gray-800">
<nav class="sticky top-0 z-40 bg-white shadow-xs dark:bg-gray-800">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div class="flex h-16 justify-between">
<div class="flex">
<% if(uiBackButton) { %>
<button aria-label="Back to Previous Page" onclick="window.history.go(-1);">
<svg xmlns="http://www.w3.org/2000/svg" class="text-gray-900 dark:text-white hover:text-gray-700 hover:dark:text-gray-100 w-10 h-10 mr-4" viewBox="0 0 24 24" fill="currentColor">
<svg xmlns="http://www.w3.org/2000/svg" class="text-gray-900 dark:text-white hover:text-gray-700 dark:hover:text-gray-100 w-10 h-10 mr-4" viewBox="0 0 24 24" fill="currentColor">
<path fill-rule="evenodd" d="M12 2.25c-5.385 0-9.75 4.365-9.75 9.75s4.365 9.75 9.75 9.75 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25Zm-4.28 9.22a.75.75 0 0 0 0 1.06l3 3a.75.75 0 1 0 1.06-1.06l-1.72-1.72h5.69a.75.75 0 0 0 0-1.5h-5.69l1.72-1.72a.75.75 0 0 0-1.06-1.06l-3 3Z" clip-rule="evenodd" />
</svg>
</button>
<% } %>
<a href="<%= baseUrl %>/vouchers" class="flex flex-shrink-0 items-center">
<a href="<%= baseUrl %>/vouchers" class="flex shrink-0 items-center">
<img class="h-12 w-auto" width="48" height="48" alt="UniFi Voucher Site Logo" src="<%= baseUrl %>/images/logo.png">
<div class="hidden sm:block ml-4 text-2xl font-semibold leading-7 text-gray-900 dark:text-white">
UniFi Voucher
@@ -18,8 +18,8 @@
</div>
<div class="flex items-center">
<% if(new_voucher_button) { %>
<div class="flex-shrink-0">
<button id="create-button-header" type="button" class="relative inline-flex items-center gap-x-1.5 rounded-md bg-sky-700 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-sky-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700">
<div class="shrink-0">
<button id="create-button-header" type="button" class="relative inline-flex items-center gap-x-1.5 rounded-md bg-sky-700 px-3 py-2 text-sm font-semibold text-white shadow-xs hover:bg-sky-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700">
<svg class="-ml-0.5 h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z" />
</svg>
@@ -27,9 +27,9 @@
</button>
</div>
<% } %>
<div class="ml-4 flex flex-shrink-0 items-center">
<div class="ml-4 flex shrink-0 items-center">
<div class="relative">
<button id="user-menu-button" class="flex items-center focus:outline-none focus:ring-2 focus:ring-sky-600 rounded-full">
<button id="user-menu-button" class="flex items-center focus:outline-hidden focus:ring-2 focus:ring-sky-600 rounded-full">
<span class="absolute -inset-1.5"></span>
<span class="sr-only">Open user menu</span>
<% if(userIcon !== '') { %>
@@ -40,7 +40,7 @@
</svg>
<% } %>
</button>
<div id="user-dropdown" class="hidden absolute bg-white dark:bg-gray-800 right-0 mt-2 w-64 rounded-md shadow-lg py-1 ring-1 ring-black ring-opacity-5">
<div id="user-dropdown" class="hidden absolute bg-white dark:bg-gray-800 right-0 mt-2 w-64 rounded-md shadow-lg py-1 ring-1 ring-black/25">
<div class="px-4 py-2 text-sm text-gray-600 dark:text-gray-400 border-b border-black/5 dark:border-white/5">
Logged in as:<br>
<span class="font-medium"><%= user.email %></span>

View File

@@ -35,7 +35,7 @@
<div class="mx-6">
<div class="max-w-7xl mx-auto mt-5 rounded-md bg-red-700 p-4">
<div class="flex">
<div class="flex-shrink-0">
<div class="shrink-0">
<svg class="h-5 w-5 text-white" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd" />
</svg>
@@ -52,7 +52,7 @@
<div class="mx-6">
<div class="max-w-7xl mx-auto mt-5 rounded-md bg-green-700 p-4">
<div class="flex">
<div class="flex-shrink-0">
<div class="shrink-0">
<svg class="h-5 w-5 text-white" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16Zm3.857-9.809a.75.75 0 0 0-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 1 0-1.06 1.061l2.5 2.5a.75.75 0 0 0 1.137-.089l4-5.5Z" clip-rule="evenodd" />
</svg>
@@ -72,7 +72,7 @@
<div class="flex flex-col md:flex-row md:items-end md:space-x-4 space-y-2 md:space-y-0">
<div class="flex flex-col">
<label for="status" class="text-xs text-gray-900 dark:text-white mb-1">Status</label>
<select id="status" name="status" class="rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 dark:text-white dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-sky-600 sm:text-sm sm:leading-6 [&_*]:text-black" aria-label="Filter by status">
<select id="status" name="status" class="rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 dark:text-white dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-sky-600 sm:text-sm sm:leading-6 **:text-black" aria-label="Filter by status">
<option value="all"<%= filters.status === 'all' ? ' selected' : '' %>>All</option>
<option value="available"<%= filters.status === 'available' ? ' selected' : '' %>>Available</option>
<option value="in-use"<%= filters.status === 'in-use' ? ' selected' : '' %>>In Use</option>
@@ -81,7 +81,7 @@
</div>
<div class="flex flex-col">
<label for="quota" class="text-xs text-gray-900 dark:text-white mb-1">Quota</label>
<select id="quota" name="quota" class="rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 dark:text-white dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-sky-600 sm:text-sm sm:leading-6 [&_*]:text-black" aria-label="Filter by quota">
<select id="quota" name="quota" class="rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 dark:text-white dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-sky-600 sm:text-sm sm:leading-6 **:text-black" aria-label="Filter by quota">
<option value="all"<%= filters.quota === 'all' ? ' selected' : '' %>>All</option>
<option value="multi-use"<%= filters.quota === 'multi-use' ? ' selected' : '' %>>Multi-use</option>
<option value="single-use"<%= filters.quota === 'single-use' ? ' selected' : '' %>>Single-use</option>
@@ -89,7 +89,7 @@
</div>
<div class="flex flex-col">
<label for="sort" class="text-xs text-gray-900 dark:text-white mb-1">Sort</label>
<select id="sort" name="sort" class="rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 dark:text-white dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-sky-600 sm:text-sm sm:leading-6 [&_*]:text-black" aria-label="Sort by">
<select id="sort" name="sort" class="rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 dark:text-white dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-sky-600 sm:text-sm sm:leading-6 **:text-black" aria-label="Sort by">
<option value="date"<%= sort === 'date' ? ' selected' : '' %>>Date</option>
<option value="code"<%= sort === 'code' ? ' selected' : '' %>>Code</option>
<option value="note"<%= sort === 'note' ? ' selected' : '' %>>Notes</option>
@@ -106,7 +106,7 @@
<span class="mb-1 text-xs text-gray-900 dark:text-white<%= !printer_enabled ? ' hidden' : '' %>">
&nbsp;
</span>
<button id="bulk-print" class="w-fit justify-self-end relative inline-flex items-center gap-x-1.5 rounded-md bg-sky-700 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-sky-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700<%= !printer_enabled ? ' hidden' : '' %>">
<button id="bulk-print" class="w-fit justify-self-end relative inline-flex items-center gap-x-1.5 rounded-md bg-sky-700 px-3 py-2 text-sm font-semibold text-white shadow-xs hover:bg-sky-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700<%= !printer_enabled ? ' hidden' : '' %>">
<svg class="-ml-0.5 h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M6.72 13.829c-.24.03-.48.062-.72.096m.72-.096a42.415 42.415 0 0 1 10.56 0m-10.56 0L6.34 18m10.94-4.171c.24.03.48.062.72.096m-.72-.096L17.66 18m0 0 .229 2.523a1.125 1.125 0 0 1-1.12 1.227H7.231c-.662 0-1.18-.568-1.12-1.227L6.34 18m11.318 0h1.091A2.25 2.25 0 0 0 21 15.75V9.456c0-1.081-.768-2.015-1.837-2.175a48.055 48.055 0 0 0-1.913-.247M6.34 18H5.25A2.25 2.25 0 0 1 3 15.75V9.456c0-1.081.768-2.015 1.837-2.175a48.041 48.041 0 0 1 1.913-.247m10.5 0a48.536 48.536 0 0 0-10.5 0m10.5 0V3.375c0-.621-.504-1.125-1.125-1.125h-8.25c-.621 0-1.125.504-1.125 1.125v3.659M18 10.5h.008v.008H18V10.5Zm-3 0h.008v.008H15V10.5Z" />
</svg>
@@ -117,7 +117,7 @@
<span class="mb-1 text-xs text-gray-900 dark:text-white">
Last Sync: <%= new Intl.DateTimeFormat('en-GB', {day: "numeric", month: "numeric", hour: "numeric", minute: "numeric", hour12: false}).format(new Date(updated)) %>
</span>
<a href="<%= baseUrl %>/vouchers?refresh=true" id="reload-vouchers" type="button" class="w-fit justify-self-end relative inline-flex items-center gap-x-1.5 rounded-md bg-sky-700 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-sky-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700">
<a href="<%= baseUrl %>/vouchers?refresh=true" id="reload-vouchers" type="button" class="w-fit justify-self-end relative inline-flex items-center gap-x-1.5 rounded-md bg-sky-700 px-3 py-2 text-sm font-semibold text-white shadow-xs hover:bg-sky-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700">
<svg class="-ml-0.5 h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M15.312 11.424a5.5 5.5 0 0 1-9.201 2.466l-.312-.311h2.433a.75.75 0 0 0 0-1.5H3.989a.75.75 0 0 0-.75.75v4.242a.75.75 0 0 0 1.5 0v-2.43l.31.31a7 7 0 0 0 11.712-3.138.75.75 0 0 0-1.449-.39Zm1.23-3.723a.75.75 0 0 0 .219-.53V2.929a.75.75 0 0 0-1.5 0V5.36l-.31-.31A7 7 0 0 0 3.239 8.188a.75.75 0 1 0 1.448.389A5.5 5.5 0 0 1 13.89 6.11l.311.31h-2.432a.75.75 0 0 0 0 1.5h4.243a.75.75 0 0 0 .53-.219Z" clip-rule="evenodd" />
</svg>
@@ -133,7 +133,7 @@
<h3 class="mt-2 text-sm font-semibold text-gray-900 dark:text-white">No vouchers</h3>
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">Get started by creating a new voucher.</p>
<div class="mt-6">
<button id="create-button-info" type="button" class="inline-flex items-center rounded-md bg-sky-700 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-sky-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700">
<button id="create-button-info" type="button" class="inline-flex items-center rounded-md bg-sky-700 px-3 py-2 text-sm font-semibold text-white shadow-xs hover:bg-sky-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700">
<svg class="-ml-0.5 mr-1.5 h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z" />
</svg>
@@ -243,7 +243,7 @@
<div id="bulk-print-dialog"></div>
<div id="create-dialog" class="hidden relative z-40" role="dialog" aria-modal="true">
<div id="create-dialog-overlay" class="fixed inset-0 bg-gray-500 bg-opacity-75"></div>
<div id="create-dialog-overlay" class="fixed inset-0 bg-gray-500/75"></div>
<div class="fixed overflow-hidden">
<div class="absolute inset-0 overflow-hidden">
@@ -265,7 +265,7 @@
<div>
<label for="voucher-type" class="block text-sm font-medium leading-6 text-gray-900 dark:text-white">Preset Voucher Type</label>
<div class="mt-2">
<select id="voucher-type" name="voucher-type" class="mt-2 block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 dark:text-white dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-sky-600 sm:text-sm sm:leading-6 [&_*]:text-black">
<select id="voucher-type" name="voucher-type" class="mt-2 block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 dark:text-white dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-sky-600 sm:text-sm sm:leading-6 **:text-black">
<% voucher_types.forEach((type) => { %>
<option value="<%= type.raw %>"><%= timeConvert(type.expiration) %>, <%= type.usage === '1' ? 'single-use' : type.usage === '0' ? 'multi-use (unlimited)' : `multi-use (${type.usage}x)` %><%= typeof type.upload === "undefined" && typeof type.download === "undefined" && typeof type.megabytes === "undefined" ? ', no limits' : '' %><%= typeof type.upload !== "undefined" ? `, upload bandwidth limit: ${type.upload} kb/s` : '' %><%= typeof type.download !== "undefined" ? `, download bandwidth limit: ${type.download} kb/s` : '' %><%= typeof type.megabytes !== "undefined" ? `, quota limit: ${type.megabytes} mb` : '' %></option>
<% }); %>
@@ -291,7 +291,7 @@
<label for="voucher-duration" class="mt-4 block text-sm font-medium leading-6 text-gray-900 dark:text-white">Duration</label>
<div class="mt-2 grid grid-cols-2 gap-2">
<input type="number" min="1" step="1" value="8" id="voucher-duration" name="voucher-duration" required class="block w-full rounded-md border-0 py-1.5 text-gray-900 dark:text-white dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-sky-600 sm:text-sm sm:leading-6">
<select id="voucher-duration-type" name="voucher-duration-type" class="block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 dark:text-white dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-sky-600 sm:text-sm sm:leading-6 [&_*]:text-black">
<select id="voucher-duration-type" name="voucher-duration-type" class="block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 dark:text-white dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-sky-600 sm:text-sm sm:leading-6 **:text-black">
<option value="minute">Minute(s)</option>
<option value="hour" selected>Hour(s)</option>
<option value="day">Day(s)</option>
@@ -301,7 +301,7 @@
<div class="custom-voucher-field hidden">
<label for="voucher-usage" class="block text-sm font-medium leading-6 text-gray-900 dark:text-white">Voucher Usage</label>
<div class="mt-2">
<select id="voucher-usage" name="voucher-usage" class="mt-2 block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 dark:text-white dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-sky-600 sm:text-sm sm:leading-6 [&_*]:text-black">
<select id="voucher-usage" name="voucher-usage" class="mt-2 block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 dark:text-white dark:bg-white/5 ring-1 ring-inset ring-gray-300 dark:ring-white/10 focus:ring-2 focus:ring-sky-600 sm:text-sm sm:leading-6 **:text-black">
<option value="0">Multi-use (Unlimited)</option>
<option value="-1">Multi-use (Limited/Quota)</option>
<option value="1">Single-use</option>
@@ -336,9 +336,9 @@
</div>
</div>
</div>
<div class="flex flex-shrink-0 justify-end px-4 py-4">
<button id="cancel" type="button" class="rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50">Cancel</button>
<button type="submit" class="ml-4 inline-flex justify-center rounded-md bg-sky-700 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-sky-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700">Create</button>
<div class="flex shrink-0 justify-end px-4 py-4">
<button id="cancel" type="button" class="rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-xs ring-1 ring-inset ring-gray-300 hover:bg-gray-50">Cancel</button>
<button type="submit" class="ml-4 inline-flex justify-center rounded-md bg-sky-700 px-3 py-2 text-sm font-semibold text-white shadow-xs hover:bg-sky-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700">Create</button>
</div>
</form>
</div>
@@ -393,10 +393,10 @@
<div aria-live="assertive" class="z-40 pointer-events-none fixed inset-0 flex items-end px-4 py-6">
<div id="copy-notification" style="display: none;" class="flex w-full flex-col items-center space-y-4">
<div class="pointer-events-auto w-full max-w-sm overflow-hidden rounded-lg bg-white dark:bg-gray-800 shadow-lg ring-1 ring-black ring-opacity-5">
<div class="pointer-events-auto w-full max-w-sm overflow-hidden rounded-lg bg-white dark:bg-gray-800 shadow-lg ring-1 ring-black/5">
<div class="p-4">
<div class="flex items-start">
<div class="flex-shrink-0">
<div class="shrink-0">
<svg class="h-6 w-6 text-green-400" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>