changeset 4:84c75d9d90be

Changing usage to be bootstrap 5, not everything is reviewed but it's been started
author luka
date Tue, 19 Aug 2025 20:33:35 -0400
parents e107504fa22c
children f282c6ef1671
files composer.json publishable/package.json publishable/resources/css/app.css publishable/resources/js/ServerTable.js publishable/resources/js/bootstrap.js publishable/resources/sass/app.scss publishable/resources/sass/badges.scss publishable/resources/sass/dashboard.scss publishable/resources/views/components/badge.blade.php publishable/resources/views/components/card.blade.php publishable/resources/views/components/dashboard-card.blade.php publishable/resources/views/components/form/checkbox.blade.php publishable/resources/views/components/form/date.blade.php publishable/resources/views/components/form/select.blade.php publishable/resources/views/components/form/text.blade.php publishable/resources/views/components/form/textarea.blade.php publishable/resources/views/components/preview-list-card.blade.php publishable/resources/views/components/primary-button.blade.php publishable/resources/views/components/responsive-nav-link.blade.php publishable/resources/views/components/secondary-button.blade.php publishable/resources/views/components/text-input.blade.php publishable/stubs/migration.create.stub publishable/stubs/migration.stub publishable/stubs/migration.update.stub publishable/vite.config.js src/Database/MarkdownParser.php src/Database/MyBlueprint.php src/Database/MySchema.php src/Database/SchemaDrawer.php src/FrameworkServiceProvider.php
diffstat 29 files changed, 527 insertions(+), 110 deletions(-) [+]
line wrap: on
line diff
--- a/composer.json	Mon Jun 23 20:20:31 2025 -0400
+++ b/composer.json	Tue Aug 19 20:33:35 2025 -0400
@@ -1,22 +1,23 @@
 {
-    "name": "wizard/framework",
-		"version": "1.0.0",
-    "description": "A reliable and repeatable base framework.",
-    "autoload": {
-        "psr-4": {
-						"Wizard\\Framework\\":"src/"
-        }
-    },
-    "minimum-stability": "stable",
-    "require": {
-        "laravel/framework": ">=12",
-        "doctrine/dbal": "*"
-    },
-		"extra": {
-    	"laravel": {
-    	    "providers": [
-						"Wizard\\Framework\\FrameworkServiceProvider"
-    	    ]
-    	}
-		}
+  "name": "wizard/framework",
+  "version": "1.0.0",
+  "description": "A reliable and repeatable base framework.",
+  "autoload": {
+    "psr-4": {
+      "Wizard\\Framework\\": "src/",
+      "Wizard\\Framework\\Components\\": "publishable/resources/views/components/"
+    }
+  },
+  "minimum-stability": "stable",
+  "require": {
+    "laravel/framework": ">=12",
+    "doctrine/dbal": "*"
+  },
+  "extra": {
+    "laravel": {
+      "providers": [
+        "Wizard\\Framework\\FrameworkServiceProvider"
+      ]
+    }
+  }
 }
--- a/publishable/package.json	Mon Jun 23 20:20:31 2025 -0400
+++ b/publishable/package.json	Tue Aug 19 20:33:35 2025 -0400
@@ -13,6 +13,7 @@
         "laravel-vite-plugin": "^1.2.0",
         "postcss": "^8.4.31",
         "prettier": "^3.5.3",
+        "sass-embedded": "^1.90.0",
         "vite": "^6.2.4"
     },
     "prettier": {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/publishable/resources/js/ServerTable.js	Tue Aug 19 20:33:35 2025 -0400
@@ -0,0 +1,247 @@
+class ServerTable {
+    /**
+     * @param {HTMLElement} rootEl - The container element for the table
+     * @param {Object} options - Configuration options
+     * @param {string} options.endpoint - API endpoint for data
+     * @param {Array} options.columns - Array of column configs: [{name, label, ...}]
+     * @param {number} [options.pageSize=10] - Default rows per page
+     * @param {Array} [options.initialSort=[]] - Default sort: [{col, dir}]
+     * @param {Object} [options.headers={}] - Additional headers for requests
+     * @param {string} [options.groupBy={}] - Which column to group by
+     * @param {Function} [options.groupRender={}] - Function to render the grouping
+     */
+    constructor(rootEl, options) {
+        this.rootEl = rootEl;
+        this.endpoint = options.endpoint;
+        this.columns = options.columns || [];
+        this.pageSize = options.pageSize || 10;
+        this.sort = options.initialSort || [];
+        this.filters = options.filters || {};
+        this.currentPage = 1;
+        this.headers = options.headers || {};
+        this.groupBy = options.groupBy;
+        this.groupRender =
+            options.groupRender ||
+            ((g, rows) =>
+                `<tr class="st-group-row"><td colspan="${this.columns.length}">Group: ${g}</td></tr>`);
+
+        this.skeleton = options.skeleton || `
+            <div class="st-table-container">
+                <table>
+                    <thead>
+                    </thead>
+                    <tbody>
+                        <!-- Data rows will go here -->
+                    </tbody>
+                </table>
+                <div class="st-controls">
+                    <span class="st-pagination"></span>
+                    <span class="st-status"></span>
+                </div>
+            </div>
+        `;
+
+        this.state = {
+            loading: false,
+            error: null,
+            totalRecords: 0,
+            records: [],
+        };
+
+        // Render initial skeleton and fetch initial data
+        this.renderSkeleton();
+        this.fetchData();
+    }
+
+    renderSkeleton() {
+        // Build base structure: table shell, controls area
+        this.rootEl.innerHTML = this.skeleton;
+        // Store references
+        this.head = this.rootEl.querySelector("thead");
+        this.tbody = this.rootEl.querySelector("tbody");
+        this.statusEl = this.rootEl.querySelector(".st-status");
+        this.paginationEl = this.rootEl.querySelector(".st-pagination");
+
+        // Draw the header
+        this.head.innerHTML = `
+                        <tr>
+                            ${this.columns.map((col) => `<th scope="col">${col.label || col.name}</th>`).join("")}
+                        </tr>
+                        `;
+    }
+
+    async fetchData() {
+        this.setLoading(true);
+        const payload = {
+            page: this.currentPage,
+            page_size: this.pageSize,
+            sort: this.sort,
+            filters: this.filters,
+        };
+
+        try {
+            const res = await fetch(this.endpoint, {
+                method: "POST",
+                headers: {
+                    ...this.headers,
+                    "Content-Type": "application/json",
+                },
+                body: JSON.stringify(payload),
+            });
+
+            if (!res.ok) {
+                throw new Error(`Server responded with ${res.status}`);
+            }
+
+            const data = await res.json();
+            // Minimal shape validation
+            if (
+                !Array.isArray(data.records) ||
+                typeof data.total_records !== "number"
+            ) {
+                throw new Error("Malformed server response");
+            }
+
+            // Save data in state
+            this.state.records = data.records;
+            this.state.totalRecords = data.total_records;
+            this.state.error = null;
+
+            this.renderRows();
+            this.updateControls();
+        } catch (err) {
+            this.state.error = err.message;
+            this.renderError();
+        } finally {
+            this.setLoading(false);
+        }
+    }
+
+    setLoading(loading) {
+        this.state.loading = loading;
+        this.statusEl.textContent = loading ? "Loading..." : "";
+    }
+
+    renderRows() {
+        const { records } = this.state;
+        const cols = this.columns;
+        const groupBy = this.groupBy;
+        const groupRender =
+            typeof this.groupRender === "function"
+                ? this.groupRender
+                : (g, rows) =>
+                      `<tr class="st-group-row"><td colspan="${cols.length}">Project: ${g}</td></tr>`;
+
+        if (!records || records.length === 0) {
+            this.tbody.innerHTML = `<tr><td colspan="${cols.length}">No data</td></tr>`;
+            return;
+        }
+
+        // Group if needed
+        if (groupBy) {
+            // Find grouping field or function
+            const getGroupValue =
+                typeof groupBy === "function" ? groupBy : (row) => row[groupBy];
+            let lastGroup = undefined;
+            let out = "";
+            let groupRows = [];
+
+            for (let i = 0; i < records.length; i++) {
+                const row = records[i];
+                const groupVal = getGroupValue(row);
+
+                // On group transition, flush previous group
+                if (i === 0 || groupVal !== lastGroup) {
+                    if (i > 0) {
+                        // Optionally do something with groupRows if groupRender wants it
+                    }
+                    // Insert group header row
+                    out += groupRender(groupVal, []);
+                    lastGroup = groupVal;
+                    groupRows = [];
+                }
+
+                groupRows.push(row);
+
+                out += `<tr>${cols
+                    .map((col, ci) => {
+                        // If grouping by this column, suppress repeated values (leave blank except for first row in group)
+                        if (
+                            typeof groupBy === "string" &&
+                            col.name === groupBy &&
+                            groupVal === lastGroup &&
+                            groupRows.length > 1
+                        ) {
+                            return `<td></td>`;
+                        }
+                        return `<td>${
+                            typeof col.render === "function"
+                                ? col.render(row, col, i)
+                                : row[col.name]
+                        }</td>`;
+                    })
+                    .join("")}</tr>`;
+            }
+            this.tbody.innerHTML = out;
+        } else {
+            // No grouping
+            this.tbody.innerHTML = records
+                .map(
+                    (row, i) =>
+                        `<tr>${cols
+                            .map(
+                                (col) =>
+                                    `<td>${
+                                        typeof col.render === "function"
+                                            ? col.render(row, col, i)
+                                            : row[col.name]
+                                    }</td>`,
+                            )
+                            .join("")}</tr>`,
+                )
+                .join("");
+        }
+    }
+    renderError() {
+        this.tbody.innerHTML = `<tr><td colspan="${this.columns.length}" style="color:red">${this.state.error}</td></tr>`;
+    }
+
+    updateControls() {
+        // Basic pagination info (full controls to come later)
+        const from = 1 + (this.currentPage - 1) * this.pageSize;
+        const to = Math.min(
+            this.currentPage * this.pageSize,
+            this.state.totalRecords,
+        );
+        this.paginationEl.textContent = `Showing ${from}-${to} of ${this.state.totalRecords}`;
+    }
+
+    // PUBLIC: force reload
+    reload() {
+        this.fetchData();
+    }
+
+    // PUBLIC: update filters, resets to page 1
+    setFilters(newFilters) {
+        this.filters = newFilters;
+        this.currentPage = 1;
+        this.fetchData();
+    }
+}
+
+// Example usage (not part of module export):
+/*
+const table = new ServerTable(document.getElementById('my-table'), {
+    endpoint: '/tickets/get_data',
+    columns: [
+        {name: 'id', label: 'ID'},
+        {name: 'subject', label: 'Subject'},
+        {name: 'project', label: 'Project'},
+        {name: 'created_at', label: 'Created At'},
+    ],
+    pageSize: 10,
+    initialSort: [{col: 'created_at', dir: 'desc'}]
+});
+*/
+
+window.ServerTable = ServerTable;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/publishable/resources/js/bootstrap.js	Tue Aug 19 20:33:35 2025 -0400
@@ -0,0 +1,4 @@
+import axios from 'axios';
+window.axios = axios;
+
+window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
--- a/publishable/resources/sass/app.scss	Mon Jun 23 20:20:31 2025 -0400
+++ b/publishable/resources/sass/app.scss	Tue Aug 19 20:33:35 2025 -0400
@@ -1,1 +1,4 @@
-@import 'bootstrap/scss/bootstrap'
+$border-radius: 0.75rem;
+@import 'bootstrap/scss/bootstrap';
+@import './dashboard';
+@import './badges';
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/publishable/resources/sass/badges.scss	Tue Aug 19 20:33:35 2025 -0400
@@ -0,0 +1,30 @@
+.badge{
+  &.yellow {
+      background-color: $yellow-100;
+      color: $yellow-700;
+  }
+
+  &.orange {
+      background-color: $orange-100;
+      color: $orange-700;
+  }
+  &.green {
+      background-color: $green-100;
+      color: $green-700;
+  }
+  
+  &.blue {
+      background-color: $blue-100;
+      color: $blue-700;
+  }
+
+  &.gray {
+      background-color: $gray-200;
+      color: $gray-700;
+  }
+
+  &.red {
+      background-color: $red-100;
+      color: $red-700;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/publishable/resources/sass/dashboard.scss	Tue Aug 19 20:33:35 2025 -0400
@@ -0,0 +1,36 @@
+.dashboard-card{
+  min-width: 170px;
+  padding: 1.5rem;
+  border-radius: var(--bs-border-radius, 12px);
+  background-color: var(--bs-white, #fff);
+  display: flex;
+  align-items: center;
+  gap: var(--bs-gap-4, 24px);
+  height: 100%;
+  box-shadow: var(--bs-box-shadow);
+
+  .icon {
+    border-radius: 99px;
+    display: inline-flex;
+    flex-wrap: wrap;
+    height: 3em;
+    width: 3em;
+    justify-content: center;
+    align-content: center;
+
+    &.blue {
+      background-color: $blue-100;
+      color: $blue-600;
+    }
+
+    &.green {
+      background-color: $teal-100;
+      color: $teal-600;
+    }
+
+    &.purple {
+      background-color: $purple-100;
+      color: $purple-600;
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/publishable/resources/views/components/badge.blade.php	Tue Aug 19 20:33:35 2025 -0400
@@ -0,0 +1,6 @@
+@props([
+    'colour' => 'gray'
+])
+<span class="badge rounded-pill {{ $colour }}">
+    {{ $slot }}
+</span>
--- a/publishable/resources/views/components/card.blade.php	Mon Jun 23 20:20:31 2025 -0400
+++ b/publishable/resources/views/components/card.blade.php	Tue Aug 19 20:33:35 2025 -0400
@@ -6,18 +6,5 @@
     '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 class="dashboard-card">
 </div>
--- a/publishable/resources/views/components/dashboard-card.blade.php	Mon Jun 23 20:20:31 2025 -0400
+++ b/publishable/resources/views/components/dashboard-card.blade.php	Tue Aug 19 20:33:35 2025 -0400
@@ -9,25 +9,25 @@
 @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';
+            'green' => 'green',
+            'blue' => 'blue',
+            'purple' => 'purple',
+            'yellow' => 'yellow',
+            'gray' => 'gray',
+        ][$color] ?? 'blue';
 @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 }}">
+<div class="dashboard-card">
+    <div class="d-flex-shrink-0">
+        <span class="inline-flex icon 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>
+        <div class="fs-4 fw-medium">{{ $value }}</div>
+        <div class="fs-5 fw-medium text-secondary">{{ $title }}</div>
         @if ($subtitle)
-            <div class="text-xs text-gray-400 mt-1">{{ $subtitle }}</div>
+            <div class="fs-6 text-secondary">{{ $subtitle }}</div>
         @endif
     </div>
 </div>
--- a/publishable/resources/views/components/form/checkbox.blade.php	Mon Jun 23 20:20:31 2025 -0400
+++ b/publishable/resources/views/components/form/checkbox.blade.php	Tue Aug 19 20:33:35 2025 -0400
@@ -1,11 +1,11 @@
 @props(['name', 'label', 'checked' => false])
 
-<div class="mb-4 flex items-center">
+<div class="mb-3 form-check">
     <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>
+        class="form-check-input">
+    <label for="{{ $name }}" class="form-check-label">{{ $label }}</label>
     @error($name)
-        <span class="text-red-600 text-sm">{{ $message }}</span>
+        <div class="text-danger small">{{ $message }}</div>
     @enderror
 </div>
--- a/publishable/resources/views/components/form/date.blade.php	Mon Jun 23 20:20:31 2025 -0400
+++ b/publishable/resources/views/components/form/date.blade.php	Tue Aug 19 20:33:35 2025 -0400
@@ -1,11 +1,10 @@
 @props(['name', 'label', 'value' => '', 'required' => false])
 
-<div class="mb-4">
-    <label for="{{ $name }}" class="block text-gray-700">{{ $label }}</label>
+<div class="mb-3">
+    <label for="{{ $name }}" class="form-label">{{ $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">
+        @if ($required) required @endif class="form-control">
     @error($name)
-        <span class="text-red-600 text-sm">{{ $message }}</span>
+        <div class="text-danger small">{{ $message }}</div>
     @enderror
 </div>
--- a/publishable/resources/views/components/form/select.blade.php	Mon Jun 23 20:20:31 2025 -0400
+++ b/publishable/resources/views/components/form/select.blade.php	Tue Aug 19 20:33:35 2025 -0400
@@ -1,8 +1,8 @@
 @props(['name', 'label', 'options' => [], 'value' => '', 'required' => false])
-<div class="mb-4">
-    <label for="{{ $name }}" class="block text-gray-700">{{ $label }}</label>
+<div class="mb-3">
+    <label for="{{ $name }}" class="form-label">{{ $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">
+        class="form-select">
         <option value="">Select...</option>
         @foreach ($options as $optionValue => $optionLabel)
             <option value="{{ $optionValue }}" {{ old($name, $value) == $optionValue ? 'selected' : '' }}>
@@ -11,6 +11,6 @@
         @endforeach
     </select>
     @error($name)
-        <span class="text-red-600 text-sm">{{ $message }}</span>
+        <div class="text-danger small">{{ $message }}</div>
     @enderror
 </div>
--- a/publishable/resources/views/components/form/text.blade.php	Mon Jun 23 20:20:31 2025 -0400
+++ b/publishable/resources/views/components/form/text.blade.php	Tue Aug 19 20:33:35 2025 -0400
@@ -1,11 +1,11 @@
 @props(['name', 'label', 'value' => '', 'required' => false])
 
-<div class="mb-4">
-    <label for="{{ $name }}" class="block text-gray-700">{{ $label }}</label>
+<div class="mb-3">
+    <label for="{{ $name }}" class="form-label">{{ $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">
+        class="form-control">
     @error($name)
-        <span class="text-red-600 text-sm">{{ $message }}</span>
+        <div class="text-danger small">{{ $message }}</div>
     @enderror
 </div>
--- a/publishable/resources/views/components/form/textarea.blade.php	Mon Jun 23 20:20:31 2025 -0400
+++ b/publishable/resources/views/components/form/textarea.blade.php	Tue Aug 19 20:33:35 2025 -0400
@@ -1,10 +1,10 @@
 @props(['name', 'label', 'value' => '', 'required' => false])
 
-<div class="mb-4">
-    <label for="{{ $name }}" class="block text-gray-700 font-medium mb-1">
+<div class="mb-3">
+    <label for="{{ $name }}" class="form-label">
         {{ $label }}
         @if($required)
-            <span class="text-red-500">*</span>
+            <span class="text-danger">*</span>
         @endif
     </label>
 
@@ -12,11 +12,11 @@
         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"
+        class="markdown-editor form-control"
     >{{ old($name, $value) }}</textarea>
 
     @error($name)
-        <span class="text-red-600 text-sm">{{ $message }}</span>
+        <div class="text-danger small">{{ $message }}</div>
     @enderror
 </div>
 @include('includes.easymde')
--- a/publishable/resources/views/components/preview-list-card.blade.php	Mon Jun 23 20:20:31 2025 -0400
+++ b/publishable/resources/views/components/preview-list-card.blade.php	Tue Aug 19 20:33:35 2025 -0400
@@ -9,14 +9,29 @@
     '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>
+{{-- <x-card :icon="$icon" :title="$title" :headerButton="$headerButton" :footer="$footer"> --}}
+<div class="dashboard-card flex-column align-items-stretch">
+    <div class="d-flex justify-content-between">
+        <h4 class=" items-center align-content-start">
+            {!! $icon ?? '' !!}
+            <span class="ml-2">{{ $title }}</span>
+        </h4>
+        {!! $headerButton !!}
+    </div>
+    <div class="card-body">
+        @if ($items->count())
+            <ul class="px-1">
+                @foreach ($items as $item)
+                    @include($itemView, array_merge(['item' => $item], $viewData ?? []))
+                @endforeach
+            </ul>
+        @else
+            <div class="text-gray-400 text-sm">{{ $empty }}</div>
+        @endif
+    </div>
+    @if ($footer)
+        <div class="text-right mt-3">
+            {!! $footer !!}
+        </div>
     @endif
-</x-card>
+</div>
--- a/publishable/resources/views/components/primary-button.blade.php	Mon Jun 23 20:20:31 2025 -0400
+++ b/publishable/resources/views/components/primary-button.blade.php	Tue Aug 19 20:33:35 2025 -0400
@@ -1,4 +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']) }}>
+<a
+{{ $attributes->merge(['type' => 'submit', 'class' => 'btn btn-primary', 'href' => '#' ]) }}>
     {{ $slot }}
-</button>
+</a>
--- a/publishable/resources/views/components/responsive-nav-link.blade.php	Mon Jun 23 20:20:31 2025 -0400
+++ b/publishable/resources/views/components/responsive-nav-link.blade.php	Tue Aug 19 20:33:35 2025 -0400
@@ -3,8 +3,8 @@
 @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';
+            ? 'd-block w-100 ps-3 pe-4 py-2 border-start border-4 border-primary text-start text-base fw-medium text-primary bg-primary bg-opacity-10 focus-outline-none focus-text-primary focus-bg-primary focus-bg-opacity-25 focus-border-primary transition duration-150 ease-in-out'
+            : 'd-block w-100 ps-3 pe-4 py-2 border-start border-4 border-transparent text-start text-base fw-medium text-secondary hover-text-dark hover-bg-light hover-border-secondary focus-outline-none focus-text-dark focus-bg-light focus-border-secondary transition duration-150 ease-in-out';
 @endphp
 
 <a {{ $attributes->merge(['class' => $classes]) }}>
--- a/publishable/resources/views/components/secondary-button.blade.php	Mon Jun 23 20:20:31 2025 -0400
+++ b/publishable/resources/views/components/secondary-button.blade.php	Tue Aug 19 20:33:35 2025 -0400
@@ -1,4 +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']) }}>
+<a
+        {{ $attributes->merge(['type' => 'button', 'class' => 'btn btn-secondary', 'href' => '#']) }}>
     {{ $slot }}
-</button>
+</a>
--- a/publishable/resources/views/components/text-input.blade.php	Mon Jun 23 20:20:31 2025 -0400
+++ b/publishable/resources/views/components/text-input.blade.php	Tue Aug 19 20:33:35 2025 -0400
@@ -1,4 +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']) }}>
+    {{ $attributes->merge(['class' => 'form-control']) }}>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/publishable/stubs/migration.create.stub	Tue Aug 19 20:33:35 2025 -0400
@@ -0,0 +1,26 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Wizard\Framework\Database\MyBlueprint as Blueprint;
+use Wizard\Framework\Database\MySchema as Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::create('{{ table }}', function (Blueprint $table) {
+            $table->baseColumns();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::dropIfExists('{{ table }}');
+    }
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/publishable/stubs/migration.stub	Tue Aug 19 20:33:35 2025 -0400
@@ -0,0 +1,24 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Wizard\Framework\Database\MyBlueprint as Blueprint;
+use Wizard\Framework\Database\MySchema as Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        //
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        //
+    }
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/publishable/stubs/migration.update.stub	Tue Aug 19 20:33:35 2025 -0400
@@ -0,0 +1,28 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Wizard\Framework\Database\MyBlueprint as Blueprint;
+use Wizard\Framework\Database\MySchema as Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::table('{{ table }}', function (Blueprint $table) {
+            //
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::table('{{ table }}', function (Blueprint $table) {
+            //
+        });
+    }
+};
--- a/publishable/vite.config.js	Mon Jun 23 20:20:31 2025 -0400
+++ b/publishable/vite.config.js	Tue Aug 19 20:33:35 2025 -0400
@@ -5,9 +5,13 @@
 import path from "path";
 
 const pagesDir = "resources/js/pages";
-const pageScripts = fs
-    .readdirSync(pagesDir)
-    .map((file) => path.join(pagesDir, file));
+let pageScripts = [];
+
+if (fs.existsSync(pagesDir)) {
+    pageScripts = fs
+        .readdirSync(pagesDir)
+        .map((file) => path.join(pagesDir, file));
+}
 
 export default defineConfig({
  // Optional: Silence Sass deprecation warnings. See note below.
@@ -30,6 +34,7 @@
                 "resources/js/app.js",
                 "resources/js/easymde.js",
                 "resources/js/prismjs.js",
+                "resources/sass/app.scss",
                 ...pageScripts,
             ],
             refresh: true,
--- a/src/Database/MarkdownParser.php	Mon Jun 23 20:20:31 2025 -0400
+++ b/src/Database/MarkdownParser.php	Tue Aug 19 20:33:35 2025 -0400
@@ -1,6 +1,6 @@
 <?php
 
-namespace Libraries;
+namespace Wizard\Framework\Database;
 
 class MarkdownParser
 {
--- a/src/Database/MyBlueprint.php	Mon Jun 23 20:20:31 2025 -0400
+++ b/src/Database/MyBlueprint.php	Tue Aug 19 20:33:35 2025 -0400
@@ -1,6 +1,6 @@
 <?php
 
-namespace Libraries;
+namespace Wizard\Framework\Database;
 
 use Illuminate\Database\Schema\ColumnDefinition;
 
--- a/src/Database/MySchema.php	Mon Jun 23 20:20:31 2025 -0400
+++ b/src/Database/MySchema.php	Tue Aug 19 20:33:35 2025 -0400
@@ -1,6 +1,6 @@
 <?php
 
-namespace Libraries;
+namespace Wizard\Framework\Database;
 
 use Illuminate\Database\Schema\Builder;
 use Illuminate\Support\Facades\Schema as BaseSchema;
--- a/src/Database/SchemaDrawer.php	Mon Jun 23 20:20:31 2025 -0400
+++ b/src/Database/SchemaDrawer.php	Tue Aug 19 20:33:35 2025 -0400
@@ -1,6 +1,6 @@
 <?php
 
-namespace Libraries;
+namespace Wizard\Framework\Database;
 
 use Illuminate\Support\Facades\Schema;
 
--- a/src/FrameworkServiceProvider.php	Mon Jun 23 20:20:31 2025 -0400
+++ b/src/FrameworkServiceProvider.php	Tue Aug 19 20:33:35 2025 -0400
@@ -3,24 +3,29 @@
 namespace Wizard\Framework;
 
 use Illuminate\Support\ServiceProvider;
+use Illuminate\Support\Facades\Blade;
+use \Wizard\Framework\Database\MySchema;
 
-class FrameworkServiceProvider extends ServiceProvider {
-	public function register():void
- 	{
-		$this->app->bind('db.schema', fn () => \Libraries\MySchema::customizedSchemaBuilder()); 
-	}
+class FrameworkServiceProvider extends ServiceProvider
+{
+    public function register(): void
+    {
+        $this->app->bind('db.schema', fn () => MySchema::customizedSchemaBuilder());
+    }
 
-	public function boot(): void {
-		$publish_path = __DIR__.'/../publishable';
-		$this->publishes([
-			$publish_path.'/resources' => resource_path(),
-			$publish_path.'/vite.config.js' => base_path('vite.config.js'),
-			$publish_path.'/package.json' => base_path('package.json'),
-		]);
+    public function boot(): void
+    {
+        $publish_path = __DIR__.'/../publishable';
+        $this->publishes([
+            $publish_path.'/resources' => resource_path(),
+            $publish_path.'/vite.config.js' => base_path('vite.config.js'),
+            $publish_path.'/package.json' => base_path('package.json'),
+            $publish_path.'/stubs' => base_path('stubs'),
+        ]);
 
-		foreach (glob(base_path('routes/resources/*.php')) as $routeFile) {
+        foreach (glob(base_path('routes/resources/*.php')) as $routeFile) {
             $this->loadRoutesFrom($routeFile);
-     }
+        }
 
-	}
+    }
 }