You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
277 lines
20 KiB
277 lines
20 KiB
<main class="bg-white dark:bg-gray-600 grow">
|
|
<section class="bg-gray-300 dark:bg-gray-600 p-3 sm:p-5 antialiased">
|
|
<div class="mx-auto max-w-screen-2xl px-4 lg:px-12 pb-6">
|
|
<div class="bg-white dark:bg-gray-800 relative shadow-md sm:rounded-lg overflow-hidden"
|
|
x-data="{ 'show': false }">
|
|
<!-- header -->
|
|
<div class="flex justify-between items-center mb-4 rounded-t border-b sm:mb-5 dark:border-gray-600 mx-4 py-4 border-b">
|
|
<button type="button"
|
|
x-on:click="show=!show"
|
|
class="flex items-center p-2 w-full text-base font-normal text-gray-900 rounded-lg transition duration-75 group hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700">
|
|
<span class="text-lg font-semibold text-gray-900 dark:text-white pr-2">About</span>
|
|
<svg x-show="show" class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 8"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 5.326 5.7a.909.909 0 0 0 1.348 0L13 1"></path></svg>
|
|
<svg x-show="!show" class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 8"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7 7.674 1.3a.91.91 0 0 0-1.348 0L1 7"></path></svg>
|
|
</button>
|
|
</div>
|
|
<!-- form -->
|
|
<form class="p-4" x-show="show">
|
|
<div class="grid gap-4 mb-4 sm:grid-cols-1">
|
|
<div hx-target="this" hx-swap="outerHTML">
|
|
<label for="content" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Content</label>
|
|
<textarea id="about-content" name="content" hx-post="/htmx/validation/about/" rows="4" class="block p-2.5 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-[#3b82f6] focus:border-[#3b82f6] dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-[#3b82f6] dark:focus:border-[#3b82f6]" placeholder="Write about you and your blog here">{{ about-content }}</textarea>
|
|
</div>
|
|
</div>
|
|
<button type="submit" hx-patch="/htmx/about/" hx-swap="none" class="text-white inline-flex items-center bg-[#1d4ed8] hover:bg-[#1e40af] focus:ring-4 focus:outline-none focus:ring-[#93c5fd] font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-[#2563eb] dark:hover:bg-[#1d4ed8] dark:focus:ring-[#1e40af]">
|
|
Update
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mx-auto max-w-screen-2xl px-4 lg:px-12">
|
|
<div class="bg-white dark:bg-gray-800 relative shadow-md sm:rounded-lg overflow-hidden">
|
|
<div class="flex flex-col md:flex-row md:items-center md:justify-between space-y-3 md:space-y-0 md:space-x-4 p-4">
|
|
<div class="flex-1 flex items-center space-x-2">
|
|
<h5>
|
|
<span class="text-gray-500">All articles:</span>
|
|
<span class="dark:text-white"
|
|
x-data="{ count: document.getElementById('articles').children.length }"
|
|
x-on:htmx:load.window="count = document.getElementById('articles').children.length"
|
|
@delete_row.window="count = document.getElementById('articles').children.length"
|
|
x-text="count">
|
|
</span>
|
|
</h5>
|
|
</div>
|
|
</div>
|
|
<div class="flex flex-col md:flex-row items-stretch md:items-center md:space-x-3 space-y-3 md:space-y-0 justify-between mx-4 py-4 border-t dark:border-gray-700">
|
|
<div class="w-full md:w-1/2"
|
|
x-data="{
|
|
search: new URLSearchParams(location.search).get('search')
|
|
}"
|
|
x-init="$watch('search', (value) => {
|
|
const url = new URL(window.location.href);
|
|
url.searchParams.set('search', value);
|
|
history.replaceState(null, null, '?'+url.searchParams.toString());
|
|
})"
|
|
>
|
|
<div class="flex items-center">
|
|
<div class="relative w-full">
|
|
<div class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none">
|
|
<svg aria-hidden="true" class="w-5 h-5 text-gray-500 dark:text-gray-400" fill="currentColor" viewbox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" />
|
|
</svg>
|
|
</div>
|
|
<input type="text"
|
|
id="search"
|
|
name="search"
|
|
x-model="search"
|
|
hx-post="/htmx/admin/search/articles/"
|
|
hx-trigger="input changed delay:500ms, search"
|
|
hx-target="#articles"
|
|
hx-swap="outerHTML"
|
|
placeholder="Search articles by title or content"
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-[#3b82f6] focus:border-[#3b82f6] block w-full pl-10 p-2 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-[#3b82f6] dark:focus:border-[#3b82f6]">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="w-full md:w-auto flex flex-col md:flex-row space-y-2 md:space-y-0 items-stretch md:items-center justify-end md:space-x-3 flex-shrink-0">
|
|
<button type="button"
|
|
id="createArticleButton"
|
|
data-modal-target="createArticleModal"
|
|
data-modal-toggle="createArticleModal"
|
|
class="flex items-center justify-center text-white bg-[#1d4ed8] hover:bg-[#1e40af] focus:ring-4 focus:ring-[#93c5fd] font-medium rounded-lg text-sm px-4 py-2 dark:bg-[#2563eb] dark:hover:bg-[#1d4ed8] focus:outline-none dark:focus:ring-[#1e40af]">
|
|
<svg class="h-3.5 w-3.5 mr-1.5 -ml-1" fill="currentColor" viewbox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
|
|
<path clip-rule="evenodd" fill-rule="evenodd" d="M10 3a1 1 0 011 1v5h5a1 1 0 110 2h-5v5a1 1 0 11-2 0v-5H4a1 1 0 110-2h5V4a1 1 0 011-1z" />
|
|
</svg>
|
|
Add article
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="overflow-x-auto">
|
|
<table class="w-full text-sm text-left text-gray-500 dark:text-gray-400">
|
|
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
|
<tr>
|
|
<th scope="col" class="p-4">Title</th>
|
|
<th scope="col" class="p-4">Brief content</th>
|
|
<th scope="col" class="p-4">Created/Update</th>
|
|
<th scope="col" class="p-4"></th>
|
|
</tr>
|
|
</thead>
|
|
{{> article_rows articles=articles }}
|
|
</table>
|
|
</div>
|
|
<nav class="flex flex-col md:flex-row justify-between items-start md:items-center space-y-3 md:space-y-0 p-4" aria-label="Table navigation">
|
|
<span class="text-sm font-normal text-gray-500 dark:text-gray-400">
|
|
Showing
|
|
<span class="font-semibold text-gray-900 dark:text-white"
|
|
x-data="{ count: document.getElementById('articles').children.length }"
|
|
x-on:htmx:load.window="count = document.getElementById('articles').children.length"
|
|
@delete_row.window="count = document.getElementById('articles').children.length"
|
|
x-text="count">
|
|
</span>
|
|
</span>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Create modal -->
|
|
<div id="createArticleModal" tabindex="-1" aria-hidden="true" class="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
|
<div class="relative p-4 w-full max-w-2xl max-h-full">
|
|
<!-- Modal content -->
|
|
<div class="relative p-4 bg-white rounded-lg shadow dark:bg-gray-800 sm:p-5">
|
|
<!-- Modal header -->
|
|
<div class="flex justify-between items-center pb-4 mb-4 rounded-t border-b sm:mb-5 dark:border-gray-600">
|
|
<h3 class="text-lg font-semibold text-gray-900 dark:text-white">Add Article</h3>
|
|
<button type="button" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white" data-modal-target="createArticleModal" data-modal-toggle="createArticleModal">
|
|
<svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewbox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
|
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
|
|
</svg>
|
|
<span class="sr-only">Close modal</span>
|
|
</button>
|
|
</div>
|
|
<!-- Modal body -->
|
|
<form x-data="{ disabled: true }">
|
|
<div class="grid gap-4 mb-4 sm:grid-cols-1">
|
|
<div hx-target="this" hx-swap="outerHTML">
|
|
<label for="title" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Title</label>
|
|
<input type="text" name="title" id="title" hx-post="/htmx/validation/create-title/" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-[#2563eb] focus:border-[#2563eb] block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-[#3b82f6] dark:focus:border-[#3b82f6]" placeholder="Type article title" required="" aria-invalid="true">
|
|
</div>
|
|
<div hx-target="this" hx-swap="outerHTML">
|
|
<label for="content" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Content</label>
|
|
<textarea id="content" name="content" hx-post="/htmx/validation/create-content/" rows="4" class="block p-2.5 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-[#3b82f6] focus:border-[#3b82f6] dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-[#3b82f6] dark:focus:border-[#3b82f6]" placeholder="Write article here" aria-invalid="true"></textarea>
|
|
</div>
|
|
</div>
|
|
<button type="submit"
|
|
x-on:htmx:load.window="if(htmx.find('#title').parentElement.innerHTML.toString().match(new RegExp('aria-invalid'))){disabled=true;}else{disabled=false;}"
|
|
x-bind:disabled="disabled"
|
|
hx-post="/htmx/articles/"
|
|
hx-target="#articles"
|
|
hx-swap="afterbegin"
|
|
data-modal-target="createArticleModal"
|
|
data-modal-toggle="createArticleModal"
|
|
class="text-white inline-flex items-center bg-[#1d4ed8] hover:bg-[#1e40af] focus:ring-4 focus:outline-none focus:ring-[#93c5fd] font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-[#2563eb] dark:hover:bg-[#1d4ed8] dark:focus:ring-[#1e40af]">
|
|
<svg class="mr-1 -ml-1 w-6 h-6" fill="currentColor" viewbox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
|
<path fill-rule="evenodd" d="M10 5a1 1 0 011 1v3h3a1 1 0 110 2h-3v3a1 1 0 11-2 0v-3H6a1 1 0 110-2h3V6a1 1 0 011-1z" clip-rule="evenodd" />
|
|
</svg>
|
|
Add new article
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Update modal -->
|
|
<form id="drawer-update-article"
|
|
x-data="{ id: null, title: '', created: '', disabled: false }"
|
|
@update_article.window="id = $event.detail.id; title = $event.detail.title; created = $event.detail.created;"
|
|
x-init="$watch('id', () => htmx.process(htmx.find('#confirmUpdateButton')))"
|
|
tabindex="-1"
|
|
aria-labelledby="drawer-update-article-label"
|
|
aria-hidden="true"
|
|
class="fixed top-0 left-0 z-40 w-full h-screen max-w-3xl p-4 overflow-y-auto transition-transform -translate-x-full bg-white dark:bg-gray-800">
|
|
<h5 id="drawer-label" class="inline-flex items-center mb-6 text-sm font-semibold text-gray-500 uppercase dark:text-gray-400">Update Article</h5>
|
|
<button type="button" data-drawer-dismiss="drawer-update-article" aria-controls="drawer-update-article" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 absolute top-2.5 right-2.5 inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white">
|
|
<svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewbox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
|
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
|
|
</svg>
|
|
<span class="sr-only">Close menu</span>
|
|
</button>
|
|
<div class="grid gap-4 sm:grid-cols-1 sm:gap-6 ">
|
|
<div class="space-y-4 sm:col-span-1 sm:space-y-6">
|
|
<div hx-target="this" hx-swap="outerHTML">
|
|
<label for="title" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Title</label>
|
|
<input type="text"
|
|
name="title"
|
|
hx-post="/htmx/validation/update-title/"
|
|
x-model="title"
|
|
id="update-title"
|
|
placeholder="Type article title"
|
|
required=""
|
|
class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-[#2563eb] focus:border-[#2563eb] block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-[#3b82f6] dark:focus:border-[#3b82f6]">
|
|
</div>
|
|
<div hx-target="this" hx-swap="outerHTML">
|
|
<label for="content" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Content</label>
|
|
<textarea id="update-drawer-content" name="content" hx-post="/htmx/validation/update-content/" rows="4" class="block p-2.5 w-full text-sm text-gray-900 bg-gray-50 rounded-lg border border-gray-300 focus:ring-[#3b82f6] focus:border-[#3b82f6] dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-[#3b82f6] dark:focus:border-[#3b82f6]" placeholder="Write article here"></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-2 gap-4 mt-6 sm:w-1/2">
|
|
<button id="confirmUpdateButton"
|
|
x-on:htmx:load.window="if(htmx.find('#update-title').parentElement.innerHTML.toString().match(new RegExp('aria-invalid'))){disabled=true;}else{disabled=false;}"
|
|
x-bind:disabled="disabled"
|
|
data-drawer-toggle="drawer-update-article"
|
|
aria-controls="drawer-update-article"
|
|
type="submit"
|
|
hx-swap="outerHTML"
|
|
x-bind:hx-patch="`/htmx/admin/rows/article/${id}`"
|
|
x-bind:hx-target="`#row-${id}`"
|
|
class="text-white bg-[#1d4ed8] hover:bg-[#1e40af] focus:ring-4 focus:outline-none focus:ring-[#93c5fd] font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-[#2563eb] dark:hover:bg-[#1d4ed8] dark:focus:ring-[#1e40af]">Update</button>
|
|
<button type="button"
|
|
data-drawer-dismiss="drawer-update-article"
|
|
aria-controls="drawer-update-article"
|
|
class="text-sm font-medium text-center text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-[#1d4ed8] focus:z-10 focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700">
|
|
Cancel
|
|
</button>
|
|
</div>
|
|
</form>
|
|
|
|
<!-- Preview Drawer -->
|
|
<div id="drawer-read-article-advanced"
|
|
x-data="{ title: '', created: '' }"
|
|
@preview_article.window="title = $event.detail.title; created = $event.detail.created;"
|
|
class="overflow-y-auto fixed top-0 left-0 z-40 p-4 w-full max-w-3xl h-screen bg-white transition-transform -translate-x-full dark:bg-gray-800" tabindex="-1" aria-labelledby="drawer-label" aria-hidden="true">
|
|
<div>
|
|
<h4 id="read-drawer-label" class="mb-1.5 leading-none text-xl font-semibold text-gray-900 dark:text-white" x-text="title"></h4>
|
|
</div>
|
|
<button type="button" data-drawer-dismiss="drawer-read-article-advanced" aria-controls="drawer-read-article-advanced" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 absolute top-2.5 right-2.5 inline-flex items-center dark:hover:bg-gray-600 dark:hover:text-white">
|
|
<svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewbox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
|
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
|
|
</svg>
|
|
<span class="sr-only">Close menu</span>
|
|
</button>
|
|
<h2 class="mb-2 font-semibold leading-none text-gray-900 dark:text-white" x-text="created"></h2>
|
|
<p id="read-drawer-content" class="mb-4 font-light text-gray-500 sm:mb-5 dark:text-gray-400">
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Delete Modal -->
|
|
<div id="delete-modal"
|
|
x-data="{ deleteId: null }"
|
|
@show_delete_modal.window="deleteId = $event.detail.id;"
|
|
tabindex="-1"
|
|
class="fixed top-0 left-0 right-0 z-50 hidden p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
|
<div class="relative w-full h-auto max-w-md max-h-full">
|
|
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
|
|
<button type="button" class="absolute top-3 right-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center dark:hover:bg-gray-800 dark:hover:text-white" data-modal-toggle="delete-modal">
|
|
<svg aria-hidden="true" class="w-5 h-5" fill="currentColor" viewbox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
|
|
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
|
|
</svg>
|
|
<span class="sr-only">Close modal</span>
|
|
</button>
|
|
<div class="p-6 text-center">
|
|
<svg aria-hidden="true" class="mx-auto mb-4 text-gray-400 w-14 h-14 dark:text-gray-200" fill="none" stroke="currentColor" viewbox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
</svg>
|
|
<h3 class="mb-5 text-lg font-normal text-gray-500 dark:text-gray-400">Are you sure you want to delete this article?</h3>
|
|
<button id="confirmDeleteButton"
|
|
x-init="$watch('deleteId', () => htmx.process(htmx.find('#confirmDeleteButton')))"
|
|
@click="$dispatch('delete_row');"
|
|
hx-swap="outerHTML"
|
|
x-bind:hx-delete="`/htmx/admin/rows/article/${deleteId}`"
|
|
x-bind:hx-target="`#row-${deleteId}`"
|
|
data-modal-toggle="delete-modal"
|
|
type="button"
|
|
class="text-white bg-red-600 hover:bg-red-800 focus:ring-4 focus:outline-none focus:ring-red-300 dark:focus:ring-red-800 font-medium rounded-lg text-sm inline-flex items-center px-5 py-2.5 text-center mr-2">
|
|
Yes, I'm sure
|
|
</button>
|
|
<button data-modal-toggle="delete-modal" type="button" class="text-gray-500 bg-white hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-gray-200 rounded-lg border border-gray-200 text-sm font-medium px-5 py-2.5 hover:text-gray-900 focus:z-10 dark:bg-gray-700 dark:text-gray-300 dark:border-gray-500 dark:hover:text-white dark:hover:bg-gray-600 dark:focus:ring-gray-600">No, cancel</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
|