diff --git a/app/Models/User.php b/app/Models/User.php
index 4f597117..d8ba8414 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -355,6 +355,21 @@ class User extends Authenticatable
*/
protected static function bootSearchable(): void
{
+ // Register the SearchableScope so that the Builder::searchable() macro
+ // is available (needed for scout:import and manual ::searchable() calls).
+ // We intentionally skip static::observe(new ModelObserver) so that
+ // User saves/deletes do not auto-enqueue Scout sync jobs.
+ static::addGlobalScope(new \Laravel\Scout\SearchableScope);
+
+ $whenBootedCallback = function () {
+ (new static)->registerSearchableMacros();
+ };
+
+ if (method_exists(static::class, 'whenBooted')) {
+ static::whenBooted($whenBootedCallback);
+ } else {
+ $whenBootedCallback();
+ }
}
/**
diff --git a/resources/js/components/Studio/StudioContentBrowser.jsx b/resources/js/components/Studio/StudioContentBrowser.jsx
index 9526002e..21a42cab 100644
--- a/resources/js/components/Studio/StudioContentBrowser.jsx
+++ b/resources/js/components/Studio/StudioContentBrowser.jsx
@@ -554,7 +554,7 @@ export default function StudioContentBrowser({
{items.length > 0 ? (
viewMode === 'grid' ? (
-
+
{items.map((item) => )}
) : (
diff --git a/scripts/deploy-production.sh b/scripts/deploy-production.sh
index cf96d8bc..fefbe159 100644
--- a/scripts/deploy-production.sh
+++ b/scripts/deploy-production.sh
@@ -16,6 +16,7 @@ local_build_command="${LOCAL_BUILD_COMMAND:-}"
run_local_build=1
run_remote_migrations=1
run_db_sync=0
+run_meilisearch_setup=0
db_sync_source=""
legacy_db_sync_mode=0
force_db_sync=0
@@ -37,6 +38,7 @@ Options:
Must equal 'replace production db from local' when running non-interactively.
--with-db Legacy alias for --with-db-from=local.
--force-db-sync Legacy extra confirmation flag for --with-db.
+ --with-meilisearch Sync index settings then reimport all searchable models.
--no-maintenance Skip php artisan down/up during deploy.
--help Show this help.
@@ -157,6 +159,9 @@ while [[ $# -gt 0 ]]; do
--confirm-db-sync-phrase=*)
db_sync_confirm_phrase="${1#*=}"
;;
+ --with-meilisearch)
+ run_meilisearch_setup=1
+ ;;
--no-maintenance)
skip_maintenance=1
;;
@@ -235,6 +240,7 @@ echo "Running remote Composer and Artisan steps..."
COMPOSER_BIN="$(printf '%q' "$composer_bin")" \
RUN_REMOTE_MIGRATIONS="$run_remote_migrations" \
SKIP_MAINTENANCE="$skip_maintenance" \
+ RUN_MEILISEARCH_SETUP="$run_meilisearch_setup" \
'bash -s' <<'EOF'
set -euo pipefail
@@ -263,6 +269,18 @@ fi
"$PHP_BIN" artisan view:cache
"$PHP_BIN" artisan queue:restart || true
+if [[ "$RUN_MEILISEARCH_SETUP" -eq 1 ]]; then
+ echo "Importing searchable models into Meilisearch (auto-creates indexes)..."
+ "$PHP_BIN" artisan scout:import "App\\Models\\Artwork"
+ "$PHP_BIN" artisan scout:import "App\\Models\\User"
+ "$PHP_BIN" artisan scout:import "App\\Models\\Group"
+ "$PHP_BIN" artisan scout:import "App\\Models\\Post"
+ "$PHP_BIN" artisan scout:import "App\\Models\\Message"
+ echo "Syncing Meilisearch index settings..."
+ "$PHP_BIN" artisan scout:sync-index-settings
+ echo "Meilisearch setup complete."
+fi
+
if [[ "$SKIP_MAINTENANCE" -eq 0 ]]; then
"$PHP_BIN" artisan up
trap - EXIT
diff --git a/scripts/fix_artworks_updated_at.php b/scripts/fix_artworks_updated_at.php
new file mode 100644
index 00000000..7a93baa7
--- /dev/null
+++ b/scripts/fix_artworks_updated_at.php
@@ -0,0 +1,44 @@
+make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
+
+$options = getopt('', ['dry-run']);
+$isDryRun = array_key_exists('dry-run', $options);
+
+use Illuminate\Support\Facades\DB;
+
+// Count rows that need updating
+$total = DB::table('artworks')
+ ->whereColumn('updated_at', '!=', 'created_at')
+ ->orWhereNull('updated_at')
+ ->count();
+
+if ($total === 0) {
+ fwrite(STDOUT, "Nothing to do: all artworks already have updated_at = created_at.\n");
+ exit(0);
+}
+
+fwrite(STDOUT, sprintf("Rows to fix: %d%s\n", $total, $isDryRun ? ' (dry-run, no changes written)' : ''));
+
+if ($isDryRun) {
+ exit(0);
+}
+
+// Single bulk UPDATE — fast even on large tables, uses the existing index on created_at
+$affected = DB::statement('UPDATE artworks SET updated_at = created_at WHERE updated_at != created_at OR updated_at IS NULL');
+
+fwrite(STDOUT, sprintf("Done. updated_at synced to created_at for %d rows.\n", $total));