Enhances container/image management ui and refactor updates notification functionality
- Implement dynamic message templating as const - Refactor JS version check logic using arrow functions for cleaner syntax - Introduce wiggle animation to the update notification nerdfont icon - Improve user experience for the container and image list with added hover effects - Correct HTMX response targeting to handle dynamic responses accurately
- commit
- faf993e85ee8726b865573bd59995a055b50ba75
- author
- bnema · · 2023-11-06 09:54 UTC
- parent
- efb1aec
- signature
- unsigned
6 files · +97 -76
M internal/templating/models/html/admin/manager.gohtml +0 -4
| @@ -16,10 +16,8 @@ | |||
| 16 | 16 | hx-trigger="load" | |
| 17 | 17 | hx-swap="innerHTML" | |
| 18 | 18 | hx-swap-oob="true" | |
| 19 | - | hx-target-error="container-manager-error" | |
| 20 | 19 | hx-poll="5s" | |
| 21 | 20 | ></div> | |
| 22 | - | <div id="container-manager-error" hidden></div> | |
| 23 | 21 | <!-- List all the images --> | |
| 24 | 22 | <div | |
| 25 | 23 | id="image-manager" | |
| @@ -27,10 +25,8 @@ | |||
| 27 | 25 | hx-trigger="load" | |
| 28 | 26 | hx-swap="innerHTML" | |
| 29 | 27 | hx-swap-oob="true" | |
| 30 | - | hx-target-error="image-manager-error" | |
| 31 | 28 | hx-poll="5s" | |
| 32 | 29 | ></div> | |
| 33 | - | <div id="image-manager-error" hidden></div> | |
| 34 | 30 | </div> | |
| 35 | 31 | </div> | |
| 36 | 32 | ||
| @@ -16,10 +16,8 @@ | |||||
| 16 | hx-trigger="load" | 16 | hx-trigger="load" | ||
| 17 | hx-swap="innerHTML" | 17 | hx-swap="innerHTML" | ||
| 18 | hx-swap-oob="true" | 18 | hx-swap-oob="true" | ||
| 19 | - | hx-target-error="container-manager-error" | |||
| 20 | hx-poll="5s" | 19 | hx-poll="5s" | ||
| 21 | ></div> | 20 | ></div> | ||
| 22 | - | <div id="container-manager-error" hidden></div> | |||
| 23 | <!-- List all the images --> | 21 | <!-- List all the images --> | ||
| 24 | <div | 22 | <div | ||
| 25 | id="image-manager" | 23 | id="image-manager" | ||
| @@ -27,10 +25,8 @@ | |||||
| 27 | hx-trigger="load" | 25 | hx-trigger="load" | ||
| 28 | hx-swap="innerHTML" | 26 | hx-swap="innerHTML" | ||
| 29 | hx-swap-oob="true" | 27 | hx-swap-oob="true" | ||
| 30 | - | hx-target-error="image-manager-error" | |||
| 31 | hx-poll="5s" | 28 | hx-poll="5s" | ||
| 32 | ></div> | 29 | ></div> | ||
| 33 | - | <div id="image-manager-error" hidden></div> | |||
| 34 | </div> | 30 | </div> | ||
| 35 | </div> | 31 | </div> | ||
| 36 | 32 | ||||
M internal/templating/models/html/fragments/containerlist.gohtml +16 -18
| @@ -1,10 +1,8 @@ | |||
| 1 | - | <div id="actionResponse"></div> | |
| 2 | - | ||
| 3 | - | <div class="flex flex-col p-4" hx-boost="true"> | |
| 1 | + | <div class="flex flex-col p-4" hx-ext="response-targets"> | |
| 4 | 2 | <h1 class="text-lg md:text-xl font-bold text-gordonmascot-hblue"> | |
| 5 | 3 | {{index .Lang "manager" "content" "h1-containers"}} | |
| 6 | 4 | </h1> | |
| 7 | - | <table class="p-4 border-t-2 table-auto" hx-ext="response-targets"> | |
| 5 | + | <table class="p-4 border-t-2 table-auto"> | |
| 8 | 6 | <thead> | |
| 9 | 7 | <tr class="text-center text-gray-400"> | |
| 10 | 8 | <th>{{index .Lang "manager" "content" "thName"}}</th> | |
| @@ -15,11 +13,11 @@ | |||
| 15 | 13 | </tr> | |
| 16 | 14 | </thead> | |
| 17 | 15 | <tbody> | |
| 18 | - | {{ $Lang := .Lang }} | |
| 16 | + | {{ $Lang := .Lang }} | |
| 19 | 17 | {{ | |
| 20 | 18 | range.containers | |
| 21 | 19 | }} | |
| 22 | - | <tr id="container-{{.ID}}" class="text-center"> | |
| 20 | + | <tr id="container-{{.ID}}" class="text-center hover:bg-slate-100"> | |
| 23 | 21 | <td> | |
| 24 | 22 | <span | |
| 25 | 23 | title="{{.Container.ID}}" | |
| @@ -31,11 +29,10 @@ | |||
| 31 | 29 | <td class="status-container">{{.Status}} (Up since {{.UpSince}})</td> | |
| 32 | 30 | <td style="color: {{.StateColor}};">{{.State}}</td> | |
| 33 | 31 | <td> | |
| 34 | - | <!-- iconoir start --> | |
| 35 | 32 | <button | |
| 36 | 33 | id="start-button-{{.ID}}" | |
| 37 | 34 | hx-post="/htmx/container-manager/start/{{.ID}}" | |
| 38 | - | hx-target="#actionResponse" | |
| 35 | + | hx-target="#actionResponse-{{.ID}}" | |
| 39 | 36 | hx-target-*="#error-message-{{.ID}}" | |
| 40 | 37 | hx-swap="outerHTML" | |
| 41 | 38 | class="py-2 px-4" | |
| @@ -46,11 +43,12 @@ | |||
| 46 | 43 | <button | |
| 47 | 44 | id="edit-button-{{.ID}}" | |
| 48 | 45 | hx-get="/htmx/container-manager/edit/{{.ID}}" | |
| 49 | - | hx-target="#actionResponse" | |
| 46 | + | hx-target="#actionResponse-{{.ID}}" | |
| 50 | 47 | hx-target-*="#error-message-{{.ID}}" | |
| 51 | 48 | hx-swap="innerHTML" | |
| 52 | 49 | class="py-2 px-4" | |
| 53 | 50 | data-active="false" | |
| 51 | + | _="on click toggle between .visible and .hidden on #actionResponse-{{.ID}}" | |
| 54 | 52 | > | |
| 55 | 53 | <span title="Edit the container" class="iconf text-2xl text-blue-600 hover:text-gordonmascot-hblue"></span> | |
| 56 | 54 | </button> | |
| @@ -58,7 +56,7 @@ | |||
| 58 | 56 | <button | |
| 59 | 57 | id="stop-button-{{.ID}}" | |
| 60 | 58 | hx-post="/htmx/container-manager/stop/{{.ID}}" | |
| 61 | - | hx-target="#actionResponse" | |
| 59 | + | hx-target="#actionResponse-{{.ID}}" | |
| 62 | 60 | hx-target-*="#error-message-{{.ID}}" | |
| 63 | 61 | hx-swap="innerHTML" | |
| 64 | 62 | hx-confirm="{{index $Lang "manager" "content" "confirm-container-stop"}}" | |
| @@ -70,7 +68,7 @@ | |||
| 70 | 68 | <button | |
| 71 | 69 | id="remove-button-img-{{.ID}}" | |
| 72 | 70 | hx-delete="/htmx/container-manager/delete/{{.ID}}" | |
| 73 | - | hx-target="#actionResponse" | |
| 71 | + | hx-target="#actionResponse-{{.ID}}" | |
| 74 | 72 | hx-target-*="#error-message-{{.ID}}" | |
| 75 | 73 | hx-swap="innerHTML" | |
| 76 | 74 | hx-confirm="{{index $Lang "manager" "content" "confirm-delete"}}" | |
| @@ -80,20 +78,20 @@ | |||
| 80 | 78 | <span title="Remove the container" class="iconf text-xl text-red-600 hover:text-red-900"></span> | |
| 81 | 79 | </button> | |
| 82 | 80 | </td> | |
| 83 | - | </tr> | |
| 84 | - | <tr> | |
| 85 | - | <td colspan="7"> | |
| 86 | - | <div id="edit-container-{{.ID}}"></div> | |
| 87 | - | </td> | |
| 88 | - | </tr> | |
| 81 | + | </tr> | |
| 89 | 82 | <tr> | |
| 90 | 83 | <td colspan="7"> | |
| 84 | + | <div id="actionResponse-{{.ID}}" hidden></div> | |
| 91 | 85 | <div id="error-message-{{.ID}}" class="text-red-500"></div> | |
| 92 | 86 | </td> | |
| 93 | 87 | </tr> | |
| 94 | 88 | {{ else }} | |
| 95 | 89 | <tr> | |
| 96 | - | <td colspan="7" class="mt-2">{{index $Lang "manager" "content" "no-containers"}}.</td> | |
| 90 | + | <td colspan="7" class="mt-2"> | |
| 91 | + | <p class="text-center text-gray-400 text-xl"> | |
| 92 | + | {{index $Lang "manager" "content" "no-containers"}} | |
| 93 | + | </p> | |
| 94 | + | </td> | |
| 97 | 95 | </tr> | |
| 98 | 96 | {{ | |
| 99 | 97 | end | |
| @@ -1,10 +1,8 @@ | |||||
| 1 | - | <div id="actionResponse"></div> | 1 | + | <div class="flex flex-col p-4" hx-ext="response-targets"> |
| 2 | - | ||||
| 3 | - | <div class="flex flex-col p-4" hx-boost="true"> | |||
| 4 | <h1 class="text-lg md:text-xl font-bold text-gordonmascot-hblue"> | 2 | <h1 class="text-lg md:text-xl font-bold text-gordonmascot-hblue"> | ||
| 5 | {{index .Lang "manager" "content" "h1-containers"}} | 3 | {{index .Lang "manager" "content" "h1-containers"}} | ||
| 6 | </h1> | 4 | </h1> | ||
| 7 | - | <table class="p-4 border-t-2 table-auto" hx-ext="response-targets"> | 5 | + | <table class="p-4 border-t-2 table-auto"> |
| 8 | <thead> | 6 | <thead> | ||
| 9 | <tr class="text-center text-gray-400"> | 7 | <tr class="text-center text-gray-400"> | ||
| 10 | <th>{{index .Lang "manager" "content" "thName"}}</th> | 8 | <th>{{index .Lang "manager" "content" "thName"}}</th> | ||
| @@ -15,11 +13,11 @@ | |||||
| 15 | </tr> | 13 | </tr> | ||
| 16 | </thead> | 14 | </thead> | ||
| 17 | <tbody> | 15 | <tbody> | ||
| 18 | - | {{ $Lang := .Lang }} | 16 | + | {{ $Lang := .Lang }} |
| 19 | {{ | 17 | {{ | ||
| 20 | range.containers | 18 | range.containers | ||
| 21 | }} | 19 | }} | ||
| 22 | - | <tr id="container-{{.ID}}" class="text-center"> | 20 | + | <tr id="container-{{.ID}}" class="text-center hover:bg-slate-100"> |
| 23 | <td> | 21 | <td> | ||
| 24 | <span | 22 | <span | ||
| 25 | title="{{.Container.ID}}" | 23 | title="{{.Container.ID}}" | ||
| @@ -31,11 +29,10 @@ | |||||
| 31 | <td class="status-container">{{.Status}} (Up since {{.UpSince}})</td> | 29 | <td class="status-container">{{.Status}} (Up since {{.UpSince}})</td> | ||
| 32 | <td style="color: {{.StateColor}};">{{.State}}</td> | 30 | <td style="color: {{.StateColor}};">{{.State}}</td> | ||
| 33 | <td> | 31 | <td> | ||
| 34 | - | <!-- iconoir start --> | |||
| 35 | <button | 32 | <button | ||
| 36 | id="start-button-{{.ID}}" | 33 | id="start-button-{{.ID}}" | ||
| 37 | hx-post="/htmx/container-manager/start/{{.ID}}" | 34 | hx-post="/htmx/container-manager/start/{{.ID}}" | ||
| 38 | - | hx-target="#actionResponse" | 35 | + | hx-target="#actionResponse-{{.ID}}" |
| 39 | hx-target-*="#error-message-{{.ID}}" | 36 | hx-target-*="#error-message-{{.ID}}" | ||
| 40 | hx-swap="outerHTML" | 37 | hx-swap="outerHTML" | ||
| 41 | class="py-2 px-4" | 38 | class="py-2 px-4" | ||
| @@ -46,11 +43,12 @@ | |||||
| 46 | <button | 43 | <button | ||
| 47 | id="edit-button-{{.ID}}" | 44 | id="edit-button-{{.ID}}" | ||
| 48 | hx-get="/htmx/container-manager/edit/{{.ID}}" | 45 | hx-get="/htmx/container-manager/edit/{{.ID}}" | ||
| 49 | - | hx-target="#actionResponse" | 46 | + | hx-target="#actionResponse-{{.ID}}" |
| 50 | hx-target-*="#error-message-{{.ID}}" | 47 | hx-target-*="#error-message-{{.ID}}" | ||
| 51 | hx-swap="innerHTML" | 48 | hx-swap="innerHTML" | ||
| 52 | class="py-2 px-4" | 49 | class="py-2 px-4" | ||
| 53 | data-active="false" | 50 | data-active="false" | ||
| 51 | + | _="on click toggle between .visible and .hidden on #actionResponse-{{.ID}}" | |||
| 54 | > | 52 | > | ||
| 55 | <span title="Edit the container" class="iconf text-2xl text-blue-600 hover:text-gordonmascot-hblue"></span> | 53 | <span title="Edit the container" class="iconf text-2xl text-blue-600 hover:text-gordonmascot-hblue"></span> | ||
| 56 | </button> | 54 | </button> | ||
| @@ -58,7 +56,7 @@ | |||||
| 58 | <button | 56 | <button | ||
| 59 | id="stop-button-{{.ID}}" | 57 | id="stop-button-{{.ID}}" | ||
| 60 | hx-post="/htmx/container-manager/stop/{{.ID}}" | 58 | hx-post="/htmx/container-manager/stop/{{.ID}}" | ||
| 61 | - | hx-target="#actionResponse" | 59 | + | hx-target="#actionResponse-{{.ID}}" |
| 62 | hx-target-*="#error-message-{{.ID}}" | 60 | hx-target-*="#error-message-{{.ID}}" | ||
| 63 | hx-swap="innerHTML" | 61 | hx-swap="innerHTML" | ||
| 64 | hx-confirm="{{index $Lang "manager" "content" "confirm-container-stop"}}" | 62 | hx-confirm="{{index $Lang "manager" "content" "confirm-container-stop"}}" | ||
| @@ -70,7 +68,7 @@ | |||||
| 70 | <button | 68 | <button | ||
| 71 | id="remove-button-img-{{.ID}}" | 69 | id="remove-button-img-{{.ID}}" | ||
| 72 | hx-delete="/htmx/container-manager/delete/{{.ID}}" | 70 | hx-delete="/htmx/container-manager/delete/{{.ID}}" | ||
| 73 | - | hx-target="#actionResponse" | 71 | + | hx-target="#actionResponse-{{.ID}}" |
| 74 | hx-target-*="#error-message-{{.ID}}" | 72 | hx-target-*="#error-message-{{.ID}}" | ||
| 75 | hx-swap="innerHTML" | 73 | hx-swap="innerHTML" | ||
| 76 | hx-confirm="{{index $Lang "manager" "content" "confirm-delete"}}" | 74 | hx-confirm="{{index $Lang "manager" "content" "confirm-delete"}}" | ||
| @@ -80,20 +78,20 @@ | |||||
| 80 | <span title="Remove the container" class="iconf text-xl text-red-600 hover:text-red-900"></span> | 78 | <span title="Remove the container" class="iconf text-xl text-red-600 hover:text-red-900"></span> | ||
| 81 | </button> | 79 | </button> | ||
| 82 | </td> | 80 | </td> | ||
| 83 | - | </tr> | 81 | + | </tr> |
| 84 | - | <tr> | |||
| 85 | - | <td colspan="7"> | |||
| 86 | - | <div id="edit-container-{{.ID}}"></div> | |||
| 87 | - | </td> | |||
| 88 | - | </tr> | |||
| 89 | <tr> | 82 | <tr> | ||
| 90 | <td colspan="7"> | 83 | <td colspan="7"> | ||
| 84 | + | <div id="actionResponse-{{.ID}}" hidden></div> | |||
| 91 | <div id="error-message-{{.ID}}" class="text-red-500"></div> | 85 | <div id="error-message-{{.ID}}" class="text-red-500"></div> | ||
| 92 | </td> | 86 | </td> | ||
| 93 | </tr> | 87 | </tr> | ||
| 94 | {{ else }} | 88 | {{ else }} | ||
| 95 | <tr> | 89 | <tr> | ||
| 96 | - | <td colspan="7" class="mt-2">{{index $Lang "manager" "content" "no-containers"}}.</td> | 90 | + | <td colspan="7" class="mt-2"> |
| 91 | + | <p class="text-center text-gray-400 text-xl"> | |||
| 92 | + | {{index $Lang "manager" "content" "no-containers"}} | |||
| 93 | + | </p> | |||
| 94 | + | </td> | |||
| 97 | </tr> | 95 | </tr> | ||
| 98 | {{ | 96 | {{ | ||
| 99 | end | 97 | end | ||
M internal/templating/models/html/fragments/imagelist.gohtml +6 -6
| @@ -9,19 +9,20 @@ | |||
| 9 | 9 | hx-target="#upload-image" | |
| 10 | 10 | hx-swap="innerHTML" | |
| 11 | 11 | title="Manually upload a .tar container image" | |
| 12 | - | _="on click toggle between .visible and .hidden on #upload-image then add .hidden to #create-container" | |
| 12 | + | _="on click toggle between .visible and .hidden on #upload-image then add .hidden to .create-container" | |
| 13 | 13 | > | |
| 14 | 14 | ||
| 15 | 15 | <span class="iconf text-xl align-middle"> </span> | |
| 16 | 16 | Upload | |
| 17 | 17 | </button> | |
| 18 | 18 | </div> | |
| 19 | + | <div id="upload-image"></div> | |
| 19 | 20 | <table class="p-4 border-t-2"> | |
| 20 | 21 | <tbody> | |
| 21 | 22 | {{ | |
| 22 | 23 | range.Images | |
| 23 | 24 | }} | |
| 24 | - | <tr id="img-{{.ID}}" hx-ext="response-targets"> | |
| 25 | + | <tr id="img-{{.ID}}" hx-ext="response-targets" class=" hover:bg-slate-100"> | |
| 25 | 26 | <td> | |
| 26 | 27 | <span title="{{.ImageSummary.ID}}">{{.Name}}</span> | |
| 27 | 28 | </td> | |
| @@ -34,11 +35,11 @@ | |||
| 34 | 35 | <button | |
| 35 | 36 | id="add-button-img-{{.ID}}" | |
| 36 | 37 | hx-get="/htmx/create-container/{{.ID}}" | |
| 37 | - | hx-target="#create-container" | |
| 38 | + | hx-target="#create-container-{{.ID}}" | |
| 38 | 39 | hx-target-*="#error-message-div-{{.ID}}" | |
| 39 | 40 | hx-swap="innerHTML" | |
| 40 | 41 | class="py-2 px-4" | |
| 41 | - | _="on click toggle between .visible and .hidden on #create-container then add .hidden to #upload-image" | |
| 42 | + | _="on click toggle between .visible and .hidden on #create-container-{{.ID}} then add .hidden to #upload-image" | |
| 42 | 43 | > | |
| 43 | 44 | <span title="Deploy a container using this image" class="iconf text-xl text-blue-600 hover:text-gordonmascot-hblue"></span> | |
| 44 | 45 | </button> | |
| @@ -59,6 +60,7 @@ | |||
| 59 | 60 | <t> | |
| 60 | 61 | <td colspan="4"> | |
| 61 | 62 | <div id="error-message-div-{{.ID}}" class="text-red-500"></div> | |
| 63 | + | <div class="create-container" id="create-container-{{.ID}}"></div> | |
| 62 | 64 | </td> | |
| 63 | 65 | </tr> | |
| 64 | 66 | {{ else }} | |
| @@ -70,6 +72,4 @@ | |||
| 70 | 72 | }} | |
| 71 | 73 | </tbody> | |
| 72 | 74 | </table> | |
| 73 | - | <div id="upload-image"></div> | |
| 74 | - | <div id="create-container"></div> | |
| 75 | 75 | </div> | |
| @@ -9,19 +9,20 @@ | |||||
| 9 | hx-target="#upload-image" | 9 | hx-target="#upload-image" | ||
| 10 | hx-swap="innerHTML" | 10 | hx-swap="innerHTML" | ||
| 11 | title="Manually upload a .tar container image" | 11 | title="Manually upload a .tar container image" | ||
| 12 | - | _="on click toggle between .visible and .hidden on #upload-image then add .hidden to #create-container" | 12 | + | _="on click toggle between .visible and .hidden on #upload-image then add .hidden to .create-container" |
| 13 | > | 13 | > | ||
| 14 | 14 | ||||
| 15 | <span class="iconf text-xl align-middle"> </span> | 15 | <span class="iconf text-xl align-middle"> </span> | ||
| 16 | Upload | 16 | Upload | ||
| 17 | </button> | 17 | </button> | ||
| 18 | </div> | 18 | </div> | ||
| 19 | + | <div id="upload-image"></div> | |||
| 19 | <table class="p-4 border-t-2"> | 20 | <table class="p-4 border-t-2"> | ||
| 20 | <tbody> | 21 | <tbody> | ||
| 21 | {{ | 22 | {{ | ||
| 22 | range.Images | 23 | range.Images | ||
| 23 | }} | 24 | }} | ||
| 24 | - | <tr id="img-{{.ID}}" hx-ext="response-targets"> | 25 | + | <tr id="img-{{.ID}}" hx-ext="response-targets" class=" hover:bg-slate-100"> |
| 25 | <td> | 26 | <td> | ||
| 26 | <span title="{{.ImageSummary.ID}}">{{.Name}}</span> | 27 | <span title="{{.ImageSummary.ID}}">{{.Name}}</span> | ||
| 27 | </td> | 28 | </td> | ||
| @@ -34,11 +35,11 @@ | |||||
| 34 | <button | 35 | <button | ||
| 35 | id="add-button-img-{{.ID}}" | 36 | id="add-button-img-{{.ID}}" | ||
| 36 | hx-get="/htmx/create-container/{{.ID}}" | 37 | hx-get="/htmx/create-container/{{.ID}}" | ||
| 37 | - | hx-target="#create-container" | 38 | + | hx-target="#create-container-{{.ID}}" |
| 38 | hx-target-*="#error-message-div-{{.ID}}" | 39 | hx-target-*="#error-message-div-{{.ID}}" | ||
| 39 | hx-swap="innerHTML" | 40 | hx-swap="innerHTML" | ||
| 40 | class="py-2 px-4" | 41 | class="py-2 px-4" | ||
| 41 | - | _="on click toggle between .visible and .hidden on #create-container then add .hidden to #upload-image" | 42 | + | _="on click toggle between .visible and .hidden on #create-container-{{.ID}} then add .hidden to #upload-image" |
| 42 | > | 43 | > | ||
| 43 | <span title="Deploy a container using this image" class="iconf text-xl text-blue-600 hover:text-gordonmascot-hblue"></span> | 44 | <span title="Deploy a container using this image" class="iconf text-xl text-blue-600 hover:text-gordonmascot-hblue"></span> | ||
| 44 | </button> | 45 | </button> | ||
| @@ -59,6 +60,7 @@ | |||||
| 59 | <t> | 60 | <t> | ||
| 60 | <td colspan="4"> | 61 | <td colspan="4"> | ||
| 61 | <div id="error-message-div-{{.ID}}" class="text-red-500"></div> | 62 | <div id="error-message-div-{{.ID}}" class="text-red-500"></div> | ||
| 63 | + | <div class="create-container" id="create-container-{{.ID}}"></div> | |||
| 62 | </td> | 64 | </td> | ||
| 63 | </tr> | 65 | </tr> | ||
| 64 | {{ else }} | 66 | {{ else }} | ||
| @@ -70,6 +72,4 @@ | |||||
| 70 | }} | 72 | }} | ||
| 71 | </tbody> | 73 | </tbody> | ||
| 72 | </table> | 74 | </table> | ||
| 73 | - | <div id="upload-image"></div> | |||
| 74 | - | <div id="create-container"></div> | |||
| 75 | </div> | 75 | </div> | ||
M internal/templating/models/html/fragments/menu.gohtml +23 -20
| @@ -1,34 +1,38 @@ | |||
| 1 | 1 | <script> | |
| 2 | - | function checkVersion(versionData) { | |
| 3 | - | var currentVersion = document | |
| 2 | + | const updateStrTemplate = | |
| 3 | + | "New version %VERSION% available, consider pulling the latest image"; | |
| 4 | + | ||
| 5 | + | const checkVersion = (versionData) => { | |
| 6 | + | const currentVersion = document | |
| 4 | 7 | .getElementById("actual-version") | |
| 5 | 8 | .textContent.trim(); | |
| 6 | - | // Extract the numeric part of the version string using a regular expression | |
| 7 | - | var fetchedVersionNumber = | |
| 8 | - | versionData["amd64"]["name"].match(/\d+\.\d+\.\d+/)[0]; | |
| 9 | + | const fetchedVersionNumber = | |
| 10 | + | versionData?.amd64?.name.match(/\d+\.\d+\.\d+/)?.[0]; | |
| 9 | 11 | ||
| 10 | 12 | if (currentVersion !== fetchedVersionNumber) { | |
| 11 | - | console.log("New version available:", fetchedVersionNumber); | |
| 12 | - | // Show the update-available span by removing the 'hidden' attribute | |
| 13 | - | document.getElementById("update-available").removeAttribute("hidden"); | |
| 13 | + | const updateStr = updateStrTemplate.replace( | |
| 14 | + | "%VERSION%", | |
| 15 | + | fetchedVersionNumber | |
| 16 | + | ); | |
| 17 | + | console.log(updateStr); | |
| 18 | + | const updateElement = document.getElementById("update-available"); | |
| 19 | + | updateElement.title = updateStr; // Set the title attribute | |
| 20 | + | updateElement.removeAttribute("hidden"); | |
| 14 | 21 | } else { | |
| 15 | 22 | console.log( | |
| 16 | 23 | "No new version. Current version is up-to-date:", | |
| 17 | 24 | currentVersion | |
| 18 | 25 | ); | |
| 19 | 26 | } | |
| 20 | - | } | |
| 27 | + | }; | |
| 21 | 28 | ||
| 22 | - | function fetchVersionInfo() { | |
| 29 | + | const fetchVersionInfo = () => { | |
| 23 | 30 | fetch("https://gordon-proxy.bnema.dev/version") | |
| 24 | 31 | .then((response) => response.json()) | |
| 25 | - | .then((data) => { | |
| 26 | - | checkVersion(data); // Pass the parsed JSON data to checkVersion | |
| 27 | - | }) | |
| 32 | + | .then(checkVersion) | |
| 28 | 33 | .catch((error) => console.error("Error fetching version:", error)); | |
| 29 | - | } | |
| 34 | + | }; | |
| 30 | 35 | ||
| 31 | - | // Run fetchVersionInfo when the window loads | |
| 32 | 36 | window.onload = fetchVersionInfo; | |
| 33 | 37 | </script> | |
| 34 | 38 | <div | |
| @@ -41,16 +45,15 @@ | |||
| 41 | 45 | </h1> | |
| 42 | 46 | <span | |
| 43 | 47 | id="actual-version" | |
| 44 | - | class="text-gordonmascot-mblue text-xs md:text-sm"> | |
| 48 | + | class="text-gordonmascot-mblue text-xs md:text-sm pr-1 JetBrainsMonoNF"> | |
| 45 | 49 | {{ .BuildVersion }} | |
| 46 | 50 | </span> | |
| 47 | - | <!-- if .LatestVersion is > to BuildVersion show arrow up --> | |
| 48 | 51 | <span | |
| 49 | 52 | hidden | |
| 50 | 53 | id="update-available" | |
| 51 | - | class="text-teal-200 text-xs md:text-sm" | |
| 52 | - | title="A new version is available" | |
| 53 | - | ><a href="{{ .LatestVersionURL }}"></a> | |
| 54 | + | class="text-green-300 text-xs md:text-sm JetBrainsMonoNF" | |
| 55 | + | title="" | |
| 56 | + | ><a href="https://github.com/bnema/gordon/releases"></a> | |
| 54 | 57 | </span> | |
| 55 | 58 | </div> | |
| 56 | 59 | <div class="w-full"> | |
| @@ -1,34 +1,38 @@ | |||||
| 1 | <script> | 1 | <script> | ||
| 2 | - | function checkVersion(versionData) { | 2 | + | const updateStrTemplate = |
| 3 | - | var currentVersion = document | 3 | + | "New version %VERSION% available, consider pulling the latest image"; |
| 4 | + | ||||
| 5 | + | const checkVersion = (versionData) => { | |||
| 6 | + | const currentVersion = document | |||
| 4 | .getElementById("actual-version") | 7 | .getElementById("actual-version") | ||
| 5 | .textContent.trim(); | 8 | .textContent.trim(); | ||
| 6 | - | // Extract the numeric part of the version string using a regular expression | 9 | + | const fetchedVersionNumber = |
| 7 | - | var fetchedVersionNumber = | 10 | + | versionData?.amd64?.name.match(/\d+\.\d+\.\d+/)?.[0]; |
| 8 | - | versionData["amd64"]["name"].match(/\d+\.\d+\.\d+/)[0]; | |||
| 9 | 11 | ||||
| 10 | if (currentVersion !== fetchedVersionNumber) { | 12 | if (currentVersion !== fetchedVersionNumber) { | ||
| 11 | - | console.log("New version available:", fetchedVersionNumber); | 13 | + | const updateStr = updateStrTemplate.replace( |
| 12 | - | // Show the update-available span by removing the 'hidden' attribute | 14 | + | "%VERSION%", |
| 13 | - | document.getElementById("update-available").removeAttribute("hidden"); | 15 | + | fetchedVersionNumber |
| 16 | + | ); | |||
| 17 | + | console.log(updateStr); | |||
| 18 | + | const updateElement = document.getElementById("update-available"); | |||
| 19 | + | updateElement.title = updateStr; // Set the title attribute | |||
| 20 | + | updateElement.removeAttribute("hidden"); | |||
| 14 | } else { | 21 | } else { | ||
| 15 | console.log( | 22 | console.log( | ||
| 16 | "No new version. Current version is up-to-date:", | 23 | "No new version. Current version is up-to-date:", | ||
| 17 | currentVersion | 24 | currentVersion | ||
| 18 | ); | 25 | ); | ||
| 19 | } | 26 | } | ||
| 20 | - | } | 27 | + | }; |
| 21 | 28 | ||||
| 22 | - | function fetchVersionInfo() { | 29 | + | const fetchVersionInfo = () => { |
| 23 | fetch("https://gordon-proxy.bnema.dev/version") | 30 | fetch("https://gordon-proxy.bnema.dev/version") | ||
| 24 | .then((response) => response.json()) | 31 | .then((response) => response.json()) | ||
| 25 | - | .then((data) => { | 32 | + | .then(checkVersion) |
| 26 | - | checkVersion(data); // Pass the parsed JSON data to checkVersion | |||
| 27 | - | }) | |||
| 28 | .catch((error) => console.error("Error fetching version:", error)); | 33 | .catch((error) => console.error("Error fetching version:", error)); | ||
| 29 | - | } | 34 | + | }; |
| 30 | 35 | ||||
| 31 | - | // Run fetchVersionInfo when the window loads | |||
| 32 | window.onload = fetchVersionInfo; | 36 | window.onload = fetchVersionInfo; | ||
| 33 | </script> | 37 | </script> | ||
| 34 | <div | 38 | <div | ||
| @@ -41,16 +45,15 @@ | |||||
| 41 | </h1> | 45 | </h1> | ||
| 42 | <span | 46 | <span | ||
| 43 | id="actual-version" | 47 | id="actual-version" | ||
| 44 | - | class="text-gordonmascot-mblue text-xs md:text-sm"> | 48 | + | class="text-gordonmascot-mblue text-xs md:text-sm pr-1 JetBrainsMonoNF"> |
| 45 | {{ .BuildVersion }} | 49 | {{ .BuildVersion }} | ||
| 46 | </span> | 50 | </span> | ||
| 47 | - | <!-- if .LatestVersion is > to BuildVersion show arrow up --> | |||
| 48 | <span | 51 | <span | ||
| 49 | hidden | 52 | hidden | ||
| 50 | id="update-available" | 53 | id="update-available" | ||
| 51 | - | class="text-teal-200 text-xs md:text-sm" | 54 | + | class="text-green-300 text-xs md:text-sm JetBrainsMonoNF" |
| 52 | - | title="A new version is available" | 55 | + | title="" |
| 53 | - | ><a href="{{ .LatestVersionURL }}"></a> | 56 | + | ><a href="https://github.com/bnema/gordon/releases"></a> |
| 54 | </span> | 57 | </span> | ||
| 55 | </div> | 58 | </div> | ||
| 56 | <div class="w-full"> | 59 | <div class="w-full"> | ||
M internal/webui/public/assets/css/custom.css +18 -0
| @@ -80,6 +80,24 @@ | |||
| 80 | 80 | } | |
| 81 | 81 | } | |
| 82 | 82 | ||
| 83 | + | @keyframes wiggle { | |
| 84 | + | ||
| 85 | + | 0%, | |
| 86 | + | 100% { | |
| 87 | + | transform: rotate(-3deg); | |
| 88 | + | } | |
| 89 | + | ||
| 90 | + | 50% { | |
| 91 | + | transform: rotate(3deg); | |
| 92 | + | } | |
| 93 | + | } | |
| 94 | + | ||
| 95 | + | #update-available { | |
| 96 | + | display: inline-block; | |
| 97 | + | /* Make sure the span is transformable */ | |
| 98 | + | animation: wiggle 1s ease-in-out infinite; | |
| 99 | + | } | |
| 100 | + | ||
| 83 | 101 | .slide-it { | |
| 84 | 102 | view-transition-name: slide-it; | |
| 85 | 103 | } | |
| @@ -80,6 +80,24 @@ | |||||
| 80 | } | 80 | } | ||
| 81 | } | 81 | } | ||
| 82 | 82 | ||||
| 83 | + | @keyframes wiggle { | |||
| 84 | + | ||||
| 85 | + | 0%, | |||
| 86 | + | 100% { | |||
| 87 | + | transform: rotate(-3deg); | |||
| 88 | + | } | |||
| 89 | + | ||||
| 90 | + | 50% { | |||
| 91 | + | transform: rotate(3deg); | |||
| 92 | + | } | |||
| 93 | + | } | |||
| 94 | + | ||||
| 95 | + | #update-available { | |||
| 96 | + | display: inline-block; | |||
| 97 | + | /* Make sure the span is transformable */ | |||
| 98 | + | animation: wiggle 1s ease-in-out infinite; | |||
| 99 | + | } | |||
| 100 | + | ||||
| 83 | .slide-it { | 101 | .slide-it { | ||
| 84 | view-transition-name: slide-it; | 102 | view-transition-name: slide-it; | ||
| 85 | } | 103 | } | ||
M internal/webui/public/assets/css/tailwind.css +34 -28
| @@ -608,10 +608,6 @@ video { | |||
| 608 | 608 | margin-left: 0.5rem; | |
| 609 | 609 | } | |
| 610 | 610 | ||
| 611 | - | .mr-2 { | |
| 612 | - | margin-right: 0.5rem; | |
| 613 | - | } | |
| 614 | - | ||
| 615 | 611 | .mt-2 { | |
| 616 | 612 | margin-top: 0.5rem; | |
| 617 | 613 | } | |
| @@ -772,6 +768,10 @@ video { | |||
| 772 | 768 | border-radius: 0.125rem; | |
| 773 | 769 | } | |
| 774 | 770 | ||
| 771 | + | .rounded-lg { | |
| 772 | + | border-radius: 0.5rem; | |
| 773 | + | } | |
| 774 | + | ||
| 775 | 775 | .border { | |
| 776 | 776 | border-width: 1px; | |
| 777 | 777 | } | |
| @@ -982,14 +982,14 @@ video { | |||
| 982 | 982 | color: rgb(107 114 128 / var(--tw-text-opacity)); | |
| 983 | 983 | } | |
| 984 | 984 | ||
| 985 | - | .text-gray-600 { | |
| 985 | + | .text-gray-800 { | |
| 986 | 986 | --tw-text-opacity: 1; | |
| 987 | - | color: rgb(75 85 99 / var(--tw-text-opacity)); | |
| 987 | + | color: rgb(31 41 55 / var(--tw-text-opacity)); | |
| 988 | 988 | } | |
| 989 | 989 | ||
| 990 | - | .text-gray-800 { | |
| 990 | + | .text-green-300 { | |
| 991 | 991 | --tw-text-opacity: 1; | |
| 992 | - | color: rgb(31 41 55 / var(--tw-text-opacity)); | |
| 992 | + | color: rgb(134 239 172 / var(--tw-text-opacity)); | |
| 993 | 993 | } | |
| 994 | 994 | ||
| 995 | 995 | .text-red-500 { | |
| @@ -1007,31 +1007,11 @@ video { | |||
| 1007 | 1007 | color: rgb(241 245 249 / var(--tw-text-opacity)); | |
| 1008 | 1008 | } | |
| 1009 | 1009 | ||
| 1010 | - | .text-teal-600 { | |
| 1011 | - | --tw-text-opacity: 1; | |
| 1012 | - | color: rgb(13 148 136 / var(--tw-text-opacity)); | |
| 1013 | - | } | |
| 1014 | - | ||
| 1015 | 1010 | .text-white { | |
| 1016 | 1011 | --tw-text-opacity: 1; | |
| 1017 | 1012 | color: rgb(255 255 255 / var(--tw-text-opacity)); | |
| 1018 | 1013 | } | |
| 1019 | 1014 | ||
| 1020 | - | .text-teal-300 { | |
| 1021 | - | --tw-text-opacity: 1; | |
| 1022 | - | color: rgb(94 234 212 / var(--tw-text-opacity)); | |
| 1023 | - | } | |
| 1024 | - | ||
| 1025 | - | .text-teal-400 { | |
| 1026 | - | --tw-text-opacity: 1; | |
| 1027 | - | color: rgb(45 212 191 / var(--tw-text-opacity)); | |
| 1028 | - | } | |
| 1029 | - | ||
| 1030 | - | .text-teal-200 { | |
| 1031 | - | --tw-text-opacity: 1; | |
| 1032 | - | color: rgb(153 246 228 / var(--tw-text-opacity)); | |
| 1033 | - | } | |
| 1034 | - | ||
| 1035 | 1015 | .shadow { | |
| 1036 | 1016 | --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); | |
| 1037 | 1017 | --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); | |
| @@ -1140,6 +1120,23 @@ video { | |||
| 1140 | 1120 | } | |
| 1141 | 1121 | } | |
| 1142 | 1122 | ||
| 1123 | + | @keyframes wiggle { | |
| 1124 | + | 0%, | |
| 1125 | + | 100% { | |
| 1126 | + | transform: rotate(-3deg); | |
| 1127 | + | } | |
| 1128 | + | ||
| 1129 | + | 50% { | |
| 1130 | + | transform: rotate(3deg); | |
| 1131 | + | } | |
| 1132 | + | } | |
| 1133 | + | ||
| 1134 | + | #update-available { | |
| 1135 | + | display: inline-block; | |
| 1136 | + | /* Make sure the span is transformable */ | |
| 1137 | + | animation: wiggle 1s ease-in-out infinite; | |
| 1138 | + | } | |
| 1139 | + | ||
| 1143 | 1140 | .slide-it { | |
| 1144 | 1141 | view-transition-name: slide-it; | |
| 1145 | 1142 | } | |
| @@ -1172,6 +1169,10 @@ video { | |||
| 1172 | 1169 | font-family: "JetBrainsMono-Nerd-Font-Bold", sans-serif; | |
| 1173 | 1170 | } | |
| 1174 | 1171 | ||
| 1172 | + | .hover\:rounded-lg:hover { | |
| 1173 | + | border-radius: 0.5rem; | |
| 1174 | + | } | |
| 1175 | + | ||
| 1175 | 1176 | .hover\:bg-blue-700:hover { | |
| 1176 | 1177 | --tw-bg-opacity: 1; | |
| 1177 | 1178 | background-color: rgb(29 78 216 / var(--tw-bg-opacity)); | |
| @@ -1182,6 +1183,11 @@ video { | |||
| 1182 | 1183 | background-color: rgb(0 82 147 / var(--tw-bg-opacity)); | |
| 1183 | 1184 | } | |
| 1184 | 1185 | ||
| 1186 | + | .hover\:bg-slate-100:hover { | |
| 1187 | + | --tw-bg-opacity: 1; | |
| 1188 | + | background-color: rgb(241 245 249 / var(--tw-bg-opacity)); | |
| 1189 | + | } | |
| 1190 | + | ||
| 1185 | 1191 | .hover\:text-gordonmascot-hblue:hover { | |
| 1186 | 1192 | --tw-text-opacity: 1; | |
| 1187 | 1193 | color: rgb(1 30 92 / var(--tw-text-opacity)); | |
| @@ -608,10 +608,6 @@ video { | |||||
| 608 | margin-left: 0.5rem; | 608 | margin-left: 0.5rem; | ||
| 609 | } | 609 | } | ||
| 610 | 610 | ||||
| 611 | - | .mr-2 { | |||
| 612 | - | margin-right: 0.5rem; | |||
| 613 | - | } | |||
| 614 | - | ||||
| 615 | .mt-2 { | 611 | .mt-2 { | ||
| 616 | margin-top: 0.5rem; | 612 | margin-top: 0.5rem; | ||
| 617 | } | 613 | } | ||
| @@ -772,6 +768,10 @@ video { | |||||
| 772 | border-radius: 0.125rem; | 768 | border-radius: 0.125rem; | ||
| 773 | } | 769 | } | ||
| 774 | 770 | ||||
| 771 | + | .rounded-lg { | |||
| 772 | + | border-radius: 0.5rem; | |||
| 773 | + | } | |||
| 774 | + | ||||
| 775 | .border { | 775 | .border { | ||
| 776 | border-width: 1px; | 776 | border-width: 1px; | ||
| 777 | } | 777 | } | ||
| @@ -982,14 +982,14 @@ video { | |||||
| 982 | color: rgb(107 114 128 / var(--tw-text-opacity)); | 982 | color: rgb(107 114 128 / var(--tw-text-opacity)); | ||
| 983 | } | 983 | } | ||
| 984 | 984 | ||||
| 985 | - | .text-gray-600 { | 985 | + | .text-gray-800 { |
| 986 | --tw-text-opacity: 1; | 986 | --tw-text-opacity: 1; | ||
| 987 | - | color: rgb(75 85 99 / var(--tw-text-opacity)); | 987 | + | color: rgb(31 41 55 / var(--tw-text-opacity)); |
| 988 | } | 988 | } | ||
| 989 | 989 | ||||
| 990 | - | .text-gray-800 { | 990 | + | .text-green-300 { |
| 991 | --tw-text-opacity: 1; | 991 | --tw-text-opacity: 1; | ||
| 992 | - | color: rgb(31 41 55 / var(--tw-text-opacity)); | 992 | + | color: rgb(134 239 172 / var(--tw-text-opacity)); |
| 993 | } | 993 | } | ||
| 994 | 994 | ||||
| 995 | .text-red-500 { | 995 | .text-red-500 { | ||
| @@ -1007,31 +1007,11 @@ video { | |||||
| 1007 | color: rgb(241 245 249 / var(--tw-text-opacity)); | 1007 | color: rgb(241 245 249 / var(--tw-text-opacity)); | ||
| 1008 | } | 1008 | } | ||
| 1009 | 1009 | ||||
| 1010 | - | .text-teal-600 { | |||
| 1011 | - | --tw-text-opacity: 1; | |||
| 1012 | - | color: rgb(13 148 136 / var(--tw-text-opacity)); | |||
| 1013 | - | } | |||
| 1014 | - | ||||
| 1015 | .text-white { | 1010 | .text-white { | ||
| 1016 | --tw-text-opacity: 1; | 1011 | --tw-text-opacity: 1; | ||
| 1017 | color: rgb(255 255 255 / var(--tw-text-opacity)); | 1012 | color: rgb(255 255 255 / var(--tw-text-opacity)); | ||
| 1018 | } | 1013 | } | ||
| 1019 | 1014 | ||||
| 1020 | - | .text-teal-300 { | |||
| 1021 | - | --tw-text-opacity: 1; | |||
| 1022 | - | color: rgb(94 234 212 / var(--tw-text-opacity)); | |||
| 1023 | - | } | |||
| 1024 | - | ||||
| 1025 | - | .text-teal-400 { | |||
| 1026 | - | --tw-text-opacity: 1; | |||
| 1027 | - | color: rgb(45 212 191 / var(--tw-text-opacity)); | |||
| 1028 | - | } | |||
| 1029 | - | ||||
| 1030 | - | .text-teal-200 { | |||
| 1031 | - | --tw-text-opacity: 1; | |||
| 1032 | - | color: rgb(153 246 228 / var(--tw-text-opacity)); | |||
| 1033 | - | } | |||
| 1034 | - | ||||
| 1035 | .shadow { | 1015 | .shadow { | ||
| 1036 | --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); | 1016 | --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); | ||
| 1037 | --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); | 1017 | --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color); | ||
| @@ -1140,6 +1120,23 @@ video { | |||||
| 1140 | } | 1120 | } | ||
| 1141 | } | 1121 | } | ||
| 1142 | 1122 | ||||
| 1123 | + | @keyframes wiggle { | |||
| 1124 | + | 0%, | |||
| 1125 | + | 100% { | |||
| 1126 | + | transform: rotate(-3deg); | |||
| 1127 | + | } | |||
| 1128 | + | ||||
| 1129 | + | 50% { | |||
| 1130 | + | transform: rotate(3deg); | |||
| 1131 | + | } | |||
| 1132 | + | } | |||
| 1133 | + | ||||
| 1134 | + | #update-available { | |||
| 1135 | + | display: inline-block; | |||
| 1136 | + | /* Make sure the span is transformable */ | |||
| 1137 | + | animation: wiggle 1s ease-in-out infinite; | |||
| 1138 | + | } | |||
| 1139 | + | ||||
| 1143 | .slide-it { | 1140 | .slide-it { | ||
| 1144 | view-transition-name: slide-it; | 1141 | view-transition-name: slide-it; | ||
| 1145 | } | 1142 | } | ||
| @@ -1172,6 +1169,10 @@ video { | |||||
| 1172 | font-family: "JetBrainsMono-Nerd-Font-Bold", sans-serif; | 1169 | font-family: "JetBrainsMono-Nerd-Font-Bold", sans-serif; | ||
| 1173 | } | 1170 | } | ||
| 1174 | 1171 | ||||
| 1172 | + | .hover\:rounded-lg:hover { | |||
| 1173 | + | border-radius: 0.5rem; | |||
| 1174 | + | } | |||
| 1175 | + | ||||
| 1175 | .hover\:bg-blue-700:hover { | 1176 | .hover\:bg-blue-700:hover { | ||
| 1176 | --tw-bg-opacity: 1; | 1177 | --tw-bg-opacity: 1; | ||
| 1177 | background-color: rgb(29 78 216 / var(--tw-bg-opacity)); | 1178 | background-color: rgb(29 78 216 / var(--tw-bg-opacity)); | ||
| @@ -1182,6 +1183,11 @@ video { | |||||
| 1182 | background-color: rgb(0 82 147 / var(--tw-bg-opacity)); | 1183 | background-color: rgb(0 82 147 / var(--tw-bg-opacity)); | ||
| 1183 | } | 1184 | } | ||
| 1184 | 1185 | ||||
| 1186 | + | .hover\:bg-slate-100:hover { | |||
| 1187 | + | --tw-bg-opacity: 1; | |||
| 1188 | + | background-color: rgb(241 245 249 / var(--tw-bg-opacity)); | |||
| 1189 | + | } | |||
| 1190 | + | ||||
| 1185 | .hover\:text-gordonmascot-hblue:hover { | 1191 | .hover\:text-gordonmascot-hblue:hover { | ||
| 1186 | --tw-text-opacity: 1; | 1192 | --tw-text-opacity: 1; | ||
| 1187 | color: rgb(1 30 92 / var(--tw-text-opacity)); | 1193 | color: rgb(1 30 92 / var(--tw-text-opacity)); | ||