init
This commit is contained in:
200
resources/css/app.css
Normal file
200
resources/css/app.css
Normal file
@@ -0,0 +1,200 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
html,
|
||||
body {
|
||||
min-height: 100vh;
|
||||
min-width: 100%;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
input:read-only {
|
||||
cursor: not-allowed;
|
||||
background-color: #eee !important;
|
||||
color: #555 !important;
|
||||
}
|
||||
|
||||
button:disabled,
|
||||
a.disabled,
|
||||
input[type="submit"]:disabled {
|
||||
cursor: not-allowed;
|
||||
pointer-events: none;
|
||||
background-color: #ccc !important;
|
||||
color: #555 !important;
|
||||
}
|
||||
|
||||
button,
|
||||
input[type="submit"] {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
a {
|
||||
@apply transition hover:text-blue;
|
||||
}
|
||||
|
||||
input {
|
||||
&[type="text"],
|
||||
&[type="password"],
|
||||
&[type="email"] {
|
||||
@apply w-full rounded border border-gray-300 p-2;
|
||||
}
|
||||
}
|
||||
|
||||
div.table-layout {
|
||||
@apply overflow-y-scroll px-4;
|
||||
|
||||
table {
|
||||
@apply mx-auto;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
@apply border-separate border border-[#ccc] rounded-lg bg-[#ddd];
|
||||
|
||||
th,
|
||||
td {
|
||||
@apply text-sm px-4 whitespace-nowrap;
|
||||
}
|
||||
|
||||
th {
|
||||
@apply pt-4 pb-3 text-[#666] text-left font-normal border-b border-[#ccc];
|
||||
}
|
||||
|
||||
td {
|
||||
@apply py-4 bg-[#f8f8f8] text-[#333];
|
||||
}
|
||||
|
||||
tbody tr:not(:last-child) td {
|
||||
@apply border-b border-[#ddd];
|
||||
}
|
||||
|
||||
tbody tr:last-child {
|
||||
td:first-child {
|
||||
@apply rounded-bl-lg;
|
||||
}
|
||||
|
||||
td:last-child {
|
||||
@apply rounded-br-lg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.floating-label {
|
||||
@apply relative my-8;
|
||||
|
||||
label {
|
||||
@apply absolute text-gray-600 z-10 pointer-events-none top-2.5 left-2.5;
|
||||
transition: all 0.1s ease-in-out;
|
||||
}
|
||||
|
||||
input::placeholder {
|
||||
@apply opacity-0;
|
||||
}
|
||||
|
||||
input:read-only,
|
||||
input:focus,
|
||||
input:valid:not(:placeholder-shown) {
|
||||
& + label {
|
||||
@apply -top-4 left-1 text-xs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input:has(~ .error) {
|
||||
border: 2px solid #e00000;
|
||||
}
|
||||
|
||||
p.error {
|
||||
padding-left: 0.25rem;
|
||||
margin-top: 0.25rem;
|
||||
font-size: 0.75rem;
|
||||
color: #e00000;
|
||||
}
|
||||
|
||||
input[type="submit"],
|
||||
button,
|
||||
a.btn {
|
||||
user-select: none;
|
||||
@apply py-2 px-6 rounded transition inline-block border border-gray-300 hover:bg-gray-300;
|
||||
|
||||
&-blue {
|
||||
@apply btn border-none bg-blue text-white hover:bg-blue-dark;
|
||||
}
|
||||
|
||||
&-green {
|
||||
@apply btn border-none bg-green text-white hover:bg-green-dark;
|
||||
}
|
||||
|
||||
&-block {
|
||||
@apply !block text-center;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-block {
|
||||
@apply btn !block text-center;
|
||||
}
|
||||
|
||||
.btn-blue {
|
||||
@apply btn border-none bg-blue text-white hover:bg-blue-dark;
|
||||
}
|
||||
|
||||
.btn-block-blue {
|
||||
@apply btn-blue !block text-center;
|
||||
}
|
||||
|
||||
.btn-orange {
|
||||
@apply btn border-none bg-orange text-white hover:bg-orange-dark;
|
||||
}
|
||||
|
||||
.btn-block-orange {
|
||||
@apply btn-orange !block text-center;
|
||||
}
|
||||
|
||||
.btn-yellow {
|
||||
@apply btn border-none bg-yellow text-white hover:bg-yellow-dark;
|
||||
}
|
||||
|
||||
.btn-block-yellow {
|
||||
@apply btn-yellow !block text-center;
|
||||
}
|
||||
|
||||
.btn-red {
|
||||
@apply btn border-none bg-red text-white hover:bg-red-dark;
|
||||
}
|
||||
|
||||
.btn-block-red {
|
||||
@apply btn-red !block text-center;
|
||||
}
|
||||
|
||||
.workshop-card:nth-child(6n + 1) .btn-block {
|
||||
@apply bg-pink hover:bg-pink-dark text-white border-none;
|
||||
}
|
||||
|
||||
.workshop-card:nth-child(6n + 2) .btn-block {
|
||||
@apply bg-orange hover:bg-orange-dark text-white border-none;
|
||||
}
|
||||
|
||||
.workshop-card:nth-child(6n + 3) .btn-block {
|
||||
@apply bg-yellow hover:bg-yellow-dark text-black border-none;
|
||||
}
|
||||
|
||||
.workshop-card:nth-child(6n + 4) .btn-block {
|
||||
@apply bg-green hover:bg-green-dark text-white border-none;
|
||||
}
|
||||
|
||||
.workshop-card:nth-child(6n + 5) .btn-block {
|
||||
@apply bg-blue hover:bg-blue-dark text-white border-none;
|
||||
}
|
||||
|
||||
.workshop-card:nth-child(6n + 6) .btn-block {
|
||||
@apply bg-red hover:bg-red-dark text-white border-none;
|
||||
}
|
||||
|
||||
.admin-card {
|
||||
@apply flex w-60 flex-col items-center border;
|
||||
}
|
||||
|
||||
.action-column {
|
||||
@apply flex gap-4 justify-center;
|
||||
}
|
||||
2
resources/js/app.ts
Normal file
2
resources/js/app.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
import "./bootstrap";
|
||||
import "./stemmech";
|
||||
29
resources/js/bootstrap.ts
Normal file
29
resources/js/bootstrap.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import axios, { AxiosStatic } from "axios";
|
||||
import stemmech, { StemmechStatic } from "./stemmech";
|
||||
|
||||
// Attach axios to the window object
|
||||
declare global {
|
||||
interface Window {
|
||||
axios: AxiosStatic;
|
||||
stemmech: StemmechStatic;
|
||||
SVGInject: any;
|
||||
}
|
||||
}
|
||||
|
||||
window.axios = axios;
|
||||
window.stemmech = stemmech;
|
||||
|
||||
// Set a default header for Axios requests
|
||||
window.axios.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest";
|
||||
|
||||
// Setup window ready
|
||||
window.stemmech.ready(() => {
|
||||
setTimeout(function () {
|
||||
window.stemmech.cleanupBackLinks();
|
||||
window.stemmech.inputErrorListener();
|
||||
window.stemmech.formSubmitListener();
|
||||
window.stemmech.formChangeListener();
|
||||
}, 1);
|
||||
|
||||
window.SVGInject(document.querySelectorAll("img.injectable"));
|
||||
});
|
||||
302
resources/js/stemmech.ts
Normal file
302
resources/js/stemmech.ts
Normal file
@@ -0,0 +1,302 @@
|
||||
export interface StemmechStatic {
|
||||
hasUnsavedChanges: number;
|
||||
unsavedChangesMessageStack: string[];
|
||||
ready(callback: () => void): void;
|
||||
getAllSiblings(
|
||||
elem: HTMLElement,
|
||||
filter?: (elem: HTMLElement) => boolean
|
||||
): HTMLElement[];
|
||||
getQueryParam(param: string, defaultValue: string | null): string | null;
|
||||
cleanupBackLinks(): void;
|
||||
inputErrorListener(): void;
|
||||
formSubmitListener(): void;
|
||||
formChangeListener(): void;
|
||||
formBusy(message: string): void;
|
||||
formIdle(popMessage: boolean): void;
|
||||
}
|
||||
|
||||
const stemmech: StemmechStatic = {
|
||||
hasUnsavedChanges: 0,
|
||||
unsavedChangesMessageStack: ['You have unsaved changes. Are you sure you want to leave this page?'],
|
||||
|
||||
/**
|
||||
* Executes the provided callback function when the DOM is fully loaded.
|
||||
*
|
||||
* @param {function} callback - The function to be executed when the DOM is ready.
|
||||
*/
|
||||
ready: function (callback) {
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
if (typeof callback === "function") {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get all siblings of an element.
|
||||
*
|
||||
* @param {HTMLElement} elem - The element to find siblings for.
|
||||
* @param {function} [filter] - A filter function to apply to the siblings.
|
||||
* @returns {HTMLElement[]} An array of sibling elements.
|
||||
*/
|
||||
getAllSiblings: function (elem, filter) {
|
||||
const sibs: HTMLElement[] = [];
|
||||
elem = elem.parentNode?.firstChild as HTMLElement;
|
||||
do {
|
||||
if (elem?.nodeType === 3) continue;
|
||||
if (!filter || (filter && filter(elem))) sibs.push(elem);
|
||||
} while ((elem = elem.nextSibling as HTMLElement));
|
||||
return sibs;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a query parameter from the current URL.
|
||||
*
|
||||
* @param {string} param - The query parameter to retrieve.
|
||||
* @param {*} [defaultValue=null] - The default value if the parameter is not found.
|
||||
* @returns {*} The value of the query parameter or the default value.
|
||||
*/
|
||||
getQueryParam: function (param, defaultValue = null) {
|
||||
const urlSearchParams = new URLSearchParams(window.location.search);
|
||||
const paramValue = urlSearchParams.get(param);
|
||||
return paramValue !== null ? paramValue : defaultValue;
|
||||
},
|
||||
|
||||
/**
|
||||
* Cleans up back links in the document by replacing links with "javascript:history.back()" href attributes
|
||||
* with the actual document.referrer value.
|
||||
*/
|
||||
cleanupBackLinks: function () {
|
||||
var links = document.getElementsByTagName("a");
|
||||
|
||||
for (var i = 0; i < links.length; i++) {
|
||||
if (links[i].getAttribute("href") === "javascript:history.back()") {
|
||||
links[i].setAttribute("href", document.referrer);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Listens for input events on input elements with error-related siblings and removes the error
|
||||
* siblings when input occurs.
|
||||
*/
|
||||
inputErrorListener: function () {
|
||||
function handleRemoveErrorSiblings(event: Event) {
|
||||
const element = event.currentTarget as HTMLInputElement;
|
||||
const siblings = window.stemmech.getAllSiblings(element, (e) => {
|
||||
return (
|
||||
e.nodeName.toUpperCase() === "P" &&
|
||||
e.classList.contains("error")
|
||||
);
|
||||
});
|
||||
|
||||
siblings.forEach((item) => {
|
||||
if (item.parentNode) item.parentNode.removeChild(item);
|
||||
});
|
||||
|
||||
element.removeEventListener("input", handleRemoveErrorSiblings);
|
||||
}
|
||||
|
||||
// Attach event listener only to those inputs that have a following p.error sibling
|
||||
document.querySelectorAll("input").forEach(function (input) {
|
||||
const hasErrorSiblings = stemmech.getAllSiblings(input, (e) => {
|
||||
return (
|
||||
e.nodeName.toUpperCase() === "P" &&
|
||||
e.classList.contains("error")
|
||||
);
|
||||
});
|
||||
|
||||
if (hasErrorSiblings.length > 0) {
|
||||
input.addEventListener("input", handleRemoveErrorSiblings);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle the form submission by disabling input elements and showing a spinner.
|
||||
*/
|
||||
formSubmitListener: function () {
|
||||
function handleFormSubmit(event: Event) {
|
||||
const element = event.currentTarget as HTMLElement;
|
||||
|
||||
// Find the submit button in the form
|
||||
var submitButtons = element.querySelectorAll(
|
||||
'input[type="submit"], button[type="submit"]'
|
||||
);
|
||||
|
||||
submitButtons.forEach((button) => {
|
||||
var style = window.getComputedStyle(button);
|
||||
|
||||
(button as HTMLElement).style.width = style.width;
|
||||
(button as HTMLElement).style.height = style.height;
|
||||
// Change the HTML of the submit button
|
||||
button.innerHTML =
|
||||
'<i class="fa-solid fa-spinner fa-spin-pulse"></i>';
|
||||
});
|
||||
|
||||
element
|
||||
.querySelectorAll('input:not([type="submit"]), textarea')
|
||||
.forEach(function (item) {
|
||||
(item as HTMLInputElement).readOnly = true;
|
||||
});
|
||||
element
|
||||
.querySelectorAll('input[type="submit"], button')
|
||||
.forEach(function (item) {
|
||||
(item as HTMLInputElement).disabled = true;
|
||||
});
|
||||
}
|
||||
|
||||
var form = document.querySelector("form");
|
||||
if (form) {
|
||||
form.addEventListener("submit", handleFormSubmit);
|
||||
}
|
||||
},
|
||||
|
||||
formChangeListener: function() {
|
||||
const forms = document.querySelectorAll('form');
|
||||
|
||||
if (forms.length > 0) {
|
||||
forms.forEach(form => {
|
||||
form.addEventListener('input', () => {
|
||||
this.hasUnsavedChanges++;
|
||||
}, { once: true });
|
||||
});
|
||||
|
||||
window.addEventListener('beforeunload', (event) => {
|
||||
if (this.hasUnsavedChanges) {
|
||||
event.preventDefault();
|
||||
event.returnValue = this.unsavedChangesMessageStack[this.unsavedChangesMessageStack.length - 1];
|
||||
return event.returnValue;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
formBusy: function (message: string = "") {
|
||||
this.hasUnsavedChanges++;
|
||||
if (message != "") {
|
||||
this.unsavedChangesMessageStack.push(message);
|
||||
}
|
||||
},
|
||||
|
||||
formIdle: function (popMessage: boolean = false) {
|
||||
this.hasUnsavedChanges--;
|
||||
if (popMessage) {
|
||||
this.unsavedChangesMessageStack.pop();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function uploadFilesWithFeedback(files: FileList, url: string, formElement: HTMLFormElement, containerElement: HTMLElement, allowedExtensions: string[]): Promise<void[]> {
|
||||
// Disable all submit buttons in the form
|
||||
const submitButtons = formElement.querySelectorAll('input[type="submit"], button[type="submit"]');
|
||||
submitButtons.forEach((button) => {
|
||||
button.disabled = true;
|
||||
});
|
||||
|
||||
// Create an array to store promises for each file upload
|
||||
const uploadPromises: Promise<void>[] = [];
|
||||
|
||||
// Iterate through the files in the FileList
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
|
||||
// Check if the file has an allowed extension
|
||||
const fileExtension = file.name.split('.').pop()?.toLowerCase();
|
||||
if (!fileExtension || !allowedExtensions.includes(fileExtension)) {
|
||||
// Skip this file if the extension is not allowed
|
||||
console.warn(`Skipping file "${file.name}" due to an invalid extension.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create a promise for each file upload
|
||||
const uploadPromise = new Promise<void>((resolve, reject) => {
|
||||
// Create a FormData object to send the file
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
// Create a new DIV element for feedback
|
||||
const feedbackDiv = document.createElement('div');
|
||||
feedbackDiv.classList.add('upload-feedback');
|
||||
|
||||
// Set the background image of the feedback DIV
|
||||
const backgroundUrl = `/public/file_icons/${fileExtension}.png`;
|
||||
feedbackDiv.style.backgroundImage = `url(${backgroundUrl}), url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"><rect width="100" height="100" fill="lightgray"/></svg>')`;
|
||||
|
||||
// Create a spinner icon
|
||||
const spinnerIcon = document.createElement('i');
|
||||
spinnerIcon.classList.add('fas', 'fa-spinner', 'fa-spin');
|
||||
|
||||
// Append the spinner icon to the feedback DIV
|
||||
feedbackDiv.appendChild(spinnerIcon);
|
||||
|
||||
// Append the feedback DIV to the container element
|
||||
containerElement.appendChild(feedbackDiv);
|
||||
|
||||
// Perform the POST request
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.ok) {
|
||||
// Remove spinner and add a tick icon for success
|
||||
feedbackDiv.innerHTML = '<i class="fas fa-check"></i>';
|
||||
resolve();
|
||||
} else {
|
||||
// Remove spinner and add an error icon for errors
|
||||
feedbackDiv.innerHTML = '<i class="fas fa-exclamation-triangle"></i>';
|
||||
console.error('File upload failed:', response.statusText);
|
||||
reject(response.statusText);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
// Handle network errors
|
||||
feedbackDiv.innerHTML = '<i class="fas fa-exclamation-circle"></i>';
|
||||
console.error('File upload error:', error);
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
|
||||
// Add the upload promise to the array
|
||||
uploadPromises.push(uploadPromise);
|
||||
}
|
||||
|
||||
// Return a promise that resolves when all uploads are complete (success or error)
|
||||
return Promise.all(uploadPromises)
|
||||
.finally(() => {
|
||||
// Enable all submit buttons in the form when all uploads are complete
|
||||
submitButtons.forEach((button) => {
|
||||
button.disabled = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Example usage:
|
||||
const fileInput = document.getElementById('file-input') as HTMLInputElement;
|
||||
const form = document.getElementById('my-form') as HTMLFormElement;
|
||||
const container = document.getElementById('feedback-container');
|
||||
const allowedExtensions = ['jpg', 'jpeg', 'png', 'gif']; // Replace with your allowed extensions
|
||||
|
||||
fileInput.addEventListener('change', () => {
|
||||
const files = fileInput.files;
|
||||
if (files.length > 0) {
|
||||
uploadFilesWithFeedback(files, '/upload-url', form, container, allowedExtensions)
|
||||
.then(() => {
|
||||
console.log('All files uploaded successfully.');
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error uploading files:', error);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
export default stemmech;
|
||||
40
resources/views/account/index.blade.php
Normal file
40
resources/views/account/index.blade.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<x-layout>
|
||||
<x-banner heading="Account" />
|
||||
|
||||
<div class="flex flex-wrap items-center justify-center gap-8 border">
|
||||
<x-card href="/account/users/me" class="admin-card">
|
||||
<i class="fa-regular fa-circle-user mb-4 text-4xl"></i>
|
||||
<p class="text-lg">My Details</p>
|
||||
</x-card>
|
||||
|
||||
<x-card href="/account/users" class="admin-card">
|
||||
<i class="fa-solid fa-users mb-4 text-4xl"></i>
|
||||
<p class="text-lg">Users</p>
|
||||
</x-card>
|
||||
|
||||
<x-card href="/account/media" class="admin-card">
|
||||
<i class="fa-solid fa-photo-film mb-4 text-4xl"></i>
|
||||
<p class="text-lg">Media</p>
|
||||
</x-card>
|
||||
|
||||
<x-card class="admin-card">
|
||||
<i class="fa-regular fa-circle-user mb-4 text-4xl"></i>
|
||||
<p class="text-lg">Posts</p>
|
||||
</x-card>
|
||||
|
||||
<x-card class="admin-card">
|
||||
<i class="fa-regular fa-circle-user mb-4 text-4xl"></i>
|
||||
<p class="text-lg">Workshops</p>
|
||||
</x-card>
|
||||
|
||||
<x-card class="admin-card">
|
||||
<i class="fa-solid fa-file-invoice-dollar mb-4 text-4xl"></i>
|
||||
<p class="text-lg">Quotes</p>
|
||||
</x-card>
|
||||
|
||||
<x-card class="admin-card">
|
||||
<i class="fa-solid fa-file-invoice-dollar mb-4 text-4xl"></i>
|
||||
<p class="text-lg">Invoices</p>
|
||||
</x-card>
|
||||
</div>
|
||||
</x-layout>
|
||||
54
resources/views/account/users-index.blade.php
Normal file
54
resources/views/account/users-index.blade.php
Normal file
@@ -0,0 +1,54 @@
|
||||
@props(['users'])
|
||||
|
||||
<x-layout>
|
||||
<x-banner heading="Users" back="account.index" />
|
||||
|
||||
<form method="post" action="/account/users">
|
||||
<div class="table-layout">
|
||||
<table cellspacing="0" cellpadding="0" class="w-full table-auto">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-1"> </th>
|
||||
<th>Username</th>
|
||||
<th>Email</th>
|
||||
<th class="hidden md:table-cell">Verified</th>
|
||||
<th class="hidden md:table-cell">Under 14</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@unless (count($users) == 0)
|
||||
|
||||
@foreach ($users as $user)
|
||||
<tr>
|
||||
<td><input type="checkbox"></td>
|
||||
<td>{{ $user->username }}</td>
|
||||
<td>{{ $user->email }}</td>
|
||||
<td class="hidden md:table-cell">{{ $user->formattedEmailVerifiedAt() }}</td>
|
||||
<td class="hidden text-center md:table-cell">{{ $user->is_under_14 ? 'Yes' : 'No' }}</td>
|
||||
<td class="action-column">
|
||||
<a href="#" title="Edit User"><i class="fa-solid fa-pen-to-square"></i></a>
|
||||
<a href="#" title="Delete User"><i
|
||||
class="fa-solid fa-trash hover:text-red"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
@else
|
||||
<tr>
|
||||
<td colspan="4">No users found</td>
|
||||
</tr>
|
||||
@endunless
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
<div>
|
||||
<input type="submit" name="action" value="Edit">
|
||||
<input type="submit" name="action" value="Delete">
|
||||
</div>
|
||||
|
||||
<div class="mt-6">
|
||||
{{ $users->links() }}
|
||||
</div>
|
||||
|
||||
</x-layout>
|
||||
35
resources/views/account/users-show.blade.php
Normal file
35
resources/views/account/users-show.blade.php
Normal file
@@ -0,0 +1,35 @@
|
||||
@props(['user'])
|
||||
|
||||
<x-layout>
|
||||
<x-banner heading="User" back="account.users.index" />
|
||||
|
||||
<div class="mx-4">
|
||||
<form class="mx-auto max-w-[40rem]" method="POST" action="/account/users/{{ $user->id }}">
|
||||
<div class="floating-label">
|
||||
<input type="text" name="username" value="{{ old('username', $user->username) }}" required />
|
||||
<label for="username">Username</label>
|
||||
</div>
|
||||
@error('username')
|
||||
<p class="error">{{ $message }}</p>
|
||||
@enderror
|
||||
|
||||
<div class="floating-label">
|
||||
<input type="email" name="email" value="{{ old('email', $user->email) }}" required />
|
||||
<label for="email">Email</label>
|
||||
</div>
|
||||
@error('email')
|
||||
<p class="error">{{ $message }}</p>
|
||||
@enderror
|
||||
|
||||
<div>
|
||||
<label><input type="checkbox" name="is_under_14" value="1"
|
||||
@if (old('is_under_14', $user->is_under_14)) checked @endif />
|
||||
Under 14 years</label>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end">
|
||||
<button class="btn-blue" type="submit">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</x-layout>
|
||||
23
resources/views/components/banner.blade.php
Normal file
23
resources/views/components/banner.blade.php
Normal file
@@ -0,0 +1,23 @@
|
||||
@props(['heading', 'back'])
|
||||
|
||||
@php
|
||||
$backTitle = '';
|
||||
if (isset($back)) {
|
||||
$parts = explode('.', $back);
|
||||
if (count($parts) > 1) {
|
||||
$backTitle = ucwords($parts[count($parts) - 2]);
|
||||
} else {
|
||||
$backTitle = ucwords($parts[0]);
|
||||
}
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div class="px42 -mt-4 mb-8 bg-blue px-6 py-10 text-white">
|
||||
<h1 class="text-4xl font-bold">{{ $heading }}</h1>
|
||||
@if (isset($back))
|
||||
<a href="{{ route($back) }}" class="mt-1 text-sm hover:text-inherit hover:underline"><i
|
||||
class="fa-solid fa-chevron-left mr-2"></i>Back
|
||||
to
|
||||
{{ $backTitle }}</a>
|
||||
@endif
|
||||
</div>
|
||||
10
resources/views/components/card.blade.php
Normal file
10
resources/views/components/card.blade.php
Normal file
@@ -0,0 +1,10 @@
|
||||
@if (isset($href))
|
||||
<a href="{{ $href }}"
|
||||
{{ $attributes->merge(['class' => 'bg-gray-50 border border-gray-300 rounded-lg p-6']) }}>
|
||||
{{ $slot }}
|
||||
</a>
|
||||
@else
|
||||
<div {{ $attributes->merge(['class' => 'bg-gray-50 border border-gray-300 rounded-lg p-6']) }}>
|
||||
{{ $slot }}
|
||||
</div>
|
||||
@endif
|
||||
25
resources/views/components/flash-message.blade.php
Normal file
25
resources/views/components/flash-message.blade.php
Normal file
@@ -0,0 +1,25 @@
|
||||
@if (session()->has('message'))
|
||||
@php
|
||||
$messageType = session('message-type', 'primary'); // Default to 'primary' if 'message_type' is not set
|
||||
$messageClasses = [
|
||||
'primary' => 'border-blue bg-blue-lighter text-blue-dark',
|
||||
'success' => 'border-green bg-green-lighter text-green-dark',
|
||||
'danger' => 'border-red bg-red-lighter text-red-dark',
|
||||
'warning' => 'border-yellow bg-yellow-lighter text-yellow-dark',
|
||||
];
|
||||
@endphp
|
||||
|
||||
<div x-data="{ show: false }" x-init="$nextTick(() => {
|
||||
show = true;
|
||||
setTimeout(() => show = false, 7000)
|
||||
})" x-show="show"
|
||||
x-transition:enter="transition ease-out duration-300 transform"
|
||||
x-transition:enter-start="opacity-0 -translate-y-4" x-transition:enter-end="opacity-100 translate-y-0"
|
||||
x-transition:leave="transition ease-in duration-300 transform"
|
||||
x-transition:leave-start="opacity-100 translate-y-0" x-transition:leave-end="opacity-0 -translate-y-4"
|
||||
class="{{ $messageClasses[$messageType] }} fixed left-1/2 top-4 min-w-[15rem] max-w-[20rem] -translate-x-1/2 rounded border px-4 py-2 text-center shadow-lg">
|
||||
<p class="text-sm">
|
||||
{{ session('message') }}
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
34
resources/views/components/layout.blade.php
Normal file
34
resources/views/components/layout.blade.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="icon" href="images/favicon.ico" />
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css"
|
||||
integrity="sha512-z3gLpd7yknf1YoNbCzqRKc4qyor8gaKU1qmn+CShxbuBusANI9QpRohGBreCFkKxLhei6S9CQXFEbbKuqLg0DA=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/alpinejs/3.13.3/cdn.min.js"
|
||||
integrity="sha512-AB2vAMVrtmmI+2BwSMqB+y1qGPNJovUOCp4w27S9pvX8yXPQNbBO4kuM952+LlOpng9VeWPb86b5N32bkvXRvQ=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer" defer></script>
|
||||
<script src="/scripts/svg-inject.min.js"></script>
|
||||
<link rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,400;0,700;1,400;1,700&display=swap"
|
||||
as="style" onload="this.onload=null;this.rel='stylesheet'">
|
||||
@vite(['resources/css/app.css', 'resources/js/app.ts'])
|
||||
<title>STEMMechanics</title>
|
||||
</head>
|
||||
|
||||
<body class="flex flex-col bg-gray-200">
|
||||
@include('partials.nav')
|
||||
<main {{ $attributes->merge(['class' => 'grow']) }}>
|
||||
{{ $slot }}
|
||||
</main>
|
||||
|
||||
@include('partials.footer')
|
||||
<x-flash-message />
|
||||
@stack('scripts')
|
||||
</body>
|
||||
|
||||
</html>
|
||||
10
resources/views/components/workshop-card.blade.php
Normal file
10
resources/views/components/workshop-card.blade.php
Normal file
@@ -0,0 +1,10 @@
|
||||
@props(['workshop'])
|
||||
|
||||
<x-card class="workshop-card grow">
|
||||
<div class="flex h-full flex-col">
|
||||
<h3 class="mb-4 text-2xl">{{ $workshop->title }}</h3>
|
||||
<p class="mb-4 grow">{{ Str::limit(strip_tags($workshop->content), 200, '...') }}</p>
|
||||
|
||||
<a href="/" class="btn btn-block">View</a>
|
||||
</div>
|
||||
</x-card>
|
||||
18
resources/views/emails/users/email-verify-plain.blade.php
Normal file
18
resources/views/emails/users/email-verify-plain.blade.php
Normal file
@@ -0,0 +1,18 @@
|
||||
Welcome {{ $user?->username }},
|
||||
|
||||
We've heard you would like to try out our workshops and courses!
|
||||
Before we can let you loose on our website, we need to make sure you are a real person and not a pesky robot or cat.
|
||||
|
||||
Enter the following URL in your browser:
|
||||
|
||||
https://www.stemmechanics.com.au/verify
|
||||
|
||||
and when asked, use the confirm code: {{ $code }}
|
||||
|
||||
Need help or got feedback? Contact us at https://www.stemmechanics.com.au/contact or touch base on twitter at
|
||||
@stemmechanics
|
||||
|
||||
--
|
||||
Sent by STEMMechanics
|
||||
https://www.stemmechanics.com.au/
|
||||
PO Box 36, Edmonton, QLD 4869, Australia
|
||||
114
resources/views/emails/users/email-verify.blade.php
Normal file
114
resources/views/emails/users/email-verify.blade.php
Normal file
@@ -0,0 +1,114 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}" xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>STEMMechanics - Forgot Password</title>
|
||||
<link rel="noopener" target="_blank"
|
||||
href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,400;0,700;1,400;1,700&display=swap"
|
||||
rel="stylesheet" />
|
||||
<!--[if gte mso 9]>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG />
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
<![endif]-->
|
||||
<style>
|
||||
@import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,400;0,700;1,400;1,700&display=swap");
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<table cellspacing="0" cellpadding="0" border="0" role="presentation"
|
||||
style="
|
||||
width: 100%;
|
||||
padding: 2rem;
|
||||
font-size: 1.1rem;
|
||||
color: #000000;
|
||||
font-family: Poppins, Arial, Helvetica, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
">
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://www.stemmechanics.com.au/">
|
||||
<img alt="STEMMechanics Logo" src="{{ $message->embed(public_path('images') . '/logo.svg') }}"
|
||||
width="400" height="62" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h2>Welcome {{ $user?->username }},</h2>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
We've heard you would like to try out our workshops and courses!
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Before we can let you loose on our website, we need to make sure you are a real person and not a
|
||||
pesky robot or cat. Click this link <a
|
||||
href="https://www.stemmechanics.com.au/verify?code={{ $code }}">stemmechanics.com.au/verify</a>
|
||||
and if you are asked, use the confirm code:
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"
|
||||
style="
|
||||
font-size: 200%;
|
||||
text-align: center;
|
||||
padding-top: 2rem;
|
||||
padding-bottom: 2rem;
|
||||
letter-spacing: 0.5rem;
|
||||
">
|
||||
<strong>{{ $code }}</strong>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding-bottom: 2rem">
|
||||
But if you didn't ask to reset your password, you can delete
|
||||
this email and your password will remain the same.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"
|
||||
style="
|
||||
font-size: 90%;
|
||||
text-align: center;
|
||||
padding-top: 2rem;
|
||||
padding-bottom: 2rem;
|
||||
border-top: 1px solid #ddd;
|
||||
">
|
||||
Need help or got feedback?
|
||||
<a href="https://www.stemmechanics.com.au/contact">Contact us</a>
|
||||
or touch base at
|
||||
<a href="https://twitter.com/stemmechanics">@stemmechanics</a>.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"
|
||||
style="
|
||||
font-size: 80%;
|
||||
text-align: center;
|
||||
padding-top: 1rem;
|
||||
padding-bottom: 2rem;
|
||||
">
|
||||
Sent by STEMMechanics ·
|
||||
<a href="https://www.stemmechanics.com.au/">Visit our Website</a>
|
||||
·
|
||||
<a href="https://twitter.com/stemmechanics">@stemmechanics</a><br />PO Box 36, Edmonton, QLD 4869,
|
||||
Australia
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
18
resources/views/home.blade.php
Normal file
18
resources/views/home.blade.php
Normal file
@@ -0,0 +1,18 @@
|
||||
@props(['workshops'])
|
||||
|
||||
<x-layout>
|
||||
@include('partials.hero')
|
||||
|
||||
<h2>Upcoming Workshops</h2>
|
||||
<div class="mx-4 justify-start gap-4 space-y-4 sm:grid sm:grid-cols-2 sm:space-y-0">
|
||||
@unless (count($workshops) == 0)
|
||||
@foreach ($workshops as $workshop)
|
||||
<x-workshop-card :workshop="$workshop" />
|
||||
@endforeach
|
||||
@else
|
||||
<p>No workshops found</p>
|
||||
@endunless
|
||||
</div>
|
||||
|
||||
|
||||
</x-layout>
|
||||
55
resources/views/partials/email-verify.blade.php
Normal file
55
resources/views/partials/email-verify.blade.php
Normal file
@@ -0,0 +1,55 @@
|
||||
@props(['resend'])
|
||||
|
||||
<p>
|
||||
Enter the verification code you received</p>
|
||||
|
||||
<div class="floating-label mb-6 mt-12">
|
||||
<input type="text" class="w-full rounded border border-gray-200 p-2" name="code" value="{{ old('code') }}"
|
||||
required autofocus />
|
||||
<label for="code" class="mx-1 mb-1 inline-block text-sm text-gray-800">Verification code</label>
|
||||
@error('code')
|
||||
<p class="error">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
@if (isset($resend))
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p id="resend-link" class="hidden text-xs text-gray-600">Didn't receive the code? <a href="?resend=1"
|
||||
class="text-blue transition hover:text-blue-dark">
|
||||
Resend Email
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit" class="rounded bg-green px-8 py-2 text-white transition hover:bg-green-dark">
|
||||
Verify Code
|
||||
</button>
|
||||
</div>
|
||||
@else
|
||||
<div class="flex items-center justify-end">
|
||||
<button type="submit" class="btn-green">
|
||||
Verify Code
|
||||
</button>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@push('scripts')
|
||||
<script type="module">
|
||||
window.stemmech.ready(() => {
|
||||
const code = stemmech.getQueryParam('code');
|
||||
const resend = document.getElementById('resend-link');
|
||||
const codeInput = document.getElementsByName('code')[0];
|
||||
|
||||
if (code && codeInput && codeInput.value.trim() === '') {
|
||||
codeInput.value = code;
|
||||
var form = document.querySelector("form");
|
||||
if (form) form.submit();
|
||||
} else if (resend) {
|
||||
window.setTimeout(() => {
|
||||
resend.classList.remove('hidden');
|
||||
}, 30000);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
43
resources/views/partials/footer.blade.php
Normal file
43
resources/views/partials/footer.blade.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<footer class="mt-16 bg-gray-800 text-gray-400">
|
||||
<section class="flex gap-4 border-b border-gray-900 p-8">
|
||||
<div class="basis-1/2 self-center pr-8 text-xs">
|
||||
<p>STEMMechanics Australia acknowledges the Traditional Owners of Country throughout Australia and the
|
||||
continuing connection to land, cultures and communities. We pay our respect to Aboriginal and Torres
|
||||
Strait Islander cultures; and to Elders both past, present and emerging.</p>
|
||||
</div>
|
||||
<div class="basis-1/4">
|
||||
<h3 class="mb-2 font-bold">Community</h3>
|
||||
<ul class="flex flex-col gap-1 text-sm">
|
||||
<li class="flex max-w-[7rem] justify-between text-2xl">
|
||||
<a href="/community"><i class="fa-brands fa-github"></i></a>
|
||||
<a href="/community"><i class="fa-brands fa-discord"></i></a>
|
||||
<a href="/community"><img src="/images/minecraft-icon.png" class="h-7 w-7"></a>
|
||||
</li>
|
||||
<li class="flex max-w-[7rem] justify-between text-2xl">
|
||||
<a href="/community"><i class="fa-brands fa-facebook"></i></a>
|
||||
<a href="/community"><i class="fa-brands fa-twitter"></i></a>
|
||||
<a href="/community"><i class="fa-brands fa-youtube"></i></a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="basis-1/4">
|
||||
<h3 class="mb-2 font-bold">STEMMechanics</h3>
|
||||
<ul class="flex flex-col gap-1 text-sm">
|
||||
<li><a href="/">Contact Us</a></li>
|
||||
<li><a href="/">Host a Workshop</a></li>
|
||||
<li><a href="/">Code of Conduct</a></li>
|
||||
<li><a href="/">Terms & Conditions</a></li>
|
||||
<li><a href="/">Privacy Policy</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
<section class="flex items-center justify-between p-8">
|
||||
<div>
|
||||
<a href="/"><img class="injectable w-40 text-white" src="{{ asset('images/logo.svg') }}"
|
||||
alt="" class="logo" /></a>
|
||||
</div>
|
||||
<div class="text-xs">
|
||||
Made with ❤️ © 2023 STEMMechanics
|
||||
</div>
|
||||
</section>
|
||||
</footer>
|
||||
9
resources/views/partials/hero.blade.php
Normal file
9
resources/views/partials/hero.blade.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<section
|
||||
class="mx-4 mb-4 flex flex-col items-center justify-center rounded bg-cover bg-center bg-no-repeat py-20 text-white"
|
||||
style="background-image: linear-gradient(to right,rgba(0,0,0,.7),rgba(0,0,0,.2)),url('/images/hero.webp')">
|
||||
<h1 class="py-4 text-4xl font-bold">Join the fun!</h1>
|
||||
<p class="max-w-2xl px-4 py-2.5">To keep up with our ever-changing world, it's important to encourage and support a
|
||||
new generation of curious minds who love science, engineering, art, and leadership.</p>
|
||||
<p class="max-w-2xl px-4 py-2.5">Our fun and exciting workshops can unlock countless opportunities for new ideas and
|
||||
improvements, giving kids the skills and tools they need to solve any problem that comes their way.</p>
|
||||
</section>
|
||||
77
resources/views/partials/nav.blade.php
Normal file
77
resources/views/partials/nav.blade.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<nav class="mb-4 flex items-center justify-between bg-white shadow-sm">
|
||||
<a href="/"><img class="injectable w-60 px-4 py-3" src="{{ asset('images/logo.svg') }}" alt=""
|
||||
class="logo" /></a>
|
||||
<ul class="text mr-6 flex">
|
||||
|
||||
<li class="b-1 z-30 flex justify-center">
|
||||
<div x-data="{
|
||||
open: false,
|
||||
toggle() {
|
||||
if (this.open) {
|
||||
return this.close()
|
||||
}
|
||||
|
||||
this.$refs.button.focus()
|
||||
|
||||
this.open = true
|
||||
},
|
||||
close(focusAfter) {
|
||||
if (!this.open) return
|
||||
|
||||
this.open = false
|
||||
|
||||
focusAfter && focusAfter.focus()
|
||||
}
|
||||
}" x-on:keydown.escape.prevent.stop="close($refs.button)"
|
||||
x-on:focusin.window="! $refs.panel.contains($event.target) && close()" x-id="['dropdown-button']"
|
||||
class="relative">
|
||||
<button x-ref="button" x-on:click="toggle()" :aria-expanded="open"
|
||||
:aria-controls="$id('dropdown-button')" type="button"
|
||||
class="flex items-center gap-2 rounded-md bg-blue px-4 py-2 text-white">
|
||||
Menu
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-white" viewBox="0 0 20 20"
|
||||
fill="currentColor">
|
||||
<path fill-rule="evenodd"
|
||||
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div x-ref="panel" x-show="open" x-transition.origin.top.left x-on:click.outside="close($refs.button)"
|
||||
:id="$id('dropdown-button')" style="display: none;"
|
||||
class="absolute right-0 mt-2 whitespace-nowrap rounded-md border bg-white p-2 shadow-md">
|
||||
<a href="/workshops"
|
||||
class="text-md flex w-full items-center gap-2 rounded-md px-4 py-2.5 transition hover:bg-blue hover:text-white">
|
||||
<i class="fa-solid fa-paintbrush"></i> Workshops
|
||||
</a>
|
||||
|
||||
<a href="/workshops"
|
||||
class="text-md flex w-full items-center gap-2 rounded-md px-4 py-2.5 transition hover:bg-blue hover:text-white">
|
||||
<i class="fa-regular fa-newspaper"></i> Blog
|
||||
</a>
|
||||
|
||||
<hr class="my-2 border-gray-200" />
|
||||
@auth
|
||||
<a href="{{ route('account.index') }}"
|
||||
class="text-md flex w-full items-center gap-2 rounded-md px-4 py-2.5 transition hover:bg-blue hover:text-white">
|
||||
<i class="fa-solid fa-toolbox"></i>My Account
|
||||
</a>
|
||||
<a href="/logout"
|
||||
class="text-md flex w-full items-center gap-2 rounded-md px-4 py-2.5 transition hover:bg-blue hover:text-white">
|
||||
<i class="fa-solid fa-right-from-bracket"></i> Log out
|
||||
</a>
|
||||
@else
|
||||
<a href="/register"
|
||||
class="text-md flex w-full items-center gap-2 rounded-md px-4 py-2.5 transition hover:bg-blue hover:text-white">
|
||||
<i class="fa-solid fa-user-plus"></i> Register
|
||||
</a>
|
||||
<a href="/login"
|
||||
class="text-md flex w-full items-center gap-2 rounded-md px-4 py-2.5 transition hover:bg-blue hover:text-white">
|
||||
<i class="fa-solid fa-right-to-bracket"></i> Log in
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
15
resources/views/partials/search.blade.php
Normal file
15
resources/views/partials/search.blade.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<form action="/">
|
||||
<div class="relative m-4 rounded-lg border border-gray-400">
|
||||
<div class="absolute left-4 top-4">
|
||||
<i class="fa fa-search z-20 text-gray-400 hover:text-gray-500"></i>
|
||||
</div>
|
||||
<input type="text" name="search" class="z-0 h-14 w-full rounded-lg pl-11 pr-20 focus:shadow focus:outline-none"
|
||||
placeholder="Search Workshops" />
|
||||
<div class="absolute right-2 top-2">
|
||||
<button type="submit"
|
||||
class="h-10 w-20 rounded-lg bg-green text-sm text-white transition hover:bg-green-dark">
|
||||
Search
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
43
resources/views/users/login.blade.php
Normal file
43
resources/views/users/login.blade.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<x-layout>
|
||||
<x-card class="relative z-40 mx-auto mt-12 max-w-lg shadow-lg">
|
||||
<header>
|
||||
<h2 class="-m-6 mb-10 rounded-t-lg bg-orange px-6 py-4 text-xl text-white">Log in to STEMMechanics</h2>
|
||||
</header>
|
||||
|
||||
<form method="POST" action="/login">
|
||||
@csrf
|
||||
|
||||
<div class="floating-label my-8">
|
||||
<input type="text" class="w-full rounded border border-gray-200 p-2" name="username"
|
||||
value="{{ old('username') }}" required />
|
||||
<label for="username" class="mb-1 inline-block text-sm text-gray-800">Username</label>
|
||||
|
||||
@error('username')
|
||||
<p class="error">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="floating-label my-8">
|
||||
<input type="password" class="w-full rounded border border-gray-200 p-2" name="password"
|
||||
value="{{ old('password') }}" placeholder="Password" />
|
||||
<label for="password">Password</label>
|
||||
@error('password')
|
||||
<p class="error">{{ $message }}</p>
|
||||
@enderror
|
||||
<p class="mt-1 px-1 text-xs"><a href="/forgot-password"
|
||||
class="text-blue transition hover:text-blue-dark">
|
||||
Forgot Password</a></p>
|
||||
</div>
|
||||
|
||||
<div class="flex items-end justify-between">
|
||||
<p class="text-xs text-gray-600">Need an account?
|
||||
<a href="/register" class="text-blue transition hover:text-blue-dark">
|
||||
Register</a>
|
||||
</p>
|
||||
<button type="submit" class="rounded bg-orange px-8 py-2 text-white transition hover:bg-orange-dark">
|
||||
Login
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</x-card>
|
||||
</x-layout>
|
||||
104
resources/views/users/register.blade.php
Normal file
104
resources/views/users/register.blade.php
Normal file
@@ -0,0 +1,104 @@
|
||||
<x-layout>
|
||||
<x-card class="relative mx-auto mt-12 max-w-lg shadow-lg">
|
||||
<header class="relative">
|
||||
<h2 class="-m-6 mb-6 rounded-t-lg bg-green px-6 py-4 text-xl text-white">Sign up to STEMMechanics</h2>
|
||||
<a href="?reset=1" class="text-white transition hover:text-red" title="Restart registration"><i
|
||||
class="fa-solid fa-xmark absolute right-0 top-0 translate-y-1/2 text-xl"></i></a>
|
||||
</header>
|
||||
|
||||
<form class="m-0" method="POST" action="/register" x-data="{ age: '' }">
|
||||
<input type="hidden" name="form_step" value="{{ $form->currentStep() }}">
|
||||
@csrf
|
||||
|
||||
@switch($form->currentStep())
|
||||
@case(1)
|
||||
<div class="floating-label mb-6 mt-12">
|
||||
<input type="text" class="w-full rounded border border-gray-200 p-2" name="username"
|
||||
value="{{ $form->getValue('username') }}" required autofocus />
|
||||
<label for="username" class="mx-1 mb-1 inline-block text-sm text-gray-800">Choose a username</label>
|
||||
@error('username')
|
||||
<p class="error">{{ $errors->first('username') }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="floating-label my-8">
|
||||
<input type="password" class="w-full rounded border border-gray-200 p-2" name="password"
|
||||
value="{{ $form->getValue('password') }}" placeholder="Password" />
|
||||
<label for="password">Choose a password</label>
|
||||
@error('password')
|
||||
<p class="error">{{ $errors->first('password') }}</p>
|
||||
@enderror
|
||||
<p class="mt-1 px-1 text-xs text-gray-400">Required to be at least 8 characters and include a
|
||||
number</p>
|
||||
</div>
|
||||
@break
|
||||
|
||||
@case(2)
|
||||
<p>Are you over or under 14 years old?</p>
|
||||
|
||||
<input type="hidden" name="age" x-model="age">
|
||||
<button x-on:click="age = $event.target.value" type="submit" value="under" class="btn mt-8 w-full"
|
||||
tabindex="1">I
|
||||
am under 14</button>
|
||||
<button x-on:click="age = $event.target.value" x-data="" value="over" type="submit"
|
||||
class="btn my-6 w-full" tabindex="2">I am 14 or older</button>
|
||||
@break
|
||||
|
||||
@case(3)
|
||||
@if ($form->getValue('age') == 'over')
|
||||
<p>Please enter your email address so we can verify your account</p>
|
||||
@else
|
||||
<p>Please find a parent or guardian's email address, and we can verify your account</p>
|
||||
@endif
|
||||
|
||||
<div class="floating-label my-6">
|
||||
<input type="email" class="w-full rounded border border-gray-200 p-2" name="email"
|
||||
value="{{ old('email') }}" required autocomplete="off" spellcheck="false" autocorrect="off"
|
||||
autofocus />
|
||||
@if ($form->getValue('age') == 'over')
|
||||
<label for="email" class="mb-1 inline-block text-sm text-gray-800">Your email</label>
|
||||
@else
|
||||
<label for="email" class="mb-1 inline-block text-sm text-gray-800">Parent or guardian's
|
||||
email</label>
|
||||
@endif
|
||||
@error('email')
|
||||
<p class="error">{{ $message }}</p>
|
||||
@enderror
|
||||
</div>
|
||||
@break
|
||||
|
||||
@case(4)
|
||||
@include('partials.email-verify', ['resend' => true])
|
||||
@break
|
||||
|
||||
@endswitch
|
||||
|
||||
@if ($form->currentStep() < 4)
|
||||
<div class="flex items-center justify-between">
|
||||
@if ($form->currentStep() == 1)
|
||||
<small class="text-xs text-gray-600">Already have an account? <a href="{{ route('login') }}"
|
||||
class="text-blue transition hover:text-blue-dark">Login</a></small>
|
||||
@elseif ($form->currentStep() < 4)
|
||||
<a href="{{ route('register', ['form_step' => $form->currentStep() - 1]) }}"
|
||||
class="text-blue transition hover:text-blue-dark">
|
||||
<i class="fa-solid fa-angle-left mr-2"></i>Back</a>
|
||||
@endif
|
||||
|
||||
@if ($form->currentStep() == 1)
|
||||
<button type="submit"
|
||||
class="rounded bg-green px-8 py-2 text-white transition hover:bg-green-dark">
|
||||
Next<i class="fa-solid fa-angle-right ml-2"></i>
|
||||
</button>
|
||||
@elseif ($form->currentStep() == 3)
|
||||
<button type="submit"
|
||||
class="rounded bg-green px-8 py-2 text-white transition hover:bg-green-dark">
|
||||
Verify
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{ $form->getValue('error') }}
|
||||
</form>
|
||||
</x-card>
|
||||
</x-layout>
|
||||
13
resources/views/users/verify.blade.php
Normal file
13
resources/views/users/verify.blade.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<x-layout>
|
||||
<x-card class="relative mx-auto mt-12 max-w-lg shadow-lg">
|
||||
<header>
|
||||
<h2 class="-m-6 mb-6 rounded-t-lg bg-green px-6 py-4 text-xl text-white">Sign up to STEMMechanics</h2>
|
||||
</header>
|
||||
|
||||
<form method="POST" action="/verify" id="verification-form">
|
||||
@csrf
|
||||
@include('partials.email-verify')
|
||||
|
||||
</form>
|
||||
</x-card>
|
||||
</x-layout>
|
||||
46
resources/views/vendor/pagination/bootstrap-4.blade.php
vendored
Normal file
46
resources/views/vendor/pagination/bootstrap-4.blade.php
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
@if ($paginator->hasPages())
|
||||
<nav>
|
||||
<ul class="pagination">
|
||||
{{-- Previous Page Link --}}
|
||||
@if ($paginator->onFirstPage())
|
||||
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.previous')">
|
||||
<span class="page-link" aria-hidden="true">‹</span>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')">‹</a>
|
||||
</li>
|
||||
@endif
|
||||
|
||||
{{-- Pagination Elements --}}
|
||||
@foreach ($elements as $element)
|
||||
{{-- "Three Dots" Separator --}}
|
||||
@if (is_string($element))
|
||||
<li class="page-item disabled" aria-disabled="true"><span class="page-link">{{ $element }}</span></li>
|
||||
@endif
|
||||
|
||||
{{-- Array Of Links --}}
|
||||
@if (is_array($element))
|
||||
@foreach ($element as $page => $url)
|
||||
@if ($page == $paginator->currentPage())
|
||||
<li class="page-item active" aria-current="page"><span class="page-link">{{ $page }}</span></li>
|
||||
@else
|
||||
<li class="page-item"><a class="page-link" href="{{ $url }}">{{ $page }}</a></li>
|
||||
@endif
|
||||
@endforeach
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
{{-- Next Page Link --}}
|
||||
@if ($paginator->hasMorePages())
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next" aria-label="@lang('pagination.next')">›</a>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.next')">
|
||||
<span class="page-link" aria-hidden="true">›</span>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</nav>
|
||||
@endif
|
||||
88
resources/views/vendor/pagination/bootstrap-5.blade.php
vendored
Normal file
88
resources/views/vendor/pagination/bootstrap-5.blade.php
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
@if ($paginator->hasPages())
|
||||
<nav class="d-flex justify-items-center justify-content-between">
|
||||
<div class="d-flex justify-content-between flex-fill d-sm-none">
|
||||
<ul class="pagination">
|
||||
{{-- Previous Page Link --}}
|
||||
@if ($paginator->onFirstPage())
|
||||
<li class="page-item disabled" aria-disabled="true">
|
||||
<span class="page-link">@lang('pagination.previous')</span>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev">@lang('pagination.previous')</a>
|
||||
</li>
|
||||
@endif
|
||||
|
||||
{{-- Next Page Link --}}
|
||||
@if ($paginator->hasMorePages())
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next">@lang('pagination.next')</a>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item disabled" aria-disabled="true">
|
||||
<span class="page-link">@lang('pagination.next')</span>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="d-none flex-sm-fill d-sm-flex align-items-sm-center justify-content-sm-between">
|
||||
<div>
|
||||
<p class="small text-muted">
|
||||
{!! __('Showing') !!}
|
||||
<span class="fw-semibold">{{ $paginator->firstItem() }}</span>
|
||||
{!! __('to') !!}
|
||||
<span class="fw-semibold">{{ $paginator->lastItem() }}</span>
|
||||
{!! __('of') !!}
|
||||
<span class="fw-semibold">{{ $paginator->total() }}</span>
|
||||
{!! __('results') !!}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<ul class="pagination">
|
||||
{{-- Previous Page Link --}}
|
||||
@if ($paginator->onFirstPage())
|
||||
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.previous')">
|
||||
<span class="page-link" aria-hidden="true">‹</span>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')">‹</a>
|
||||
</li>
|
||||
@endif
|
||||
|
||||
{{-- Pagination Elements --}}
|
||||
@foreach ($elements as $element)
|
||||
{{-- "Three Dots" Separator --}}
|
||||
@if (is_string($element))
|
||||
<li class="page-item disabled" aria-disabled="true"><span class="page-link">{{ $element }}</span></li>
|
||||
@endif
|
||||
|
||||
{{-- Array Of Links --}}
|
||||
@if (is_array($element))
|
||||
@foreach ($element as $page => $url)
|
||||
@if ($page == $paginator->currentPage())
|
||||
<li class="page-item active" aria-current="page"><span class="page-link">{{ $page }}</span></li>
|
||||
@else
|
||||
<li class="page-item"><a class="page-link" href="{{ $url }}">{{ $page }}</a></li>
|
||||
@endif
|
||||
@endforeach
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
{{-- Next Page Link --}}
|
||||
@if ($paginator->hasMorePages())
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next" aria-label="@lang('pagination.next')">›</a>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item disabled" aria-disabled="true" aria-label="@lang('pagination.next')">
|
||||
<span class="page-link" aria-hidden="true">›</span>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@endif
|
||||
46
resources/views/vendor/pagination/default.blade.php
vendored
Normal file
46
resources/views/vendor/pagination/default.blade.php
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
@if ($paginator->hasPages())
|
||||
<nav>
|
||||
<ul class="pagination">
|
||||
{{-- Previous Page Link --}}
|
||||
@if ($paginator->onFirstPage())
|
||||
<li class="disabled" aria-disabled="true" aria-label="@lang('pagination.previous')">
|
||||
<span aria-hidden="true">‹</span>
|
||||
</li>
|
||||
@else
|
||||
<li>
|
||||
<a href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')">‹</a>
|
||||
</li>
|
||||
@endif
|
||||
|
||||
{{-- Pagination Elements --}}
|
||||
@foreach ($elements as $element)
|
||||
{{-- "Three Dots" Separator --}}
|
||||
@if (is_string($element))
|
||||
<li class="disabled" aria-disabled="true"><span>{{ $element }}</span></li>
|
||||
@endif
|
||||
|
||||
{{-- Array Of Links --}}
|
||||
@if (is_array($element))
|
||||
@foreach ($element as $page => $url)
|
||||
@if ($page == $paginator->currentPage())
|
||||
<li class="active" aria-current="page"><span>{{ $page }}</span></li>
|
||||
@else
|
||||
<li><a href="{{ $url }}">{{ $page }}</a></li>
|
||||
@endif
|
||||
@endforeach
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
{{-- Next Page Link --}}
|
||||
@if ($paginator->hasMorePages())
|
||||
<li>
|
||||
<a href="{{ $paginator->nextPageUrl() }}" rel="next" aria-label="@lang('pagination.next')">›</a>
|
||||
</li>
|
||||
@else
|
||||
<li class="disabled" aria-disabled="true" aria-label="@lang('pagination.next')">
|
||||
<span aria-hidden="true">›</span>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</nav>
|
||||
@endif
|
||||
36
resources/views/vendor/pagination/semantic-ui.blade.php
vendored
Normal file
36
resources/views/vendor/pagination/semantic-ui.blade.php
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
@if ($paginator->hasPages())
|
||||
<div class="ui pagination menu" role="navigation">
|
||||
{{-- Previous Page Link --}}
|
||||
@if ($paginator->onFirstPage())
|
||||
<a class="icon item disabled" aria-disabled="true" aria-label="@lang('pagination.previous')"> <i class="left chevron icon"></i> </a>
|
||||
@else
|
||||
<a class="icon item" href="{{ $paginator->previousPageUrl() }}" rel="prev" aria-label="@lang('pagination.previous')"> <i class="left chevron icon"></i> </a>
|
||||
@endif
|
||||
|
||||
{{-- Pagination Elements --}}
|
||||
@foreach ($elements as $element)
|
||||
{{-- "Three Dots" Separator --}}
|
||||
@if (is_string($element))
|
||||
<a class="icon item disabled" aria-disabled="true">{{ $element }}</a>
|
||||
@endif
|
||||
|
||||
{{-- Array Of Links --}}
|
||||
@if (is_array($element))
|
||||
@foreach ($element as $page => $url)
|
||||
@if ($page == $paginator->currentPage())
|
||||
<a class="item active" href="{{ $url }}" aria-current="page">{{ $page }}</a>
|
||||
@else
|
||||
<a class="item" href="{{ $url }}">{{ $page }}</a>
|
||||
@endif
|
||||
@endforeach
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
{{-- Next Page Link --}}
|
||||
@if ($paginator->hasMorePages())
|
||||
<a class="icon item" href="{{ $paginator->nextPageUrl() }}" rel="next" aria-label="@lang('pagination.next')"> <i class="right chevron icon"></i> </a>
|
||||
@else
|
||||
<a class="icon item disabled" aria-disabled="true" aria-label="@lang('pagination.next')"> <i class="right chevron icon"></i> </a>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
27
resources/views/vendor/pagination/simple-bootstrap-4.blade.php
vendored
Normal file
27
resources/views/vendor/pagination/simple-bootstrap-4.blade.php
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
@if ($paginator->hasPages())
|
||||
<nav>
|
||||
<ul class="pagination">
|
||||
{{-- Previous Page Link --}}
|
||||
@if ($paginator->onFirstPage())
|
||||
<li class="page-item disabled" aria-disabled="true">
|
||||
<span class="page-link">@lang('pagination.previous')</span>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev">@lang('pagination.previous')</a>
|
||||
</li>
|
||||
@endif
|
||||
|
||||
{{-- Next Page Link --}}
|
||||
@if ($paginator->hasMorePages())
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next">@lang('pagination.next')</a>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item disabled" aria-disabled="true">
|
||||
<span class="page-link">@lang('pagination.next')</span>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</nav>
|
||||
@endif
|
||||
29
resources/views/vendor/pagination/simple-bootstrap-5.blade.php
vendored
Normal file
29
resources/views/vendor/pagination/simple-bootstrap-5.blade.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
@if ($paginator->hasPages())
|
||||
<nav role="navigation" aria-label="Pagination Navigation">
|
||||
<ul class="pagination">
|
||||
{{-- Previous Page Link --}}
|
||||
@if ($paginator->onFirstPage())
|
||||
<li class="page-item disabled" aria-disabled="true">
|
||||
<span class="page-link">{!! __('pagination.previous') !!}</span>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{{ $paginator->previousPageUrl() }}" rel="prev">
|
||||
{!! __('pagination.previous') !!}
|
||||
</a>
|
||||
</li>
|
||||
@endif
|
||||
|
||||
{{-- Next Page Link --}}
|
||||
@if ($paginator->hasMorePages())
|
||||
<li class="page-item">
|
||||
<a class="page-link" href="{{ $paginator->nextPageUrl() }}" rel="next">{!! __('pagination.next') !!}</a>
|
||||
</li>
|
||||
@else
|
||||
<li class="page-item disabled" aria-disabled="true">
|
||||
<span class="page-link">{!! __('pagination.next') !!}</span>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</nav>
|
||||
@endif
|
||||
19
resources/views/vendor/pagination/simple-default.blade.php
vendored
Normal file
19
resources/views/vendor/pagination/simple-default.blade.php
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
@if ($paginator->hasPages())
|
||||
<nav>
|
||||
<ul class="pagination">
|
||||
{{-- Previous Page Link --}}
|
||||
@if ($paginator->onFirstPage())
|
||||
<li class="disabled" aria-disabled="true"><span>@lang('pagination.previous')</span></li>
|
||||
@else
|
||||
<li><a href="{{ $paginator->previousPageUrl() }}" rel="prev">@lang('pagination.previous')</a></li>
|
||||
@endif
|
||||
|
||||
{{-- Next Page Link --}}
|
||||
@if ($paginator->hasMorePages())
|
||||
<li><a href="{{ $paginator->nextPageUrl() }}" rel="next">@lang('pagination.next')</a></li>
|
||||
@else
|
||||
<li class="disabled" aria-disabled="true"><span>@lang('pagination.next')</span></li>
|
||||
@endif
|
||||
</ul>
|
||||
</nav>
|
||||
@endif
|
||||
29
resources/views/vendor/pagination/simple-tailwind.blade.php
vendored
Normal file
29
resources/views/vendor/pagination/simple-tailwind.blade.php
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
@if ($paginator->hasPages())
|
||||
<nav role="navigation" aria-label="Pagination Navigation" class="flex justify-between">
|
||||
{{-- Previous Page Link --}}
|
||||
@if ($paginator->onFirstPage())
|
||||
<span
|
||||
class="relative inline-flex cursor-default items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium leading-5 text-gray-500">
|
||||
{!! __('pagination.previous') !!}
|
||||
</span>
|
||||
@else
|
||||
<a href="{{ $paginator->previousPageUrl() }}" rel="prev"
|
||||
class="focus:border-blue-300 relative inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium leading-5 text-gray-700 ring-gray-300 transition duration-150 ease-in-out hover:text-gray-500 focus:outline-none focus:ring active:bg-gray-100 active:text-gray-700">
|
||||
{!! __('pagination.previous') !!}
|
||||
</a>
|
||||
@endif
|
||||
|
||||
{{-- Next Page Link --}}
|
||||
@if ($paginator->hasMorePages())
|
||||
<a href="{{ $paginator->nextPageUrl() }}" rel="next"
|
||||
class="focus:border-blue-300 relative inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium leading-5 text-gray-700 ring-gray-300 transition duration-150 ease-in-out hover:text-gray-500 focus:outline-none focus:ring active:bg-gray-100 active:text-gray-700">
|
||||
{!! __('pagination.next') !!}
|
||||
</a>
|
||||
@else
|
||||
<span
|
||||
class="relative inline-flex cursor-default items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium leading-5 text-gray-500">
|
||||
{!! __('pagination.next') !!}
|
||||
</span>
|
||||
@endif
|
||||
</nav>
|
||||
@endif
|
||||
46
resources/views/vendor/pagination/tailwind.blade.php
vendored
Normal file
46
resources/views/vendor/pagination/tailwind.blade.php
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
@if ($paginator->hasPages())
|
||||
<nav role="navigation" aria-label="Pagination Navigation" class="flex items-center justify-between">
|
||||
{{-- Previous Page Link --}}
|
||||
@if ($paginator->onFirstPage())
|
||||
<span
|
||||
class="relative inline-flex cursor-default items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium leading-5 text-gray-500">
|
||||
{!! __('pagination.previous') !!}
|
||||
</span>
|
||||
@else
|
||||
<a href="{{ $paginator->previousPageUrl() }}" rel="prev"
|
||||
class="focus:border-blue-300 relative inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium leading-5 text-gray-700 ring-gray-300 transition duration-150 ease-in-out hover:text-gray-500 focus:outline-none focus:ring active:bg-gray-100 active:text-gray-700">
|
||||
{!! __('pagination.previous') !!}
|
||||
</a>
|
||||
@endif
|
||||
|
||||
<div>
|
||||
<p class="text-xs leading-5 text-gray-500">
|
||||
{!! __('Showing') !!}
|
||||
@if ($paginator->firstItem())
|
||||
<span class="font-medium">{{ $paginator->firstItem() }}</span>
|
||||
{!! __('to') !!}
|
||||
<span class="font-medium">{{ $paginator->lastItem() }}</span>
|
||||
@else
|
||||
{{ $paginator->count() }}
|
||||
@endif
|
||||
{!! __('of') !!}
|
||||
<span class="font-medium">{{ $paginator->total() }}</span>
|
||||
{!! __('results') !!}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
{{-- Next Page Link --}}
|
||||
@if ($paginator->hasMorePages())
|
||||
<a href="{{ $paginator->nextPageUrl() }}" rel="next"
|
||||
class="focus:border-blue-300 relative inline-flex items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium leading-5 text-gray-700 ring-gray-300 transition duration-150 ease-in-out hover:text-gray-500 focus:outline-none focus:ring active:bg-gray-100 active:text-gray-700">
|
||||
{!! __('pagination.next') !!}
|
||||
</a>
|
||||
@else
|
||||
<span
|
||||
class="relative inline-flex cursor-default items-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium leading-5 text-gray-500">
|
||||
{!! __('pagination.next') !!}
|
||||
</span>
|
||||
@endif
|
||||
</nav>
|
||||
@endif
|
||||
20
resources/views/workshops/index.blade.php
Normal file
20
resources/views/workshops/index.blade.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<x-layout>
|
||||
@include('partials.search')
|
||||
|
||||
<div class="mx-4 justify-start gap-4 space-y-4 sm:grid sm:grid-cols-2 sm:space-y-0">
|
||||
|
||||
@unless (count($workshops) == 0)
|
||||
|
||||
@foreach ($workshops as $workshop)
|
||||
<x-workshop-card :workshop="$workshop" />
|
||||
@endforeach
|
||||
@else
|
||||
<p>No workshops found</p>
|
||||
@endunless
|
||||
|
||||
</div>
|
||||
|
||||
<div class="mt-6 p-4">
|
||||
{{ $workshops->links() }}
|
||||
</div>
|
||||
</x-layout>
|
||||
Reference in New Issue
Block a user