Compare commits

..

1 Commits

Author SHA1 Message Date
Shift
2f7d5febdf Add .shift to open Pull Request 2025-11-16 21:17:12 +00:00
21 changed files with 2980 additions and 2725 deletions

View File

@@ -1,63 +0,0 @@
name: Dependency Update
on:
schedule:
- cron: "0 20 * * 1" # Mondays 20:00 UTC
workflow_dispatch: {}
permissions:
contents: write
pull-requests: write
jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- uses: shivammathur/setup-php@v2
with:
php-version: "8.4"
tools: composer:v2
- name: Install current deps
run: |
npm ci
composer install --no-interaction --no-progress
- name: Update deps
run: |
npm update
composer update --no-interaction --no-progress
- name: Detect changes
id: changes
run: |
if git diff --quiet; then
echo "changed=false" >> "$GITHUB_OUTPUT"
else
echo "changed=true" >> "$GITHUB_OUTPUT"
fi
# - name: Create PR
# if: steps.changes.outputs.changed == 'true'
# uses: peter-evans/create-pull-request@v6
# with:
# commit-message: "Dependency update"
# title: "Dependency update"
# body: "Automated dependency update."
# branch: "bot/dependency-update"
# delete-branch: true
- name: Commit and push
if: steps.changes.outputs.changed == 'true'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git commit -am "Dependency update"
git push origin HEAD:main

4
.shift Normal file
View File

@@ -0,0 +1,4 @@
This file was added by Shift #161875 in order to open a
Pull Request since no other commits were made.
You should remove this file.

View File

@@ -111,17 +111,15 @@ class MediaController extends Controller
public function admin_store(Request $request) public function admin_store(Request $request)
{ {
$file = null; $file = null;
$cleanupPath = null;
// Check if the endpoint received a file... // Check if the endpoint received a file...
if($request->hasFile('file')) { if($request->hasFile('file')) {
try { try {
$file = $this->upload($request); $file = $this->upload($request);
if(is_array($file) && !empty($file['chunk'])) { if($file === true) {
return response()->json([ return response()->json([
'message' => 'Chunk stored', 'message' => 'Chunk stored',
'upload_token' => $file['token'] ?? null,
]); ]);
} else if(!$file) { } else if(!$file) {
return response()->json([ return response()->json([
@@ -152,20 +150,8 @@ class MediaController extends Controller
} }
// else check if it received a file name of a previous upload... // else check if it received a file name of a previous upload...
} else if($request->has('upload_token') || $request->has('file')) { } else if($request->has('file')) {
$uploadToken = $request->input('upload_token', $request->input('file')); $tempFileName = sys_get_temp_dir() . '/chunk-' . Auth::id() . '-' . $request->file;
$chunkUploads = session()->get('chunk_uploads', []);
if(!is_string($uploadToken) || !isset($chunkUploads[$uploadToken])) {
return response()->json([
'message' => 'Could not find the referenced file on the server.',
'errors' => [
'file' => 'Could not find the referenced file on the server.'
]
], 422);
}
$tempFileName = $chunkUploads[$uploadToken];
if(!file_exists($tempFileName)) { if(!file_exists($tempFileName)) {
return response()->json([ return response()->json([
'message' => 'Could not find the referenced file on the server.', 'message' => 'Could not find the referenced file on the server.',
@@ -179,16 +165,7 @@ class MediaController extends Controller
if($fileMime === false) { if($fileMime === false) {
$fileMime = 'application/octet-stream'; $fileMime = 'application/octet-stream';
} }
$fileName = $request->input('filename', 'upload'); $file = new UploadedFile($tempFileName, $request->file, $fileMime, null, true);
$fileName = Helpers::cleanFileName($fileName);
if ($fileName === '') {
$fileName = 'upload';
}
$file = new UploadedFile($tempFileName, $fileName, $fileMime, null, true);
$cleanupPath = $tempFileName;
unset($chunkUploads[$uploadToken]);
session()->put('chunk_uploads', $chunkUploads);
} }
// Check there is an actual file // Check there is an actual file
@@ -265,13 +242,7 @@ class MediaController extends Controller
} }
} }
if(is_string($cleanupPath)) { unlink($file->getRealPath());
$realPath = realpath($cleanupPath);
$tempDir = realpath(sys_get_temp_dir());
if($realPath !== false && $tempDir !== false && str_starts_with($realPath, $tempDir . DIRECTORY_SEPARATOR)) {
@unlink($realPath);
}
}
if($request->wantsJson()) { if($request->wantsJson()) {
return response()->json([ return response()->json([
@@ -415,10 +386,6 @@ class MediaController extends Controller
$fileName = $request->input('filename', $file->getClientOriginalName()); $fileName = $request->input('filename', $file->getClientOriginalName());
$fileName = Helpers::cleanFileName($fileName); $fileName = Helpers::cleanFileName($fileName);
if ($fileName === '') {
$extension = strtolower($file->getClientOriginalExtension());
$fileName = 'upload' . ($extension !== '' ? '.' . $extension : '');
}
if(($request->has('filestart') || $request->has('fileappend')) && $request->has('filesize')) { if(($request->has('filestart') || $request->has('fileappend')) && $request->has('filesize')) {
$fileSize = $request->get('filesize'); $fileSize = $request->get('filesize');
@@ -427,25 +394,7 @@ class MediaController extends Controller
throw new FileTooLargeException('The file is larger than the maximum size allowed of ' . Helpers::bytesToString($max_size)); throw new FileTooLargeException('The file is larger than the maximum size allowed of ' . Helpers::bytesToString($max_size));
} }
$chunkUploads = session()->get('chunk_uploads', []); $tempFilePath = sys_get_temp_dir() . '/chunk-' . Auth::id() . '-' . $fileName;
$uploadToken = $request->input('upload_token');
if($request->has('filestart')) {
$uploadToken = bin2hex(random_bytes(16));
$tempFilePath = tempnam(sys_get_temp_dir(), 'chunk-' . Auth::id() . '-');
if($tempFilePath === false) {
throw new FileInvalidException('Unable to create a temporary upload file.');
}
$chunkUploads[$uploadToken] = $tempFilePath;
session()->put('chunk_uploads', $chunkUploads);
} else {
if(!is_string($uploadToken) || !isset($chunkUploads[$uploadToken])) {
throw new FileInvalidException('Invalid upload token.');
}
$tempFilePath = $chunkUploads[$uploadToken];
}
$filemode = 'a'; $filemode = 'a';
if($request->has('filestart')) { if($request->has('filestart')) {
@@ -466,17 +415,9 @@ class MediaController extends Controller
$fileMime = 'application/octet-stream'; $fileMime = 'application/octet-stream';
} }
if(is_string($uploadToken) && isset($chunkUploads[$uploadToken])) {
unset($chunkUploads[$uploadToken]);
session()->put('chunk_uploads', $chunkUploads);
}
return new UploadedFile($tempFilePath, $fileName, $fileMime, null, true); return new UploadedFile($tempFilePath, $fileName, $fileMime, null, true);
} else { } else {
return [ return true;
'chunk' => true,
'token' => $uploadToken,
];
} }
} }

View File

@@ -34,23 +34,23 @@ class SearchController extends Controller
$workshops = $workshopQuery->orderBy('starts_at', 'desc') $workshops = $workshopQuery->orderBy('starts_at', 'desc')
->paginate(6, ['*'], 'workshop'); ->paginate(6, ['*'], 'workshop');
// $postQuery = Post::query()->where('status', 'published'); $postQuery = Post::query()->where('status', 'published');
// $postQuery->where(function ($query) use ($search_words) { $postQuery->where(function ($query) use ($search_words) {
// foreach ($search_words as $word) { foreach ($search_words as $word) {
// $query->where(function ($subQuery) use ($word) { $query->where(function ($subQuery) use ($word) {
// $subQuery->where('title', 'like', '%' . $word . '%') $subQuery->where('title', 'like', '%' . $word . '%')
// ->orWhere('content', 'like', '%' . $word . '%'); ->orWhere('content', 'like', '%' . $word . '%');
// }); });
// } }
// }); });
//
// $posts = $postQuery->orderBy('created_at', 'desc') $posts = $postQuery->orderBy('created_at', 'desc')
// ->paginate(6, ['*'], 'post') ->paginate(6, ['*'], 'post')
// ->onEachSide(1); ->onEachSide(1);
return view('search', [ return view('search', [
'workshops' => $workshops, 'workshops' => $workshops,
// 'posts' => $posts, 'posts' => $posts,
'search' => $search, 'search' => $search,
]); ]);
} }

View File

@@ -5,14 +5,14 @@
"keywords": ["laravel", "framework"], "keywords": ["laravel", "framework"],
"license": "MIT", "license": "MIT",
"require": { "require": {
"php": "^8.4", "php": "^8.2",
"ext-imagick": "*", "ext-imagick": "*",
"chillerlan/php-qrcode": "^5.0", "chillerlan/php-qrcode": "^5.0",
"gehrisandro/tailwind-merge-laravel": "^1.2", "gehrisandro/tailwind-merge-laravel": "^1.2",
"intervention/image": "^3.5", "intervention/image": "^3.5",
"laravel/framework": "^12.0", "laravel/framework": "^11.0",
"laravel/tinker": "^2.9", "laravel/tinker": "^2.9",
"livewire/livewire": "^4.1", "livewire/livewire": "^3.4",
"php-ffmpeg/php-ffmpeg": "^1.2", "php-ffmpeg/php-ffmpeg": "^1.2",
"robthree/twofactorauth": "^3.0" "robthree/twofactorauth": "^3.0"
}, },
@@ -22,7 +22,8 @@
"laravel/pint": "^1.13", "laravel/pint": "^1.13",
"laravel/sail": "^1.26", "laravel/sail": "^1.26",
"mockery/mockery": "^1.6", "mockery/mockery": "^1.6",
"phpunit/phpunit": "^12.5", "nunomaduro/collision": "^8.0",
"phpunit/phpunit": "^10.5",
"spatie/laravel-ignition": "^2.4" "spatie/laravel-ignition": "^2.4"
}, },
"autoload": { "autoload": {
@@ -60,9 +61,6 @@
} }
}, },
"config": { "config": {
"platform": {
"php": "8.4.17"
},
"optimize-autoloader": true, "optimize-autoloader": true,
"preferred-install": "dist", "preferred-install": "dist",
"sort-packages": true, "sort-packages": true,

2285
composer.lock generated

File diff suppressed because it is too large Load Diff

3021
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,29 +3,27 @@
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build"
"snyk:test": "snyk test --all-projects --exclude=vendor"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/vite": "^4.1.17",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"axios": "^1.13.4", "axios": "^1.6.4",
"laravel-vite-plugin": "^2.0.1", "laravel-vite-plugin": "^1.0",
"postcss": "^8.4.38", "postcss": "^8.4.38",
"vite": "^7.3.0" "tailwindcss": "^3.4.3",
"vite": "^5.0"
}, },
"dependencies": { "dependencies": {
"@tiptap/core": "^3.10.7", "@tiptap/core": "^2.3.0",
"@tiptap/extension-highlight": "^3.18.0", "@tiptap/extension-highlight": "^2.3.0",
"@tiptap/extension-image": "^3.10.7", "@tiptap/extension-image": "^2.3.0",
"@tiptap/extension-link": "^3.18.0", "@tiptap/extension-link": "^2.3.0",
"@tiptap/extension-subscript": "^3.10.7", "@tiptap/extension-subscript": "^2.3.0",
"@tiptap/extension-superscript": "^3.18.0", "@tiptap/extension-superscript": "^2.3.0",
"@tiptap/extension-text-align": "^3.10.7", "@tiptap/extension-text-align": "^2.3.0",
"@tiptap/extension-typography": "^3.10.7", "@tiptap/extension-typography": "^2.3.0",
"@tiptap/extension-underline": "^3.18.0", "@tiptap/extension-underline": "^2.3.0",
"@tiptap/pm": "^3.10.7", "@tiptap/pm": "^2.3.0",
"@tiptap/starter-kit": "^3.10.7", "@tiptap/starter-kit": "^2.3.0"
"tailwindcss": "^4.1.17"
} }
} }

6
postcss.config.js Normal file
View File

@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View File

@@ -1,26 +1,4 @@
let SM = { let SM = {
redirectIfSafe: (target) => {
if (typeof target !== 'string' || target === '') {
window.location.assign('/');
return;
}
let url;
try {
url = new URL(target, window.location.origin);
} catch (error) {
window.location.assign('/');
return;
}
if (url.origin !== window.location.origin) {
window.location.assign('/');
return;
}
window.location.assign(url.href);
},
alert: (title, text, type = 'info') =>{ alert: (title, text, type = 'info') =>{
const data = { const data = {
position: 'top-end', position: 'top-end',
@@ -106,7 +84,7 @@ let SM = {
axios.delete(url) axios.delete(url)
.then((response) => { .then((response) => {
if(response.data.success){ if(response.data.success){
SM.redirectIfSafe(response.data.redirect); window.location.href = response.data.redirect;
} }
}) })
.catch(() => { .catch(() => {
@@ -158,7 +136,7 @@ let SM = {
} }
} }
const uploadFile = (file, start, title, idx, count, uploadToken = null) => { const uploadFile = (file, start, title, idx, count) => {
const showPercentDecimals = (file.size > (1024 * 1024 * 40)); const showPercentDecimals = (file.size > (1024 * 1024 * 40));
const chunkSize = 1024 * 1024 * 2; const chunkSize = 1024 * 1024 * 2;
const end = Math.min(file.size, start + chunkSize); const end = Math.min(file.size, start + chunkSize);
@@ -168,9 +146,6 @@ let SM = {
formData.append('file', chunk); formData.append('file', chunk);
formData.append('filename', file.name); formData.append('filename', file.name);
formData.append('filesize', file.size); formData.append('filesize', file.size);
if (uploadToken) {
formData.append('upload_token', uploadToken);
}
if (start === 0) { if (start === 0) {
formData.append('filestart', 'true'); formData.append('filestart', 'true');
@@ -208,10 +183,6 @@ let SM = {
} }
}).then((response) => { }).then((response) => {
if (response.status === 200) { if (response.status === 200) {
if (response.data && response.data.upload_token) {
uploadToken = response.data.upload_token;
}
if (end >= file.size) { if (end >= file.size) {
uploadedFiles.push({ file: file, title: title, data: response.data }); uploadedFiles.push({ file: file, title: title, data: response.data });
@@ -234,13 +205,12 @@ let SM = {
} else { } else {
start = 0; start = 0;
idx += 1; idx += 1;
uploadToken = null;
} }
} else { } else {
start = end; start = end;
} }
uploadFile(files[idx], start, titles[idx] || '', idx, files.length, uploadToken); uploadFile(files[idx], start, titles[idx] || '', idx, files.length);
} else { } else {
showError(response.data.message); showError(response.data.message);
} }

View File

@@ -1,24 +1,6 @@
@import "tailwindcss"; @tailwind base;
@tailwind components;
@source "../**/*.{blade.php,js,php,vue}"; @tailwind utilities;
@theme {
/* Color tokens */
--color-primary-color: #0284C7;
--color-primary-color-dark: #0370A1;
--color-primary-color-light: #0EA5E9;
--color-danger-color: #b91c1c;
--color-danger-color-dark: #991b1b;
--color-danger-color-light: #dc2626;
--color-success-color: #16a34a;
--color-success-color-dark: #22c55e;
--color-success-color-light: #4ade80;
/* Box shadows */
--shadow-deep: 0 10px 15px rgba(0, 0, 0, 0.5);
}
html, body { html, body {
@apply bg-gray-100; @apply bg-gray-100;

View File

@@ -1,7 +1,6 @@
import Link from "@tiptap/extension-link"; import Link from "@tiptap/extension-link";
import {Editor} from "@tiptap/core"; import {Editor} from "@tiptap/core";
import StarterKit from "@tiptap/starter-kit"; import StarterKit from "@tiptap/starter-kit";
import Underline from "@tiptap/extension-underline";
import Highlight from "@tiptap/extension-highlight"; import Highlight from "@tiptap/extension-highlight";
import TextAlign from "@tiptap/extension-text-align"; import TextAlign from "@tiptap/extension-text-align";
import Typography from "@tiptap/extension-typography"; import Typography from "@tiptap/extension-typography";
@@ -50,16 +49,12 @@ document.addEventListener('alpine:init', () => {
editor = new Editor({ editor = new Editor({
element: this.$refs.element, element: this.$refs.element,
extensions: [ extensions: [
StarterKit.configure({ StarterKit,
link: false,
underline: false,
}),
Highlight, Highlight,
CustomLink.configure({ CustomLink.configure({
rel: 'noopener noreferrer', rel: 'noopener noreferrer',
openOnClick: 'whenNotEditable', openOnClick: 'whenNotEditable',
}), }),
Underline,
TextAlign.configure({ TextAlign.configure({
types: ['heading', 'paragraph', 'small', 'extraSmall'], types: ['heading', 'paragraph', 'small', 'extraSmall'],
}), }),
@@ -70,11 +65,6 @@ document.addEventListener('alpine:init', () => {
ExtraSmall, ExtraSmall,
Box Box
], ],
editorProps: {
attributes: {
class: 'tiptap content',
},
},
content: content, content: content,
onCreate({/* editor */}) { onCreate({/* editor */}) {
_this.updatedAt = Date.now() _this.updatedAt = Date.now()
@@ -151,9 +141,6 @@ document.addEventListener('alpine:init', () => {
unsetAllMarks() { unsetAllMarks() {
editor.chain().focus().unsetAllMarks().run() editor.chain().focus().unsetAllMarks().run()
}, },
clearNodes() {
editor.chain().focus().clearNodes().run()
},
clearNotes() { clearNotes() {
editor.chain().focus().clearNodes().run() editor.chain().focus().clearNodes().run()
}, },

View File

@@ -7,7 +7,6 @@
<li><a href="https://discord.gg/yNzk4x7mpD" class="text-sm hover:text-primary-color">Discord</a></li> <li><a href="https://discord.gg/yNzk4x7mpD" class="text-sm hover:text-primary-color">Discord</a></li>
<li><a href="https://www.facebook.com/stemmechanics" class="text-sm hover:text-primary-color">Facebook</a></li> <li><a href="https://www.facebook.com/stemmechanics" class="text-sm hover:text-primary-color">Facebook</a></li>
<li><a href="https://www.stemcraft.com.au/" class="text-sm hover:text-primary-color">STEMCraft (Minecraft)</a></li> <li><a href="https://www.stemcraft.com.au/" class="text-sm hover:text-primary-color">STEMCraft (Minecraft)</a></li>
<li><a href="https://jenkins.stemmechanics.com.au/" class="text-sm hover:text-primary-color">Jenkins</a></li>
<li><a href="https://youtube.com/@STEMMechanics" class="text-sm hover:text-primary-color">YouTube</a></li> <li><a href="https://youtube.com/@STEMMechanics" class="text-sm hover:text-primary-color">YouTube</a></li>
</ul> </ul>
<ul class="sm:w-1/3 flex flex-col gap-0.5 text-center sm:text-left"> <ul class="sm:w-1/3 flex flex-col gap-0.5 text-center sm:text-left">

View File

@@ -43,7 +43,7 @@
</div> </div>
<div x-show="pageMenuOpen" @click.away="pageMenuOpen=false" x-cloak class="fixed left-0 top-0 h-full w-full z-20" role="menu" aria-labelledby="page-menu-button" tabindex="-1"> <div x-show="pageMenuOpen" @click.away="pageMenuOpen=false" x-cloak class="fixed left-0 top-0 h-full w-full z-20" role="menu" aria-labelledby="page-menu-button" tabindex="-1">
<div x-show="pageMenuOpen" @click="pageMenuOpen=false" class="absolute inset-0 bg-black/40 backdrop-blur-sm" <div x-show="pageMenuOpen" @click="pageMenuOpen=false" class="absolute inset-0 bg-black bg-opacity-40 backdrop-blur-sm"
x-transition:enter="transition ease-out duration-300" x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0" x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100" x-transition:enter-end="opacity-100"
@@ -90,7 +90,7 @@
@click.away="userMenuOpen=false" @click.away="userMenuOpen=false"
x-cloak x-cloak
> >
<div x-show="userMenuOpen" @click="userMenuOpen=false" class="fixed left-0 w-screen z-20 h-screen bg-black/40 backdrop-blur-sm" <div x-show="userMenuOpen" @click="userMenuOpen=false" class="fixed left-0 w-screen z-20 h-screen bg-black bg-opacity-40 backdrop-blur-sm"
x-transition:enter="transition ease-out duration-300" x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0" x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100" x-transition:enter-end="opacity-100"
@@ -99,7 +99,7 @@
x-transition:leave-end="opacity-0"></div> x-transition:leave-end="opacity-0"></div>
<div <div
x-show="userMenuOpen" x-show="userMenuOpen"
class="absolute w-full right-0 sm:right-5 sm:top-12 z-50 sm:mt-2 sm:w-64 origin-top-right sm:rounded-md bg-white py-3 px-2 shadow-lg border-t border-gray-200 sm:ring-1 ring-black/25 focus:outline-none"> class="absolute w-full right-0 sm:right-5 sm:top-12 z-50 sm:mt-2 sm:w-64 origin-top-right sm:rounded-md bg-white py-3 px-2 shadow-lg border-t sm:ring-1 ring-black ring-opacity-25 focus:outline-none">
@if(auth()->guest()) @if(auth()->guest())
<a href="{{ route('register') }}" class="block px-4 py-2 text-sm text-gray-700 rounded transition hover:bg-sky-600 hover:text-white" role="menuitem" tabindex="-1"><i class="fa-solid fa-pen-to-square w-4 mr-2"></i>Register</a> <a href="{{ route('register') }}" class="block px-4 py-2 text-sm text-gray-700 rounded transition hover:bg-sky-600 hover:text-white" role="menuitem" tabindex="-1"><i class="fa-solid fa-pen-to-square w-4 mr-2"></i>Register</a>
<a href="{{ route('login') }}" class="block px-4 py-2 text-sm text-gray-700 rounded transition hover:bg-sky-600 hover:text-white" role="menuitem" tabindex="-1"><i class="fa-solid fa-right-to-bracket w-4 mr-2"></i>Log in</a> <a href="{{ route('login') }}" class="block px-4 py-2 text-sm text-gray-700 rounded transition hover:bg-sky-600 hover:text-white" role="menuitem" tabindex="-1"><i class="fa-solid fa-right-to-bracket w-4 mr-2"></i>Log in</a>
@@ -127,7 +127,7 @@
}) })
} }
})"> })">
<div class="absolute inset-0 backdrop-blur-sm bg-black/40"></div> <div class="absolute inset-0 backdrop-blur-sm bg-opacity-40 bg-black"></div>
<div class="relative w-full mx-8 max-w-2xl bg-gray-50 p-2 rounded-lg shadow-lg" x-on:click.stop> <div class="relative w-full mx-8 max-w-2xl bg-gray-50 p-2 rounded-lg shadow-lg" x-on:click.stop>
<form action="{{ route('search.index') }}" method="GET"> <form action="{{ route('search.index') }}" method="GET">
<x-ui.search type="text" name="q" label="Search..." /> <x-ui.search type="text" name="q" label="Search..." />

View File

@@ -1,19 +0,0 @@
@props(['item' => 'results', 'search', 'message', 'title'])
@php
if(!isset($message)) {
if(!isset($search) || $search == '')
$message = "We couldn't find any $item";
else
$message = "We couldn't find any $item matching \"$search\"";
}
if(!isset($title)) {
$title = "No results found";
}
@endphp
<div class="flex flex-col items-center my-8 w-full">
<i class="text-gray-300 mb-6 text-8xl fa-solid fa-cat"></i>
<p class="text-gray-500 mt-2">No workshops coming up. Were on a short break, mostly by playing with the cat.</p>
</div>

View File

@@ -10,12 +10,12 @@
} }
@endphp @endphp
<a href="{{ route('workshop.show', $workshop) }}" class="flex flex-col bg-white border border-gray-200 rounded-lg overflow-hidden hover:shadow-lg hover:scale-[101%] transition-all relative {{ $attributes->get('class') }}"> <a href="{{ route('workshop.show', $workshop) }}" class="flex flex-col bg-white border rounded-lg overflow-hidden hover:shadow-lg hover:scale-[101%] transition-all relative {{ $attributes->get('class') }}">
<div class="shadow border border-gray-200 rounded px-3 py-2 absolute top-2 left-2 flex flex-col justify-center items-center bg-white"> <div class="shadow border rounded px-3 py-2 absolute top-2 left-2 flex flex-col justify-center items-center bg-white">
<div class="text-gray-600 font-bold leading-none">{{ $workshop->starts_at->format('j') }}</div> <div class="text-gray-600 font-bold leading-none">{{ $workshop->starts_at->format('j') }}</div>
<div class="text-gray-600 text-xs uppercase">{{ $workshop->starts_at->format('M') }}</div> <div class="text-gray-600 text-xs uppercase">{{ $workshop->starts_at->format('M') }}</div>
</div> </div>
<div class="border border-white/50 absolute flex items-center justify-center top-5 -right-9 bg-gray-500 w-36 text-sm text-white font-bold uppercase py-1 rotate-45 h-8 sm-banner-{{ strtolower($statusClass) }}">{{ $statusTitle }}</div> <div class="border border-white border-opacity-50 absolute flex items-center justify-center top-5 -right-9 bg-gray-500 w-36 text-sm text-white font-bold uppercase py-1 rotate-45 h-8 sm-banner-{{ strtolower($statusClass) }}">{{ $statusTitle }}</div>
<img src="{{ $workshop->hero?->url }}?md" alt="{{ $workshop->title }}" class="w-full h-64 object-cover object-center"> <img src="{{ $workshop->hero?->url }}?md" alt="{{ $workshop->title }}" class="w-full h-64 object-cover object-center">
<div class="flex-grow p-4 flex flex-col"> <div class="flex-grow p-4 flex flex-col">
<h2 class="flex-grow {{ strlen($workshop->title) > 25 ? 'text-lg' : 'text-xl' }} font-bold mb-2">{{ $workshop->title }}</h2> <h2 class="flex-grow {{ strlen($workshop->title) > 25 ? 'text-lg' : 'text-xl' }} font-bold mb-2">{{ $workshop->title }}</h2>

View File

@@ -36,7 +36,7 @@
imgElement.src = details.thumbnail; imgElement.src = details.thumbnail;
imgElement.classList.remove('hidden'); imgElement.classList.remove('hidden');
placeholderElement.classList.add('hidden!'); placeholderElement.classList.add('hidden');
document.getElementById(name).value = value; document.getElementById(name).value = value;
}); });

View File

@@ -12,7 +12,7 @@
<x-container> <x-container>
<h2 class="text-2xl font-bold mb-6">Upcoming workshops</h2> <h2 class="text-2xl font-bold mb-6">Upcoming workshops</h2>
@if($workshops->isEmpty()) @if($workshops->isEmpty())
<x-on-holiday /> <x-none-found item="workshops" message="No workshops have been scheduled at this time" title="" />
@else @else
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-8 w-full"> <div class="grid md:grid-cols-2 lg:grid-cols-3 gap-8 w-full">
@foreach($workshops as $index => $workshop) @foreach($workshops as $index => $workshop)

View File

@@ -1,23 +1,23 @@
<x-layout> <x-layout>
<x-mast title="Search" description='Results for "{{ $search }}"' /> <x-mast title="Search" description='Results for "{{ $search }}"' />
<x-container> <x-container>
{{-- <section class="bg-gray-100">--}} <section class="bg-gray-100">
{{-- <h2 class="text-2xl font-bold my-6">Posts</h2>--}} <h2 class="text-2xl font-bold my-6">Posts</h2>
{{-- @if($posts->isEmpty())--}} @if($posts->isEmpty())
{{-- <x-container class="mt-8">--}} <x-container class="mt-8">
{{-- <x-none-found item="posts" search="{{ request()->get('search') }}" />--}} <x-none-found item="posts" search="{{ request()->get('search') }}" />
{{-- </x-container>--}} </x-container>
{{-- @else--}} @else
{{-- <x-container class="mt-4" inner-class="grid md:grid-cols-2 lg:grid-cols-3 gap-8 w-full">--}} <x-container class="mt-4" inner-class="grid md:grid-cols-2 lg:grid-cols-3 gap-8 w-full">
{{-- @foreach ($posts as $post)--}} @foreach ($posts as $post)
{{-- <x-panel-post :post="$post" />--}} <x-panel-post :post="$post" />
{{-- @endforeach--}} @endforeach
{{-- </x-container>--}} </x-container>
{{-- <x-container>--}} <x-container>
{{-- {{ $posts->appends(request()->except('post'))->links('', ['pageName' => 'post']) }}--}} {{ $posts->appends(request()->except('post'))->links('', ['pageName' => 'post']) }}
{{-- </x-container>--}} </x-container>
{{-- @endif--}} @endif
{{-- </section>--}} </section>
<section class="bg-gray-100"> <section class="bg-gray-100">
<h2 class="text-2xl font-bold my-6">Workshops</h2> <h2 class="text-2xl font-bold my-6">Workshops</h2>

30
tailwind.config.js Normal file
View File

@@ -0,0 +1,30 @@
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./resources/**/*.blade.php",
"./resources/**/*.js",
'./vendor/**/*.blade.php',
],
theme: {
extend: {
boxShadow: {
'deep': '0 10px 15px rgba(0, 0, 0, 0.5)',
},
colors: {
'primary-color': '#0284C7', // sky-600
'primary-color-dark': '#0370A1', // sky-500
'primary-color-light': '#0EA5E9', // sky-400
'danger-color': '#b91c1c', // red-600
'danger-color-dark': '#991b1b', // red-500
'danger-color-light': '#dc2626', // red-400
'success-color': '#16a34a', // green-600
'success-color-dark': '#22c55e', // green-500
'success-color-light': '#4ade80', // green-400
},
},
},
plugins: [],
}

View File

@@ -1,6 +1,5 @@
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin'; import laravel from 'laravel-vite-plugin';
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [
@@ -8,6 +7,5 @@ export default defineConfig({
input: ['resources/css/app.css', 'resources/js/app.js'], input: ['resources/css/app.css', 'resources/js/app.js'],
refresh: true, refresh: true,
}), }),
tailwindcss(),
], ],
}); });