feat(hosting): add Build POC button with template selector on site detail
Draft sites with a linked prospect show a "Build POC from Template" section with: - Template dropdown (generic, restaurant, construction, auto-parts, professional-services) loaded from /admin/hosting/sites/templates API - "Build POC" button that calls POST /admin/hosting/sites/poc/build - Loading state + success message with pages created count - Auto-refreshes site detail after build (status changes to POC_READY) Visible only for draft sites with a prospect_id. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Build POC (draft sites only) -->
|
||||||
|
<div x-show="site.status === 'draft' && site.prospect_id" class="p-4 bg-white rounded-lg shadow-xs dark:bg-gray-800">
|
||||||
|
<h3 class="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-3">Build POC from Template</h3>
|
||||||
|
<div class="flex flex-wrap items-end gap-3">
|
||||||
|
<div class="flex-1 min-w-[200px]">
|
||||||
|
<select x-model="selectedTemplate"
|
||||||
|
class="w-full px-3 py-2 text-sm border border-gray-300 dark:border-gray-600 rounded-lg focus:border-teal-400 focus:outline-none dark:bg-gray-700 dark:text-gray-300">
|
||||||
|
<option value="">Select a template...</option>
|
||||||
|
<template x-for="t in templates" :key="t.id">
|
||||||
|
<option :value="t.id" x-text="t.name + ' — ' + t.description"></option>
|
||||||
|
</template>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button type="button" @click="buildPoc()" :disabled="!selectedTemplate || buildingPoc"
|
||||||
|
class="inline-flex items-center px-4 py-2 text-sm font-medium leading-5 text-white transition-colors duration-150 bg-teal-600 border border-transparent rounded-lg hover:bg-teal-700 focus:outline-none disabled:opacity-50">
|
||||||
|
<span x-show="!buildingPoc" x-html="$icon('sparkles', 'w-4 h-4 mr-2')"></span>
|
||||||
|
<span x-show="buildingPoc" x-html="$icon('spinner', 'w-4 h-4 mr-2')"></span>
|
||||||
|
<span x-text="buildingPoc ? 'Building...' : 'Build POC'"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<p x-show="pocResult" class="mt-2 text-sm text-green-600" x-text="pocResult"></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Lifecycle Actions -->
|
<!-- Lifecycle Actions -->
|
||||||
<div class="flex flex-wrap gap-3 p-4 bg-white rounded-lg shadow-xs dark:bg-gray-800">
|
<div class="flex flex-wrap gap-3 p-4 bg-white rounded-lg shadow-xs dark:bg-gray-800">
|
||||||
<button type="button" x-show="site.status === 'draft'" @click="doAction('mark-poc-ready')"
|
<button type="button" x-show="site.status === 'draft'" @click="doAction('mark-poc-ready')"
|
||||||
@@ -307,7 +330,39 @@ function hostingSiteDetail(siteId) {
|
|||||||
acceptMerchantId: '',
|
acceptMerchantId: '',
|
||||||
goLiveDomain: '',
|
goLiveDomain: '',
|
||||||
newService: { service_type: 'domain', name: '', price_cents: null, billing_period: 'monthly' },
|
newService: { service_type: 'domain', name: '', price_cents: null, billing_period: 'monthly' },
|
||||||
async init() { await this.loadSite(); },
|
// POC builder
|
||||||
|
templates: [],
|
||||||
|
selectedTemplate: '',
|
||||||
|
buildingPoc: false,
|
||||||
|
pocResult: '',
|
||||||
|
async init() {
|
||||||
|
await this.loadSite();
|
||||||
|
await this.loadTemplates();
|
||||||
|
},
|
||||||
|
async loadTemplates() {
|
||||||
|
try {
|
||||||
|
var resp = await apiClient.get('/admin/hosting/sites/templates');
|
||||||
|
this.templates = resp.templates || [];
|
||||||
|
} catch (e) { /* ignore */ }
|
||||||
|
},
|
||||||
|
async buildPoc() {
|
||||||
|
if (!this.selectedTemplate || !this.site.prospect_id) return;
|
||||||
|
this.buildingPoc = true;
|
||||||
|
this.pocResult = '';
|
||||||
|
try {
|
||||||
|
var result = await apiClient.post('/admin/hosting/sites/poc/build', {
|
||||||
|
prospect_id: this.site.prospect_id,
|
||||||
|
template_id: this.selectedTemplate,
|
||||||
|
});
|
||||||
|
this.pocResult = 'POC built! ' + result.pages_created + ' pages created.';
|
||||||
|
Utils.showToast('POC built successfully', 'success');
|
||||||
|
await this.loadSite();
|
||||||
|
} catch (e) {
|
||||||
|
Utils.showToast('Build failed: ' + e.message, 'error');
|
||||||
|
} finally {
|
||||||
|
this.buildingPoc = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
async loadSite() {
|
async loadSite() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.error = null;
|
this.error = null;
|
||||||
|
|||||||
Reference in New Issue
Block a user