Mercurial > packages > framework
changeset 2:b44434aaa767
Moving around the components.
Made a big step in the right direction with the Builder and named joins being accessible.
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/resources/views/components/application-logo.blade.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,1 @@ +<img src="{{ asset(config('app.logo', null)) }}" class="size-[64px]"></img>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/resources/views/components/auth-session-status.blade.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,7 @@ +@props(['status']) + +@if ($status) + <div {{ $attributes->merge(['class' => 'font-medium text-sm text-green-600']) }}> + {{ $status }} + </div> +@endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/resources/views/components/card.blade.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,23 @@ +@props([ + 'title' => null, // e.g. 'Upcoming Milestones' + 'icon' => null, // SVG (pass as a Blade include or html) + 'empty' => 'Nothing to show.', + 'footer' => null, // Optional (e.g. a "View All" link) + 'headerButton' => null, //button or link for the right side of the header +]) + +<div class="bg-white p-4 rounded-lg shadow min-h-[200px]"> + <div class="flex justify-between items-center"> + <h4 class="font-semibold text-gray-800 mb-3 flex items-center"> + {!! $icon ?? '' !!} + <span class="ml-2">{{ $title }}</span> + </h4> + {!! $headerButton !!} + </div> + {{ $slot }} + @if ($footer) + <div class="text-right mt-3"> + {!! $footer !!} + </div> + @endif +</div>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/resources/views/components/danger-button.blade.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,4 @@ +<button + {{ $attributes->merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-red-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-red-500 active:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 transition ease-in-out duration-150']) }}> + {{ $slot }} +</button>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/resources/views/components/dashboard-card.blade.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,33 @@ +@props([ + 'icon', // SVG icon as a Blade include or HTML + 'title', + 'value', + 'subtitle' => '', + 'color' => 'blue', // For icon background +]) + +@php + $bgColor = + [ + 'green' => 'bg-green-100 text-green-600', + 'blue' => 'bg-blue-100 text-blue-600', + 'purple' => 'bg-purple-100 text-purple-600', + 'yellow' => 'bg-yellow-100 text-yellow-600', + 'gray' => 'bg-gray-100 text-gray-600', + ][$color] ?? 'bg-blue-100 text-blue-600'; +@endphp + +<div class="flex items-center bg-white rounded-lg shadow p-4 min-w-[170px]"> + <div class="flex-shrink-0"> + <span class="inline-flex items-center justify-center h-12 w-12 rounded-full {{ $bgColor }}"> + {!! $icon !!} + </span> + </div> + <div class="ml-4"> + <div class="text-lg font-semibold text-gray-900">{{ $value }}</div> + <div class="text-gray-500">{{ $title }}</div> + @if ($subtitle) + <div class="text-xs text-gray-400 mt-1">{{ $subtitle }}</div> + @endif + </div> +</div>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/resources/views/components/dropdown-link.blade.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,2 @@ +<a + {{ $attributes->merge(['class' => 'block w-full px-4 py-2 text-start text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out']) }}>{{ $slot }}</a>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/resources/views/components/dropdown.blade.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,30 @@ +@props(['align' => 'right', 'width' => '48', 'contentClasses' => 'py-1 bg-white']) + +@php + $alignmentClasses = match ($align) { + 'left' => 'ltr:origin-top-left rtl:origin-top-right start-0', + 'top' => 'origin-top', + default => 'ltr:origin-top-right rtl:origin-top-left end-0', + }; + + $width = match ($width) { + '48' => 'w-48', + default => $width, + }; +@endphp + +<div class="relative" x-data="{ open: false }" @click.outside="open = false" @close.stop="open = false"> + <div @click="open = ! open"> + {{ $trigger }} + </div> + + <div x-show="open" x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0 scale-95" + x-transition:enter-end="opacity-100 scale-100" x-transition:leave="transition ease-in duration-75" + x-transition:leave-start="opacity-100 scale-100" x-transition:leave-end="opacity-0 scale-95" + class="absolute z-50 mt-2 {{ $width }} rounded-md shadow-lg {{ $alignmentClasses }}" + style="display: none;" @click="open = false"> + <div class="rounded-md ring-1 ring-black ring-opacity-5 {{ $contentClasses }}"> + {{ $content }} + </div> + </div> +</div>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/resources/views/components/filter/badge.blade.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,13 @@ +<span id="badge-dismiss-dark" + class="inline-flex items-center px-2 py-1 me-2 text-sm font-medium text-gray-800 bg-gray-100 rounded-sm dark:bg-gray-700 dark:text-gray-300"> + {{ $name }} + <button type="button" + class="inline-flex items-center p-1 ms-2 text-sm text-gray-400 bg-transparent rounded-xs hover:bg-gray-200 hover:text-gray-900 dark:hover:bg-gray-600 dark:hover:text-gray-300" + data-dismiss-target="#badge-dismiss-dark" aria-label="Remove"> + <svg class="w-2 h-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14"> + <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" + d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" /> + </svg> + <span class="sr-only">Remove Filter</span> + </button> +</span>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/resources/views/components/form/checkbox.blade.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,11 @@ +@props(['name', 'label', 'checked' => false]) + +<div class="mb-4 flex items-center"> + <input type="checkbox" id="{{ $name }}" name="{{ $name }}" value="1" + {{ old($name, $checked) ? 'checked' : '' }} + class="rounded border-gray-300 text-blue-600 shadow-sm focus:ring-blue-200"> + <label for="{{ $name }}" class="ml-2 text-gray-700">{{ $label }}</label> + @error($name) + <span class="text-red-600 text-sm">{{ $message }}</span> + @enderror +</div>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/resources/views/components/form/date.blade.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,11 @@ +@props(['name', 'label', 'value' => '', 'required' => false]) + +<div class="mb-4"> + <label for="{{ $name }}" class="block text-gray-700">{{ $label }}</label> + <input type="date" name="{{ $name }}" id="{{ $name }}" value="{{ old($name, $value) }}" + @if ($required) required @endif + class="mt-1 block w-full rounded border-gray-300 shadow-sm focus:ring focus:ring-blue-200"> + @error($name) + <span class="text-red-600 text-sm">{{ $message }}</span> + @enderror +</div>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/resources/views/components/form/select.blade.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,16 @@ +@props(['name', 'label', 'options' => [], 'value' => '', 'required' => false]) +<div class="mb-4"> + <label for="{{ $name }}" class="block text-gray-700">{{ $label }}</label> + <select name="{{ $name }}" id="{{ $name }}" @if ($required) required @endif + class="mt-1 block w-full rounded border-gray-300 shadow-sm focus:ring focus:ring-blue-200"> + <option value="">Select...</option> + @foreach ($options as $optionValue => $optionLabel) + <option value="{{ $optionValue }}" {{ old($name, $value) == $optionValue ? 'selected' : '' }}> + {{ $optionLabel }} + </option> + @endforeach + </select> + @error($name) + <span class="text-red-600 text-sm">{{ $message }}</span> + @enderror +</div>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/resources/views/components/form/text.blade.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,11 @@ +@props(['name', 'label', 'value' => '', 'required' => false]) + +<div class="mb-4"> + <label for="{{ $name }}" class="block text-gray-700">{{ $label }}</label> + <input type="text" name="{{ $name }}" id="{{ $name }}" value="{{ old($name, $value) }}" + @if ($required) required @endif + class="mt-1 block w-full rounded border-gray-300 shadow-sm focus:ring focus:ring-blue-200"> + @error($name) + <span class="text-red-600 text-sm">{{ $message }}</span> + @enderror +</div>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/resources/views/components/form/textarea.blade.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,22 @@ +@props(['name', 'label', 'value' => '', 'required' => false]) + +<div class="mb-4"> + <label for="{{ $name }}" class="block text-gray-700 font-medium mb-1"> + {{ $label }} + @if($required) + <span class="text-red-500">*</span> + @endif + </label> + + <textarea + name="{{ $name }}" + id="{{ $name }}" + @if ($required) required @endif + class="markdown-editor mt-1 block w-full rounded border-gray-300 shadow-sm focus:ring focus:ring-blue-200" + >{{ old($name, $value) }}</textarea> + + @error($name) + <span class="text-red-600 text-sm">{{ $message }}</span> + @enderror +</div> +@include('includes.easymde')
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/resources/views/components/input-error.blade.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,9 @@ +@props(['messages']) + +@if ($messages) + <ul {{ $attributes->merge(['class' => 'text-sm text-red-600 space-y-1']) }}> + @foreach ((array) $messages as $message) + <li>{{ $message }}</li> + @endforeach + </ul> +@endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/resources/views/components/input-label.blade.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,5 @@ +@props(['value']) + +<label {{ $attributes->merge(['class' => 'block font-medium text-sm text-gray-700']) }}> + {{ $value ?? $slot }} +</label>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/resources/views/components/modal.blade.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,57 @@ +@props(['name', 'show' => false, 'maxWidth' => '2xl']) + +@php + $maxWidth = [ + 'sm' => 'sm:max-w-sm', + 'md' => 'sm:max-w-md', + 'lg' => 'sm:max-w-lg', + 'xl' => 'sm:max-w-xl', + '2xl' => 'sm:max-w-2xl', + ][$maxWidth]; +@endphp + +<div x-data="{ + show: @js($show), + focusables() { + // All focusable element types... + let selector = 'a, button, input:not([type=\'hidden\']), textarea, select, details, [tabindex]:not([tabindex=\'-1\'])' + return [...$el.querySelectorAll(selector)] + // All non-disabled elements... + .filter(el => !el.hasAttribute('disabled')) + }, + firstFocusable() { return this.focusables()[0] }, + lastFocusable() { return this.focusables().slice(-1)[0] }, + nextFocusable() { return this.focusables()[this.nextFocusableIndex()] || this.firstFocusable() }, + prevFocusable() { return this.focusables()[this.prevFocusableIndex()] || this.lastFocusable() }, + nextFocusableIndex() { return (this.focusables().indexOf(document.activeElement) + 1) % (this.focusables().length + 1) }, + prevFocusableIndex() { return Math.max(0, this.focusables().indexOf(document.activeElement)) - 1 }, +}" x-init="$watch('show', value => { + if (value) { + document.body.classList.add('overflow-y-hidden'); + {{ $attributes->has('focusable') ? 'setTimeout(() => firstFocusable().focus(), 100)' : '' }} + } else { + document.body.classList.remove('overflow-y-hidden'); + } +})" + x-on:open-modal.window="$event.detail == '{{ $name }}' ? show = true : null" + x-on:close-modal.window="$event.detail == '{{ $name }}' ? show = false : null" x-on:close.stop="show = false" + x-on:keydown.escape.window="show = false" x-on:keydown.tab.prevent="$event.shiftKey || nextFocusable().focus()" + x-on:keydown.shift.tab.prevent="prevFocusable().focus()" x-show="show" + class="fixed inset-0 overflow-y-auto px-4 py-6 sm:px-0 z-50" style="display: {{ $show ? 'block' : 'none' }};"> + <div x-show="show" class="fixed inset-0 transform transition-all" x-on:click="show = false" + x-transition:enter="ease-out duration-300" x-transition:enter-start="opacity-0" + x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-200" + x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0"> + <div class="absolute inset-0 bg-gray-500 opacity-75"></div> + </div> + + <div x-show="show" + class="mb-6 bg-white rounded-lg overflow-hidden shadow-xl transform transition-all sm:w-full {{ $maxWidth }} sm:mx-auto" + x-transition:enter="ease-out duration-300" + x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" + x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100" x-transition:leave="ease-in duration-200" + x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100" + x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"> + {{ $slot }} + </div> +</div>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/resources/views/components/nav-link.blade.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,12 @@ +@props(['active']) + +@php + $classes = + $active ?? false + ? 'inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out' + : 'inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300 transition duration-150 ease-in-out'; +@endphp + +<a {{ $attributes->merge(['class' => $classes]) }}> + {{ $slot }} +</a>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/resources/views/components/preview-footer.blade.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,1 @@ +<a href="{{ $route }}" class="text-blue-600 text-sm hover:underline">{{ $label }}</a>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/resources/views/components/preview-list-card.blade.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,22 @@ +@props([ + 'title', // e.g. 'Upcoming Milestones' + 'icon', // SVG (pass as a Blade include or html) + 'items', // Collection of items to show + 'itemView', // Partial for rendering each item + 'viewData' => [], // Additional data to pass to the partial + 'empty' => 'Nothing to show.', + 'footer' => null, // Optional (e.g. a "View All" link) + 'headerButton' => null, // Optional (e.g. a "Add" link) +]) + +<x-card :icon="$icon" :title="$title" :headerButton="$headerButton" :footer="$footer"> + @if ($items->count()) + <ul class="divide-y divide-gray-100"> + @foreach ($items as $item) + @include($itemView, array_merge(['item' => $item], $viewData ?? [])) + @endforeach + </ul> + @else + <div class="text-gray-400 text-sm">{{ $empty }}</div> + @endif +</x-card>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/resources/views/components/primary-button.blade.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,4 @@ +<button + {{ $attributes->merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 focus:bg-gray-700 active:bg-gray-900 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition ease-in-out duration-150']) }}> + {{ $slot }} +</button>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/resources/views/components/responsive-nav-link.blade.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,12 @@ +@props(['active']) + +@php + $classes = + $active ?? false + ? 'block w-full ps-3 pe-4 py-2 border-l-4 border-indigo-400 text-start text-base font-medium text-indigo-700 bg-indigo-50 focus:outline-none focus:text-indigo-800 focus:bg-indigo-100 focus:border-indigo-700 transition duration-150 ease-in-out' + : 'block w-full ps-3 pe-4 py-2 border-l-4 border-transparent text-start text-base font-medium text-gray-600 hover:text-gray-800 hover:bg-gray-50 hover:border-gray-300 focus:outline-none focus:text-gray-800 focus:bg-gray-50 focus:border-gray-300 transition duration-150 ease-in-out'; +@endphp + +<a {{ $attributes->merge(['class' => $classes]) }}> + {{ $slot }} +</a>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/resources/views/components/secondary-button.blade.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,4 @@ +<button + {{ $attributes->merge(['type' => 'button', 'class' => 'inline-flex items-center px-4 py-2 bg-white border border-gray-300 rounded-md font-semibold text-xs text-gray-700 uppercase tracking-widest shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:opacity-25 transition ease-in-out duration-150']) }}> + {{ $slot }} +</button>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/resources/views/components/text-input.blade.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,4 @@ +@props(['disabled' => false]) + +<input @disabled($disabled) + {{ $attributes->merge(['class' => 'border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm']) }}>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/publishable/vite.config.js Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,30 @@ +import { defineConfig } from "vite"; +import laravel from "laravel-vite-plugin"; +import prism from 'vite-plugin-prismjs'; +import fs from "fs"; +import path from "path"; + +const pagesDir = "resources/js/pages"; +const pageScripts = fs + .readdirSync(pagesDir) + .map((file) => path.join(pagesDir, file)); + +export default defineConfig({ + plugins: [ + laravel({ + input: [ + "resources/css/app.css", + "resources/js/app.js", + "resources/js/easymde.js", + ...pageScripts, + ], + refresh: true, + }), + prism({ + languages: ["javascript", "css", "html", "typescript", "php", "sql", "bash", "sh"], + plugins: ["line-numbers"], + theme: "tomorrow", + css: true, + }), + ], +});
--- a/resources/views/components/application-logo.blade.php Mon Jun 09 23:07:17 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -<img src="{{ asset(config('app.logo', null)) }}" class="size-[64px]"></img>
--- a/resources/views/components/auth-session-status.blade.php Mon Jun 09 23:07:17 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -@props(['status']) - -@if ($status) - <div {{ $attributes->merge(['class' => 'font-medium text-sm text-green-600']) }}> - {{ $status }} - </div> -@endif
--- a/resources/views/components/card.blade.php Mon Jun 09 23:07:17 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -@props([ - 'title' => null, // e.g. 'Upcoming Milestones' - 'icon' => null, // SVG (pass as a Blade include or html) - 'empty' => 'Nothing to show.', - 'footer' => null, // Optional (e.g. a "View All" link) - 'headerButton' => null, //button or link for the right side of the header -]) - -<div class="bg-white p-4 rounded-lg shadow min-h-[200px]"> - <div class="flex justify-between items-center"> - <h4 class="font-semibold text-gray-800 mb-3 flex items-center"> - {!! $icon ?? '' !!} - <span class="ml-2">{{ $title }}</span> - </h4> - {!! $headerButton !!} - </div> - {{ $slot }} - @if ($footer) - <div class="text-right mt-3"> - {!! $footer !!} - </div> - @endif -</div>
--- a/resources/views/components/danger-button.blade.php Mon Jun 09 23:07:17 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -<button - {{ $attributes->merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-red-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-red-500 active:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 transition ease-in-out duration-150']) }}> - {{ $slot }} -</button>
--- a/resources/views/components/dashboard-card.blade.php Mon Jun 09 23:07:17 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -@props([ - 'icon', // SVG icon as a Blade include or HTML - 'title', - 'value', - 'subtitle' => '', - 'color' => 'blue', // For icon background -]) - -@php - $bgColor = - [ - 'green' => 'bg-green-100 text-green-600', - 'blue' => 'bg-blue-100 text-blue-600', - 'purple' => 'bg-purple-100 text-purple-600', - 'yellow' => 'bg-yellow-100 text-yellow-600', - 'gray' => 'bg-gray-100 text-gray-600', - ][$color] ?? 'bg-blue-100 text-blue-600'; -@endphp - -<div class="flex items-center bg-white rounded-lg shadow p-4 min-w-[170px]"> - <div class="flex-shrink-0"> - <span class="inline-flex items-center justify-center h-12 w-12 rounded-full {{ $bgColor }}"> - {!! $icon !!} - </span> - </div> - <div class="ml-4"> - <div class="text-lg font-semibold text-gray-900">{{ $value }}</div> - <div class="text-gray-500">{{ $title }}</div> - @if ($subtitle) - <div class="text-xs text-gray-400 mt-1">{{ $subtitle }}</div> - @endif - </div> -</div>
--- a/resources/views/components/dropdown-link.blade.php Mon Jun 09 23:07:17 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -<a - {{ $attributes->merge(['class' => 'block w-full px-4 py-2 text-start text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out']) }}>{{ $slot }}</a>
--- a/resources/views/components/dropdown.blade.php Mon Jun 09 23:07:17 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -@props(['align' => 'right', 'width' => '48', 'contentClasses' => 'py-1 bg-white']) - -@php - $alignmentClasses = match ($align) { - 'left' => 'ltr:origin-top-left rtl:origin-top-right start-0', - 'top' => 'origin-top', - default => 'ltr:origin-top-right rtl:origin-top-left end-0', - }; - - $width = match ($width) { - '48' => 'w-48', - default => $width, - }; -@endphp - -<div class="relative" x-data="{ open: false }" @click.outside="open = false" @close.stop="open = false"> - <div @click="open = ! open"> - {{ $trigger }} - </div> - - <div x-show="open" x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0 scale-95" - x-transition:enter-end="opacity-100 scale-100" x-transition:leave="transition ease-in duration-75" - x-transition:leave-start="opacity-100 scale-100" x-transition:leave-end="opacity-0 scale-95" - class="absolute z-50 mt-2 {{ $width }} rounded-md shadow-lg {{ $alignmentClasses }}" - style="display: none;" @click="open = false"> - <div class="rounded-md ring-1 ring-black ring-opacity-5 {{ $contentClasses }}"> - {{ $content }} - </div> - </div> -</div>
--- a/resources/views/components/filter/badge.blade.php Mon Jun 09 23:07:17 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,13 +0,0 @@ -<span id="badge-dismiss-dark" - class="inline-flex items-center px-2 py-1 me-2 text-sm font-medium text-gray-800 bg-gray-100 rounded-sm dark:bg-gray-700 dark:text-gray-300"> - {{ $name }} - <button type="button" - class="inline-flex items-center p-1 ms-2 text-sm text-gray-400 bg-transparent rounded-xs hover:bg-gray-200 hover:text-gray-900 dark:hover:bg-gray-600 dark:hover:text-gray-300" - data-dismiss-target="#badge-dismiss-dark" aria-label="Remove"> - <svg class="w-2 h-2" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14"> - <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" - d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" /> - </svg> - <span class="sr-only">Remove Filter</span> - </button> -</span>
--- a/resources/views/components/form/checkbox.blade.php Mon Jun 09 23:07:17 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -@props(['name', 'label', 'checked' => false]) - -<div class="mb-4 flex items-center"> - <input type="checkbox" id="{{ $name }}" name="{{ $name }}" value="1" - {{ old($name, $checked) ? 'checked' : '' }} - class="rounded border-gray-300 text-blue-600 shadow-sm focus:ring-blue-200"> - <label for="{{ $name }}" class="ml-2 text-gray-700">{{ $label }}</label> - @error($name) - <span class="text-red-600 text-sm">{{ $message }}</span> - @enderror -</div>
--- a/resources/views/components/form/date.blade.php Mon Jun 09 23:07:17 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -@props(['name', 'label', 'value' => '', 'required' => false]) - -<div class="mb-4"> - <label for="{{ $name }}" class="block text-gray-700">{{ $label }}</label> - <input type="date" name="{{ $name }}" id="{{ $name }}" value="{{ old($name, $value) }}" - @if ($required) required @endif - class="mt-1 block w-full rounded border-gray-300 shadow-sm focus:ring focus:ring-blue-200"> - @error($name) - <span class="text-red-600 text-sm">{{ $message }}</span> - @enderror -</div>
--- a/resources/views/components/form/select.blade.php Mon Jun 09 23:07:17 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ -@props(['name', 'label', 'options' => [], 'value' => '', 'required' => false]) -<div class="mb-4"> - <label for="{{ $name }}" class="block text-gray-700">{{ $label }}</label> - <select name="{{ $name }}" id="{{ $name }}" @if ($required) required @endif - class="mt-1 block w-full rounded border-gray-300 shadow-sm focus:ring focus:ring-blue-200"> - <option value="">Select...</option> - @foreach ($options as $optionValue => $optionLabel) - <option value="{{ $optionValue }}" {{ old($name, $value) == $optionValue ? 'selected' : '' }}> - {{ $optionLabel }} - </option> - @endforeach - </select> - @error($name) - <span class="text-red-600 text-sm">{{ $message }}</span> - @enderror -</div>
--- a/resources/views/components/form/text.blade.php Mon Jun 09 23:07:17 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -@props(['name', 'label', 'value' => '', 'required' => false]) - -<div class="mb-4"> - <label for="{{ $name }}" class="block text-gray-700">{{ $label }}</label> - <input type="text" name="{{ $name }}" id="{{ $name }}" value="{{ old($name, $value) }}" - @if ($required) required @endif - class="mt-1 block w-full rounded border-gray-300 shadow-sm focus:ring focus:ring-blue-200"> - @error($name) - <span class="text-red-600 text-sm">{{ $message }}</span> - @enderror -</div>
--- a/resources/views/components/form/textarea.blade.php Mon Jun 09 23:07:17 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ -@props(['name', 'label', 'value' => '', 'required' => false]) - -<div class="mb-4"> - <label for="{{ $name }}" class="block text-gray-700 font-medium mb-1"> - {{ $label }} - @if($required) - <span class="text-red-500">*</span> - @endif - </label> - - <textarea - name="{{ $name }}" - id="{{ $name }}" - @if ($required) required @endif - class="markdown-editor mt-1 block w-full rounded border-gray-300 shadow-sm focus:ring focus:ring-blue-200" - >{{ old($name, $value) }}</textarea> - - @error($name) - <span class="text-red-600 text-sm">{{ $message }}</span> - @enderror -</div> -@include('includes.easymde')
--- a/resources/views/components/input-error.blade.php Mon Jun 09 23:07:17 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,9 +0,0 @@ -@props(['messages']) - -@if ($messages) - <ul {{ $attributes->merge(['class' => 'text-sm text-red-600 space-y-1']) }}> - @foreach ((array) $messages as $message) - <li>{{ $message }}</li> - @endforeach - </ul> -@endif
--- a/resources/views/components/input-label.blade.php Mon Jun 09 23:07:17 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -@props(['value']) - -<label {{ $attributes->merge(['class' => 'block font-medium text-sm text-gray-700']) }}> - {{ $value ?? $slot }} -</label>
--- a/resources/views/components/modal.blade.php Mon Jun 09 23:07:17 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -@props(['name', 'show' => false, 'maxWidth' => '2xl']) - -@php - $maxWidth = [ - 'sm' => 'sm:max-w-sm', - 'md' => 'sm:max-w-md', - 'lg' => 'sm:max-w-lg', - 'xl' => 'sm:max-w-xl', - '2xl' => 'sm:max-w-2xl', - ][$maxWidth]; -@endphp - -<div x-data="{ - show: @js($show), - focusables() { - // All focusable element types... - let selector = 'a, button, input:not([type=\'hidden\']), textarea, select, details, [tabindex]:not([tabindex=\'-1\'])' - return [...$el.querySelectorAll(selector)] - // All non-disabled elements... - .filter(el => !el.hasAttribute('disabled')) - }, - firstFocusable() { return this.focusables()[0] }, - lastFocusable() { return this.focusables().slice(-1)[0] }, - nextFocusable() { return this.focusables()[this.nextFocusableIndex()] || this.firstFocusable() }, - prevFocusable() { return this.focusables()[this.prevFocusableIndex()] || this.lastFocusable() }, - nextFocusableIndex() { return (this.focusables().indexOf(document.activeElement) + 1) % (this.focusables().length + 1) }, - prevFocusableIndex() { return Math.max(0, this.focusables().indexOf(document.activeElement)) - 1 }, -}" x-init="$watch('show', value => { - if (value) { - document.body.classList.add('overflow-y-hidden'); - {{ $attributes->has('focusable') ? 'setTimeout(() => firstFocusable().focus(), 100)' : '' }} - } else { - document.body.classList.remove('overflow-y-hidden'); - } -})" - x-on:open-modal.window="$event.detail == '{{ $name }}' ? show = true : null" - x-on:close-modal.window="$event.detail == '{{ $name }}' ? show = false : null" x-on:close.stop="show = false" - x-on:keydown.escape.window="show = false" x-on:keydown.tab.prevent="$event.shiftKey || nextFocusable().focus()" - x-on:keydown.shift.tab.prevent="prevFocusable().focus()" x-show="show" - class="fixed inset-0 overflow-y-auto px-4 py-6 sm:px-0 z-50" style="display: {{ $show ? 'block' : 'none' }};"> - <div x-show="show" class="fixed inset-0 transform transition-all" x-on:click="show = false" - x-transition:enter="ease-out duration-300" x-transition:enter-start="opacity-0" - x-transition:enter-end="opacity-100" x-transition:leave="ease-in duration-200" - x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0"> - <div class="absolute inset-0 bg-gray-500 opacity-75"></div> - </div> - - <div x-show="show" - class="mb-6 bg-white rounded-lg overflow-hidden shadow-xl transform transition-all sm:w-full {{ $maxWidth }} sm:mx-auto" - x-transition:enter="ease-out duration-300" - x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" - x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100" x-transition:leave="ease-in duration-200" - x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100" - x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"> - {{ $slot }} - </div> -</div>
--- a/resources/views/components/nav-link.blade.php Mon Jun 09 23:07:17 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -@props(['active']) - -@php - $classes = - $active ?? false - ? 'inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out' - : 'inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300 transition duration-150 ease-in-out'; -@endphp - -<a {{ $attributes->merge(['class' => $classes]) }}> - {{ $slot }} -</a>
--- a/resources/views/components/preview-footer.blade.php Mon Jun 09 23:07:17 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -<a href="{{ $route }}" class="text-blue-600 text-sm hover:underline">{{ $label }}</a>
--- a/resources/views/components/preview-list-card.blade.php Mon Jun 09 23:07:17 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ -@props([ - 'title', // e.g. 'Upcoming Milestones' - 'icon', // SVG (pass as a Blade include or html) - 'items', // Collection of items to show - 'itemView', // Partial for rendering each item - 'viewData' => [], // Additional data to pass to the partial - 'empty' => 'Nothing to show.', - 'footer' => null, // Optional (e.g. a "View All" link) - 'headerButton' => null, // Optional (e.g. a "Add" link) -]) - -<x-card :icon="$icon" :title="$title" :headerButton="$headerButton" :footer="$footer"> - @if ($items->count()) - <ul class="divide-y divide-gray-100"> - @foreach ($items as $item) - @include($itemView, array_merge(['item' => $item], $viewData ?? [])) - @endforeach - </ul> - @else - <div class="text-gray-400 text-sm">{{ $empty }}</div> - @endif -</x-card>
--- a/resources/views/components/primary-button.blade.php Mon Jun 09 23:07:17 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -<button - {{ $attributes->merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 focus:bg-gray-700 active:bg-gray-900 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition ease-in-out duration-150']) }}> - {{ $slot }} -</button>
--- a/resources/views/components/responsive-nav-link.blade.php Mon Jun 09 23:07:17 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -@props(['active']) - -@php - $classes = - $active ?? false - ? 'block w-full ps-3 pe-4 py-2 border-l-4 border-indigo-400 text-start text-base font-medium text-indigo-700 bg-indigo-50 focus:outline-none focus:text-indigo-800 focus:bg-indigo-100 focus:border-indigo-700 transition duration-150 ease-in-out' - : 'block w-full ps-3 pe-4 py-2 border-l-4 border-transparent text-start text-base font-medium text-gray-600 hover:text-gray-800 hover:bg-gray-50 hover:border-gray-300 focus:outline-none focus:text-gray-800 focus:bg-gray-50 focus:border-gray-300 transition duration-150 ease-in-out'; -@endphp - -<a {{ $attributes->merge(['class' => $classes]) }}> - {{ $slot }} -</a>
--- a/resources/views/components/secondary-button.blade.php Mon Jun 09 23:07:17 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -<button - {{ $attributes->merge(['type' => 'button', 'class' => 'inline-flex items-center px-4 py-2 bg-white border border-gray-300 rounded-md font-semibold text-xs text-gray-700 uppercase tracking-widest shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:opacity-25 transition ease-in-out duration-150']) }}> - {{ $slot }} -</button>
--- a/resources/views/components/text-input.blade.php Mon Jun 09 23:07:17 2025 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -@props(['disabled' => false]) - -<input @disabled($disabled) - {{ $attributes->merge(['class' => 'border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm']) }}>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Database/Builder.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,23 @@ +<?php + +namespace Wizard\Framework\Database; + +use Illuminate\Database\Eloquent\Builder as BaseBuilder; + +class Builder extends BaseBuilder { + + public $named_joins = []; + + /* + * Add's a named join to the query, this will only add the joins + * a single time, and keep track of joins that are already in the query. + * + */ + public function addNamedJoin($join = '') { + $available_joins = $this->model?->named_joins() ?? []; + if(isset($available_joins[$join]) && ($named_joins[$join] ?? false) == false) { + $this->named_joins[$join] = true; + $available_joins[$join]($this); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Database/MarkdownParser.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,65 @@ +<?php + +namespace Libraries; + +class MarkdownParser +{ + public static function toHtml(string $markdown): string + { + $code_block_pattern = '/```([a-z]+)\s+(.*?)\s+```/si'; + $inline_code_pattern = '/`\s*(.*?)\s*`/si'; + $matches = []; + + preg_match_all($code_block_pattern, $markdown, $matches, PREG_SET_ORDER); + $code_blocks = []; + foreach ($matches as $index => $match) { + $code_blocks[] = [ + 'language' => $match[1], + 'code' => $match[2], + ]; + } + + foreach ($matches as $index => $match) { + $markdown = preg_replace('/'.preg_quote($match[0], '/').'/', '[CODE_BLOCK_'.$index.']', $markdown, 1); + } + + preg_match_all($inline_code_pattern, $markdown, $matches, PREG_SET_ORDER); + $inline_code = []; + foreach ($matches as $index => $match) { + $inline_code[] = [ + 'code' => $match[1], + ]; + } + + foreach ($matches as $index => $match) { + $markdown = preg_replace('/'.preg_quote($match[0], '/').'/', '[INLINE_CODE_'.$index.']', $markdown, 1); + } + + $markdown = preg_replace('/`(.+?)`/', '<code>$1</code>', $markdown); + $markdown = preg_replace('/\#{6}\s(.+)/', '<h6>$1</h6>', $markdown); + $markdown = preg_replace('/\#{5}\s(.+)/', '<h5>$1</h5>', $markdown); + $markdown = preg_replace('/\#{4}\s(.+)/', '<h4>$1</h4>', $markdown); + $markdown = preg_replace('/\#{3}\s(.+)/', '<h3>$1</h3>', $markdown); + $markdown = preg_replace('/\#{2}\s(.+)/', '<h2>$1</h2>', $markdown); + $markdown = preg_replace('/\#\s(.+)/', '<h1>$1</h1>', $markdown); + $markdown = preg_replace('/\*\*\*(.+?)\*\*\*/', '<strong><em>$1</em></strong>', $markdown); + $markdown = preg_replace('/\*\*(.+?)\*\*/', '<strong>$1</strong>', $markdown); + $markdown = preg_replace('/\*(.+?)\*/', '<em>$1</em>', $markdown); + $markdown = preg_replace('/\[(.+?)\]\((.+?)\)/', '<a href="$2">$1</a>', $markdown); + $markdown = preg_replace("/\r\n|\r|\n/", '<br>', $markdown); + + foreach ($code_blocks as $index => $code_block) { + $language = $code_block['language']; + $language = $language == 'blade' ? 'html' : $language; + $code_block['code'] = '<pre><code class="match-braces language-'.$language.'">'.htmlentities($code_block['code']).'</code></pre>'; + $markdown = str_replace('[CODE_BLOCK_'.$index.']', $code_block['code'], $markdown); + } + + foreach ($inline_code as $index => $code) { + $code['code'] = '<code>'.htmlentities($code['code']).'</code>'; + $markdown = str_replace('[INLINE_CODE_'.$index.']', $code['code'], $markdown); + } + + return $markdown; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Database/MyBlueprint.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,102 @@ +<?php + +namespace Libraries; + +use Illuminate\Database\Schema\ColumnDefinition; + +class MyBlueprint extends \Illuminate\Database\Schema\Blueprint +{ + /** + * Generates the base columns for a regular table + */ + public function baseColumns(): void + { + $this->id(); + $this->reference('users', 'created_by')->nullable(); + $this->reference('users', 'updated_by')->nullable(); + $this->timestamps(); + $this->softDeletes(); + } + + /** + * @param mixed $abbreviation + */ + public function baseTextColumns($abbreviation = false): void + { + $this->string('name', 255); + $this->text('description')->nullable(); + if ($abbreviation) { + $this->string('abbreviation', 50)->nullable(); + } + } + + public function pivotColumns(string $table1, string $table2): void + { + $this->comment('PIVOT:'.json_encode(['table1' => $table1, 'table2' => $table2])); + $this->id(); + $this->reference($table1); + $this->reference($table2); + } + + public function settingsColumns(): void + { + $this->baseColumns(); + $this->baseTextColumns(); + } + + /** + * @param mixed $keep_column + */ + public function dropReference(string $table, ?string $override_column_name = null, $keep_column = false): void + { + // set up relevant strings + $column_name = self::get_foreign_column_name($table, $override_column_name); + $fk_name = self::get_foreign_key_name($this->getTable(), $table, $column_name); + + // drop the reference + $this->dropForeign($fk_name); + + // drop the column if necessary + if (! $keep_column) { + $this->dropColumn($column_name); + } + } + + public function reference(string $table, ?string $override_column_name = null): ColumnDefinition + { + // set up relevant strings + $column_name = self::get_foreign_column_name($table, $override_column_name); + $fk_name = self::get_foreign_key_name($this->getTable(), $table, $column_name); + + // create the column + $column = $this->unsignedBigInteger($column_name); + + // create the foreign key + $this->foreign($column_name, $fk_name)->references('id')->on($table); + + return $column; + } + + protected static function get_foreign_column_name(string $input, ?string $override = null): string + { + return ! is_null($override) ? $override : \Str::singular($input).'_id'; + } + + /* + * Generates a foreign key name + * 'FK_' + current table name + foreign column name + * + * */ + protected static function get_foreign_key_name(string $current_table, string $table, string $column_name): string + { + $fk_name = 'FK_'; + + // add the current table name + $fk_name .= \Str::singular($current_table); + + // add the foreign column name + $fk_name .= '_'.$column_name; + + return $fk_name; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Database/MySchema.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,32 @@ +<?php + +namespace Libraries; + +use Illuminate\Database\Schema\Builder; +use Illuminate\Support\Facades\Schema as BaseSchema; + +class MySchema extends BaseSchema +{ + /** + * Get a schema builder instance for a connection. + * + * @param string|null $name + */ + public static function connection($name): Builder + { + return self::customizedSchemaBuilder($name); + } + + /** + * Retrieves an instance of the schema `Builder` with a customized `Blueprint` class. + */ + public static function customizedSchemaBuilder(?string $name = null): Builder + { + /** @var Builder $builder */ + $builder = static::$app['db']->connection($name)->getSchemaBuilder(); + + $builder->blueprintResolver(static fn ($table, $callback, $prefix) => new MyBlueprint($table, $callback, $prefix)); + + return $builder; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Database/SchemaDrawer.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,103 @@ +<?php + +namespace Libraries; + +use Illuminate\Support\Facades\Schema; + +class SchemaDrawer +{ + public $schema; + + public $table; + + public $tables; + + public function __construct() + { + $this->tables = $this->getTables(); + } + + public static function createSchema($file_name = null) + { + $sd = new self; + + $schema = $sd->list_tables_and_fks(); + if (is_null($file_name)) { + return $schema; + } else { + $f = fopen($file_name, 'w+'); + fwrite($f, $schema); + fclose($f); + } + + return true; + } + + protected static function getTableColumns(string $table_name) + { + return Schema::getColumns($table_name); + } + + protected static function getTableForeignKeys(string $table_name) + { + return Schema::getForeignKeys($table_name); + } + + /** + * Get the tables in the schema. + */ + protected function getTables() + { + if (is_null($this->tables)) { + $key = 'DATABASE()'; + $schema = \DB::select('SELECT DATABASE()')[0]->$key; + $this->tables = Schema::getTables(schema: [$schema]); + } + + return $this->tables; + } + + public function list_tables_and_fks(): string + { + $fk_labels = false; + $str = ' +digraph mydb { + fontname="Helvetica,Arial,sans-serif" + graph [layout="circo"] + node [fontname="Helvetica,Arial,sans-serif", shape="record"] + edge [fontname="Helvetica,Arial,sans-serif"] +'; + foreach ($this->tables as $table) { + $table_name = $table['name']; + $str .= "\t".$table_name.'[label="{'.$table_name; + foreach (self::getTableColumns($table_name) as $column) { + $col_name = $column['name']; + $str .= ' | <'.$col_name.'> '.$col_name; + // TODO: should we show the type? + } + $str .= '}"]'."\n"; + + $fks = self::getTableForeignKeys($table_name); + foreach ($fks as $fk) { + $name = $fk['name']; + + if (! (str_contains($name, 'created_by') || str_contains($name, 'updated_by'))) { + $local_column = $fk['columns']; + $local_column = (isset($local_column[0])) ? ':'.$local_column[0] : ''; + $foreign_column = $fk['foreign_columns']; + $foreign_column = (isset($foreign_column[0])) ? ':'.$foreign_column[0] : ''; + $str .= "\t".$table_name.$local_column.' -> '.$fk['foreign_table'].$foreign_column; + if ($fk_labels) { + $str .= '[label="'.$name."\"];\n"; + } else { + $str .= ";\n"; + } + } + } + $str .= "\n"; + } + $str .= '}'; + + return $str; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/FrameworkServiceProvider.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,25 @@ +<?php + +namespace Wizard\Framework; + +use Illuminate\Support\ServiceProvider; + +class FrameworkServiceProvider extends ServiceProvider { + public function register():void + { + $this->app->bind('db.schema', fn () => \Libraries\MySchema::customizedSchemaBuilder()); + } + + public function boot(): void { + $publish_path = __DIR__.'/../publishable'; + $this->publishes([ + $publish_path.'/resources/views/components' => resource_path('views/components'), + $publish_path.'/vite.config.js' => base_path(), + ]); + + foreach (glob(base_path('routes/resources/*.php')) as $routeFile) { + $this->loadRoutesFrom($routeFile); + } + + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Http/Controllers/BaseController.php Wed Jun 18 22:28:47 2025 -0400 @@ -0,0 +1,7 @@ +<?php + +namespace Wizard\Framework\Http\Controllers; + +abstract class BaseController { + +}
--- a/src/Models/BaseModel.php Mon Jun 09 23:07:17 2025 -0400 +++ b/src/Models/BaseModel.php Wed Jun 18 22:28:47 2025 -0400 @@ -3,12 +3,15 @@ namespace Wizard\Framework\Models; use Wizard\Framework\Traits\Filterable; +use Wizard\Framework\Database\Builder; use Illuminate\Database\Eloquent\Model; class BaseModel extends Model { use Filterable; + protected static string $builder = Builder::class; + protected $default_relations = []; public static function boot(): void @@ -25,6 +28,7 @@ }); } + protected static function load_auxilary_data() { $data = []; @@ -55,6 +59,10 @@ return static::load_auxilary_data(); } + public static function named_joins() { + return []; + } + /** * Retrieve a query builder instance with default relations loaded. *
--- a/src/Traits/Filterable.php Mon Jun 09 23:07:17 2025 -0400 +++ b/src/Traits/Filterable.php Wed Jun 18 22:28:47 2025 -0400 @@ -6,19 +6,6 @@ { protected static $filters = []; - // End goal: 'column_name' 'operator' 'value' - // string: name LIKE '%test%' - // value: id = 3 - // date: - // - // - // table - // column - // operator ? based on the value? - // value - // required joins - // - public function scopeFilter($query, $validated) { $filters = static::$filters;
