feat(database): integrate wizard into settings page, improve accessibility and test coverage

- Add "DB Wizard" link button to settings page header
- Add help_link to database_url SETTING_METADATA pointing to /database-wizard
- Add help_link rendering in settings template for any setting with a help_link
- Fix SQLite whitespace path handling in build_connection_string
- Add dark mode CSS overrides for wizard template
- Add aria-describedby for all form inputs with help text
- Add prefers-reduced-motion media query for smooth scrolling
- Expand test coverage: 106 tests (up from 49)
  - db_wizard.py: 100% coverage
  - db_wizard view: 100% coverage
  - database.py API: 97.37% coverage
  - db_migrate.py: 96.60% coverage

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-03-06 10:02:04 +00:00
parent 330c3aedb6
commit 174e4890dd
7 changed files with 629 additions and 12 deletions
+21 -9
View File
@@ -13,6 +13,14 @@
.migration-table th, .migration-table td { padding: 0.5rem 1rem; text-align: left; }
@keyframes pulse-bar { 0%,100%{opacity:1} 50%{opacity:.5} }
.animate-pulse-bar { animation: pulse-bar 1.5s ease-in-out infinite; }
/* Dark mode overrides for db-wizard specific styles */
html.dark .from-blue-50 { --tw-gradient-from: #1e3a5f; }
html.dark .to-indigo-100 { --tw-gradient-to: #1e1b4b; }
html.dark .db-card { background-color: #1f2937; border-color: #374151; }
html.dark .db-card:hover { border-color: #818cf8; }
html.dark .db-card.selected { border-color: #818cf8; box-shadow: 0 0 0 3px rgba(129,140,248,0.3); }
/* Ensure smooth scroll only when user permits */
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }
</style>
{% endblock %}
@@ -124,8 +132,9 @@
<label for="sqlite_path" class="block text-sm font-medium text-gray-700">Database File Path</label>
<input id="sqlite_path" x-model="form.sqlite_path" type="text"
class="mono-input w-full px-4 py-3 border border-gray-300 rounded-md shadow-sm focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
placeholder="./app/database.db" />
<p class="text-xs text-gray-500">Leave blank to use the default path <code>./app/database.db</code>.</p>
placeholder="./app/database.db"
aria-describedby="sqlite_path_help" />
<p id="sqlite_path_help" class="text-xs text-gray-500">Leave blank to use the default path <code>./app/database.db</code>.</p>
</div>
</template>
@@ -169,13 +178,14 @@
<div x-show="form.backend==='postgresql'">
<label for="ssl_mode" class="block text-sm font-medium text-gray-700">SSL Mode</label>
<select id="ssl_mode" x-model="form.ssl_mode"
class="w-full px-4 py-3 border border-gray-300 rounded-md focus:ring-2 focus:ring-indigo-500">
class="w-full px-4 py-3 border border-gray-300 rounded-md focus:ring-2 focus:ring-indigo-500"
aria-describedby="ssl_mode_help">
<option value="">None (disable)</option>
<option value="require">require</option>
<option value="verify-ca">verify-ca</option>
<option value="verify-full">verify-full</option>
</select>
<p class="text-xs text-gray-500 mt-1">Use <code>require</code> for managed cloud databases (AWS RDS, Supabase, etc.).</p>
<p id="ssl_mode_help" class="text-xs text-gray-500 mt-1">Use <code>require</code> for managed cloud databases (AWS RDS, Supabase, etc.).</p>
</div>
</div>
</template>
@@ -238,7 +248,7 @@
</div>
<div class="p-4 border rounded-lg bg-gray-50">
<label for="env_snippet" class="block text-sm font-medium text-gray-700 mb-2">
<label for="env_snippet" id="env_snippet_label" class="block text-sm font-medium text-gray-700 mb-2">
Copy this into your <code>.env</code> file:
</label>
<div class="flex items-center">
@@ -297,8 +307,9 @@
</label>
<input id="mig_source" x-model="migrate.source" type="text"
class="mono-input w-full px-4 py-3 border border-gray-300 rounded-md focus:ring-2 focus:ring-indigo-500"
:placeholder="currentDbUrl || 'sqlite:///./app/database.db'" />
<p class="text-xs text-gray-500 mt-1">
:placeholder="currentDbUrl || 'sqlite:///./app/database.db'"
aria-describedby="mig_source_help" />
<p id="mig_source_help" class="text-xs text-gray-500 mt-1">
This is your current database. Pre-filled with the running configuration.
</p>
<button @click="migrate.source = currentDbUrl"
@@ -315,8 +326,9 @@
</label>
<input id="mig_target" x-model="migrate.target" type="text"
class="mono-input w-full px-4 py-3 border border-gray-300 rounded-md focus:ring-2 focus:ring-indigo-500"
placeholder="postgresql://user:pass@host:5432/docuelevate" />
<p class="text-xs text-gray-500 mt-1">
placeholder="postgresql://user:pass@host:5432/docuelevate"
aria-describedby="mig_target_help" />
<p id="mig_target_help" class="text-xs text-gray-500 mt-1">
The new database to copy data into. Must be empty (schema will be created automatically).
</p>
<button @click="if(builtUrl) migrate.target = builtUrl"
+12
View File
@@ -60,6 +60,10 @@
class="inline-flex items-center px-3 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50">
<i class="fas fa-magic mr-1.5" aria-hidden="true"></i> Wizard
</a>
<a href="/database-wizard"
class="inline-flex items-center px-3 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50">
<i class="fas fa-database mr-1.5" aria-hidden="true"></i> DB Wizard
</a>
<div class="relative" x-data="{ exportOpen: false }">
<button @click="exportOpen = !exportOpen"
class="inline-flex items-center px-3 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50">
@@ -236,6 +240,14 @@
</div>
<p class="text-xs text-gray-500 mb-2">{{ setting.metadata.description }}</p>
{% if setting.metadata.get('help_link') %}
<p class="text-xs mb-2">
<a href="{{ setting.metadata.help_link }}"
class="text-indigo-600 hover:underline focus:outline-none focus:ring-2 focus:ring-indigo-500 rounded">
<i class="fas fa-external-link-alt mr-1" aria-hidden="true"></i>{{ setting.metadata.get('help_link_label', 'More info') }}
</a>
</p>
{% endif %}
{% if setting.metadata.type == 'boolean' %}
<!-- Boolean/Checkbox Input -->